From e349a41b163d0341e75ae10857ed9326c92741fb Mon Sep 17 00:00:00 2001 From: andy Date: Wed, 6 Jul 2022 22:41:30 +0000 Subject: [PATCH 01/60] room tab info page 1 --- src/ship/map/area.rs | 55 ++++++++++++++++++++++++++++++++ src/ship/packet/handler/lobby.rs | 26 +++++++++++++-- src/ship/ship.rs | 8 +++-- 3 files changed, 84 insertions(+), 5 deletions(-) diff --git a/src/ship/map/area.rs b/src/ship/map/area.rs index 33b51aa..3eaa06b 100644 --- a/src/ship/map/area.rs +++ b/src/ship/map/area.rs @@ -3,6 +3,7 @@ use serde::{Serialize, Deserialize}; use std::collections::HashMap; use thiserror::Error; use crate::ship::room::Episode; +use std::fmt; #[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)] pub enum MapArea { @@ -256,6 +257,60 @@ impl MapArea { 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()) + } } diff --git a/src/ship/packet/handler/lobby.rs b/src/ship/packet/handler/lobby.rs index 37f18da..ae9edee 100644 --- a/src/ship/packet/handler/lobby.rs +++ b/src/ship/packet/handler/lobby.rs @@ -3,11 +3,11 @@ use crate::common::serverstate::ClientId; use crate::common::leveltable::CharacterLevelTable; use crate::ship::ship::{SendShipPacket, ShipError, Clients, Rooms}; 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::items::ItemManager; use crate::entity::gateway::EntityGateway; +use crate::ship::map::MapArea; // this function needs a better home pub fn block_selected(id: ClientId, @@ -130,3 +130,25 @@ pub fn remove_from_lobby(id: ClientId, (n.client, leave_lobby_pkt.clone()) }).collect()) } + +pub fn get_room_tab_info(id: ClientId, + pkt: &MenuDetail, + client_location: &mut ClientLocation, + clients: &Clients) + -> Result, anyhow::Error> { + let room_id = RoomId(pkt.item as usize); + let clients_in_room = client_location.get_clients_in_room(room_id).map_err(|err| -> ClientLocationError { err.into() })?; + let mut room_info = String::new(); + 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 cls = cs.character.char_class; + let leveltable = CharacterLevelTable::default(); + let lv = leveltable.get_level_from_exp(cls, cs.character.exp); + let floor = cs.area.unwrap_or(MapArea::Pioneer2Ep1); + + room_info += format!("{} Lv{} {}\n{} {}\n", gc,lv,name,cls,floor).as_str(); + } + Ok(vec![(id, SendShipPacket::SmallLeftDialog(SmallLeftDialog::new(room_info)))]) +} \ No newline at end of file diff --git a/src/ship/ship.rs b/src/ship/ship.rs index 4cf5e2a..304eac3 100644 --- a/src/ship/ship.rs +++ b/src/ship/ship.rs @@ -178,6 +178,7 @@ pub enum SendShipPacket { DirectMessage(DirectMessage), PlayerChat(PlayerChat), SmallDialog(SmallDialog), + SmallLeftDialog(SmallLeftDialog), JoinRoom(JoinRoom), AddToRoom(AddToRoom), LeaveLobby(LeaveLobby), @@ -219,6 +220,7 @@ impl SendServerPacket for SendShipPacket { SendShipPacket::DirectMessage(pkt) => pkt.as_bytes(), SendShipPacket::PlayerChat(pkt) => pkt.as_bytes(), SendShipPacket::SmallDialog(pkt) => pkt.as_bytes(), + SendShipPacket::SmallLeftDialog(pkt) => pkt.as_bytes(), SendShipPacket::JoinRoom(pkt) => pkt.as_bytes(), SendShipPacket::AddToRoom(pkt) => pkt.as_bytes(), SendShipPacket::LeaveLobby(pkt) => pkt.as_bytes(), @@ -636,9 +638,9 @@ impl ServerState for ShipServerState { 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)? }, - 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, &mut self.clients)?.into_iter()) }, RecvShipPacket::RoomPasswordReq(room_password_req) => { let block = self.blocks.with_client(id, &self.clients)?; -- 2.36.0 From 4202ef8ff71c6ebbdae43963a34aa43128f140de Mon Sep 17 00:00:00 2001 From: andy Date: Thu, 7 Jul 2022 00:00:04 +0000 Subject: [PATCH 02/60] handle rooms that close after downloading the room list in the lobby --- src/ship/packet/handler/lobby.rs | 31 +++++---- src/ship/packet/handler/room.rs | 110 ++++++++++++++++--------------- src/ship/ship.rs | 2 +- 3 files changed, 76 insertions(+), 67 deletions(-) diff --git a/src/ship/packet/handler/lobby.rs b/src/ship/packet/handler/lobby.rs index ae9edee..ff2a0c2 100644 --- a/src/ship/packet/handler/lobby.rs +++ b/src/ship/packet/handler/lobby.rs @@ -134,21 +134,26 @@ pub fn remove_from_lobby(id: ClientId, pub fn get_room_tab_info(id: ClientId, pkt: &MenuDetail, client_location: &mut ClientLocation, - clients: &Clients) + clients: &Clients, + rooms: &mut Rooms) -> Result, anyhow::Error> { let room_id = RoomId(pkt.item as usize); - let clients_in_room = client_location.get_clients_in_room(room_id).map_err(|err| -> ClientLocationError { err.into() })?; - let mut room_info = String::new(); - 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 cls = cs.character.char_class; - let leveltable = CharacterLevelTable::default(); - let lv = leveltable.get_level_from_exp(cls, cs.character.exp); - let floor = cs.area.unwrap_or(MapArea::Pioneer2Ep1); + 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(); + 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,cls,floor).as_str(); + 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())))]) } - Ok(vec![(id, SendShipPacket::SmallLeftDialog(SmallLeftDialog::new(room_info)))]) } \ No newline at end of file diff --git a/src/ship/packet/handler/room.rs b/src/ship/packet/handler/room.rs index ba82b2b..9e89469 100644 --- a/src/ship/packet/handler/room.rs +++ b/src/ship/packet/handler/room.rs @@ -86,62 +86,66 @@ pub fn join_room(id: ClientId, -> Result + Send>, ShipError> { 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 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_manager.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_manager, level_table, room_id)?; - - let room = rooms.get_mut(room_id.0).unwrap().as_mut().unwrap(); - room.bursting = true; - - let mut result: Box + 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_manager.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_manager, level_table, room_id)?; + + let room = rooms.get_mut(room_id.0).unwrap().as_mut().unwrap(); + room.bursting = true; + + let mut result: Box + 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, diff --git a/src/ship/ship.rs b/src/ship/ship.rs index 304eac3..d375520 100644 --- a/src/ship/ship.rs +++ b/src/ship/ship.rs @@ -640,7 +640,7 @@ impl ServerState for ShipServerState { }, 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, &mut self.clients)?.into_iter()) + Box::new(handler::lobby::get_room_tab_info(id, menudetail, &mut block.client_location, &mut self.clients, &mut block.rooms)?.into_iter()) }, RecvShipPacket::RoomPasswordReq(room_password_req) => { let block = self.blocks.with_client(id, &self.clients)?; -- 2.36.0 From 74e54935ad4f9d74a87d5b6fc0a1ed798c67b970 Mon Sep 17 00:00:00 2001 From: andy Date: Thu, 7 Jul 2022 00:13:33 +0000 Subject: [PATCH 03/60] thanks clippy you are the best --- src/ship/ship.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ship/ship.rs b/src/ship/ship.rs index d375520..11c5fb9 100644 --- a/src/ship/ship.rs +++ b/src/ship/ship.rs @@ -640,7 +640,7 @@ impl ServerState for ShipServerState { }, 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, &mut self.clients, &mut block.rooms)?.into_iter()) + 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) => { let block = self.blocks.with_client(id, &self.clients)?; -- 2.36.0 From 963818126163ed79e85327042f24c328c357d458 Mon Sep 17 00:00:00 2001 From: andy Date: Mon, 11 Jul 2022 03:48:21 +0000 Subject: [PATCH 04/60] 'u up?' tests for rooms --- tests/test_rooms.rs | 69 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 68 insertions(+), 1 deletion(-) diff --git a/tests/test_rooms.rs b/tests/test_rooms.rs index f435c45..c42226e 100644 --- a/tests/test_rooms.rs +++ b/tests/test_rooms.rs @@ -156,4 +156,71 @@ async fn test_set_invalid_quest_group() { }, _ => panic!("Wrong quest category"), } -} \ No newline at end of file +} + +#[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::>(); + assert!(matches!(&packets[0], (ClientId(2), SendShipPacket::SmallLeftDialog(SmallLeftDialog{ + padding: [17664, 1157645568], + msg: _expectedmsg, // new characters have a blank name by default so it's missing here + })))); +} + +#[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::>(); + 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::>(); + 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. + })))); +} -- 2.36.0 From 94171053593a324af9290e644a9a8cdc9331baa9 Mon Sep 17 00:00:00 2001 From: andy Date: Mon, 11 Jul 2022 20:28:03 +0000 Subject: [PATCH 05/60] giving this character a name to prove i am a wise and omnipotent diety --- tests/test_rooms.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_rooms.rs b/tests/test_rooms.rs index c42226e..96311fe 100644 --- a/tests/test_rooms.rs +++ b/tests/test_rooms.rs @@ -177,7 +177,7 @@ async fn test_get_room_info() { let packets = ship.handle(ClientId(2), &RecvShipPacket::MenuDetail(MenuDetail{menu: 3, item: 0})).await.unwrap().collect::>(); assert!(matches!(&packets[0], (ClientId(2), SendShipPacket::SmallLeftDialog(SmallLeftDialog{ padding: [17664, 1157645568], - msg: _expectedmsg, // new characters have a blank name by default so it's missing here + msg: _expectedmsg, })))); } -- 2.36.0 From 4e1c6300d4f2869a2c26ed92a21a17f6f3e2291f Mon Sep 17 00:00:00 2001 From: jake Date: Tue, 19 Apr 2022 23:08:18 -0600 Subject: [PATCH 06/60] update sqlx --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 582634f..3f17eea 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,7 +28,7 @@ async-trait = "0.1.51" lazy_static = "1.4.0" barrel = { version = "0.6.5", features = ["pg"] } refinery = { version = "0.5.0", features = ["postgres"] } -sqlx = { version = "0.4.0", features = ["runtime-async-std-native-tls", "postgres", "json", "chrono"] } +sqlx = { version = "0.5.10", features = ["runtime-async-std-native-tls", "postgres", "json", "chrono"] } strum = "0.19.5" strum_macros = "0.19" anyhow = { version = "1.0.47", features = ["backtrace"] } -- 2.36.0 From a2e78014ee463ed53b381e7a2d85283f934ae23a Mon Sep 17 00:00:00 2001 From: jake Date: Tue, 19 Apr 2022 23:17:20 -0600 Subject: [PATCH 07/60] make all entitygateway functions take &mut self, move postgres functions to separate function for future use --- Cargo.lock | 321 +++----- src/entity/gateway/entitygateway.rs | 17 +- src/entity/gateway/inmemory.rs | 10 +- src/entity/gateway/postgres/postgres.rs | 970 +++++++++++++----------- src/login/character.rs | 2 +- src/login/login.rs | 7 +- 6 files changed, 657 insertions(+), 670 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5b56ad3..f134f13 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -28,15 +28,9 @@ dependencies = [ [[package]] name = "ahash" -version = "0.4.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "739f4a8db6605981345c5654f3a85b056ce52f37a39d34da03f25bf2151ea16e" - -[[package]] -name = "ahash" -version = "0.6.3" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "796540673305a66d127804eef19ad696f1f204b8c1025aaca4958c17eab32877" +checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" dependencies = [ "getrandom 0.2.6", "once_cell", @@ -309,15 +303,6 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" -[[package]] -name = "block-buffer" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" -dependencies = [ - "generic-array", -] - [[package]] name = "block-buffer" version = "0.10.2" @@ -370,12 +355,6 @@ version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" -[[package]] -name = "bytes" -version = "0.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e4cec68f03f32e44924783795810fa50a7035d8c8ebe78580ad7e6c703fba38" - [[package]] name = "bytes" version = "1.1.0" @@ -388,28 +367,6 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c1db59621ec70f09c5e9b597b220c7a2b43611f4710dc03ceb8748637775692c" -[[package]] -name = "cargo-platform" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbdb825da8a5df079a43676dbe042702f1707b1109f713a01420fbb4cc71fa27" -dependencies = [ - "serde", -] - -[[package]] -name = "cargo_metadata" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7714a157da7991e23d90686b9524b9e12e0407a108647f52e9328f4b3d51ac7f" -dependencies = [ - "cargo-platform", - "semver 0.11.0", - "semver-parser", - "serde", - "serde_json", -] - [[package]] name = "cc" version = "1.0.73" @@ -514,15 +471,20 @@ dependencies = [ ] [[package]] -name = "crossbeam-channel" -version = "0.5.4" +name = "crc" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aaa7bd5fb665c6864b5f963dd9097905c54125909c7aa94c9e18507cdbe6c53" +checksum = "49fc9a695bca7f35f5f4c15cddc84415f66a74ea78eef08e90c5024f2b540e23" dependencies = [ - "cfg-if", - "crossbeam-utils", + "crc-catalog", ] +[[package]] +name = "crc-catalog" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccaeedb56da03b09f598226e25e80088cb4cd25f316e6e4df7d695f0feeb1403" + [[package]] name = "crossbeam-queue" version = "0.3.5" @@ -553,16 +515,6 @@ dependencies = [ "typenum", ] -[[package]] -name = "crypto-mac" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bff07008ec701e8028e2ceb8f83f0e4274ee62bd2dbdc4fefff2e9a91824081a" -dependencies = [ - "generic-array", - "subtle", -] - [[package]] name = "ctor" version = "0.1.22" @@ -588,22 +540,33 @@ dependencies = [ [[package]] name = "digest" -version = "0.9.0" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +checksum = "f2fb860ca6fafa5552fb6d0e816a69c8e49f0908bf524e30a90d97c85892d506" dependencies = [ - "generic-array", + "block-buffer", + "crypto-common", + "subtle", ] [[package]] -name = "digest" -version = "0.10.3" +name = "dirs" +version = "4.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2fb860ca6fafa5552fb6d0e816a69c8e49f0908bf524e30a90d97c85892d506" +checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059" dependencies = [ - "block-buffer 0.10.2", - "crypto-common", - "subtle", + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" +dependencies = [ + "libc", + "redox_users", + "winapi", ] [[package]] @@ -630,7 +593,7 @@ dependencies = [ "bcrypt", "byteorder", "chrono", - "crc", + "crc 1.8.1", "derive_more", "enum-utils", "fern", @@ -789,6 +752,17 @@ dependencies = [ "futures-util", ] +[[package]] +name = "futures-intrusive" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62007592ac46aa7c2b6416f7deb9a8a8f63a01e0f1d6e1787d5630170db2b63e" +dependencies = [ + "futures-core", + "lock_api", + "parking_lot", +] + [[package]] name = "futures-io" version = "0.3.21" @@ -901,28 +875,22 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "hashbrown" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04" -dependencies = [ - "ahash 0.4.7", -] - [[package]] name = "hashbrown" version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" +dependencies = [ + "ahash", +] [[package]] name = "hashlink" -version = "0.6.0" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d99cf782f0dc4372d26846bec3de7804ceb5df083c2d4462c0b8d2330e894fa8" +checksum = "7249a3129cbc1ffccd74857f81464a323a152173cdb134e0fd81bc803b29facf" dependencies = [ - "hashbrown 0.9.1", + "hashbrown", ] [[package]] @@ -934,6 +902,15 @@ dependencies = [ "unicode-segmentation", ] +[[package]] +name = "heck" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" +dependencies = [ + "unicode-segmentation", +] + [[package]] name = "hermit-abi" version = "0.1.19" @@ -950,13 +927,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" [[package]] -name = "hmac" -version = "0.10.1" +name = "hkdf" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1441c6b1e930e2817404b5046f1f989899143a12bf92de603b69f4e0aee1e15" +checksum = "791a029f6b9fc27657f6f188ec6e5e43f6911f6f878e0dc5501396e09809d437" dependencies = [ - "crypto-mac", - "digest 0.9.0", + "hmac", ] [[package]] @@ -965,7 +941,7 @@ version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" dependencies = [ - "digest 0.10.3", + "digest", ] [[package]] @@ -986,7 +962,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0f647032dfaa1f8b6dc29bd3edb7bbef4861b8b8007ebb118d6db284fd59f6ee" dependencies = [ "autocfg 1.1.0", - "hashbrown 0.11.2", + "hashbrown", ] [[package]] @@ -1007,12 +983,6 @@ dependencies = [ "either", ] -[[package]] -name = "itoa" -version = "0.4.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" - [[package]] name = "itoa" version = "1.0.1" @@ -1091,24 +1061,13 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" -[[package]] -name = "md-5" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b5a279bb9607f9f53c22d496eade00d138d1bdcccd07d74650387cf94942a15" -dependencies = [ - "block-buffer 0.9.0", - "digest 0.9.0", - "opaque-debug", -] - [[package]] name = "md-5" version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "658646b21e0b72f7866c7038ab086d3d5e1cd6271f060fd37defb241949d0582" dependencies = [ - "digest 0.10.3", + "digest", ] [[package]] @@ -1307,19 +1266,16 @@ dependencies = [ ] [[package]] -name = "percent-encoding" -version = "2.1.0" +name = "paste" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" +checksum = "0c520e05135d6e763148b6426a837e239041653ba7becd2e538c076c738025fc" [[package]] -name = "pest" -version = "2.1.3" +name = "percent-encoding" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53" -dependencies = [ - "ucd-trie", -] +checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" [[package]] name = "phf" @@ -1376,7 +1332,7 @@ version = "0.19.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eb76d6535496f633fa799bb872ffb4790e9cbdedda9d35564ca0252f930c0dd5" dependencies = [ - "bytes 1.1.0", + "bytes", "fallible-iterator", "futures", "log", @@ -1392,13 +1348,13 @@ checksum = "79ec03bce71f18b4a27c4c64c6ba2ddf74686d69b91d8714fb32ead3adaed713" dependencies = [ "base64", "byteorder", - "bytes 1.1.0", + "bytes", "fallible-iterator", - "hmac 0.12.1", - "md-5 0.10.1", + "hmac", + "md-5", "memchr", "rand 0.8.5", - "sha2 0.10.2", + "sha2", "stringprep", ] @@ -1408,7 +1364,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04619f94ba0cc80999f4fc7073607cb825bc739a883cb6d20900fc5e009d6b0d" dependencies = [ - "bytes 1.1.0", + "bytes", "fallible-iterator", "postgres-protocol", ] @@ -1642,6 +1598,17 @@ dependencies = [ "bitflags", ] +[[package]] +name = "redox_users" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" +dependencies = [ + "getrandom 0.2.6", + "redox_syscall", + "thiserror", +] + [[package]] name = "refinery" version = "0.5.0" @@ -1735,7 +1702,7 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" dependencies = [ - "semver 1.0.7", + "semver", ] [[package]] @@ -1792,31 +1759,12 @@ dependencies = [ "libc", ] -[[package]] -name = "semver" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6" -dependencies = [ - "semver-parser", - "serde", -] - [[package]] name = "semver" version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d65bd28f48be7196d222d95b9243287f48d27aca604e08497513019ff0502cc4" -[[package]] -name = "semver-parser" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0bef5b7f9e0df16536d3961cfb6e84331c065b4066afb39768d0e319411f7" -dependencies = [ - "pest", -] - [[package]] name = "serde" version = "1.0.136" @@ -1854,36 +1802,20 @@ version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e8d9fa5c3b304765ce1fd9c4c8a3de2c8db365a5b91be52f186efc675681d95" dependencies = [ - "indexmap", - "itoa 1.0.1", + "itoa", "ryu", "serde", ] [[package]] name = "sha-1" -version = "0.9.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99cd6713db3cf16b6c84e06321e049a9b9f699826e16096d23bbcc44d15d51a6" -dependencies = [ - "block-buffer 0.9.0", - "cfg-if", - "cpufeatures", - "digest 0.9.0", - "opaque-debug", -] - -[[package]] -name = "sha2" -version = "0.9.9" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" +checksum = "028f48d513f9678cda28f6e4064755b3fbb2af6acd672f2c209b62323f7aea0f" dependencies = [ - "block-buffer 0.9.0", "cfg-if", "cpufeatures", - "digest 0.9.0", - "opaque-debug", + "digest", ] [[package]] @@ -1894,7 +1826,7 @@ checksum = "55deaec60f81eefe3cce0dc50bda92d6d8e88f2a27df7c5033b42afeb1ed2676" dependencies = [ "cfg-if", "cpufeatures", - "digest 0.10.3", + "digest", ] [[package]] @@ -1957,9 +1889,9 @@ dependencies = [ [[package]] name = "sqlx" -version = "0.4.2" +version = "0.5.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1a98f9bf17b690f026b6fec565293a995b46dfbd6293debcb654dcffd2d1b34" +checksum = "551873805652ba0d912fec5bbb0f8b4cdd96baf8e2ebf5970e5671092966019b" dependencies = [ "sqlx-core", "sqlx-macros", @@ -1967,41 +1899,44 @@ dependencies = [ [[package]] name = "sqlx-core" -version = "0.4.2" +version = "0.5.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36bb6a2ca3345a86493bc3b71eabc2c6c16a8bb1aa476cf5303bee27f67627d7" +checksum = "e48c61941ccf5ddcada342cd59e3e5173b007c509e1e8e990dafc830294d9dc5" dependencies = [ - "ahash 0.6.3", + "ahash", "atoi", "base64", "bitflags", "byteorder", - "bytes 0.5.6", + "bytes", "chrono", - "crc", - "crossbeam-channel", + "crc 2.1.0", "crossbeam-queue", - "crossbeam-utils", + "dirs", "either", + "event-listener", "futures-channel", "futures-core", + "futures-intrusive", "futures-util", "hashlink", "hex", - "hmac 0.10.1", - "itoa 0.4.8", + "hkdf", + "hmac", + "indexmap", + "itoa", "libc", "log", - "md-5 0.9.1", + "md-5", "memchr", "once_cell", - "parking_lot", + "paste", "percent-encoding", - "rand 0.7.3", + "rand 0.8.5", "serde", "serde_json", "sha-1", - "sha2 0.9.9", + "sha2", "smallvec", "sqlformat", "sqlx-rt", @@ -2013,20 +1948,18 @@ dependencies = [ [[package]] name = "sqlx-macros" -version = "0.4.2" +version = "0.5.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b5ada8b3b565331275ce913368565a273a74faf2a34da58c4dc010ce3286844" +checksum = "bc0fba2b0cae21fc00fe6046f8baa4c7fcb49e379f0f592b04696607f69ed2e1" dependencies = [ - "cargo_metadata", "dotenv", "either", - "futures", - "heck", - "lazy_static", + "heck 0.4.0", + "once_cell", "proc-macro2", "quote", "serde_json", - "sha2 0.9.9", + "sha2", "sqlx-core", "sqlx-rt", "syn", @@ -2035,9 +1968,9 @@ dependencies = [ [[package]] name = "sqlx-rt" -version = "0.2.0" +version = "0.5.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63fc5454c9dd7aaea3a0eeeb65ca40d06d0d8e7413a8184f7c3a3ffa5056190b" +checksum = "4db708cd3e459078f85f39f96a00960bd841f66ee2a669e90bf36907f5a79aae" dependencies = [ "async-native-tls", "async-std", @@ -2066,7 +1999,7 @@ version = "0.19.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e61bb0be289045cb80bfce000512e32d09f8337e54c186725da381377ad1f8d5" dependencies = [ - "heck", + "heck 0.3.3", "proc-macro2", "quote", "syn", @@ -2154,7 +2087,7 @@ version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0f48b6d60512a392e34dbf7fd456249fd2de3c83669ab642e021903f4015185b" dependencies = [ - "bytes 1.1.0", + "bytes", "libc", "memchr", "mio", @@ -2172,7 +2105,7 @@ checksum = "4b6c8b33df661b548dcd8f9bf87debb8c56c05657ed291122e1188698c2ece95" dependencies = [ "async-trait", "byteorder", - "bytes 1.1.0", + "bytes", "fallible-iterator", "futures", "log", @@ -2193,7 +2126,7 @@ version = "0.6.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e99e1983e5d376cd8eb4b66604d2e99e79f5bd988c3055891dcd8c9e2604cc0" dependencies = [ - "bytes 1.1.0", + "bytes", "futures-core", "futures-sink", "log", @@ -2216,12 +2149,6 @@ version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" -[[package]] -name = "ucd-trie" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c" - [[package]] name = "unicode-bidi" version = "0.3.8" diff --git a/src/entity/gateway/entitygateway.rs b/src/entity/gateway/entitygateway.rs index fd8b1aa..dbdc401 100644 --- a/src/entity/gateway/entitygateway.rs +++ b/src/entity/gateway/entitygateway.rs @@ -20,11 +20,11 @@ pub trait EntityGateway: Send + Sync + Clone { unimplemented!() } - async fn get_user_by_id(&self, _id: UserAccountId) -> Result { + async fn get_user_by_id(&mut self, _id: UserAccountId) -> Result { unimplemented!(); } - async fn get_user_by_name(&self, _username: String) -> Result { + async fn get_user_by_name(&mut self, _username: String) -> Result { unimplemented!(); } @@ -36,7 +36,7 @@ pub trait EntityGateway: Send + Sync + Clone { unimplemented!(); } - async fn get_user_settings_by_user(&self, _user: &UserAccountEntity) -> Result { + async fn get_user_settings_by_user(&mut self, _user: &UserAccountEntity) -> Result { unimplemented!(); } @@ -49,7 +49,7 @@ pub trait EntityGateway: Send + Sync + Clone { } // TODO: just make this a vec sorted by slot order? - async fn get_characters_by_user(&self, _user: &UserAccountEntity) -> Result<[Option; 4], GatewayError> { + async fn get_characters_by_user(&mut self, _user: &UserAccountEntity) -> Result<[Option; 4], GatewayError> { unimplemented!(); } @@ -57,7 +57,7 @@ pub trait EntityGateway: Send + Sync + Clone { unimplemented!(); } - async fn get_guild_card_data_by_user(&self, _user: &UserAccountEntity) -> Result { + async fn get_guild_card_data_by_user(&mut self, _user: &UserAccountEntity) -> Result { unimplemented!(); } @@ -85,13 +85,6 @@ pub trait EntityGateway: Send + Sync + Clone { unimplemented!(); } - - /* - async fn get_items_by_character(&self, _char_id: &CharacterEntityId) -> Result, GatewayError> { - unimplemented!(); - } - */ - async fn get_character_inventory(&mut self, _char_id: &CharacterEntityId) -> Result { unimplemented!(); } diff --git a/src/entity/gateway/inmemory.rs b/src/entity/gateway/inmemory.rs index 5c7afb4..4c38815 100644 --- a/src/entity/gateway/inmemory.rs +++ b/src/entity/gateway/inmemory.rs @@ -124,12 +124,12 @@ impl EntityGateway for InMemoryGateway { Ok(user) } - async fn get_user_by_id(&self, id: UserAccountId) -> Result { + async fn get_user_by_id(&mut self, id: UserAccountId) -> Result { let users = self.users.lock().unwrap(); users.get(&id).cloned().ok_or(GatewayError::Error) } - async fn get_user_by_name(&self, username: String) -> Result { + async fn get_user_by_name(&mut self, username: String) -> Result { let users = self.users.lock().unwrap(); users .iter() @@ -159,7 +159,7 @@ impl EntityGateway for InMemoryGateway { Ok(new_settings) } - async fn get_user_settings_by_user(&self, user: &UserAccountEntity) -> Result { + async fn get_user_settings_by_user(&mut self, user: &UserAccountEntity) -> Result { let user_settings = self.user_settings.lock().unwrap(); user_settings .iter() @@ -168,7 +168,7 @@ impl EntityGateway for InMemoryGateway { .ok_or(GatewayError::Error) } - async fn get_characters_by_user(&self, user: &UserAccountEntity) -> Result<[Option; 4], GatewayError> { + async fn get_characters_by_user(&mut self, user: &UserAccountEntity) -> Result<[Option; 4], GatewayError> { let characters = self.characters.lock().unwrap(); const NONE: Option = None; let mut chars = [NONE; 4]; @@ -215,7 +215,7 @@ impl EntityGateway for InMemoryGateway { Ok(()) } - async fn get_guild_card_data_by_user(&self, user: &UserAccountEntity) -> Result { + async fn get_guild_card_data_by_user(&mut self, user: &UserAccountEntity) -> Result { Ok(GuildCardDataEntity::new(user.id)) } diff --git a/src/entity/gateway/postgres/postgres.rs b/src/entity/gateway/postgres/postgres.rs index 39be9ac..10961a5 100644 --- a/src/entity/gateway/postgres/postgres.rs +++ b/src/entity/gateway/postgres/postgres.rs @@ -42,241 +42,544 @@ impl PostgresGateway { pool, } } +} + - async fn apply_item_modifications(&self, item: ItemEntity) -> ItemEntity { - let ItemEntity {id, item} = item; +async fn apply_item_modifications(conn: &mut sqlx::PgConnection, item: ItemEntity) -> ItemEntity +{ + let ItemEntity {id, item} = item; - let item = match item { - ItemDetail::Weapon(mut weapon) => { - let q = r#"select weapon, modifier + let item = match item { + ItemDetail::Weapon(mut weapon) => { + let q = r#"select weapon, modifier from weapon_modifier where weapon = $1 order by created_at"#; - let weapon_modifiers = sqlx::query_as::<_, PgWeaponModifier>(q) - .bind(id.0 as i32) - .fetch(&self.pool); + let weapon_modifiers = sqlx::query_as::<_, PgWeaponModifier>(q) + .bind(id.0 as i32) + .fetch(conn); - weapon_modifiers.for_each(|modifier| { - if let Ok(modifier) = modifier { - weapon.apply_modifier(&modifier.modifier); - } - }).await; + weapon_modifiers.for_each(|modifier| { + if let Ok(modifier) = modifier { + weapon.apply_modifier(&modifier.modifier); + } + }).await; - ItemDetail::Weapon(weapon) - }, - ItemDetail::Mag(mut mag) => { - let q = r#"select mag, modifier, item.item -> 'Tool' as feed, item2.item -> 'Tool' as cell + ItemDetail::Weapon(weapon) + }, + ItemDetail::Mag(mut mag) => { + let q = r#"select mag, modifier, item.item -> 'Tool' as feed, item2.item -> 'Tool' as cell from mag_modifier left join item on item.id = cast (modifier ->> 'FeedMag' as integer) left join item as item2 on item2.id = cast (modifier ->> 'MagCell' as integer) where mag = $1 order by created_at"#; - let mag_modifiers = sqlx::query_as::<_, PgMagModifierWithParameters>(q) - .bind(id.0 as i32) - .fetch(&self.pool); - - mag_modifiers.for_each(|modifier| { - let PgMagModifierWithParameters {modifier, feed, cell, ..} = modifier.unwrap(); - let modifier: mag::MagModifier = modifier.0.into(); - match modifier { - mag::MagModifier::FeedMag{..} => { - mag.feed(feed.unwrap().tool) - }, - mag::MagModifier::BankMag => { - mag.bank() - }, - mag::MagModifier::MagCell(_) => { - mag.apply_mag_cell(mag::MagCell::try_from(Into::::into(cell.unwrap().0).tool).unwrap()) - }, - mag::MagModifier::OwnerChange(class, section_id) => { - mag.change_owner(class, section_id) - }, - } - }).await; - - ItemDetail::Mag(mag) + let mag_modifiers = sqlx::query_as::<_, PgMagModifierWithParameters>(q) + .bind(id.0 as i32) + .fetch(conn); + + mag_modifiers.for_each(|modifier| { + let PgMagModifierWithParameters {modifier, feed, cell, ..} = modifier.unwrap(); + let modifier: mag::MagModifier = modifier.0.into(); + match modifier { + mag::MagModifier::FeedMag{..} => { + mag.feed(feed.unwrap().tool) + }, + mag::MagModifier::BankMag => { + mag.bank() + }, + mag::MagModifier::MagCell(_) => { + mag.apply_mag_cell(mag::MagCell::try_from(Into::::into(cell.unwrap().0).tool).unwrap()) + }, + mag::MagModifier::OwnerChange(class, section_id) => { + mag.change_owner(class, section_id) + }, + } + }).await; + + ItemDetail::Mag(mag) + }, + item => item + }; + + ItemEntity { + id, + item, + } +} + +async fn create_user(conn: &mut sqlx::PgConnection, user: NewUserAccountEntity) -> Result +{ + let new_user = sqlx::query_as::<_, PgUserAccount>("insert into user_accounts (email, username, password, activated) values ($1, $2, $3, $4) returning *;") + .bind(user.email) + .bind(user.username) + .bind(user.password) + .bind(user.activated) + .fetch_one(conn).await?; + Ok(new_user.into()) +} + +async fn get_user_by_id(conn: &mut sqlx::PgConnection, id: UserAccountId) -> Result +{ + let user = sqlx::query_as::<_, PgUserAccount>("select * from user_accounts where id = $1") + .bind(id.0) + .fetch_one(conn).await?; + Ok(user.into()) +} + +async fn get_user_by_name(conn: &mut sqlx::PgConnection, username: String) -> Result +{ + let user = sqlx::query_as::<_, PgUserAccount>("select * from user_accounts where username = $1") + .bind(username) + .fetch_one(conn).await?; + Ok(user.into()) +} + + +async fn save_user(conn: &mut sqlx::PgConnection, user: &UserAccountEntity) -> Result<(), GatewayError> +{ + sqlx::query("UPDATE user_accounts set username=$1, password=$2, banned=$3, muted=$4, flags=$5 where id=$6") + .bind(&user.username) + .bind(&user.password) + .bind(&user.banned_until) + .bind(&user.muted_until) + .bind(&user.flags) + .bind(&user.id.0) + .execute(conn).await?; + Ok(()) +} + +async fn create_user_settings(conn: &mut sqlx::PgConnection, settings: NewUserSettingsEntity) -> Result +{ + let new_settings = sqlx::query_as::<_, PgUserSettings>("insert into user_settings (user_account, blocked_users, key_config, joystick_config, option_flags, shortcuts, symbol_chats, team_name) + values ($1, $2, $3, $4, $5, $6, $7, $8) returning *;") + .bind(settings.user_id.0) + .bind(settings.settings.blocked_users.iter().copied().flat_map(|i| i.to_le_bytes().to_vec()).collect::>()) + .bind(settings.settings.keyboard_config.to_vec()) + .bind(settings.settings.gamepad_config.to_vec()) + .bind(settings.settings.option_flags as i32) + .bind(settings.settings.shortcuts.to_vec()) + .bind(settings.settings.symbol_chats.to_vec()) + .bind(settings.settings.team_name.iter().copied().flat_map(|i| i.to_le_bytes().to_vec()).collect::>()) + .fetch_one(conn).await?; + Ok(new_settings.into()) +} + +async fn get_user_settings_by_user(conn: &mut sqlx::PgConnection, user: &UserAccountEntity) -> Result +{ + let settings = sqlx::query_as::<_, PgUserSettings>("select * from user_settings where user_account = $1") + .bind(user.id.0) + .fetch_one(conn).await?; + Ok(settings.into()) +} + +async fn save_user_settings(conn: &mut sqlx::PgConnection, settings: &UserSettingsEntity) -> Result<(), GatewayError> +{ + sqlx::query("update user_settings set blocked_users=$1, key_config=$2, joystick_config=$3, option_flags=$4, shortcuts=$5, symbol_chats=$6, team_name=$7 where id=$8") + .bind(settings.settings.blocked_users.iter().copied().flat_map(|i| i.to_le_bytes().to_vec()).collect::>()) + .bind(&settings.settings.keyboard_config.to_vec()) + .bind(&settings.settings.gamepad_config.to_vec()) + .bind(&settings.settings.option_flags) + .bind(&settings.settings.shortcuts.to_vec()) + .bind(&settings.settings.symbol_chats.to_vec()) + .bind(settings.settings.team_name.iter().copied().flat_map(|i| i.to_le_bytes().to_vec()).collect::>()) + .bind(&settings.id.0) + .fetch_one(conn).await?; + Ok(()) +} + +async fn create_character(conn: &mut sqlx::PgConnection, char: NewCharacterEntity) -> Result +{ + let q = r#"insert into player_character + (user_account, slot, name, exp, class, section_id, costume, skin, face, head, hair, hair_r, hair_g, hair_b, prop_x, prop_y, techs, + config, infoboard, guildcard, power, mind, def, evade, luck, hp, tp, tech_menu, option_flags) + values + ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22, $23, $24, $25, $26, $27, $28, $29, $30, $31) + returning *;"#; + let character = sqlx::query_as::<_, PgCharacter>(q) + .bind(char.user_id.0) + .bind(char.slot as i16) + .bind(char.name) + .bind(char.exp as i32) + .bind(char.char_class.to_string()) + .bind(char.section_id.to_string()) + .bind(char.appearance.costume as i16) + .bind(char.appearance.skin as i16) + .bind(char.appearance.face as i16) + .bind(char.appearance.head as i16) + .bind(char.appearance.hair as i16) + .bind(char.appearance.hair_r as i16) + .bind(char.appearance.hair_g as i16) + .bind(char.appearance.hair_b as i16) + .bind(char.appearance.prop_x) + .bind(char.appearance.prop_y) + .bind(&char.techs.as_bytes().to_vec()) + .bind(&char.config.as_bytes().to_vec()) + .bind(String::from_utf16_lossy(&char.info_board.board).trim_matches(char::from(0))) + .bind(char.guildcard.description) + .bind(char.materials.power as i16) + .bind(char.materials.mind as i16) + .bind(char.materials.def as i16) + .bind(char.materials.evade as i16) + .bind(char.materials.luck as i16) + .bind(char.materials.hp as i16) + .bind(char.materials.tp as i16) + .bind(char.tech_menu.tech_menu.to_vec()) + .bind(char.option_flags as i32) + .fetch_one(conn).await?; + + Ok(character.into()) +} + +async fn get_characters_by_user(conn: &mut sqlx::PgConnection, user: &UserAccountEntity) -> Result<[Option; 4], GatewayError> +{ + let mut stream = sqlx::query_as::<_, PgCharacter>("select * from player_character where user_account = $1 and slot < 4 order by slot") + .bind(user.id.0) + .fetch(conn); + const NONE: Option = None; + let mut result = [NONE; 4]; + while let Some(character) = stream.try_next().await? { + let index = character.slot as usize; + result[index] = Some(character.into()) + } + + Ok(result) +} + +async fn save_character(conn: &mut sqlx::PgConnection, char: &CharacterEntity) -> Result<(), GatewayError> +{ + let q = r#"update player_character set + user_account=$1, slot=$2, name=$3, exp=$4, class=$5, section_id=$6, costume=$7, skin=$8, face=$9, head=$10, hair=$11, hair_r=$12, + hair_g=$13, hair_b=$14, prop_x=$15, prop_y=$16, techs=$17, config=$18, infoboard=$19, guildcard=$20, power=$21, mind=$22, def=$23, + evade=$24, luck=$25, hp=$26, tp=$27, tech_menu=$28, option_flags=$29 + where id=$32;"#; + sqlx::query(q) + .bind(char.user_id.0) + .bind(char.slot as i16) + .bind(&char.name) + .bind(char.exp as i32) + .bind(char.char_class.to_string()) + .bind(char.section_id.to_string()) + .bind(char.appearance.costume as i16) + .bind(char.appearance.skin as i16) + .bind(char.appearance.face as i16) + .bind(char.appearance.head as i16) + .bind(char.appearance.hair as i16) + .bind(char.appearance.hair_r as i16) + .bind(char.appearance.hair_g as i16) + .bind(char.appearance.hair_b as i16) + .bind(char.appearance.prop_x) + .bind(char.appearance.prop_y) + .bind(&char.techs.as_bytes().to_vec()) + .bind(&char.config.as_bytes().to_vec()) + .bind(String::from_utf16_lossy(&char.info_board.board).trim_matches(char::from(0))) + .bind(&char.guildcard.description) + .bind(char.materials.power as i16) + .bind(char.materials.mind as i16) + .bind(char.materials.def as i16) + .bind(char.materials.evade as i16) + .bind(char.materials.luck as i16) + .bind(char.materials.hp as i16) + .bind(char.materials.tp as i16) + .bind(char.tech_menu.tech_menu.to_vec()) + .bind(char.option_flags as i32) + .bind(char.id.0 as i32) + .execute(conn).await?; + Ok(()) +} + +async fn create_item(conn: &mut sqlx::PgConnection, item: NewItemEntity) -> Result +{ + let new_item = sqlx::query_as::<_, PgItem>("insert into item (item) values ($1) returning *;") + .bind(sqlx::types::Json(PgItemDetail::from(item.item))) + .fetch_one(conn).await?; + + Ok(ItemEntity { + id: ItemEntityId(new_item.id as u32), + item: new_item.item.0.into(), + }) +} + +async fn add_item_note(conn: &mut sqlx::PgConnection, item_id: &ItemEntityId, item_note: ItemNote) -> Result<(), GatewayError> +{ + sqlx::query("insert into item_note(item, note) values ($1, $2)") + .bind(item_id.0) + .bind(sqlx::types::Json(PgItemNoteDetail::from(item_note))) + .execute(conn).await?; + Ok(()) +} + +async fn feed_mag(conn: &mut sqlx::PgConnection, mag_item_id: &ItemEntityId, tool_item_id: &ItemEntityId) -> Result<(), GatewayError> +{ + sqlx::query("insert into mag_modifier (mag, modifier) values ($1, $2);") + .bind(mag_item_id.0) + .bind(sqlx::types::Json(PgMagModifierDetail::from(mag::MagModifier::FeedMag{food: *tool_item_id}))) + .execute(conn).await?; + Ok(()) +} + +async fn change_mag_owner(conn: &mut sqlx::PgConnection, mag_item_id: &ItemEntityId, character: &CharacterEntity) -> Result<(), GatewayError> +{ + sqlx::query("insert into mag_modifier (mag, modifier) values ($1, $2);") + .bind(mag_item_id.0) + .bind(sqlx::types::Json(PgMagModifierDetail::from(mag::MagModifier::OwnerChange(character.char_class, character.section_id)))) + .execute(conn).await?; + Ok(()) +} + +async fn use_mag_cell(conn: &mut sqlx::PgConnection, mag_item_id: &ItemEntityId, mag_cell_id: &ItemEntityId) -> Result<(), GatewayError> +{ + sqlx::query("insert into mag_modifier (mag, modifier) values ($1, $2);") + .bind(mag_item_id.0) + .bind(sqlx::types::Json(PgMagModifierDetail::from(mag::MagModifier::MagCell(*mag_cell_id)))) + .execute(conn).await?; + Ok(()) +} + +async fn add_weapon_modifier(conn: &mut sqlx::PgConnection, item_id: &ItemEntityId, modifier: weapon::WeaponModifier) -> Result<(), GatewayError> +{ + sqlx::query("insert into weapon_modifier (weapon, modifier) values ($1, $2);") + .bind(item_id.0) + .bind(sqlx::types::Json(modifier)) + .execute(conn).await?; + Ok(()) +} + +async fn get_character_inventory(conn: &mut sqlx::PgConnection, char_id: &CharacterEntityId) -> Result +{ + let mut t = conn.begin().await?; + let inventory = sqlx::query_as::<_, PgInventoryEntity>("select * from inventory where pchar = $1") + .bind(char_id.0) + .fetch_one(&mut t).await?; + + // TODO: inefficient + let mut real_inventory = Vec::new(); + for inv_item in inventory.items.0.into_iter() { + match inv_item { + PgInventoryItemEntity::Individual(item) => { + let entity = sqlx::query_as::<_, PgItemEntity>("select item.id, item.item from item where id = $1") + .bind(item) + .fetch_one(&mut t).await + .map(|item| item.into()) + .map(|item| apply_item_modifications(&mut t, item))? + .await; + real_inventory.push(InventoryItemEntity::Individual(entity)); }, - item => item - }; + PgInventoryItemEntity::Stacked(items) => { + let mut stacked_item = Vec::new(); + for s_item in items { + stacked_item.push(sqlx::query_as::<_, PgItemEntity>("select item.id, item.item from item where id = $1") + .bind(s_item) + .fetch_one(&mut t).await + .map(|item| item.into())?) + } + real_inventory.push(InventoryItemEntity::Stacked(stacked_item)); + } + } + } + + Ok(InventoryEntity::new(real_inventory)) +} - ItemEntity { - id, - item, +async fn get_character_bank(conn: &mut sqlx::PgConnection, char_id: &CharacterEntityId, bank_name: BankName) -> Result +{ + let mut t = conn.begin().await?; + let bank = sqlx::query_as::<_, PgInventoryEntity>("select * from bank where pchar = $1 and name = $2") + .bind(char_id.0) + .bind(bank_name.0) + .fetch_one(&mut t).await?; + // TODO: inefficient + let mut real_bank = Vec::new(); + for bank_item in bank.items.0.into_iter() { + match bank_item { + PgInventoryItemEntity::Individual(item) => { + let entity = sqlx::query_as::<_, PgItemEntity>("select item.id, item.item from item where id = $1") + .bind(item) + .fetch_one(&mut t).await + .map(|item| item.into()) + .map(|item| apply_item_modifications(&mut t, item))? + .await; + real_bank.push(BankItemEntity::Individual(entity)); + }, + PgInventoryItemEntity::Stacked(items) => { + let mut stacked_item = Vec::new(); + for s_item in items { + stacked_item.push(sqlx::query_as::<_, PgItemEntity>("select item.id, item.item from item where id = $1") + .bind(s_item) + .fetch_one(&mut t).await + .map(|item| item.into()) + .map(|item| apply_item_modifications(&mut t, item))? + .await) + } + real_bank.push(BankItemEntity::Stacked(stacked_item)); + } } } + + Ok(BankEntity::new(real_bank)) +} + +async fn set_character_inventory(conn: &mut sqlx::PgConnection, char_id: &CharacterEntityId, inventory: &InventoryEntity) -> Result<(), GatewayError> +{ + let inventory = inventory.items.iter() + .map(|item| { + match item { + InventoryItemEntity::Individual(item) => { + PgInventoryItemEntity::Individual(item.id.0 as i32) + }, + InventoryItemEntity::Stacked(items) => { + PgInventoryItemEntity::Stacked(items.iter().map(|i| i.id.0 as i32).collect()) + }, + } + }) + .collect::>(); + + sqlx::query("insert into inventory (pchar, items) values ($1, $2) on conflict (pchar) do update set items = $2") + .bind(char_id.0) + .bind(sqlx::types::Json(inventory)) + .execute(conn) + .await?; + Ok(()) +} + +async fn set_character_bank(conn: &mut sqlx::PgConnection, char_id: &CharacterEntityId, bank: &BankEntity, bank_name: BankName) -> Result<(), GatewayError> +{ + let bank = bank.items.iter() + .map(|item| { + match item { + BankItemEntity::Individual(item) => { + PgInventoryItemEntity::Individual(item.id.0 as i32) + }, + BankItemEntity::Stacked(items) => { + PgInventoryItemEntity::Stacked(items.iter().map(|i| i.id.0 as i32).collect()) + }, + } + }) + .collect::>(); + + sqlx::query("insert into bank (pchar, items, name) values ($1, $2, $3) on conflict (pchar, name) do update set items = $2") + .bind(char_id.0) + .bind(sqlx::types::Json(bank)) + .bind(bank_name.0) + .execute(conn) + .await?; + Ok(()) +} + +async fn get_character_equips(conn: &mut sqlx::PgConnection, char_id: &CharacterEntityId) -> Result +{ + let equips = sqlx::query_as::<_, PgEquipped>("select * from equipped where pchar = $1") + .bind(char_id.0) + .fetch_one(conn) + .await?; + + Ok(equips.into()) +} + +async fn set_character_equips(conn: &mut sqlx::PgConnection, char_id: &CharacterEntityId, equips: &EquippedEntity) -> Result<(), GatewayError> +{ + sqlx::query(r#"insert into equipped (pchar, weapon, armor, shield, unit0, unit1, unit2, unit3, mag) values ($1, $2, $3, $4, $5, $6, $7, $8, $9) + on conflict (pchar) do update set weapon=$2, armor=$3, shield=$4, unit0=$5, unit1=$6, unit2=$7, unit3=$8, mag=$9"#) + .bind(char_id.0) + .bind(equips.weapon.map(|i| i.0 as i32)) + .bind(equips.armor.map(|i| i.0 as i32)) + .bind(equips.shield.map(|i| i.0 as i32)) + .bind(equips.unit[0].map(|i| i.0 as i32)) + .bind(equips.unit[1].map(|i| i.0 as i32)) + .bind(equips.unit[2].map(|i| i.0 as i32)) + .bind(equips.unit[3].map(|i| i.0 as i32)) + .bind(equips.mag.map(|i| i.0 as i32)) + .execute(conn) + .await?; + Ok(()) +} + +async fn set_character_meseta(conn: &mut sqlx::PgConnection, char_id: &CharacterEntityId, meseta: Meseta) -> Result<(), GatewayError> +{ + sqlx::query("insert into character_meseta values ($1, $2) on conflict (pchar) do update set items = $2") + .bind(char_id.0) + .bind(meseta.0 as i32) + .execute(conn) + .await?; + Ok(()) +} + +async fn get_character_meseta(conn: &mut sqlx::PgConnection, char_id: &CharacterEntityId) -> Result +{ + #[derive(sqlx::FromRow)] + struct PgMeseta(i32); + let meseta = sqlx::query_as::<_, PgMeseta>(r#"select meseta from character_meseta where id = $1"#) + .bind(char_id.0) + .fetch_one(conn) + .await?; + Ok(Meseta(meseta.0 as u32)) +} + +async fn set_bank_meseta(conn: &mut sqlx::PgConnection, char_id: &CharacterEntityId, bank: BankName, meseta: Meseta) -> Result<(), GatewayError> +{ + sqlx::query("insert into bank_meseta values ($1, $2, $3) on conflict (pchar, bank) do update set items = $2") + .bind(char_id.0) + .bind(meseta.0 as i32) + .bind(bank.0) + .execute(conn) + .await?; + Ok(()) +} + +async fn get_bank_meseta(conn: &mut sqlx::PgConnection, char_id: &CharacterEntityId, bank: BankName) -> Result +{ + #[derive(sqlx::FromRow)] + struct PgMeseta(i32); + let meseta = sqlx::query_as::<_, PgMeseta>(r#"select meseta from character_meseta where id = $1 and bank = $2"#) + .bind(char_id.0) + .bind(bank.0) + .fetch_one(conn) + .await?; + Ok(Meseta(meseta.0 as u32)) } #[async_trait::async_trait] impl EntityGateway for PostgresGateway { async fn create_user(&mut self, user: NewUserAccountEntity) -> Result { - let new_user = sqlx::query_as::<_, PgUserAccount>("insert into user_accounts (email, username, password, activated) values ($1, $2, $3, $4) returning *;") - .bind(user.email) - .bind(user.username) - .bind(user.password) - .bind(user.activated) - .fetch_one(&self.pool).await?; - Ok(new_user.into()) + create_user(&mut *self.pool.acquire().await?, user).await } - async fn get_user_by_id(&self, id: UserAccountId) -> Result { - let user = sqlx::query_as::<_, PgUserAccount>("select * from user_accounts where id = $1") - .bind(id.0) - .fetch_one(&self.pool).await?; - Ok(user.into()) + async fn get_user_by_id(&mut self, id: UserAccountId) -> Result { + get_user_by_id(&mut *self.pool.acquire().await?, id).await } - async fn get_user_by_name(&self, username: String) -> Result { - let user = sqlx::query_as::<_, PgUserAccount>("select * from user_accounts where username = $1") - .bind(username) - .fetch_one(&self.pool).await?; - Ok(user.into()) + async fn get_user_by_name(&mut self, username: String) -> Result { + get_user_by_name(&mut *self.pool.acquire().await?, username).await } async fn save_user(&mut self, user: &UserAccountEntity) -> Result<(), GatewayError> { - sqlx::query("UPDATE user_accounts set username=$1, password=$2, banned=$3, muted=$4, flags=$5 where id=$6") - .bind(&user.username) - .bind(&user.password) - .bind(&user.banned_until) - .bind(&user.muted_until) - .bind(&user.flags) - .bind(&user.id.0) - .execute(&self.pool).await?; - Ok(()) + save_user(&mut *self.pool.acquire().await?, user).await } async fn create_user_settings(&mut self, settings: NewUserSettingsEntity) -> Result { - let new_settings = sqlx::query_as::<_, PgUserSettings>("insert into user_settings (user_account, blocked_users, keyboard_config, gamepad_config, option_flags, shortcuts, symbol_chats, team_name) - values ($1, $2, $3, $4, $5, $6, $7, $8) returning *;") - .bind(settings.user_id.0) - .bind(settings.settings.blocked_users.iter().copied().flat_map(|i| i.to_le_bytes().to_vec()).collect::>()) - .bind(settings.settings.keyboard_config.to_vec()) - .bind(settings.settings.gamepad_config.to_vec()) - .bind(settings.settings.option_flags as i32) - .bind(settings.settings.shortcuts.to_vec()) - .bind(settings.settings.symbol_chats.to_vec()) - .bind(settings.settings.team_name.iter().copied().flat_map(|i| i.to_le_bytes().to_vec()).collect::>()) - .fetch_one(&self.pool).await?; - Ok(new_settings.into()) - } - - async fn get_user_settings_by_user(&self, user: &UserAccountEntity) -> Result { - let settings = sqlx::query_as::<_, PgUserSettings>("select * from user_settings where user_account = $1") - .bind(user.id.0) - .fetch_one(&self.pool).await?; - Ok(settings.into()) + create_user_settings(&mut *self.pool.acquire().await?, settings).await + } + + async fn get_user_settings_by_user(&mut self, user: &UserAccountEntity) -> Result { + get_user_settings_by_user(&mut *self.pool.acquire().await?, user).await } async fn save_user_settings(&mut self, settings: &UserSettingsEntity) -> Result<(), GatewayError> { - sqlx::query("update user_settings set blocked_users=$1, keyboard_config=$2, gamepad_config=$3, option_flags=$4, shortcuts=$5, symbol_chats=$6, team_name=$7 where id=$8") - .bind(settings.settings.blocked_users.iter().copied().flat_map(|i| i.to_le_bytes().to_vec()).collect::>()) - .bind(&settings.settings.keyboard_config.to_vec()) - .bind(&settings.settings.gamepad_config.to_vec()) - .bind(&settings.settings.option_flags) - .bind(&settings.settings.shortcuts.to_vec()) - .bind(&settings.settings.symbol_chats.to_vec()) - .bind(settings.settings.team_name.iter().copied().flat_map(|i| i.to_le_bytes().to_vec()).collect::>()) - .bind(&settings.id.0) - .fetch_one(&self.pool).await?; - Ok(()) + save_user_settings(&mut *self.pool.acquire().await?, settings).await } async fn create_character(&mut self, char: NewCharacterEntity) -> Result { - let q = r#"insert into player_character - (user_account, slot, name, exp, class, section_id, costume, skin, face, head, hair, hair_r, hair_g, hair_b, prop_x, prop_y, techs, - config, infoboard, guildcard, power, mind, def, evade, luck, hp, tp, tech_menu, option_flags) - values - ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22, $23, $24, $25, $26, $27, $28, $29, $30, $31) - returning *;"#; - let character = sqlx::query_as::<_, PgCharacter>(q) - .bind(char.user_id.0) - .bind(char.slot as i16) - .bind(char.name) - .bind(char.exp as i32) - .bind(char.char_class.to_string()) - .bind(char.section_id.to_string()) - .bind(char.appearance.costume as i16) - .bind(char.appearance.skin as i16) - .bind(char.appearance.face as i16) - .bind(char.appearance.head as i16) - .bind(char.appearance.hair as i16) - .bind(char.appearance.hair_r as i16) - .bind(char.appearance.hair_g as i16) - .bind(char.appearance.hair_b as i16) - .bind(char.appearance.prop_x) - .bind(char.appearance.prop_y) - .bind(&char.techs.as_bytes().to_vec()) - .bind(&char.config.as_bytes().to_vec()) - .bind(String::from_utf16_lossy(&char.info_board.board).trim_matches(char::from(0))) - .bind(char.guildcard.description) - .bind(char.materials.power as i16) - .bind(char.materials.mind as i16) - .bind(char.materials.def as i16) - .bind(char.materials.evade as i16) - .bind(char.materials.luck as i16) - .bind(char.materials.hp as i16) - .bind(char.materials.tp as i16) - .bind(char.tech_menu.tech_menu.to_vec()) - .bind(char.option_flags as i32) - .fetch_one(&self.pool).await?; - - Ok(character.into()) - } - - async fn get_characters_by_user(&self, user: &UserAccountEntity) -> Result<[Option; 4], GatewayError> { - let mut stream = sqlx::query_as::<_, PgCharacter>("select * from player_character where user_account = $1 and slot < 4 order by slot") - .bind(user.id.0) - .fetch(&self.pool); - const NONE: Option = None; - let mut result = [NONE; 4]; - while let Some(character) = stream.try_next().await? { - let index = character.slot as usize; - result[index] = Some(character.into()) - } + create_character(&mut *self.pool.acquire().await?, char).await + } - Ok(result) + async fn get_characters_by_user(&mut self, user: &UserAccountEntity) -> Result<[Option; 4], GatewayError> { + get_characters_by_user(&mut *self.pool.acquire().await?, user).await } async fn save_character(&mut self, char: &CharacterEntity) -> Result<(), GatewayError> { - let q = r#"update player_character set - user_account=$1, slot=$2, name=$3, exp=$4, class=$5, section_id=$6, costume=$7, skin=$8, face=$9, head=$10, hair=$11, hair_r=$12, - hair_g=$13, hair_b=$14, prop_x=$15, prop_y=$16, techs=$17, config=$18, infoboard=$19, guildcard=$20, power=$21, mind=$22, def=$23, - evade=$24, luck=$25, hp=$26, tp=$27, tech_menu=$28, option_flags=$29 - where id=$32;"#; - sqlx::query(q) - .bind(char.user_id.0) - .bind(char.slot as i16) - .bind(&char.name) - .bind(char.exp as i32) - .bind(char.char_class.to_string()) - .bind(char.section_id.to_string()) - .bind(char.appearance.costume as i16) - .bind(char.appearance.skin as i16) - .bind(char.appearance.face as i16) - .bind(char.appearance.head as i16) - .bind(char.appearance.hair as i16) - .bind(char.appearance.hair_r as i16) - .bind(char.appearance.hair_g as i16) - .bind(char.appearance.hair_b as i16) - .bind(char.appearance.prop_x) - .bind(char.appearance.prop_y) - .bind(&char.techs.as_bytes().to_vec()) - .bind(&char.config.as_bytes().to_vec()) - .bind(String::from_utf16_lossy(&char.info_board.board).trim_matches(char::from(0))) - .bind(&char.guildcard.description) - .bind(char.materials.power as i16) - .bind(char.materials.mind as i16) - .bind(char.materials.def as i16) - .bind(char.materials.evade as i16) - .bind(char.materials.luck as i16) - .bind(char.materials.hp as i16) - .bind(char.materials.tp as i16) - .bind(char.tech_menu.tech_menu.to_vec()) - .bind(char.option_flags as i32) - .bind(char.id.0 as i32) - .execute(&self.pool).await?; - Ok(()) - } - - async fn get_guild_card_data_by_user(&self, user: &UserAccountEntity) -> Result { + save_character(&mut *self.pool.acquire().await?, char).await + } + + async fn get_guild_card_data_by_user(&mut self, user: &UserAccountEntity) -> Result { Ok(GuildCardDataEntity { id: GuildCardDataId(0), user_id: user.id, @@ -285,305 +588,68 @@ impl EntityGateway for PostgresGateway { } async fn create_item(&mut self, item: NewItemEntity) -> Result { - let mut tx = self.pool.begin().await?; - let new_item = sqlx::query_as::<_, PgItem>("insert into item (item) values ($1) returning *;") - .bind(sqlx::types::Json(PgItemDetail::from(item.item))) - .fetch_one(&mut tx).await?; - - tx.commit().await?; - Ok(ItemEntity { - id: ItemEntityId(new_item.id as u32), - item: new_item.item.0.into(), - }) + create_item(&mut *self.pool.acquire().await?, item).await } async fn add_item_note(&mut self, item_id: &ItemEntityId, item_note: ItemNote) -> Result<(), GatewayError> { - sqlx::query("insert into item_note(item, note) values ($1, $2)") - .bind(item_id.0) - .bind(sqlx::types::Json(PgItemNoteDetail::from(item_note))) - .execute(&self.pool).await?; - Ok(()) - - /* - let mut tx = self.pool.begin().await?; - if let ItemLocation::Inventory{slot, ..} = &item_location { - sqlx::query("delete from inventory_slot where item = $1") - .bind(item_id.0 as i32) - .execute(&mut tx).await?; - sqlx::query("insert into inventory_slot (item, slot) values ($1, $2)") - .bind(item_id.0) - .bind(*slot as i32) - .execute(&mut tx).await?; - sqlx::query(r#"insert into item_location (item, location) - select $1, $2 - where (select jsonb_object_keys(location) from item_location where item=$1 - order by created_at desc limit 1) != 'Inventory'"#) - .bind(item_id.0) - .bind(sqlx::types::Json(PgItemLocationDetail::from(item_location))) - .execute(&mut tx).await?; - } - else { - sqlx::query("insert into item_location (item, location) values ($1, $2)") - .bind(item_id.0) - .bind(sqlx::types::Json(PgItemLocationDetail::from(item_location))) - .execute(&mut tx).await?; - } - tx.commit().await?; - Ok(()) - */ + add_item_note(&mut *self.pool.acquire().await?, item_id, item_note).await } async fn feed_mag(&mut self, mag_item_id: &ItemEntityId, tool_item_id: &ItemEntityId) -> Result<(), GatewayError> { - sqlx::query("insert into mag_modifier (mag, modifier) values ($1, $2);") - .bind(mag_item_id.0) - .bind(sqlx::types::Json(PgMagModifierDetail::from(mag::MagModifier::FeedMag{food: *tool_item_id}))) - .execute(&self.pool).await?; - Ok(()) + feed_mag(&mut *self.pool.acquire().await?, mag_item_id, tool_item_id).await } async fn change_mag_owner(&mut self, mag_item_id: &ItemEntityId, character: &CharacterEntity) -> Result<(), GatewayError> { - sqlx::query("insert into mag_modifier (mag, modifier) values ($1, $2);") - .bind(mag_item_id.0) - .bind(sqlx::types::Json(PgMagModifierDetail::from(mag::MagModifier::OwnerChange(character.char_class, character.section_id)))) - .execute(&self.pool).await?; - Ok(()) + change_mag_owner(&mut *self.pool.acquire().await?, mag_item_id, character).await } async fn use_mag_cell(&mut self, mag_item_id: &ItemEntityId, mag_cell_id: &ItemEntityId) -> Result<(), GatewayError> { - sqlx::query("insert into mag_modifier (mag, modifier) values ($1, $2);") - .bind(mag_item_id.0) - .bind(sqlx::types::Json(PgMagModifierDetail::from(mag::MagModifier::MagCell(*mag_cell_id)))) - .execute(&self.pool).await?; - Ok(()) + use_mag_cell(&mut *self.pool.acquire().await?, mag_item_id, mag_cell_id).await } async fn add_weapon_modifier(&mut self, item_id: &ItemEntityId, modifier: weapon::WeaponModifier) -> Result<(), GatewayError> { - sqlx::query("insert into weapon_modifier (weapon, modifier) values ($1, $2);") - .bind(item_id.0) - .bind(sqlx::types::Json(modifier)) - .execute(&self.pool).await?; - Ok(()) - } - -/* - async fn get_items_by_character(&self, char_id: &CharacterEntityId) -> Result, GatewayError> { - let q = r#"select * from ( - select distinct on (item_location.item) - item.id, - case - when item_location.location -> 'Inventory' is not null then - jsonb_set(item_location.location, '{Inventory,slot}', inventory_slot.slot::text::jsonb) - else - item_location.location - end, - item.item - from item_location - join item on item.id = item_location.item - join inventory_slot on inventory_slot.item = item.id - order by item_location.item, item_location.created_at desc - ) as i - where cast (location -> 'Inventory' ->> 'character_id' as integer) = $1 - or cast (location -> 'Bank' ->> 'character_id' as integer) = $1 - "#; - let items = sqlx::query_as::<_, PgItemWithLocation>(q) - .bind(char_id.0) - .fetch(&self.pool); - Ok(join_all(items - .filter_map(|item: Result| { - let item = item.ok()?; - Some(ItemEntity { - id: ItemEntityId(item.id as u32), - item: item.item.0.into(), - location: item.location.0.into() - }) - }) - .map(|item: ItemEntity| { - self.apply_item_modifications(item) - }) - .collect::>() - .await - ).await) - } -*/ + add_weapon_modifier(&mut *self.pool.acquire().await?, item_id, modifier).await + } + async fn get_character_inventory(&mut self, char_id: &CharacterEntityId) -> Result { - let inventory = sqlx::query_as::<_, PgInventoryEntity>("select * from inventory where pchar = $1") - .bind(char_id.0) - .fetch_one(&self.pool).await?; - // TODO: inefficient - let mut real_inventory = Vec::new(); - for inv_item in inventory.items.0.into_iter() { - match inv_item { - PgInventoryItemEntity::Individual(item) => { - let entity = sqlx::query_as::<_, PgItemEntity>("select item.id, item.item from item where id = $1") - .bind(item) - .fetch_one(&self.pool).await - .map(|item| item.into()) - .map(|item| self.apply_item_modifications(item))? - .await; - real_inventory.push(InventoryItemEntity::Individual(entity)); - }, - PgInventoryItemEntity::Stacked(items) => { - let mut stacked_item = Vec::new(); - for s_item in items { - stacked_item.push(sqlx::query_as::<_, PgItemEntity>("select item.id, item.item from item where id = $1") - .bind(s_item) - .fetch_one(&self.pool).await - .map(|item| item.into()) - .map(|item| self.apply_item_modifications(item))? - .await) - } - real_inventory.push(InventoryItemEntity::Stacked(stacked_item)); - } - } - } - - Ok(InventoryEntity::new(real_inventory)) + get_character_inventory(&mut *self.pool.acquire().await?, char_id).await } async fn get_character_bank(&mut self, char_id: &CharacterEntityId, bank_name: BankName) -> Result { - let bank = sqlx::query_as::<_, PgInventoryEntity>("select * from bank where pchar = $1 and name = $2") - .bind(char_id.0) - .bind(bank_name.0) - .fetch_one(&self.pool).await?; - // TODO: inefficient - let mut real_bank = Vec::new(); - for bank_item in bank.items.0.into_iter() { - match bank_item { - PgInventoryItemEntity::Individual(item) => { - let entity = sqlx::query_as::<_, PgItemEntity>("select item.id, item.item from item where id = $1") - .bind(item) - .fetch_one(&self.pool).await - .map(|item| item.into()) - .map(|item| self.apply_item_modifications(item))? - .await; - real_bank.push(BankItemEntity::Individual(entity)); - }, - PgInventoryItemEntity::Stacked(items) => { - let mut stacked_item = Vec::new(); - for s_item in items { - stacked_item.push(sqlx::query_as::<_, PgItemEntity>("select item.id, item.item from item where id = $1") - .bind(s_item) - .fetch_one(&self.pool).await - .map(|item| item.into()) - .map(|item| self.apply_item_modifications(item))? - .await) - } - real_bank.push(BankItemEntity::Stacked(stacked_item)); - } - } - } - - Ok(BankEntity::new(real_bank)) + get_character_bank(&mut *self.pool.acquire().await?, char_id, bank_name).await } async fn set_character_inventory(&mut self, char_id: &CharacterEntityId, inventory: &InventoryEntity) -> Result<(), GatewayError> { - let inventory = inventory.items.iter() - .map(|item| { - match item { - InventoryItemEntity::Individual(item) => { - PgInventoryItemEntity::Individual(item.id.0 as i32) - }, - InventoryItemEntity::Stacked(items) => { - PgInventoryItemEntity::Stacked(items.iter().map(|i| i.id.0 as i32).collect()) - }, - } - }) - .collect::>(); - - sqlx::query("insert into inventory (pchar, items) values ($1, $2) on conflict (pchar) do update set items = $2") - .bind(char_id.0) - .bind(sqlx::types::Json(inventory)) - .execute(&self.pool) - .await?; - Ok(()) + set_character_inventory(&mut *self.pool.acquire().await?, char_id, inventory).await } async fn set_character_bank(&mut self, char_id: &CharacterEntityId, bank: &BankEntity, bank_name: BankName) -> Result<(), GatewayError> { - let bank = bank.items.iter() - .map(|item| { - match item { - BankItemEntity::Individual(item) => { - PgInventoryItemEntity::Individual(item.id.0 as i32) - }, - BankItemEntity::Stacked(items) => { - PgInventoryItemEntity::Stacked(items.iter().map(|i| i.id.0 as i32).collect()) - }, - } - }) - .collect::>(); - - sqlx::query("insert into bank (pchar, items, name) values ($1, $2, $3) on conflict (pchar, name) do update set items = $2") - .bind(char_id.0) - .bind(sqlx::types::Json(bank)) - .bind(bank_name.0) - .execute(&self.pool) - .await?; - Ok(()) + set_character_bank(&mut *self.pool.acquire().await?, char_id, bank, bank_name).await } async fn get_character_equips(&mut self, char_id: &CharacterEntityId) -> Result { - let equips = sqlx::query_as::<_, PgEquipped>("select * from equipped where pchar = $1") - .bind(char_id.0) - .fetch_one(&self.pool) - .await?; - - Ok(equips.into()) + get_character_equips(&mut *self.pool.acquire().await?, char_id).await } async fn set_character_equips(&mut self, char_id: &CharacterEntityId, equips: &EquippedEntity) -> Result<(), GatewayError> { - sqlx::query(r#"insert into equipped (pchar, weapon, armor, shield, unit0, unit1, unit2, unit3, mag) values ($1, $2, $3, $4, $5, $6, $7, $8, $9) - on conflict (pchar) do update set weapon=$2, armor=$3, shield=$4, unit0=$5, unit1=$6, unit2=$7, unit3=$8, mag=$9"#) - .bind(char_id.0) - .bind(equips.weapon.map(|i| i.0 as i32)) - .bind(equips.armor.map(|i| i.0 as i32)) - .bind(equips.shield.map(|i| i.0 as i32)) - .bind(equips.unit[0].map(|i| i.0 as i32)) - .bind(equips.unit[1].map(|i| i.0 as i32)) - .bind(equips.unit[2].map(|i| i.0 as i32)) - .bind(equips.unit[3].map(|i| i.0 as i32)) - .bind(equips.mag.map(|i| i.0 as i32)) - .execute(&self.pool) - .await?; - Ok(()) + set_character_equips(&mut *self.pool.acquire().await?, char_id, equips).await } async fn set_character_meseta(&mut self, char_id: &CharacterEntityId, meseta: Meseta) -> Result<(), GatewayError> { - sqlx::query("insert into character_meseta values ($1, $2) on conflict (pchar) do update set items = $2") - .bind(char_id.0) - .bind(meseta.0 as i32) - .execute(&self.pool) - .await?; - Ok(()) + set_character_meseta(&mut *self.pool.acquire().await?, char_id, meseta).await } async fn get_character_meseta(&mut self, char_id: &CharacterEntityId) -> Result { - #[derive(sqlx::FromRow)] - struct PgMeseta(i32); - let meseta = sqlx::query_as::<_, PgMeseta>(r#"select meseta from character_meseta where id = $1"#) - .bind(char_id.0) - .fetch_one(&self.pool) - .await?; - Ok(Meseta(meseta.0 as u32)) + get_character_meseta(&mut *self.pool.acquire().await?, char_id).await } async fn set_bank_meseta(&mut self, char_id: &CharacterEntityId, bank: BankName, meseta: Meseta) -> Result<(), GatewayError> { - sqlx::query("insert into bank_meseta values ($1, $2, $3) on conflict (pchar, bank) do update set items = $2") - .bind(char_id.0) - .bind(meseta.0 as i32) - .bind(bank.0) - .execute(&self.pool) - .await?; - Ok(()) + set_bank_meseta(&mut *self.pool.acquire().await?, char_id, bank, meseta).await } async fn get_bank_meseta(&mut self, char_id: &CharacterEntityId, bank: BankName) -> Result { - #[derive(sqlx::FromRow)] - struct PgMeseta(i32); - let meseta = sqlx::query_as::<_, PgMeseta>(r#"select meseta from character_meseta where id = $1 and bank = $2"#) - .bind(char_id.0) - .bind(bank.0) - .fetch_one(&self.pool) - .await?; - Ok(Meseta(meseta.0 as u32)) + get_bank_meseta(&mut *self.pool.acquire().await?, char_id, bank).await } } + + diff --git a/src/login/character.rs b/src/login/character.rs index f1192eb..e1e49cf 100644 --- a/src/login/character.rs +++ b/src/login/character.rs @@ -326,7 +326,7 @@ impl CharacterServerState { } async fn validate_login(&mut self, id: ClientId, pkt: &Login) -> Result, anyhow::Error> { - match get_login_status(&self.entity_gateway, pkt).await { + match get_login_status(&mut self.entity_gateway, pkt).await { Ok(user) => { if let Some(connected_client) = self.connected_clients.get(&user.id) { if let Some(expires) = connected_client.expires { diff --git a/src/login/login.rs b/src/login/login.rs index 4181136..8e6020f 100644 --- a/src/login/login.rs +++ b/src/login/login.rs @@ -59,7 +59,8 @@ impl SendServerPacket for SendLoginPacket { } } -pub async fn get_login_status(entity_gateway: &impl EntityGateway, pkt: &Login) -> Result { +// TODO: MORE impl EntityGateway? +pub async fn get_login_status(entity_gateway: &mut impl EntityGateway, pkt: &Login) -> Result { let username = array_to_utf8(pkt.username).map_err(|_err| AccountStatus::Error)?; let password = array_to_utf8(pkt.password).map_err(|_err| AccountStatus::Error)?; let user = entity_gateway.get_user_by_name(username).await.map_err(|_| AccountStatus::InvalidUser)?; @@ -108,7 +109,7 @@ impl LoginServerState { } async fn validate_login(&mut self, id: ClientId, pkt: &Login) -> Result, anyhow::Error> { - match get_login_status(&self.entity_gateway, pkt).await.and_then(check_if_already_online) { + match get_login_status(&mut self.entity_gateway, pkt).await.and_then(check_if_already_online) { Ok(mut user) => { user.at_login = true; self.entity_gateway.save_user(&user).await.map_err(|_| LoginError::DbError)?; @@ -351,7 +352,7 @@ mod test { #[async_trait::async_trait] impl EntityGateway for TestData { - async fn get_user_by_name(&self, name: String) -> Result { + async fn get_user_by_name(&mut self, name: String) -> Result { assert!(name == "testuser"); Ok(UserAccountEntity { id: UserAccountId(1), -- 2.36.0 From cbb5c1fffd615fdf9fe99e6f31a1f132ba5d18d8 Mon Sep 17 00:00:00 2001 From: jake Date: Tue, 19 Apr 2022 23:20:01 -0600 Subject: [PATCH 08/60] add transactions! --- src/entity/gateway/entitygateway.rs | 32 ++++- src/entity/gateway/inmemory.rs | 113 ++++++++++++++++- src/entity/gateway/mod.rs | 2 +- src/entity/gateway/postgres/postgres.rs | 162 +++++++++++++++++++++++- 4 files changed, 304 insertions(+), 5 deletions(-) diff --git a/src/entity/gateway/entitygateway.rs b/src/entity/gateway/entitygateway.rs index dbdc401..4ec2c74 100644 --- a/src/entity/gateway/entitygateway.rs +++ b/src/entity/gateway/entitygateway.rs @@ -1,4 +1,6 @@ +use std::convert::From; use thiserror::Error; +use futures::Future; use crate::entity::account::*; use crate::entity::character::*; @@ -15,7 +17,35 @@ pub enum GatewayError { } #[async_trait::async_trait] -pub trait EntityGateway: Send + Sync + Clone { +pub trait EntityGatewayTransaction: Send + Sync { + fn gateway<'a>(&'a mut self) -> &'a mut dyn EntityGateway { + unimplemented!() + } + + async fn commit(self: Box) -> Result<(), GatewayError> { + unimplemented!() + } +} + + +#[async_trait::async_trait] +pub trait EntityGateway: Send + Sync { + async fn transaction(&'static mut self) -> Result, GatewayError> + { + unimplemented!(); + } + + async fn with_transaction(&'static mut self, _func: F) -> Result + where + Fut: Future, R), E>> + Send, + F: FnOnce(Box) -> Fut + Send, + R: Send, + E: From, + Self: Sized + { + unimplemented!(); + } + async fn create_user(&mut self, _user: NewUserAccountEntity) -> Result { unimplemented!() } diff --git a/src/entity/gateway/inmemory.rs b/src/entity/gateway/inmemory.rs index 4c38815..aa9762d 100644 --- a/src/entity/gateway/inmemory.rs +++ b/src/entity/gateway/inmemory.rs @@ -1,13 +1,43 @@ use std::collections::BTreeMap; use std::convert::TryInto; +use futures::Future; use crate::entity::account::*; use crate::entity::character::*; -use crate::entity::gateway::{EntityGateway, GatewayError}; +use crate::entity::gateway::{EntityGateway, EntityGatewayTransaction, GatewayError}; use crate::entity::item::*; use std::sync::{Arc, Mutex}; + +pub struct InMemoryGatewayTransaction { + working_gateway: InMemoryGateway, + original_gateway: &'static mut InMemoryGateway, +} + +#[async_trait::async_trait] +impl EntityGatewayTransaction for InMemoryGatewayTransaction { + fn gateway<'b>(&'b mut self) -> &'b mut dyn EntityGateway { + &mut self.working_gateway + } + + async fn commit(self: Box) -> Result<(), GatewayError> { + self.original_gateway.users = self.working_gateway.users.clone(); + self.original_gateway.user_settings = self.working_gateway.user_settings.clone(); + self.original_gateway.characters = self.working_gateway.characters.clone(); + self.original_gateway.character_meseta = self.working_gateway.character_meseta.clone(); + self.original_gateway.bank_meseta = self.working_gateway.bank_meseta.clone(); + self.original_gateway.items = self.working_gateway.items.clone(); + self.original_gateway.inventories = self.working_gateway.inventories.clone(); + self.original_gateway.banks = self.working_gateway.banks.clone(); + self.original_gateway.equips = self.working_gateway.equips.clone(); + self.original_gateway.mag_modifiers = self.working_gateway.mag_modifiers.clone(); + self.original_gateway.weapon_modifiers = self.working_gateway.weapon_modifiers.clone(); + + Ok(()) + } +} + #[derive(Clone)] pub struct InMemoryGateway { users: Arc>>, @@ -99,6 +129,87 @@ impl InMemoryGateway { #[async_trait::async_trait] impl EntityGateway for InMemoryGateway { + async fn transaction(&'static mut self) -> Result, GatewayError> + { + let working_gateway = { + let users = self.users.lock().unwrap().clone(); + let user_settings = self.user_settings.lock().unwrap().clone(); + let characters = self.characters.lock().unwrap().clone(); + let character_meseta = self.character_meseta.lock().unwrap().clone(); + let bank_meseta = self.bank_meseta.lock().unwrap().clone(); + let items = self.items.lock().unwrap().clone(); + let inventories = self.inventories.lock().unwrap().clone(); + let banks = self.banks.lock().unwrap().clone(); + let equips = self.equips.lock().unwrap().clone(); + let mag_modifiers = self.mag_modifiers.lock().unwrap().clone(); + let weapon_modifiers = self.weapon_modifiers.lock().unwrap().clone(); + + InMemoryGateway { + users: Arc::new(Mutex::new(users)), + user_settings: Arc::new(Mutex::new(user_settings)), + characters: Arc::new(Mutex::new(characters)), + character_meseta: Arc::new(Mutex::new(character_meseta)), + bank_meseta: Arc::new(Mutex::new(bank_meseta)), + items: Arc::new(Mutex::new(items)), + inventories: Arc::new(Mutex::new(inventories)), + banks: Arc::new(Mutex::new(banks)), + equips: Arc::new(Mutex::new(equips)), + mag_modifiers: Arc::new(Mutex::new(mag_modifiers)), + weapon_modifiers: Arc::new(Mutex::new(weapon_modifiers)), + } + }; + + Ok(Box::new(InMemoryGatewayTransaction { + working_gateway, + original_gateway: self, + })) + } + + + async fn with_transaction(&'static mut self, func: F) -> Result + where + Fut: Future, R), E>> + Send, + F: FnOnce(Box) -> Fut + Send, + R: Send, + E: From, + { + let users = self.users.lock().unwrap().clone(); + let user_settings = self.user_settings.lock().unwrap().clone(); + let characters = self.characters.lock().unwrap().clone(); + let character_meseta = self.character_meseta.lock().unwrap().clone(); + let bank_meseta = self.bank_meseta.lock().unwrap().clone(); + let items = self.items.lock().unwrap().clone(); + let inventories = self.inventories.lock().unwrap().clone(); + let banks = self.banks.lock().unwrap().clone(); + let equips = self.equips.lock().unwrap().clone(); + let mag_modifiers = self.mag_modifiers.lock().unwrap().clone(); + let weapon_modifiers = self.weapon_modifiers.lock().unwrap().clone(); + + let working_gateway = InMemoryGateway { + users: Arc::new(Mutex::new(users)), + user_settings: Arc::new(Mutex::new(user_settings)), + characters: Arc::new(Mutex::new(characters)), + character_meseta: Arc::new(Mutex::new(character_meseta)), + bank_meseta: Arc::new(Mutex::new(bank_meseta)), + items: Arc::new(Mutex::new(items)), + inventories: Arc::new(Mutex::new(inventories)), + banks: Arc::new(Mutex::new(banks)), + equips: Arc::new(Mutex::new(equips)), + mag_modifiers: Arc::new(Mutex::new(mag_modifiers)), + weapon_modifiers: Arc::new(Mutex::new(weapon_modifiers)), + }; + + let transaction = Box::new(InMemoryGatewayTransaction { + working_gateway, + original_gateway: self, + }); + + let (mut transaction, result) = func(transaction).await?; + + transaction.commit().await?; + Ok(result) + } + async fn create_user(&mut self, user: NewUserAccountEntity) -> Result { let mut users = self.users.lock().unwrap(); let id = users diff --git a/src/entity/gateway/mod.rs b/src/entity/gateway/mod.rs index d819ef5..4ce029d 100644 --- a/src/entity/gateway/mod.rs +++ b/src/entity/gateway/mod.rs @@ -2,6 +2,6 @@ pub mod entitygateway; pub mod inmemory; pub mod postgres; -pub use entitygateway::{EntityGateway, GatewayError}; +pub use entitygateway::{EntityGateway, EntityGatewayTransaction, GatewayError}; pub use inmemory::InMemoryGateway; pub use self::postgres::PostgresGateway; diff --git a/src/entity/gateway/postgres/postgres.rs b/src/entity/gateway/postgres/postgres.rs index 10961a5..b87dfad 100644 --- a/src/entity/gateway/postgres/postgres.rs +++ b/src/entity/gateway/postgres/postgres.rs @@ -1,14 +1,17 @@ +use async_std::sync::{Arc, Mutex}; use std::convert::{From, TryFrom, Into}; -use futures::TryStreamExt; +use futures::{Future, TryStreamExt}; use async_std::stream::StreamExt; use libpso::character::guildcard; use crate::entity::account::*; use crate::entity::character::*; -use crate::entity::gateway::{EntityGateway, GatewayError}; +use crate::entity::gateway::{EntityGateway, EntityGatewayTransaction, GatewayError}; use crate::entity::item::*; use super::models::*; use sqlx::postgres::PgPoolOptions; +use sqlx::Connection; + mod embedded { use refinery::embed_migrations; @@ -16,6 +19,24 @@ mod embedded { } +pub struct PostgresTransaction<'t> { + pgtransaction: sqlx::Transaction<'t, sqlx::Postgres>, +} + + +#[async_trait::async_trait] +impl<'t> EntityGatewayTransaction for PostgresTransaction<'t> { + fn gateway<'b>(&'b mut self) -> &'b mut dyn EntityGateway { + self + } + + async fn commit(self: Box) -> Result<(), GatewayError> { + self.pgtransaction.commit().await?; + Ok(()) + } +} + + #[derive(Clone)] pub struct PostgresGateway { pool: sqlx::Pool, @@ -539,6 +560,28 @@ async fn get_bank_meseta(conn: &mut sqlx::PgConnection, char_id: &CharacterEntit #[async_trait::async_trait] impl EntityGateway for PostgresGateway { + async fn transaction(&'static mut self) -> Result, GatewayError> + { + Ok(Box::new(PostgresTransaction { + pgtransaction: self.pool.begin().await?, + })) + } + + async fn with_transaction(&'static mut self, func: F) -> Result + where + Fut: Future, R), E>> + Send, + F: FnOnce(Box) -> Fut + Send, + R: Send, + E: From, + { + let mut transaction = Box::new(PostgresTransaction { + pgtransaction: self.pool.begin().await.map_err(|_| ()).unwrap() + }); + let (mut transaction, result) = func(transaction).await.map_err(|_| ()).unwrap(); + transaction.commit().await.map_err(|_| ()).unwrap(); + Ok(result) + } + async fn create_user(&mut self, user: NewUserAccountEntity) -> Result { create_user(&mut *self.pool.acquire().await?, user).await } @@ -653,3 +696,118 @@ impl EntityGateway for PostgresGateway { } +#[async_trait::async_trait] +impl<'c> EntityGateway for PostgresTransaction<'c> { + async fn create_user(&mut self, user: NewUserAccountEntity) -> Result { + create_user(&mut *self.pgtransaction, user).await + } + + async fn get_user_by_id(&mut self, id: UserAccountId) -> Result { + get_user_by_id(&mut *self.pgtransaction, id).await + } + + async fn get_user_by_name(&mut self, username: String) -> Result { + get_user_by_name(&mut *self.pgtransaction, username).await + } + + async fn save_user(&mut self, user: &UserAccountEntity) -> Result<(), GatewayError> { + save_user(&mut *self.pgtransaction, user).await + } + + async fn create_user_settings(&mut self, settings: NewUserSettingsEntity) -> Result { + create_user_settings(&mut *self.pgtransaction, settings).await + } + + async fn get_user_settings_by_user(&mut self, user: &UserAccountEntity) -> Result { + get_user_settings_by_user(&mut *self.pgtransaction, user).await + } + + async fn save_user_settings(&mut self, settings: &UserSettingsEntity) -> Result<(), GatewayError> { + save_user_settings(&mut *self.pgtransaction, settings).await + } + + async fn create_character(&mut self, char: NewCharacterEntity) -> Result { + create_character(&mut *self.pgtransaction, char).await + } + + async fn get_characters_by_user(&mut self, user: &UserAccountEntity) -> Result<[Option; 4], GatewayError> { + get_characters_by_user(&mut *self.pgtransaction, user).await + } + + async fn save_character(&mut self, char: &CharacterEntity) -> Result<(), GatewayError> { + save_character(&mut *self.pgtransaction, char).await + } + + async fn get_guild_card_data_by_user(&mut self, user: &UserAccountEntity) -> Result { + Ok(GuildCardDataEntity { + id: GuildCardDataId(0), + user_id: user.id, + guildcard: guildcard::GuildCardData::default(), + }) + } + + async fn create_item(&mut self, item: NewItemEntity) -> Result { + create_item(&mut *self.pgtransaction, item).await + } + + async fn add_item_note(&mut self, item_id: &ItemEntityId, item_note: ItemNote) -> Result<(), GatewayError> { + add_item_note(&mut *self.pgtransaction, item_id, item_note).await + } + + async fn feed_mag(&mut self, mag_item_id: &ItemEntityId, tool_item_id: &ItemEntityId) -> Result<(), GatewayError> { + feed_mag(&mut *self.pgtransaction, mag_item_id, tool_item_id).await + } + + async fn change_mag_owner(&mut self, mag_item_id: &ItemEntityId, character: &CharacterEntity) -> Result<(), GatewayError> { + change_mag_owner(&mut *self.pgtransaction, mag_item_id, character).await + } + + async fn use_mag_cell(&mut self, mag_item_id: &ItemEntityId, mag_cell_id: &ItemEntityId) -> Result<(), GatewayError> { + use_mag_cell(&mut *self.pgtransaction, mag_item_id, mag_cell_id).await + } + + async fn add_weapon_modifier(&mut self, item_id: &ItemEntityId, modifier: weapon::WeaponModifier) -> Result<(), GatewayError> { + add_weapon_modifier(&mut *self.pgtransaction, item_id, modifier).await + } + + async fn get_character_inventory(&mut self, char_id: &CharacterEntityId) -> Result { + get_character_inventory(&mut *self.pgtransaction, char_id).await + } + + async fn get_character_bank(&mut self, char_id: &CharacterEntityId, bank_name: BankName) -> Result { + get_character_bank(&mut *self.pgtransaction, char_id, bank_name).await + } + + async fn set_character_inventory(&mut self, char_id: &CharacterEntityId, inventory: &InventoryEntity) -> Result<(), GatewayError> { + set_character_inventory(&mut *self.pgtransaction, char_id, inventory).await + } + + async fn set_character_bank(&mut self, char_id: &CharacterEntityId, bank: &BankEntity, bank_name: BankName) -> Result<(), GatewayError> { + set_character_bank(&mut *self.pgtransaction, char_id, bank, bank_name).await + } + + async fn get_character_equips(&mut self, char_id: &CharacterEntityId) -> Result { + get_character_equips(&mut *self.pgtransaction, char_id).await + } + + async fn set_character_equips(&mut self, char_id: &CharacterEntityId, equips: &EquippedEntity) -> Result<(), GatewayError> { + set_character_equips(&mut *self.pgtransaction, char_id, equips).await + } + + async fn set_character_meseta(&mut self, char_id: &CharacterEntityId, meseta: Meseta) -> Result<(), GatewayError> { + set_character_meseta(&mut *self.pgtransaction, char_id, meseta).await + } + + async fn get_character_meseta(&mut self, char_id: &CharacterEntityId) -> Result { + get_character_meseta(&mut *self.pgtransaction, char_id).await + } + + async fn set_bank_meseta(&mut self, char_id: &CharacterEntityId, bank: BankName, meseta: Meseta) -> Result<(), GatewayError> { + set_bank_meseta(&mut *self.pgtransaction, char_id, bank, meseta).await + } + + async fn get_bank_meseta(&mut self, char_id: &CharacterEntityId, bank: BankName) -> Result { + get_bank_meseta(&mut *self.pgtransaction, char_id, bank).await + } +} + -- 2.36.0 From bc702375d2f7933fe3288fe2592c3b1b004a1dbc Mon Sep 17 00:00:00 2001 From: jake Date: Tue, 19 Apr 2022 23:20:16 -0600 Subject: [PATCH 09/60] redo this cause entitygateway isnt Clone anymore --- src/login/character.rs | 59 ++++++++++++++++++++++-------------------- 1 file changed, 31 insertions(+), 28 deletions(-) diff --git a/src/login/character.rs b/src/login/character.rs index e1e49cf..595b261 100644 --- a/src/login/character.rs +++ b/src/login/character.rs @@ -257,34 +257,37 @@ async fn new_character(entity_gateway: &mut EG, user: &UserAc character_id: character.id, }).await?; - let (monomates, monofluids) = futures::future::join_all((0..4usize).map(|_| { - let mut eg = entity_gateway.clone(); - let character_id = character.id; - async move { - let monomate = eg.create_item( - NewItemEntity { - item: ItemDetail::Tool ( - Tool { - tool: item::tool::ToolType::Monomate, - })}).await?; - - eg.add_item_note(&monomate.id, ItemNote::CharacterCreation { - character_id - }).await?; - - let monofluid = eg.create_item( - NewItemEntity { - item: ItemDetail::Tool ( - Tool { - tool: item::tool::ToolType::Monofluid, - })}).await?; - - eg.add_item_note(&monofluid.id, ItemNote::CharacterCreation { - character_id - }).await?; - - Ok((monomate, monofluid)) - }})).await.into_iter().collect::, GatewayError>>()?.into_iter().unzip(); + let mut monomates = Vec::new(); + for _ in 0..4usize { + let monomate = entity_gateway.create_item( + NewItemEntity { + item: ItemDetail::Tool ( + Tool { + tool: item::tool::ToolType::Monomate, + })}).await?; + + entity_gateway.add_item_note(&monomate.id, ItemNote::CharacterCreation { + character_id: character.id + }).await?; + + monomates.push(monomate); + } + + let mut monofluids = Vec::new(); + for _ in 0..4usize { + let monofluid = entity_gateway.create_item( + NewItemEntity { + item: ItemDetail::Tool ( + Tool { + tool: item::tool::ToolType::Monofluid, + })}).await?; + + entity_gateway.add_item_note(&monofluid.id, ItemNote::CharacterCreation { + character_id: character.id + }).await?; + + monofluids.push(monofluid); + } let inventory = InventoryEntity { items: vec![InventoryItemEntity::Individual(weapon.clone()), InventoryItemEntity::Individual(armor.clone()), InventoryItemEntity::Individual(mag.clone()), -- 2.36.0 From e337852d726bd1e8068ed8a041d6e41f750d2bbf Mon Sep 17 00:00:00 2001 From: jake Date: Tue, 19 Apr 2022 23:20:50 -0600 Subject: [PATCH 10/60] initial ItemState --- Cargo.toml | 2 +- src/lib.rs | 2 + src/ship/items/mod.rs | 1 + src/ship/items/state.rs | 813 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 817 insertions(+), 1 deletion(-) create mode 100644 src/ship/items/state.rs diff --git a/Cargo.toml b/Cargo.toml index 3f17eea..789571d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,4 +32,4 @@ sqlx = { version = "0.5.10", features = ["runtime-async-std-native-tls", "postgr strum = "0.19.5" strum_macros = "0.19" anyhow = { version = "1.0.47", features = ["backtrace"] } - +fix-hidden-lifetime-bug = "0.2.4" diff --git a/src/lib.rs b/src/lib.rs index b8ceb02..9934022 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,6 +3,8 @@ #![feature(drain_filter)] #![feature(try_blocks)] +#[macro_use] +extern crate fix_hidden_lifetime_bug; pub mod common; diff --git a/src/ship/items/mod.rs b/src/ship/items/mod.rs index 8a917a8..2b88239 100644 --- a/src/ship/items/mod.rs +++ b/src/ship/items/mod.rs @@ -4,6 +4,7 @@ pub mod inventory; pub mod manager; pub mod transaction; pub mod use_tool; +pub mod state; use serde::{Serialize, Deserialize}; #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, Serialize, Deserialize, derive_more::Display)] diff --git a/src/ship/items/state.rs b/src/ship/items/state.rs new file mode 100644 index 0000000..b7ce7d4 --- /dev/null +++ b/src/ship/items/state.rs @@ -0,0 +1,813 @@ +use std::collections::HashMap; +use crate::ship::items::ClientItemId; +use crate::entity::item::{Meseta, ItemEntityId, ItemDetail, ItemEntity, ItemType, InventoryEntity, InventoryItemEntity, EquippedEntity, ItemNote}; +use std::cell::{RefMut, RefCell}; +use std::ops::{Deref, DerefMut}; +use std::convert::{From, Into}; +use std::future::Future; +use std::pin::Pin; +use async_std::sync::{Arc, Mutex}; +use std::borrow::BorrowMut; + +use crate::ship::location::{AreaClient, RoomId}; +use crate::entity::character::{CharacterEntity, CharacterEntityId, TechLevel}; +use crate::entity::gateway::{EntityGateway, GatewayError}; +use crate::entity::gateway::entitygateway::EntityGatewayTransaction; +use crate::entity::item::tool::{Tool, ToolType}; +use crate::ship::drops::ItemDrop; + +pub enum TriggerCreateItem { + Yes, + No +} + + + +#[derive(thiserror::Error, Debug)] +enum ItemStateError { + #[error("character {0} not found")] + NoCharacter(CharacterEntityId), + #[error("room {0} not found")] + NoRoom(RoomId), + #[error("floor item {0} not found")] + NoFloorItem(ClientItemId), + + #[error("inventory error {0}")] + InventoryError(#[from] InventoryError), + + #[error("invalid drop? {0:?} (this shouldn't occur)")] + BadItemDrop(ItemDrop), + + #[error("idk")] + Dummy, + + #[error("gateway")] + GatewayError(GatewayError), +} + +impl From for ItemStateError { + fn from(other: GatewayError) -> ItemStateError { + ItemStateError::GatewayError(other) + } +} + + + +enum GatewayActions { + ItemNote(ItemEntityId, ItemNote), +} + +/* +enum ItemStateChange { + Inventory(CharacterInventory), + Bank(CharacterBank), + CharacterMeseta(CharacterMeseta), + BankMeseta(BankMeseta), + SharedFloor(SharedFloor), + LocalFloor(LocalFloor), +} +*/ + +#[async_trait::async_trait] +trait ItemAction { + type Input; + type Output; + type Start; + type Error; + + async fn action(&self, s: Self::Start, i: Self::Input) -> Result<(Self::Start, Self::Output), Self::Error>; + async fn commit(&self, v: Self::Start) -> Result<(Self::Start, Self::Output), Self::Error>; +} + + +struct ItemStateAction { + _t: std::marker::PhantomData, + _s: std::marker::PhantomData, + _e: std::marker::PhantomData, +} + +impl Default for ItemStateAction { + fn default() -> ItemStateAction { + ItemStateAction { + _t: std::marker::PhantomData, + _s: std::marker::PhantomData, + _e: std::marker::PhantomData, + } + } +} + +impl ItemStateAction +where + T: Send + Sync, + S: Send + Sync, + E: Send + Sync, +{ + fn act(self, f: F) -> ItemActionStage, F, S, E> + where + F: Fn(S, ()) -> Pin> + Send>> + Send + Sync + { + ItemActionStage { + _s: Default::default(), + _e: std::marker::PhantomData, + prev: self, + actionf: f, + //actionf: |s, t| f(s, ()), + } + } +} + +struct ItemActionStage +where + P: ItemAction, + //F: Fn(&mut S, P::Output) -> Result + for<'r> std::ops::Fn<(&'r mut S, T)>, + F: Fn(S, P::Output) -> Pin> + Send>> + Send + Sync, +{ + _s: std::marker::PhantomData, + _e: std::marker::PhantomData, + prev: P, + actionf: F, +} + +#[async_trait::async_trait] +impl ItemAction for ItemActionStage +where + P: ItemAction + ItemAction + Send + Sync, + F: Fn(S, P::Output) -> Pin> + Send>> + Send + Sync, + S: Send + Sync, + P::Output: Send + Sync, + E: Send + Sync, + O: Send + Sync, + P::Error: Send + Sync, +{ + type Input = P::Output; + type Output = O; + type Start = S; + type Error = P::Error; + + async fn action(&self, s: Self::Start, i: Self::Input) -> Result<(Self::Start, Self::Output), Self::Error> { + (self.actionf)(s, i).await + } + + async fn commit(&self, i: Self::Start) -> Result<(Self::Start, Self::Output), Self::Error> { + let (i, prev) = self.prev.commit(i).await?; + self.action(i, prev).await + } +} + +impl ItemActionStage +where + P: ItemAction + Send + Sync, + F: Fn(S, P::Output) -> Pin> + Send>> + Send + Sync, + S: Send + Sync, + P::Output: Send + Sync, + E: Send + Sync, + O: Send + Sync, + P::Error: Send + Sync, +{ + fn act(self, g: G) -> ItemActionStage, G, S, E> + where + S: Send + Sync, + G: Fn(S, as ItemAction>::Output) -> Pin> + Send>> + Send + Sync, + O2: Send + Sync, + { + ItemActionStage { + _s: Default::default(), + _e: Default::default(), + prev: self, + actionf: g, + } + } +} + +#[async_trait::async_trait] +impl ItemAction for ItemStateAction +where + T: Send + Sync, + S: Send + Sync, + E: Send + Sync, +{ + type Input = T; + type Output = (); + type Start = T; + type Error = E; + + async fn action(&self, s: Self::Start, i: Self::Input) -> Result<(Self::Start, Self::Output), Self::Error> { + Ok((s, ())) + } + + async fn commit(&self, i: Self::Start) -> Result<(Self::Start, Self::Output), Self::Error> { + Ok((i, ())) + } +} + + +#[derive(Clone)] +struct IndividualItemDetail { + entity_id: ItemEntityId, + item: ItemDetail, +} + +#[derive(Clone)] +pub struct StackedItemDetail { + pub entity_ids: Vec, + pub tool: Tool, +} + +#[derive(Clone)] +enum InventoryItemDetail { + Individual(IndividualItemDetail), + Stacked(StackedItemDetail), +} + +impl InventoryItemDetail { + fn stacked<'a>(&'a self) -> Option<&'a StackedItemDetail> { + match self { + InventoryItemDetail::Stacked(sitem) => Some(sitem), + _ => None, + } + } + fn stacked_mut <'a>(&'a mut self) -> Option<&'a mut StackedItemDetail> { + match self { + InventoryItemDetail::Stacked(sitem) => Some(sitem), + _ => None, + } + } +} + + +#[derive(Clone)] +struct InventoryItem { + item_id: ClientItemId, + item: InventoryItemDetail, +} + +#[derive(Clone)] +enum FloorItemDetail { + Individual(IndividualItemDetail), + Stacked(StackedItemDetail), + Meseta(Meseta), +} + +impl FloorItemDetail { + fn stacked<'a>(&'a self) -> Option<&'a StackedItemDetail> { + match self { + FloorItemDetail::Stacked(sitem) => Some(sitem), + _ => None, + } + } +} + +#[derive(Clone)] +struct FloorItem { + item_id: ClientItemId, + item: FloorItemDetail, +} + +// meseta is a floor item +/* +impl Into for FloorItem { + fn into(&self) -> InventoryItem { + InventoryItem { + item_id: self.item_id, + item: + } + } +} +*/ + +#[derive(Clone)] +struct Inventory(Vec); + + +#[derive(thiserror::Error, Debug)] +enum InventoryError { + #[error("inventory full")] + InventoryFull, + #[error("stack full")] + StackFull, + #[error("meseta full")] + MesetaFull, +} + +enum AddItemResult { + NewItem, + AddToStack, + Meseta, +} + + + + + +#[derive(Clone)] +struct LocalFloor(Vec); +#[derive(Clone)] +struct SharedFloor(Vec); +#[derive(Clone)] +struct RoomFloorItems(Vec); + +struct InventoryState { + character_id: CharacterEntityId, + inventory: Inventory, + meseta: Meseta, +} + + +/* +impl Deref for InventoryState { + type Target = Inventory; + + fn deref(&self) -> &Self::Target { + &self.inventory + } +} + + +impl DerefMut for InventoryState { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.inventory + } +} +*/ + +impl InventoryState { + fn add_floor_item(&mut self, item: FloorItem) -> Result { + match item.item { + FloorItemDetail::Individual(iitem) => { + if self.inventory.0.len() >= 30 { + return Err(InventoryError::InventoryFull) + } + else { + self.inventory.0.push(InventoryItem { + item_id: item.item_id, + item: InventoryItemDetail::Individual(iitem) + }); + Ok(AddItemResult::NewItem) + } + }, + FloorItemDetail::Stacked(sitem) => { + let existing_stack = self.inventory.0 + .iter_mut() + .filter_map(|item| item.item.stacked_mut()) + .find(|item| { + item.tool == sitem.tool + }); + match existing_stack { + Some(existing_stack) => { + if existing_stack.entity_ids.len() + sitem.entity_ids.len() > sitem.tool.max_stack() { + return Err(InventoryError::StackFull) + } + else { + existing_stack.entity_ids.append(&mut sitem.entity_ids.clone()); + Ok(AddItemResult::AddToStack) + } + }, + None => { + if self.inventory.0.len() >= 30 { + return Err(InventoryError::InventoryFull) + } + else { + self.inventory.0.push(InventoryItem { + item_id: item.item_id, + item: InventoryItemDetail::Stacked(sitem) + }); + Ok(AddItemResult::NewItem) + } + } + } + + }, + FloorItemDetail::Meseta(meseta) => { + if self.meseta == Meseta(999999) { + Err(InventoryError::MesetaFull) + } + else { + self.meseta.0 = std::cmp::min(self.meseta.0 + meseta.0 ,999999); + Ok(AddItemResult::Meseta) + } + }, + } + } +} + +struct FloorState { + character_id: CharacterEntityId, + local: LocalFloor, + shared: SharedFloor, +} + +impl FloorState { + fn take_item(&mut self, item_id: &ClientItemId) -> Option { + let item = self.local.0 + .drain_filter(|item| { + item.item_id == *item_id + }) + .next(); + item.or_else(|| { + self.shared.0 + .drain_filter(|item| { + item.item_id == *item_id + }) + .next() + }) + } +} + + +struct ItemState { + character_inventory: HashMap, + //character_bank: HashMap, + character_meseta: HashMap, + //bank_meseta: HashMap, + + character_room: HashMap, + character_floor: HashMap, + room_floor: HashMap, + + //room_item_id_counter: Arc ClientItemId + Send + Sync>>>>, + room_item_id_counter: u32, +} + +impl Default for ItemState { + fn default() -> ItemState { + ItemState { + character_inventory: HashMap::new(), + character_meseta: HashMap::new(), + character_room: HashMap::new(), + character_floor: HashMap::new(), + room_floor: HashMap::new(), + room_item_id_counter: 0x00810000, + } + } +} + + +/* +struct ProxiedItemState { + character_inventory: RefCell>>, + //character_bank: HashMap>, + //character_meseta: HashMap>, + //bank_meseta: HashMap>, + + character_room: RefCell>>, + character_floor: RefCell>>, + room_floor: RefCell>>, + + //room_item_id_counter: HashMap ClientItemId + Send>>>, +} + +impl Default for ProxiedItemState { + fn default() -> Self { + ProxiedItemState { + character_inventory: RefCell::new(HashMap::new()), + //character_bank: HashMap::new(), + //character_meseta: HashMap::new(), + //bank_meseta: HashMap::new(), + character_floor: RefCell::new(HashMap::new()), + character_room: RefCell::new(HashMap::new()), + room_floor: RefCell::new(HashMap::new()), + //room_item_id_counter: HashMap::new(), + } + } +} +*/ + +struct ProxiedItemState { + character_inventory: HashMap, + //character_bank: HashMap>, + character_meseta: HashMap, + //bank_meseta: HashMap>, + + character_room: HashMap, + character_floor: HashMap, + room_floor: HashMap, + + //room_item_id_counter: HashMap ClientItemId + Send>>, + +} + +impl Default for ProxiedItemState { + fn default() -> Self { + ProxiedItemState { + character_inventory: HashMap::new(), + //character_bank: HashMap::new(), + character_meseta: HashMap::new(), + //bank_meseta: HashMap::new(), + character_floor: HashMap::new(), + character_room: HashMap::new(), + room_floor: HashMap::new(), + //room_item_id_counter: HashMap::new(), + } + } +} + +struct ItemStateProxy<'a> { + item_state: &'a mut ItemState, + //entity_gateway: &'a mut dyn EntityGateway, + transaction: Box, + //entity_gateway: &'a mut Box, + //entity_gateway: &'a mut Box, + proxied_state: ProxiedItemState, + + gateway_actions: Vec, + + //_eg: std::marker::PhantomData, +} + +/* +fn get_or_clone<'a, K, V>(master: &HashMap, proxy: &'a RefCell>>, key: K, err: fn(K) -> ItemStateError) -> Result + 'a, ItemStateError> +where + K: Eq + std::hash::Hash + Copy, + V: Clone +{ + let existing_element = master.get(&key).ok_or_else(|| err(key))?; + Ok(proxy.borrow_mut().entry(key) + .or_insert(RefCell::new(existing_element.clone())) + .borrow_mut()) + +} +*/ + + +/* +fn get_or_clone<'a, K, V>(master: &HashMap, proxy: &'a mut HashMap>, key: K, err: fn(K) -> ItemStateError) -> Result + 'a, ItemStateError> +where + K: Eq + std::hash::Hash + Copy, + V: Clone +{ + let existing_element = master.get(&key).ok_or_else(|| err(key))?; + Ok(proxy.entry(key) + .or_insert(RefCell::new(existing_element.clone())) + .borrow_mut()) + +} +*/ + +fn get_or_clone(master: &HashMap, proxy: &mut HashMap, key: K, err: fn(K) -> ItemStateError) -> Result +where + K: Eq + std::hash::Hash + Copy, + V: Clone +{ + let existing_element = master.get(&key).ok_or_else(|| err(key))?; + Ok(proxy.entry(key) + .or_insert(existing_element.clone()).clone()) + +} + +impl<'a> ItemStateProxy<'a> { + //fn new(item_state: &'a mut ItemState, entity_gateway: &'a mut EG) -> Self { + //fn new(item_state: &'a mut ItemState, entity_gateway: &'a mut dyn EntityGateway) -> Self { + fn new(item_state: &'a mut ItemState, transaction: Box) -> Self { + ItemStateProxy { + item_state, + //entity_gateway, + transaction, + proxied_state: Default::default(), + gateway_actions: Vec::new(), + //_eg: std::marker::PhantomData, + } + } + + fn inventory(&mut self, character_id: &CharacterEntityId) -> Result { + Ok(InventoryState { + character_id: *character_id, + inventory: get_or_clone(&self.item_state.character_inventory, &mut self.proxied_state.character_inventory, *character_id, |c| ItemStateError::NoCharacter(c))?, + meseta: get_or_clone(&self.item_state.character_meseta, &mut self.proxied_state.character_meseta, *character_id, |c| ItemStateError::NoCharacter(c))?, + }) + } + + fn set_inventory(&mut self, inventory: InventoryState) { + self.proxied_state.character_inventory.insert(inventory.character_id, inventory.inventory); + self.proxied_state.character_meseta.insert(inventory.character_id, inventory.meseta); + } + + fn floor(&mut self, character_id: &CharacterEntityId) -> Result { + let room_id = get_or_clone(&self.item_state.character_room, &mut self.proxied_state.character_room, *character_id, |c| ItemStateError::NoCharacter(c))?; + Ok(FloorState { + character_id: *character_id, + local: get_or_clone(&self.item_state.character_floor, &mut self.proxied_state.character_floor, *character_id, |c| ItemStateError::NoCharacter(c))?, + shared: get_or_clone(&self.item_state.room_floor, &mut self.proxied_state.room_floor, room_id, |r| ItemStateError::NoRoom(r))?, + }) + } + + fn set_floor(&mut self, floor: FloorState) { + let room_id = get_or_clone(&self.item_state.character_room, &mut self.proxied_state.character_room, floor.character_id, |c| ItemStateError::NoCharacter(c)).unwrap(); + self.proxied_state.character_floor.insert(floor.character_id, floor.local); + self.proxied_state.room_floor.insert(room_id, floor.shared); + } + + fn new_item_id(&mut self) -> Result { + //let room_id = get_or_clone(&self.item_state.character_room, &mut self.proxied_state.character_room, *character_id, |c| ItemStateError::NoCharacter(c))?; + self.item_state.room_item_id_counter += 1; + Ok(ClientItemId(self.item_state.room_item_id_counter)) + /* + self.item_state.room_item_id_counter + .borrow_mut() + .get_mut(&room_id) + .ok_or(ItemStateError::NoRoom(room_id)) + .map(|f| f()) + */ + } +} + + +fn take_item_from_floor(character_id: CharacterEntityId, item_id: ClientItemId) + -> impl for<'a> Fn(ItemStateProxy<'a>, ()) + -> Pin> + Send + 'a>> { + move |mut item_state, _| { + Box::pin(async move { + let mut floor = item_state.floor(&character_id)?; + let item = floor.take_item(&item_id).ok_or(ItemStateError::NoFloorItem(item_id))?; + item_state.set_floor(floor); + Ok((item_state, item)) + }) + } +} + +fn add_floor_item_to_inventory(character_id: CharacterEntityId) + -> impl for<'a> Fn(ItemStateProxy<'a>, FloorItem) + -> Pin> + Send + 'a>> { + move |mut item_state, floor_item| Box::pin(async move { + let mut inventory = item_state.inventory(&character_id)?; + let add_result = inventory.add_floor_item(floor_item)?; + item_state.set_inventory(inventory); + Ok((item_state, + match add_result { + AddItemResult::NewItem => TriggerCreateItem::Yes, + AddItemResult::AddToStack => TriggerCreateItem::No, + AddItemResult::Meseta => TriggerCreateItem::No, + })) + }) +} + + +//hint R>(f: F) -> F {f} + + +#[fix_hidden_lifetime_bug] +async fn pick_up_item<'a, EG>( + item_state: &'a mut ItemState, + //item_state: Arc>, + entity_gateway: &'a mut EG, + character_id: &CharacterEntityId, + item_id: &ClientItemId) + -> Result +where + 'a: 'static, + EG: EntityGateway, +{ + /* + let transaction = entity_gateway.transaction().await.unwrap(); + + let item_state_proxy = ItemStateProxy::new(item_state, transaction); + let (mut item_state_proxy, result) = ItemStateAction::default() + .act(take_item_from_floor(*character_id, *item_id)) + .act(add_floor_item_to_inventory(*character_id)) + .commit(item_state_proxy) + .await?; + item_state_proxy.transaction.commit().await.unwrap(); + */ + + //drop(item_state_proxy); + //transaction.commit().await.unwrap(); + //item_state_proxy.entity_gateway.commit().await.unwrap(); + + let k: Result = entity_gateway.with_transaction(|transaction| async move { + let item_state_proxy = ItemStateProxy::new(item_state, transaction); + let (item_state_proxy, result) = ItemStateAction::default() + .act(take_item_from_floor(*character_id, *item_id)) + .act(add_floor_item_to_inventory(*character_id)) + .commit(item_state_proxy) + .await?; + + //let u = transaction.get_user_by_id(crate::entity::account::UserAccountId(0)).await?; + //Ok((transaction, ())) + //drop(item_state_proxy); + Ok((item_state_proxy.transaction, result)) + }).await; + + /* + //fn subaction(transaction: &mut dyn EntityGateway) -> Pin> + Send + 'static>> { + async fn subaction(transaction: &mut dyn EntityGateway) -> Result<(), GatewayError> { + let item_state_proxy = ItemStateProxy::new(item_state, transaction); + + Ok(()) + } + + //async fn action(item_state: &mut ItemState, entity_gateway: &mut EG) -> Result<(), GatewayError> { + fn action(item_state: &mut ItemState) + -> impl FnOnce(&mut dyn EntityGateway) -> Pin> + Send + 'static>> + { + /* + |transaction| Box::pin(async move { + Ok(()) + }) + */ + subaction + } + */ + + //let k: Result<(), GatewayError> = entity_gateway.with_transaction(action(item_state)).await; + + //let item_state = item_state.clone(); + /* + let k: Result<(), GatewayError> = entity_gateway.with_transaction(|transaction| async move { + let item_state_proxy = ItemStateProxy::new(item_state, transaction); + /* + let item_state = item_state.clone(); + let mut s = item_state.lock().await; + //let s = s.borrow_mut(); + let item_state_proxy = ItemStateProxy::new(&mut s, transaction); + ItemStateAction::default() + .act(take_item_from_floor(*character_id, *item_id)) + .act(add_floor_item_to_inventory(*character_id)) + .commit(item_state_proxy) + .await + */ + Ok(()) + }).await; + */ + + //let mut transaction = entity_gateway.transaction().await?; + + /*entity_gateway.with_transaction(|transaction| { + item_state_proxy.execute(&mut transaction)?.await + }).await?;*/ + + //Ok(result) + Ok(TriggerCreateItem::Yes) + //.exec_gateway(&mut entity_gateway) +} + + +/* +fn record_item_drop(character_id: CharacterEntityId, item_drop: ItemDrop) -> impl Fn(&mut ItemStateProxy, ()) -> Result<(), ItemStateError> { + move |item_state, _| { + // how do I do this? I need EG to add_item_note but how should I kick that till later? + // general purpose vec in item_state `misc_gateway_actions`? + // can't quite use actions here as it relies on an ItemEntityId which at this point does not yet exist for the dropped item + //item_state.gateway_actions.push(GatewayAction::ItemNote(item_drop.)) + Ok(()) + } +} +*/ + +/* +fn map_drop_to_floor_item(character_id: CharacterEntityId, item_drop: ItemDrop) -> impl Fn(&mut ItemStateProxy, ()) -> Result { + move |item_state, _| { + match item_drop.item { + ItemDropType::Weapon(w) => FloorItem { + item_id: item_state.new_item_id(&character_id)?, + item: FloorItemDetail::Individual(item_drop.item) + }, + ItemDropType::Armor(w) => ItemOrMeseta::Individual(ItemDetail::Armor(w)), + ItemDropType::Shield(w) => ItemOrMeseta::Individual(ItemDetail::Shield(w)), + ItemDropType::Unit(w) => ItemOrMeseta::Individual(ItemDetail::Unit(w)), + ItemDropType::TechniqueDisk(w) => ItemOrMeseta::Individual(ItemDetail::TechniqueDisk(w)), + ItemDropType::Mag(w) => ItemOrMeseta::Individual(ItemDetail::Mag(w)), + //ItemDropType::IndividualTool(t) => ItemOrMeseta::Individual(ItemDetail::Tool(t)), + //ItemDropType::StackedTool(t, _) => ItemOrMeseta::Stacked(t), + ItemDropType::Tool(t) if t.tool.is_stackable() => ItemOrMeseta::Stacked(t), + ItemDropType::Tool(t) if !t.tool.is_stackable() => ItemOrMeseta::Individual(ItemDetail::Tool(t)), + ItemDropType::Meseta(m) => ItemOrMeseta::Meseta(Meseta(m)), + _ => unreachable!() // rust isnt smart enough to see that the conditional on tool catches everything + } + } +} + + + +fn enemy_drops_item(item_state: &mut ItemState, + entity_gateway: &mut EG, + character: &CharacterEntity, + item_drop: ItemDrop) + -> Result +where + EG: EntityGateway, +{ + ItemStateAction::default() + .act(record_item_drop(character.id, item_drop.clone())) + .act(map_drop_to_floor_item(character.id, item_drop)) + .act(add_item_drop_to_floor(character.id)) + .commit(&mut ItemStateProxy::new(item_state)) +} +*/ + +/* +fn sell_item(item_state: &mut ItemState, + entity_gateway: &mut EG, + character_id: &CharacterEntityId, + item_id: &ClientItemId, + amount: usize) + -> Result +where + EG: EntityGateway, +{ + ItemStateAction::default() + .act(take_item_from_inventory(*character_id, *item_id)) + .act(sell_inventory_item(*character_id, *item_id)) + .exec_state(&mut ItemStateProxy::new(item_state)) + .exec_gateway(&mut entity_gateway) +} +*/ + -- 2.36.0 From 3c8a35de8317e60e23ea22fa4e21b76b91964a9e Mon Sep 17 00:00:00 2001 From: jake Date: Wed, 20 Apr 2022 00:04:33 -0600 Subject: [PATCH 11/60] remove unused use --- src/entity/gateway/postgres/postgres.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/entity/gateway/postgres/postgres.rs b/src/entity/gateway/postgres/postgres.rs index b87dfad..ceef84e 100644 --- a/src/entity/gateway/postgres/postgres.rs +++ b/src/entity/gateway/postgres/postgres.rs @@ -1,4 +1,3 @@ -use async_std::sync::{Arc, Mutex}; use std::convert::{From, TryFrom, Into}; use futures::{Future, TryStreamExt}; use async_std::stream::StreamExt; -- 2.36.0 From bdfcb5c0ad7c4c4a5ff52c07dcdcb5f9a7a8be2e Mon Sep 17 00:00:00 2001 From: jake Date: Wed, 20 Apr 2022 18:35:24 -0600 Subject: [PATCH 12/60] redundant bit of code --- src/ship/items/state.rs | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/src/ship/items/state.rs b/src/ship/items/state.rs index b7ce7d4..6f1c550 100644 --- a/src/ship/items/state.rs +++ b/src/ship/items/state.rs @@ -22,7 +22,6 @@ pub enum TriggerCreateItem { } - #[derive(thiserror::Error, Debug)] enum ItemStateError { #[error("character {0} not found")] @@ -42,17 +41,9 @@ enum ItemStateError { Dummy, #[error("gateway")] - GatewayError(GatewayError), -} - -impl From for ItemStateError { - fn from(other: GatewayError) -> ItemStateError { - ItemStateError::GatewayError(other) - } + GatewayError(#[from] GatewayError), } - - enum GatewayActions { ItemNote(ItemEntityId, ItemNote), } -- 2.36.0 From 7aa4a6394fcbc66f4c390cfe1bfcd4847721dd66 Mon Sep 17 00:00:00 2001 From: jake Date: Wed, 20 Apr 2022 18:35:44 -0600 Subject: [PATCH 13/60] cleanup --- src/ship/items/state.rs | 103 +++------------------------------------- 1 file changed, 6 insertions(+), 97 deletions(-) diff --git a/src/ship/items/state.rs b/src/ship/items/state.rs index 6f1c550..d5a46f5 100644 --- a/src/ship/items/state.rs +++ b/src/ship/items/state.rs @@ -44,20 +44,6 @@ enum ItemStateError { GatewayError(#[from] GatewayError), } -enum GatewayActions { - ItemNote(ItemEntityId, ItemNote), -} - -/* -enum ItemStateChange { - Inventory(CharacterInventory), - Bank(CharacterBank), - CharacterMeseta(CharacterMeseta), - BankMeseta(BankMeseta), - SharedFloor(SharedFloor), - LocalFloor(LocalFloor), -} -*/ #[async_trait::async_trait] trait ItemAction { @@ -102,7 +88,6 @@ where _e: std::marker::PhantomData, prev: self, actionf: f, - //actionf: |s, t| f(s, ()), } } } @@ -110,7 +95,6 @@ where struct ItemActionStage where P: ItemAction, - //F: Fn(&mut S, P::Output) -> Result + for<'r> std::ops::Fn<(&'r mut S, T)>, F: Fn(S, P::Output) -> Pin> + Send>> + Send + Sync, { _s: std::marker::PhantomData, @@ -632,13 +616,9 @@ fn add_floor_item_to_inventory(character_id: CharacterEntityId) } -//hint R>(f: F) -> F {f} - - #[fix_hidden_lifetime_bug] async fn pick_up_item<'a, EG>( item_state: &'a mut ItemState, - //item_state: Arc>, entity_gateway: &'a mut EG, character_id: &CharacterEntityId, item_id: &ClientItemId) @@ -647,87 +627,16 @@ where 'a: 'static, EG: EntityGateway, { - /* - let transaction = entity_gateway.transaction().await.unwrap(); - - let item_state_proxy = ItemStateProxy::new(item_state, transaction); - let (mut item_state_proxy, result) = ItemStateAction::default() - .act(take_item_from_floor(*character_id, *item_id)) - .act(add_floor_item_to_inventory(*character_id)) - .commit(item_state_proxy) - .await?; - item_state_proxy.transaction.commit().await.unwrap(); - */ - - //drop(item_state_proxy); - //transaction.commit().await.unwrap(); - //item_state_proxy.entity_gateway.commit().await.unwrap(); - - let k: Result = entity_gateway.with_transaction(|transaction| async move { - let item_state_proxy = ItemStateProxy::new(item_state, transaction); - let (item_state_proxy, result) = ItemStateAction::default() + let result: Result = entity_gateway.with_transaction(|transaction| async move { + let item_state_proxy = ItemStateProxy::new(item_state); + let ((item_state_proxy, transaction), result) = ItemStateAction::default() .act(take_item_from_floor(*character_id, *item_id)) .act(add_floor_item_to_inventory(*character_id)) - .commit(item_state_proxy) + .commit((item_state_proxy, transaction)) .await?; - - //let u = transaction.get_user_by_id(crate::entity::account::UserAccountId(0)).await?; - //Ok((transaction, ())) - //drop(item_state_proxy); - Ok((item_state_proxy.transaction, result)) + Ok((transaction, result)) }).await; - - /* - //fn subaction(transaction: &mut dyn EntityGateway) -> Pin> + Send + 'static>> { - async fn subaction(transaction: &mut dyn EntityGateway) -> Result<(), GatewayError> { - let item_state_proxy = ItemStateProxy::new(item_state, transaction); - - Ok(()) - } - - //async fn action(item_state: &mut ItemState, entity_gateway: &mut EG) -> Result<(), GatewayError> { - fn action(item_state: &mut ItemState) - -> impl FnOnce(&mut dyn EntityGateway) -> Pin> + Send + 'static>> - { - /* - |transaction| Box::pin(async move { - Ok(()) - }) - */ - subaction - } - */ - - //let k: Result<(), GatewayError> = entity_gateway.with_transaction(action(item_state)).await; - - //let item_state = item_state.clone(); - /* - let k: Result<(), GatewayError> = entity_gateway.with_transaction(|transaction| async move { - let item_state_proxy = ItemStateProxy::new(item_state, transaction); - /* - let item_state = item_state.clone(); - let mut s = item_state.lock().await; - //let s = s.borrow_mut(); - let item_state_proxy = ItemStateProxy::new(&mut s, transaction); - ItemStateAction::default() - .act(take_item_from_floor(*character_id, *item_id)) - .act(add_floor_item_to_inventory(*character_id)) - .commit(item_state_proxy) - .await - */ - Ok(()) - }).await; - */ - - //let mut transaction = entity_gateway.transaction().await?; - - /*entity_gateway.with_transaction(|transaction| { - item_state_proxy.execute(&mut transaction)?.await - }).await?;*/ - - //Ok(result) - Ok(TriggerCreateItem::Yes) - //.exec_gateway(&mut entity_gateway) + Ok(result?) } -- 2.36.0 From da066a344515fdc7f1a3a107605c8116cc8a8b36 Mon Sep 17 00:00:00 2001 From: jake Date: Wed, 27 Apr 2022 13:09:58 -0600 Subject: [PATCH 14/60] move EntityGatewayTransaction to end of file --- src/entity/gateway/entitygateway.rs | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/src/entity/gateway/entitygateway.rs b/src/entity/gateway/entitygateway.rs index 4ec2c74..aaf7972 100644 --- a/src/entity/gateway/entitygateway.rs +++ b/src/entity/gateway/entitygateway.rs @@ -16,17 +16,6 @@ pub enum GatewayError { PgError(#[from] sqlx::Error) } -#[async_trait::async_trait] -pub trait EntityGatewayTransaction: Send + Sync { - fn gateway<'a>(&'a mut self) -> &'a mut dyn EntityGateway { - unimplemented!() - } - - async fn commit(self: Box) -> Result<(), GatewayError> { - unimplemented!() - } -} - #[async_trait::async_trait] pub trait EntityGateway: Send + Sync { @@ -155,3 +144,15 @@ pub trait EntityGateway: Send + Sync { unimplemented!(); } } + + +#[async_trait::async_trait] +pub trait EntityGatewayTransaction: Send + Sync { + fn gateway<'a>(&'a mut self) -> &'a mut dyn EntityGateway { + unimplemented!() + } + + async fn commit(self: Box) -> Result<(), GatewayError> { + unimplemented!() + } +} -- 2.36.0 From 3d51b3ee572de82c0dfd2a7c1ca69c7d65ebd749 Mon Sep 17 00:00:00 2001 From: jake Date: Wed, 27 Apr 2022 20:55:22 -0600 Subject: [PATCH 15/60] don't need static for with_transaction --- src/entity/gateway/entitygateway.rs | 8 ++++---- src/entity/gateway/inmemory.rs | 14 +++++++------- src/entity/gateway/postgres/postgres.rs | 8 ++++---- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/entity/gateway/entitygateway.rs b/src/entity/gateway/entitygateway.rs index aaf7972..d3ce500 100644 --- a/src/entity/gateway/entitygateway.rs +++ b/src/entity/gateway/entitygateway.rs @@ -19,15 +19,15 @@ pub enum GatewayError { #[async_trait::async_trait] pub trait EntityGateway: Send + Sync { - async fn transaction(&'static mut self) -> Result, GatewayError> + async fn transaction<'a>(&'a mut self) -> Result, GatewayError> { unimplemented!(); } - async fn with_transaction(&'static mut self, _func: F) -> Result + async fn with_transaction<'a, F, Fut, R, E>(&'a mut self, _func: F) -> Result where - Fut: Future, R), E>> + Send, - F: FnOnce(Box) -> Fut + Send, + Fut: Future, R), E>> + Send + 'a, + F: FnOnce(Box) -> Fut + Send, R: Send, E: From, Self: Sized diff --git a/src/entity/gateway/inmemory.rs b/src/entity/gateway/inmemory.rs index aa9762d..d9d53c7 100644 --- a/src/entity/gateway/inmemory.rs +++ b/src/entity/gateway/inmemory.rs @@ -10,13 +10,13 @@ use crate::entity::item::*; use std::sync::{Arc, Mutex}; -pub struct InMemoryGatewayTransaction { +pub struct InMemoryGatewayTransaction<'a> { working_gateway: InMemoryGateway, - original_gateway: &'static mut InMemoryGateway, + original_gateway: &'a mut InMemoryGateway, } #[async_trait::async_trait] -impl EntityGatewayTransaction for InMemoryGatewayTransaction { +impl<'a> EntityGatewayTransaction for InMemoryGatewayTransaction<'a> { fn gateway<'b>(&'b mut self) -> &'b mut dyn EntityGateway { &mut self.working_gateway } @@ -129,7 +129,7 @@ impl InMemoryGateway { #[async_trait::async_trait] impl EntityGateway for InMemoryGateway { - async fn transaction(&'static mut self) -> Result, GatewayError> + async fn transaction<'a>(&'a mut self) -> Result, GatewayError> { let working_gateway = { let users = self.users.lock().unwrap().clone(); @@ -166,10 +166,10 @@ impl EntityGateway for InMemoryGateway { } - async fn with_transaction(&'static mut self, func: F) -> Result + async fn with_transaction<'a, F, Fut, R, E>(&'a mut self, func: F) -> Result where - Fut: Future, R), E>> + Send, - F: FnOnce(Box) -> Fut + Send, + Fut: Future, R), E>> + Send + 'a, + F: FnOnce(Box) -> Fut + Send, R: Send, E: From, { diff --git a/src/entity/gateway/postgres/postgres.rs b/src/entity/gateway/postgres/postgres.rs index ceef84e..34f4ab3 100644 --- a/src/entity/gateway/postgres/postgres.rs +++ b/src/entity/gateway/postgres/postgres.rs @@ -559,17 +559,17 @@ async fn get_bank_meseta(conn: &mut sqlx::PgConnection, char_id: &CharacterEntit #[async_trait::async_trait] impl EntityGateway for PostgresGateway { - async fn transaction(&'static mut self) -> Result, GatewayError> + async fn transaction<'a>(&'a mut self) -> Result, GatewayError> { Ok(Box::new(PostgresTransaction { pgtransaction: self.pool.begin().await?, })) } - async fn with_transaction(&'static mut self, func: F) -> Result + async fn with_transaction<'a, F, Fut, R, E>(&'a mut self, func: F) -> Result where - Fut: Future, R), E>> + Send, - F: FnOnce(Box) -> Fut + Send, + Fut: Future, R), E>> + Send + 'a, + F: FnOnce(Box) -> Fut + Send, R: Send, E: From, { -- 2.36.0 From e58aff70424cdc2b593fcb4581ac179d3a5c69fc Mon Sep 17 00:00:00 2001 From: jake Date: Wed, 27 Apr 2022 20:56:39 -0600 Subject: [PATCH 16/60] remove boxes from ItemStateAction structs to remove need for static lifetimes --- src/ship/items/state.rs | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/src/ship/items/state.rs b/src/ship/items/state.rs index d5a46f5..b31ed9c 100644 --- a/src/ship/items/state.rs +++ b/src/ship/items/state.rs @@ -79,9 +79,11 @@ where S: Send + Sync, E: Send + Sync, { - fn act(self, f: F) -> ItemActionStage, F, S, E> + pub fn act(self, f: F) -> ItemActionStage, F, Fut, S, E> where - F: Fn(S, ()) -> Pin> + Send>> + Send + Sync + //F: FnOnce(S, ()) -> Pin> + Send>> + Send + Sync + F: Fn(S, ()) -> Fut + Send + Sync, + Fut: Future> + Send { ItemActionStage { _s: Default::default(), @@ -92,10 +94,12 @@ where } } -struct ItemActionStage +pub struct ItemActionStage where P: ItemAction, - F: Fn(S, P::Output) -> Pin> + Send>> + Send + Sync, + F: Fn(S, P::Output) -> Fut + Send + Sync, + Fut: Future> + Send, + //F: FnOnce(S, P::Output) -> Pin> + Send>> + Send + Sync, { _s: std::marker::PhantomData, _e: std::marker::PhantomData, @@ -104,10 +108,12 @@ where } #[async_trait::async_trait] -impl ItemAction for ItemActionStage +impl ItemAction for ItemActionStage where P: ItemAction + ItemAction + Send + Sync, - F: Fn(S, P::Output) -> Pin> + Send>> + Send + Sync, + //F: FnOnce(S, P::Output) -> Pin> + Send>> + Send + Sync, + F: Fn(S, P::Output) -> Fut + Send + Sync, + Fut: Future> + Send, S: Send + Sync, P::Output: Send + Sync, E: Send + Sync, @@ -129,20 +135,23 @@ where } } -impl ItemActionStage +impl ItemActionStage where P: ItemAction + Send + Sync, - F: Fn(S, P::Output) -> Pin> + Send>> + Send + Sync, + F: Fn(S, P::Output) -> Fut + Send + Sync, + Fut: Future> + Send, S: Send + Sync, P::Output: Send + Sync, E: Send + Sync, O: Send + Sync, P::Error: Send + Sync, { - fn act(self, g: G) -> ItemActionStage, G, S, E> + pub fn act(self, g: G) -> ItemActionStage, G, GFut, S, E> where S: Send + Sync, - G: Fn(S, as ItemAction>::Output) -> Pin> + Send>> + Send + Sync, + //G: FnOnce(S, as ItemAction>::Output) -> Pin> + Send>> + Send + Sync, + G: Fn(S, as ItemAction>::Output) -> GFut + Send + Sync, + GFut: Future> + Send, O2: Send + Sync, { ItemActionStage { -- 2.36.0 From 916bb316716b241fd19f9d2840d89e8c7e2aa3a4 Mon Sep 17 00:00:00 2001 From: jake Date: Wed, 27 Apr 2022 20:57:47 -0600 Subject: [PATCH 17/60] move item state action functions to new file --- src/ship/items/actions.rs | 314 ++++++++++++++++++++++++++++++++++++++ src/ship/items/mod.rs | 1 + src/ship/items/state.rs | 279 +++++++++++++++++++-------------- 3 files changed, 478 insertions(+), 116 deletions(-) create mode 100644 src/ship/items/actions.rs diff --git a/src/ship/items/actions.rs b/src/ship/items/actions.rs new file mode 100644 index 0000000..934fac7 --- /dev/null +++ b/src/ship/items/actions.rs @@ -0,0 +1,314 @@ +use std::collections::HashMap; +use crate::ship::items::ClientItemId; +use crate::entity::item::{Meseta, ItemEntityId, ItemDetail, ItemEntity, ItemType, InventoryEntity, InventoryItemEntity, EquippedEntity, ItemNote}; +use std::cell::{RefMut, RefCell}; +use std::ops::{Deref, DerefMut}; +use std::convert::{From, Into}; +use std::future::Future; +use std::pin::Pin; +use async_std::sync::{Arc, Mutex}; +use std::borrow::BorrowMut; + +use crate::ship::location::{AreaClient, RoomId}; +use crate::entity::character::{CharacterEntity, CharacterEntityId, TechLevel}; +use crate::entity::gateway::{EntityGateway, GatewayError}; +use crate::entity::gateway::entitygateway::EntityGatewayTransaction; +use crate::entity::item::tool::{Tool, ToolType}; +use crate::ship::items::state::{ItemState, ItemStateProxy, ItemStateAction, ItemAction, ItemStateError, FloorItem, InventoryItem, AddItemResult}; + +pub enum TriggerCreateItem {ItemAction, + Yes, + No +} + +/* +fn take_item_from_floor(character_id: CharacterEntityId, item_id: ClientItemId) + -> impl for<'a> Fn((ItemStateProxy<'a>, Box), ()) + -> Pin), FloorItem), ItemStateError>> + Send + 'a>> +{ + move |(mut item_state, transaction), _| { + Box::pin(async move { + let mut floor = item_state.floor(&character_id)?; + let item = floor.take_item(&item_id).ok_or(ItemStateError::NoFloorItem(item_id))?; + item_state.set_floor(floor); + + Ok(((item_state, transaction), item)) + }) + } +} +*/ + +fn take_item_from_floor(character_id: CharacterEntityId, item_id: ClientItemId) + -> impl for<'a> Fn((ItemStateProxy<'a>, Box), ()) + -> Pin), FloorItem), ItemStateError>> + Send + 'a>> +{ + move |(mut item_state, transaction), _| { + Box::pin(async move { + let mut floor = item_state.floor(&character_id)?; + let item = floor.take_item(&item_id).ok_or(ItemStateError::NoFloorItem(item_id))?; + item_state.set_floor(floor); + + Ok(((item_state, transaction), item)) + }) + } +} + +/* +fn take_item_from_floor<'a, F, Fut>(character_id: CharacterEntityId, item_id: ClientItemId) -> F +where + F: Fn((ItemStateProxy<'a>, Box), ()) -> Fut, + Fut: Future, Box), FloorItem), ItemStateError>> + Send + //Fut: Result, Box), FloorItem)> + Send, ItemStateError> + +{ + async move |(mut item_state, transaction): (ItemStateProxy<'a>, Box), _: ()| { + let mut floor = item_state.floor(&character_id)?; + let item = floor.take_item(&item_id).ok_or(ItemStateError::NoFloorItem(item_id))?; + item_state.set_floor(floor); + + Ok(((item_state, transaction), item)) + } +} +*/ + +/* +fn add_floor_item_to_inventory<'a, Fut>(character: &CharacterEntity) + -> impl Fn((ItemStateProxy<'a>, Box), FloorItem) -> Fut +where + Fut: Future, Box), TriggerCreateItem), ItemStateError>> + Send +{ + let character = character.clone(); + move |(mut item_state, transaction): (ItemStateProxy<'a>, Box), floor_item: FloorItem| { + let character = character.clone(); + async move { + let mut inventory = item_state.inventory(&character.id)?; + + let character_id = character.id; + let transaction = floor_item.with_entity_id(Ok(transaction), |mut transaction: Result<_, ItemStateError>, entity_id| { + async move { + if let Ok(transaction) = &mut transaction { + transaction.gateway().add_item_note(&entity_id, ItemNote::Pickup { + character_id + }).await?; + } + transaction + }}).await?; + + let mut transaction = floor_item.with_mag(Ok(transaction), |mut transaction: Result<_, ItemStateError>, entity_id, _mag| { + let character = character.clone(); + async move { + if let Ok(transaction) = &mut transaction { + transaction.gateway().change_mag_owner(&entity_id, &character).await?; + } + transaction + }}).await?; + + let add_result = inventory.add_floor_item(floor_item)?; + transaction.gateway().set_character_inventory(&character.id, &inventory.inventory.as_inventory_entity(&character.id)).await?; + item_state.set_inventory(inventory); + + Ok(((item_state, transaction), + match add_result { + AddItemResult::NewItem => TriggerCreateItem::Yes, + AddItemResult::AddToStack => TriggerCreateItem::No, + AddItemResult::Meseta => TriggerCreateItem::No, + })) + } + } +} +*/ + +fn add_floor_item_to_inventory(character: &CharacterEntity) + -> impl for<'a> Fn((ItemStateProxy<'a>, Box), FloorItem) + -> Pin), TriggerCreateItem), ItemStateError>> + Send + 'a>> +{ + let character = character.clone(); + move |(mut item_state, transaction), floor_item| { + let character = character.clone(); + Box::pin(async move { + let mut inventory = item_state.inventory(&character.id)?; + + let character_id = character.id; + let transaction = floor_item.with_entity_id(Ok(transaction), |mut transaction: Result<_, ItemStateError>, entity_id| { + async move { + if let Ok(transaction) = &mut transaction { + transaction.gateway().add_item_note(&entity_id, ItemNote::Pickup { + character_id + }).await?; + } + transaction + }}).await?; + + let mut transaction = floor_item.with_mag(Ok(transaction), |mut transaction: Result<_, ItemStateError>, entity_id, _mag| { + let character = character.clone(); + async move { + if let Ok(transaction) = &mut transaction { + transaction.gateway().change_mag_owner(&entity_id, &character).await?; + } + transaction + }}).await?; + + let add_result = inventory.add_floor_item(floor_item)?; + transaction.gateway().set_character_inventory(&character.id, &inventory.inventory.as_inventory_entity(&character.id)).await?; + item_state.set_inventory(inventory); + + Ok(((item_state, transaction), + match add_result { + AddItemResult::NewItem => TriggerCreateItem::Yes, + AddItemResult::AddToStack => TriggerCreateItem::No, + AddItemResult::Meseta => TriggerCreateItem::No, + })) + }) + } +} + +/* +fn add_floor_item_to_inventory(character: &CharacterEntity) + -> impl for<'a> Fn((ItemStateProxy<'a>, Box), FloorItem) + -> Pin), TriggerCreateItem), ItemStateError>> + Send + 'a>> +{ + let character = character.clone(); + move |(mut item_state, transaction), floor_item| { + let character = character.clone(); + Box::pin(async move { + let mut inventory = item_state.inventory(&character.id)?; + + let character_id = character.id; + let transaction = floor_item.with_entity_id(Ok(transaction), |mut transaction: Result<_, ItemStateError>, entity_id| { + async move { + if let Ok(transaction) = &mut transaction { + transaction.gateway().add_item_note(&entity_id, ItemNote::Pickup { + character_id + }).await?; + } + transaction + }}).await?; + + let mut transaction = floor_item.with_mag(Ok(transaction), |mut transaction: Result<_, ItemStateError>, entity_id, _mag| { + let character = character.clone(); + async move { + if let Ok(transaction) = &mut transaction { + transaction.gateway().change_mag_owner(&entity_id, &character).await?; + } + transaction + }}).await?; + + let add_result = inventory.add_floor_item(floor_item)?; + transaction.gateway().set_character_inventory(&character.id, &inventory.inventory.as_inventory_entity(&character.id)).await?; + item_state.set_inventory(inventory); + + Ok(((item_state, transaction), + match add_result { + AddItemResult::NewItem => TriggerCreateItem::Yes, + AddItemResult::AddToStack => TriggerCreateItem::No, + AddItemResult::Meseta => TriggerCreateItem::No, + })) + }) + } +} +*/ + +/* +fn take_item_from_inventory(character_id: CharacterEntityId, item_id: ClientItemId) + -> impl for<'a> Fn((ItemStateProxy<'a>, Box), ()) + -> Pin), InventoryItem), ItemStateError>> + Send + 'a>> +{ + move |(mut item_state, transaction), _| { + Box::pin(async move { + let mut inventory = item_state.inventory(&character_id)?; + let item = inventory.take_item(&item_id); + + transaction.gateway().set_character_inventory(&character_id, &inventory.inventory.as_inventory_entity(&character_id)).await?; + item_state.set_inventory(inventory); + Ok((item_state, transaction), item) + }) + } +} + + +fn add_inventory_item_to_shared_floor(character: &CharacterEntity) + -> impl for<'a> Fn((ItemStateProxy<'a>, Box), InventoryItem) + -> Pin), ()), ItemStateError>> + Send + 'a>> +{ + let character = character.clone(); + move |(mut item_state, transaction), inventory_item| { + let character = character.clone(); + Box::pin(async move { + + }) + } +} +*/ + +/* +pub async fn pick_up_item<'a, EG>( + item_state: &'a mut ItemState, + entity_gateway: &mut EG, + character: &CharacterEntity, + item_id: &ClientItemId) + -> Result +where + //'a: 'static, + EG: EntityGateway, +{ + let result: Result = entity_gateway.with_transaction(|transaction| async move { + let item_state_proxy = ItemStateProxy::new(item_state); + let ((item_state_proxy, transaction), result) = ItemStateAction::default() + .act(take_item_from_floor(character.id, *item_id)) + .act(add_floor_item_to_inventory(character)) + .commit((item_state_proxy, transaction)) + .await?; + item_state_proxy.commit(); + Ok((transaction, result)) + }).await; + Ok(result?) +} +*/ + +pub async fn pick_up_item( + item_state: &mut ItemState, + entity_gateway: &mut EG, + character: &CharacterEntity, + item_id: &ClientItemId) + -> Result +where + EG: EntityGateway, +{ + let result: Result = entity_gateway.with_transaction(|transaction| async move { + let item_state_proxy = ItemStateProxy::new(item_state); + let ((item_state_proxy, transaction), result) = ItemStateAction::default() + .act(take_item_from_floor(character.id, *item_id)) + .act(add_floor_item_to_inventory(character)) + .commit((item_state_proxy, transaction)) + .await?; + item_state_proxy.commit(); + Ok((transaction, result)) + }).await; + Ok(result?) +} + +pub async fn drop_item( + item_state: &mut ItemState, + entity_gateway: &mut EG, + character: &CharacterEntity, + item_id: &ClientItemId) + -> Result<(), ItemStateError> +where + //'a: 'static, + EG: EntityGateway, +{ +/* + let result: Result = entity_gateway.with_transaction(|transaction| async move { + let item_state_proxy = ItemStateProxy::new(item_state); + let ((item_state_proxy, transaction), result) = ItemStateAction::default() + .act(take_item_from_inventory(character.id, *item_id)) + .act(add_inventory_item_to_shared_floor(&character)) + .commit((item_state_proxy, transaction)) + .await?; + item_state_proxy.commit(); + Ok((transaction, result)) + }).await; + Ok(result?) + */ + Ok(()) +} diff --git a/src/ship/items/mod.rs b/src/ship/items/mod.rs index 2b88239..b37db38 100644 --- a/src/ship/items/mod.rs +++ b/src/ship/items/mod.rs @@ -5,6 +5,7 @@ pub mod manager; pub mod transaction; pub mod use_tool; pub mod state; +pub mod actions; use serde::{Serialize, Deserialize}; #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, Serialize, Deserialize, derive_more::Display)] diff --git a/src/ship/items/state.rs b/src/ship/items/state.rs index b31ed9c..323505d 100644 --- a/src/ship/items/state.rs +++ b/src/ship/items/state.rs @@ -14,16 +14,12 @@ use crate::entity::character::{CharacterEntity, CharacterEntityId, TechLevel}; use crate::entity::gateway::{EntityGateway, GatewayError}; use crate::entity::gateway::entitygateway::EntityGatewayTransaction; use crate::entity::item::tool::{Tool, ToolType}; +use crate::entity::item::mag::Mag; use crate::ship::drops::ItemDrop; -pub enum TriggerCreateItem { - Yes, - No -} - #[derive(thiserror::Error, Debug)] -enum ItemStateError { +pub enum ItemStateError { #[error("character {0} not found")] NoCharacter(CharacterEntityId), #[error("room {0} not found")] @@ -46,7 +42,7 @@ enum ItemStateError { #[async_trait::async_trait] -trait ItemAction { +pub trait ItemAction { type Input; type Output; type Start; @@ -57,7 +53,7 @@ trait ItemAction { } -struct ItemStateAction { +pub struct ItemStateAction { _t: std::marker::PhantomData, _s: std::marker::PhantomData, _e: std::marker::PhantomData, @@ -185,20 +181,20 @@ where } -#[derive(Clone)] -struct IndividualItemDetail { +#[derive(Clone, Debug)] +pub struct IndividualItemDetail { entity_id: ItemEntityId, item: ItemDetail, } -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct StackedItemDetail { pub entity_ids: Vec, pub tool: Tool, } -#[derive(Clone)] -enum InventoryItemDetail { +#[derive(Clone, Debug)] +pub enum InventoryItemDetail { Individual(IndividualItemDetail), Stacked(StackedItemDetail), } @@ -219,14 +215,35 @@ impl InventoryItemDetail { } -#[derive(Clone)] -struct InventoryItem { +#[derive(Clone, Debug)] +pub struct InventoryItem { item_id: ClientItemId, item: InventoryItemDetail, } +impl InventoryItem { + pub async fn with_entity_id(&self, mut param: T, mut func: F) -> T + where + F: FnMut(T, ItemEntityId) -> Fut, + Fut: Future, + { + match &self.item { + InventoryItemDetail::Individual(individual_item) => { + param = func(param, individual_item.entity_id.clone()).await; + }, + InventoryItemDetail::Stacked(stacked_item) => { + for entity_id in &stacked_item.entity_ids { + param = func(param, entity_id.clone()).await; + } + } + } + + param + } +} + #[derive(Clone)] -enum FloorItemDetail { +pub enum FloorItemDetail { Individual(IndividualItemDetail), Stacked(StackedItemDetail), Meseta(Meseta), @@ -239,14 +256,70 @@ impl FloorItemDetail { _ => None, } } + + /* + fn mag<'a>(&'a self) -> Option<&'a IndividualItemDetail> { + match self { + FloorItemDetail::Individual(individual_item) => { + match individual_item.item { + ItemDetail::Mag(mag) => Some(mag), + _ => None, + } + }, + _ => None + } + } +*/ } #[derive(Clone)] -struct FloorItem { +pub struct FloorItem { item_id: ClientItemId, item: FloorItemDetail, } +impl FloorItem { + pub async fn with_entity_id(&self, mut param: T, mut func: F) -> T + where + F: FnMut(T, ItemEntityId) -> Fut, + Fut: Future, + { + match &self.item { + FloorItemDetail::Individual(individual_item) => { + param = func(param, individual_item.entity_id.clone()).await; + }, + FloorItemDetail::Stacked(stacked_item) => { + for entity_id in &stacked_item.entity_ids { + param = func(param, entity_id.clone()).await; + } + }, + FloorItemDetail::Meseta(_meseta) => {}, + } + + param + } + + pub async fn with_mag(&self, mut param: T, mut func: F) -> T + where + F: FnMut(T, ItemEntityId, Mag) -> Fut, + Fut: Future, + { + match &self.item { + FloorItemDetail::Individual(individual_item) => { + match &individual_item.item { + ItemDetail::Mag(mag) => { + param = func(param, individual_item.entity_id.clone(), mag.clone()).await; + } + _ => {} + } + }, + _ => {} + } + + param + } +} + // meseta is a floor item /* impl Into for FloorItem { @@ -259,12 +332,49 @@ impl Into for FloorItem { } */ -#[derive(Clone)] -struct Inventory(Vec); +#[derive(Clone, Debug)] +pub struct Inventory(Vec); +/* +#[derive(Clone, Debug)] +pub struct Inventory { + item_id_counter: u32, + items: Vec, + equipped: EquippedEntity, +} +*/ + +impl Inventory { + pub fn as_inventory_entity(&self, _character_id: &CharacterEntityId) -> InventoryEntity { + InventoryEntity { + items: self.0.iter() + .map(|item| { + match &item.item { + InventoryItemDetail::Individual(item) => { + InventoryItemEntity::Individual(ItemEntity { + id: item.entity_id, + item: item.item.clone(), + }) + }, + InventoryItemDetail::Stacked(items) => { + InventoryItemEntity::Stacked(items.entity_ids.iter() + .map(|id| { + ItemEntity { + id: *id, + item: ItemDetail::Tool(items.tool) + } + }) + .collect()) + }, + } + }) + .collect() + } + } +} #[derive(thiserror::Error, Debug)] -enum InventoryError { +pub enum InventoryError { #[error("inventory full")] InventoryFull, #[error("stack full")] @@ -273,49 +383,27 @@ enum InventoryError { MesetaFull, } -enum AddItemResult { +pub enum AddItemResult { NewItem, AddToStack, Meseta, } - - - - #[derive(Clone)] -struct LocalFloor(Vec); +pub struct LocalFloor(Vec); #[derive(Clone)] -struct SharedFloor(Vec); +pub struct SharedFloor(Vec); #[derive(Clone)] -struct RoomFloorItems(Vec); +pub struct RoomFloorItems(Vec); -struct InventoryState { +pub struct InventoryState { character_id: CharacterEntityId, - inventory: Inventory, + pub inventory: Inventory, meseta: Meseta, } - -/* -impl Deref for InventoryState { - type Target = Inventory; - - fn deref(&self) -> &Self::Target { - &self.inventory - } -} - - -impl DerefMut for InventoryState { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.inventory - } -} -*/ - impl InventoryState { - fn add_floor_item(&mut self, item: FloorItem) -> Result { + pub fn add_floor_item(&mut self, item: FloorItem) -> Result { match item.item { FloorItemDetail::Individual(iitem) => { if self.inventory.0.len() >= 30 { @@ -374,14 +462,14 @@ impl InventoryState { } } -struct FloorState { +pub struct FloorState { character_id: CharacterEntityId, local: LocalFloor, shared: SharedFloor, } impl FloorState { - fn take_item(&mut self, item_id: &ClientItemId) -> Option { + pub fn take_item(&mut self, item_id: &ClientItemId) -> Option { let item = self.local.0 .drain_filter(|item| { item.item_id == *item_id @@ -398,7 +486,7 @@ impl FloorState { } -struct ItemState { +pub struct ItemState { character_inventory: HashMap, //character_bank: HashMap, character_meseta: HashMap, @@ -485,19 +573,31 @@ impl Default for ProxiedItemState { } } -struct ItemStateProxy<'a> { +pub struct ItemStateProxy<'a> { item_state: &'a mut ItemState, //entity_gateway: &'a mut dyn EntityGateway, - transaction: Box, + //transaction: Box, //entity_gateway: &'a mut Box, //entity_gateway: &'a mut Box, proxied_state: ProxiedItemState, - gateway_actions: Vec, + //gateway_actions: Vec, //_eg: std::marker::PhantomData, } +//impl<'a> Drop for ItemStateProxy<'a> { +// fn drop(&mut self) { +impl<'a> ItemStateProxy<'a> { + pub fn commit(self) { + self.item_state.character_inventory.extend(self.proxied_state.character_inventory.clone()); + self.item_state.character_meseta.extend(self.proxied_state.character_meseta.clone()); + self.item_state.character_room.extend(self.proxied_state.character_room.clone()); + self.item_state.character_floor.extend(self.proxied_state.character_floor.clone()); + self.item_state.room_floor.extend(self.proxied_state.room_floor.clone()); + } +} + /* fn get_or_clone<'a, K, V>(master: &HashMap, proxy: &'a RefCell>>, key: K, err: fn(K) -> ItemStateError) -> Result + 'a, ItemStateError> where @@ -541,18 +641,18 @@ where impl<'a> ItemStateProxy<'a> { //fn new(item_state: &'a mut ItemState, entity_gateway: &'a mut EG) -> Self { //fn new(item_state: &'a mut ItemState, entity_gateway: &'a mut dyn EntityGateway) -> Self { - fn new(item_state: &'a mut ItemState, transaction: Box) -> Self { + pub fn new(item_state: &'a mut ItemState/*, transaction: Box*/) -> Self { ItemStateProxy { item_state, //entity_gateway, - transaction, + //transaction, proxied_state: Default::default(), - gateway_actions: Vec::new(), + //gateway_actions: Vec::new(), //_eg: std::marker::PhantomData, } } - fn inventory(&mut self, character_id: &CharacterEntityId) -> Result { + pub fn inventory(&mut self, character_id: &CharacterEntityId) -> Result { Ok(InventoryState { character_id: *character_id, inventory: get_or_clone(&self.item_state.character_inventory, &mut self.proxied_state.character_inventory, *character_id, |c| ItemStateError::NoCharacter(c))?, @@ -560,12 +660,12 @@ impl<'a> ItemStateProxy<'a> { }) } - fn set_inventory(&mut self, inventory: InventoryState) { + pub fn set_inventory(&mut self, inventory: InventoryState) { self.proxied_state.character_inventory.insert(inventory.character_id, inventory.inventory); self.proxied_state.character_meseta.insert(inventory.character_id, inventory.meseta); } - fn floor(&mut self, character_id: &CharacterEntityId) -> Result { + pub fn floor(&mut self, character_id: &CharacterEntityId) -> Result { let room_id = get_or_clone(&self.item_state.character_room, &mut self.proxied_state.character_room, *character_id, |c| ItemStateError::NoCharacter(c))?; Ok(FloorState { character_id: *character_id, @@ -574,13 +674,13 @@ impl<'a> ItemStateProxy<'a> { }) } - fn set_floor(&mut self, floor: FloorState) { + pub fn set_floor(&mut self, floor: FloorState) { let room_id = get_or_clone(&self.item_state.character_room, &mut self.proxied_state.character_room, floor.character_id, |c| ItemStateError::NoCharacter(c)).unwrap(); self.proxied_state.character_floor.insert(floor.character_id, floor.local); self.proxied_state.room_floor.insert(room_id, floor.shared); } - fn new_item_id(&mut self) -> Result { + pub fn new_item_id(&mut self) -> Result { //let room_id = get_or_clone(&self.item_state.character_room, &mut self.proxied_state.character_room, *character_id, |c| ItemStateError::NoCharacter(c))?; self.item_state.room_item_id_counter += 1; Ok(ClientItemId(self.item_state.room_item_id_counter)) @@ -595,59 +695,6 @@ impl<'a> ItemStateProxy<'a> { } -fn take_item_from_floor(character_id: CharacterEntityId, item_id: ClientItemId) - -> impl for<'a> Fn(ItemStateProxy<'a>, ()) - -> Pin> + Send + 'a>> { - move |mut item_state, _| { - Box::pin(async move { - let mut floor = item_state.floor(&character_id)?; - let item = floor.take_item(&item_id).ok_or(ItemStateError::NoFloorItem(item_id))?; - item_state.set_floor(floor); - Ok((item_state, item)) - }) - } -} - -fn add_floor_item_to_inventory(character_id: CharacterEntityId) - -> impl for<'a> Fn(ItemStateProxy<'a>, FloorItem) - -> Pin> + Send + 'a>> { - move |mut item_state, floor_item| Box::pin(async move { - let mut inventory = item_state.inventory(&character_id)?; - let add_result = inventory.add_floor_item(floor_item)?; - item_state.set_inventory(inventory); - Ok((item_state, - match add_result { - AddItemResult::NewItem => TriggerCreateItem::Yes, - AddItemResult::AddToStack => TriggerCreateItem::No, - AddItemResult::Meseta => TriggerCreateItem::No, - })) - }) -} - - -#[fix_hidden_lifetime_bug] -async fn pick_up_item<'a, EG>( - item_state: &'a mut ItemState, - entity_gateway: &'a mut EG, - character_id: &CharacterEntityId, - item_id: &ClientItemId) - -> Result -where - 'a: 'static, - EG: EntityGateway, -{ - let result: Result = entity_gateway.with_transaction(|transaction| async move { - let item_state_proxy = ItemStateProxy::new(item_state); - let ((item_state_proxy, transaction), result) = ItemStateAction::default() - .act(take_item_from_floor(*character_id, *item_id)) - .act(add_floor_item_to_inventory(*character_id)) - .commit((item_state_proxy, transaction)) - .await?; - Ok((transaction, result)) - }).await; - Ok(result?) -} - /* fn record_item_drop(character_id: CharacterEntityId, item_drop: ItemDrop) -> impl Fn(&mut ItemStateProxy, ()) -> Result<(), ItemStateError> { -- 2.36.0 From da0c75fa39012fd069224d5c93356f01fccd6f03 Mon Sep 17 00:00:00 2001 From: jake Date: Wed, 27 Apr 2022 21:16:04 -0600 Subject: [PATCH 18/60] cleanup --- src/ship/items/state.rs | 195 +--------------------------------------- 1 file changed, 1 insertion(+), 194 deletions(-) diff --git a/src/ship/items/state.rs b/src/ship/items/state.rs index 323505d..6b78a8a 100644 --- a/src/ship/items/state.rs +++ b/src/ship/items/state.rs @@ -77,7 +77,6 @@ where { pub fn act(self, f: F) -> ItemActionStage, F, Fut, S, E> where - //F: FnOnce(S, ()) -> Pin> + Send>> + Send + Sync F: Fn(S, ()) -> Fut + Send + Sync, Fut: Future> + Send { @@ -95,7 +94,6 @@ where P: ItemAction, F: Fn(S, P::Output) -> Fut + Send + Sync, Fut: Future> + Send, - //F: FnOnce(S, P::Output) -> Pin> + Send>> + Send + Sync, { _s: std::marker::PhantomData, _e: std::marker::PhantomData, @@ -107,7 +105,6 @@ where impl ItemAction for ItemActionStage where P: ItemAction + ItemAction + Send + Sync, - //F: FnOnce(S, P::Output) -> Pin> + Send>> + Send + Sync, F: Fn(S, P::Output) -> Fut + Send + Sync, Fut: Future> + Send, S: Send + Sync, @@ -145,7 +142,6 @@ where pub fn act(self, g: G) -> ItemActionStage, G, GFut, S, E> where S: Send + Sync, - //G: FnOnce(S, as ItemAction>::Output) -> Pin> + Send>> + Send + Sync, G: Fn(S, as ItemAction>::Output) -> GFut + Send + Sync, GFut: Future> + Send, O2: Send + Sync, @@ -256,20 +252,6 @@ impl FloorItemDetail { _ => None, } } - - /* - fn mag<'a>(&'a self) -> Option<&'a IndividualItemDetail> { - match self { - FloorItemDetail::Individual(individual_item) => { - match individual_item.item { - ItemDetail::Mag(mag) => Some(mag), - _ => None, - } - }, - _ => None - } - } -*/ } #[derive(Clone)] @@ -320,28 +302,9 @@ impl FloorItem { } } -// meseta is a floor item -/* -impl Into for FloorItem { - fn into(&self) -> InventoryItem { - InventoryItem { - item_id: self.item_id, - item: - } - } -} -*/ #[derive(Clone, Debug)] pub struct Inventory(Vec); -/* -#[derive(Clone, Debug)] -pub struct Inventory { - item_id_counter: u32, - items: Vec, - equipped: EquippedEntity, -} -*/ impl Inventory { pub fn as_inventory_entity(&self, _character_id: &CharacterEntityId) -> InventoryEntity { @@ -514,36 +477,6 @@ impl Default for ItemState { } -/* -struct ProxiedItemState { - character_inventory: RefCell>>, - //character_bank: HashMap>, - //character_meseta: HashMap>, - //bank_meseta: HashMap>, - - character_room: RefCell>>, - character_floor: RefCell>>, - room_floor: RefCell>>, - - //room_item_id_counter: HashMap ClientItemId + Send>>>, -} - -impl Default for ProxiedItemState { - fn default() -> Self { - ProxiedItemState { - character_inventory: RefCell::new(HashMap::new()), - //character_bank: HashMap::new(), - //character_meseta: HashMap::new(), - //bank_meseta: HashMap::new(), - character_floor: RefCell::new(HashMap::new()), - character_room: RefCell::new(HashMap::new()), - room_floor: RefCell::new(HashMap::new()), - //room_item_id_counter: HashMap::new(), - } - } -} -*/ - struct ProxiedItemState { character_inventory: HashMap, //character_bank: HashMap>, @@ -575,19 +508,9 @@ impl Default for ProxiedItemState { pub struct ItemStateProxy<'a> { item_state: &'a mut ItemState, - //entity_gateway: &'a mut dyn EntityGateway, - //transaction: Box, - //entity_gateway: &'a mut Box, - //entity_gateway: &'a mut Box, proxied_state: ProxiedItemState, - - //gateway_actions: Vec, - - //_eg: std::marker::PhantomData, } -//impl<'a> Drop for ItemStateProxy<'a> { -// fn drop(&mut self) { impl<'a> ItemStateProxy<'a> { pub fn commit(self) { self.item_state.character_inventory.extend(self.proxied_state.character_inventory.clone()); @@ -598,34 +521,6 @@ impl<'a> ItemStateProxy<'a> { } } -/* -fn get_or_clone<'a, K, V>(master: &HashMap, proxy: &'a RefCell>>, key: K, err: fn(K) -> ItemStateError) -> Result + 'a, ItemStateError> -where - K: Eq + std::hash::Hash + Copy, - V: Clone -{ - let existing_element = master.get(&key).ok_or_else(|| err(key))?; - Ok(proxy.borrow_mut().entry(key) - .or_insert(RefCell::new(existing_element.clone())) - .borrow_mut()) - -} -*/ - - -/* -fn get_or_clone<'a, K, V>(master: &HashMap, proxy: &'a mut HashMap>, key: K, err: fn(K) -> ItemStateError) -> Result + 'a, ItemStateError> -where - K: Eq + std::hash::Hash + Copy, - V: Clone -{ - let existing_element = master.get(&key).ok_or_else(|| err(key))?; - Ok(proxy.entry(key) - .or_insert(RefCell::new(existing_element.clone())) - .borrow_mut()) - -} -*/ fn get_or_clone(master: &HashMap, proxy: &mut HashMap, key: K, err: fn(K) -> ItemStateError) -> Result where @@ -639,16 +534,10 @@ where } impl<'a> ItemStateProxy<'a> { - //fn new(item_state: &'a mut ItemState, entity_gateway: &'a mut EG) -> Self { - //fn new(item_state: &'a mut ItemState, entity_gateway: &'a mut dyn EntityGateway) -> Self { - pub fn new(item_state: &'a mut ItemState/*, transaction: Box*/) -> Self { + pub fn new(item_state: &'a mut ItemState) -> Self { ItemStateProxy { item_state, - //entity_gateway, - //transaction, proxied_state: Default::default(), - //gateway_actions: Vec::new(), - //_eg: std::marker::PhantomData, } } @@ -681,89 +570,7 @@ impl<'a> ItemStateProxy<'a> { } pub fn new_item_id(&mut self) -> Result { - //let room_id = get_or_clone(&self.item_state.character_room, &mut self.proxied_state.character_room, *character_id, |c| ItemStateError::NoCharacter(c))?; self.item_state.room_item_id_counter += 1; Ok(ClientItemId(self.item_state.room_item_id_counter)) - /* - self.item_state.room_item_id_counter - .borrow_mut() - .get_mut(&room_id) - .ok_or(ItemStateError::NoRoom(room_id)) - .map(|f| f()) - */ } } - - - -/* -fn record_item_drop(character_id: CharacterEntityId, item_drop: ItemDrop) -> impl Fn(&mut ItemStateProxy, ()) -> Result<(), ItemStateError> { - move |item_state, _| { - // how do I do this? I need EG to add_item_note but how should I kick that till later? - // general purpose vec in item_state `misc_gateway_actions`? - // can't quite use actions here as it relies on an ItemEntityId which at this point does not yet exist for the dropped item - //item_state.gateway_actions.push(GatewayAction::ItemNote(item_drop.)) - Ok(()) - } -} -*/ - -/* -fn map_drop_to_floor_item(character_id: CharacterEntityId, item_drop: ItemDrop) -> impl Fn(&mut ItemStateProxy, ()) -> Result { - move |item_state, _| { - match item_drop.item { - ItemDropType::Weapon(w) => FloorItem { - item_id: item_state.new_item_id(&character_id)?, - item: FloorItemDetail::Individual(item_drop.item) - }, - ItemDropType::Armor(w) => ItemOrMeseta::Individual(ItemDetail::Armor(w)), - ItemDropType::Shield(w) => ItemOrMeseta::Individual(ItemDetail::Shield(w)), - ItemDropType::Unit(w) => ItemOrMeseta::Individual(ItemDetail::Unit(w)), - ItemDropType::TechniqueDisk(w) => ItemOrMeseta::Individual(ItemDetail::TechniqueDisk(w)), - ItemDropType::Mag(w) => ItemOrMeseta::Individual(ItemDetail::Mag(w)), - //ItemDropType::IndividualTool(t) => ItemOrMeseta::Individual(ItemDetail::Tool(t)), - //ItemDropType::StackedTool(t, _) => ItemOrMeseta::Stacked(t), - ItemDropType::Tool(t) if t.tool.is_stackable() => ItemOrMeseta::Stacked(t), - ItemDropType::Tool(t) if !t.tool.is_stackable() => ItemOrMeseta::Individual(ItemDetail::Tool(t)), - ItemDropType::Meseta(m) => ItemOrMeseta::Meseta(Meseta(m)), - _ => unreachable!() // rust isnt smart enough to see that the conditional on tool catches everything - } - } -} - - - -fn enemy_drops_item(item_state: &mut ItemState, - entity_gateway: &mut EG, - character: &CharacterEntity, - item_drop: ItemDrop) - -> Result -where - EG: EntityGateway, -{ - ItemStateAction::default() - .act(record_item_drop(character.id, item_drop.clone())) - .act(map_drop_to_floor_item(character.id, item_drop)) - .act(add_item_drop_to_floor(character.id)) - .commit(&mut ItemStateProxy::new(item_state)) -} -*/ - -/* -fn sell_item(item_state: &mut ItemState, - entity_gateway: &mut EG, - character_id: &CharacterEntityId, - item_id: &ClientItemId, - amount: usize) - -> Result -where - EG: EntityGateway, -{ - ItemStateAction::default() - .act(take_item_from_inventory(*character_id, *item_id)) - .act(sell_inventory_item(*character_id, *item_id)) - .exec_state(&mut ItemStateProxy::new(item_state)) - .exec_gateway(&mut entity_gateway) -} -*/ - -- 2.36.0 From 5d6f9deb591f546e89f1a2601c575d9cf6e34e0c Mon Sep 17 00:00:00 2001 From: jake Date: Wed, 27 Apr 2022 23:53:26 -0600 Subject: [PATCH 19/60] mostly appease clippy --- src/entity/gateway/entitygateway.rs | 2 +- src/entity/gateway/inmemory.rs | 4 +- src/entity/gateway/postgres/postgres.rs | 6 +- src/lib.rs | 1 - src/ship/items/actions.rs | 205 +----------------------- src/ship/items/state.rs | 69 ++++---- 6 files changed, 41 insertions(+), 246 deletions(-) diff --git a/src/entity/gateway/entitygateway.rs b/src/entity/gateway/entitygateway.rs index d3ce500..cf5599c 100644 --- a/src/entity/gateway/entitygateway.rs +++ b/src/entity/gateway/entitygateway.rs @@ -148,7 +148,7 @@ pub trait EntityGateway: Send + Sync { #[async_trait::async_trait] pub trait EntityGatewayTransaction: Send + Sync { - fn gateway<'a>(&'a mut self) -> &'a mut dyn EntityGateway { + fn gateway(&mut self) -> &mut dyn EntityGateway { unimplemented!() } diff --git a/src/entity/gateway/inmemory.rs b/src/entity/gateway/inmemory.rs index d9d53c7..0e3d02e 100644 --- a/src/entity/gateway/inmemory.rs +++ b/src/entity/gateway/inmemory.rs @@ -17,7 +17,7 @@ pub struct InMemoryGatewayTransaction<'a> { #[async_trait::async_trait] impl<'a> EntityGatewayTransaction for InMemoryGatewayTransaction<'a> { - fn gateway<'b>(&'b mut self) -> &'b mut dyn EntityGateway { + fn gateway(&mut self) -> &mut dyn EntityGateway { &mut self.working_gateway } @@ -204,7 +204,7 @@ impl EntityGateway for InMemoryGateway { original_gateway: self, }); - let (mut transaction, result) = func(transaction).await?; + let (transaction, result) = func(transaction).await?; transaction.commit().await?; Ok(result) diff --git a/src/entity/gateway/postgres/postgres.rs b/src/entity/gateway/postgres/postgres.rs index 34f4ab3..b6f9cc9 100644 --- a/src/entity/gateway/postgres/postgres.rs +++ b/src/entity/gateway/postgres/postgres.rs @@ -25,7 +25,7 @@ pub struct PostgresTransaction<'t> { #[async_trait::async_trait] impl<'t> EntityGatewayTransaction for PostgresTransaction<'t> { - fn gateway<'b>(&'b mut self) -> &'b mut dyn EntityGateway { + fn gateway(&mut self) -> &mut dyn EntityGateway { self } @@ -573,10 +573,10 @@ impl EntityGateway for PostgresGateway { R: Send, E: From, { - let mut transaction = Box::new(PostgresTransaction { + let transaction = Box::new(PostgresTransaction { pgtransaction: self.pool.begin().await.map_err(|_| ()).unwrap() }); - let (mut transaction, result) = func(transaction).await.map_err(|_| ()).unwrap(); + let (transaction, result) = func(transaction).await.map_err(|_| ()).unwrap(); transaction.commit().await.map_err(|_| ()).unwrap(); Ok(result) } diff --git a/src/lib.rs b/src/lib.rs index 9934022..d908e0b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,7 +3,6 @@ #![feature(drain_filter)] #![feature(try_blocks)] -#[macro_use] extern crate fix_hidden_lifetime_bug; diff --git a/src/ship/items/actions.rs b/src/ship/items/actions.rs index 934fac7..4f2fdad 100644 --- a/src/ship/items/actions.rs +++ b/src/ship/items/actions.rs @@ -1,43 +1,17 @@ -use std::collections::HashMap; use crate::ship::items::ClientItemId; -use crate::entity::item::{Meseta, ItemEntityId, ItemDetail, ItemEntity, ItemType, InventoryEntity, InventoryItemEntity, EquippedEntity, ItemNote}; -use std::cell::{RefMut, RefCell}; -use std::ops::{Deref, DerefMut}; -use std::convert::{From, Into}; +use crate::entity::item::ItemNote; use std::future::Future; use std::pin::Pin; -use async_std::sync::{Arc, Mutex}; -use std::borrow::BorrowMut; -use crate::ship::location::{AreaClient, RoomId}; -use crate::entity::character::{CharacterEntity, CharacterEntityId, TechLevel}; -use crate::entity::gateway::{EntityGateway, GatewayError}; -use crate::entity::gateway::entitygateway::EntityGatewayTransaction; -use crate::entity::item::tool::{Tool, ToolType}; -use crate::ship::items::state::{ItemState, ItemStateProxy, ItemStateAction, ItemAction, ItemStateError, FloorItem, InventoryItem, AddItemResult}; +use crate::entity::character::{CharacterEntity, CharacterEntityId}; +use crate::entity::gateway::EntityGateway; +use crate::ship::items::state::{ItemState, ItemStateProxy, ItemStateAction, ItemAction, ItemStateError, FloorItem, AddItemResult}; pub enum TriggerCreateItem {ItemAction, Yes, No } -/* -fn take_item_from_floor(character_id: CharacterEntityId, item_id: ClientItemId) - -> impl for<'a> Fn((ItemStateProxy<'a>, Box), ()) - -> Pin), FloorItem), ItemStateError>> + Send + 'a>> -{ - move |(mut item_state, transaction), _| { - Box::pin(async move { - let mut floor = item_state.floor(&character_id)?; - let item = floor.take_item(&item_id).ok_or(ItemStateError::NoFloorItem(item_id))?; - item_state.set_floor(floor); - - Ok(((item_state, transaction), item)) - }) - } -} -*/ - fn take_item_from_floor(character_id: CharacterEntityId, item_id: ClientItemId) -> impl for<'a> Fn((ItemStateProxy<'a>, Box), ()) -> Pin), FloorItem), ItemStateError>> + Send + 'a>> @@ -53,71 +27,6 @@ fn take_item_from_floor(character_id: CharacterEntityId, item_id: ClientItemId) } } -/* -fn take_item_from_floor<'a, F, Fut>(character_id: CharacterEntityId, item_id: ClientItemId) -> F -where - F: Fn((ItemStateProxy<'a>, Box), ()) -> Fut, - Fut: Future, Box), FloorItem), ItemStateError>> + Send - //Fut: Result, Box), FloorItem)> + Send, ItemStateError> - -{ - async move |(mut item_state, transaction): (ItemStateProxy<'a>, Box), _: ()| { - let mut floor = item_state.floor(&character_id)?; - let item = floor.take_item(&item_id).ok_or(ItemStateError::NoFloorItem(item_id))?; - item_state.set_floor(floor); - - Ok(((item_state, transaction), item)) - } -} -*/ - -/* -fn add_floor_item_to_inventory<'a, Fut>(character: &CharacterEntity) - -> impl Fn((ItemStateProxy<'a>, Box), FloorItem) -> Fut -where - Fut: Future, Box), TriggerCreateItem), ItemStateError>> + Send -{ - let character = character.clone(); - move |(mut item_state, transaction): (ItemStateProxy<'a>, Box), floor_item: FloorItem| { - let character = character.clone(); - async move { - let mut inventory = item_state.inventory(&character.id)?; - - let character_id = character.id; - let transaction = floor_item.with_entity_id(Ok(transaction), |mut transaction: Result<_, ItemStateError>, entity_id| { - async move { - if let Ok(transaction) = &mut transaction { - transaction.gateway().add_item_note(&entity_id, ItemNote::Pickup { - character_id - }).await?; - } - transaction - }}).await?; - - let mut transaction = floor_item.with_mag(Ok(transaction), |mut transaction: Result<_, ItemStateError>, entity_id, _mag| { - let character = character.clone(); - async move { - if let Ok(transaction) = &mut transaction { - transaction.gateway().change_mag_owner(&entity_id, &character).await?; - } - transaction - }}).await?; - - let add_result = inventory.add_floor_item(floor_item)?; - transaction.gateway().set_character_inventory(&character.id, &inventory.inventory.as_inventory_entity(&character.id)).await?; - item_state.set_inventory(inventory); - - Ok(((item_state, transaction), - match add_result { - AddItemResult::NewItem => TriggerCreateItem::Yes, - AddItemResult::AddToStack => TriggerCreateItem::No, - AddItemResult::Meseta => TriggerCreateItem::No, - })) - } - } -} -*/ - fn add_floor_item_to_inventory(character: &CharacterEntity) -> impl for<'a> Fn((ItemStateProxy<'a>, Box), FloorItem) -> Pin), TriggerCreateItem), ItemStateError>> + Send + 'a>> @@ -162,109 +71,6 @@ fn add_floor_item_to_inventory(character: &CharacterEntity) } } -/* -fn add_floor_item_to_inventory(character: &CharacterEntity) - -> impl for<'a> Fn((ItemStateProxy<'a>, Box), FloorItem) - -> Pin), TriggerCreateItem), ItemStateError>> + Send + 'a>> -{ - let character = character.clone(); - move |(mut item_state, transaction), floor_item| { - let character = character.clone(); - Box::pin(async move { - let mut inventory = item_state.inventory(&character.id)?; - - let character_id = character.id; - let transaction = floor_item.with_entity_id(Ok(transaction), |mut transaction: Result<_, ItemStateError>, entity_id| { - async move { - if let Ok(transaction) = &mut transaction { - transaction.gateway().add_item_note(&entity_id, ItemNote::Pickup { - character_id - }).await?; - } - transaction - }}).await?; - - let mut transaction = floor_item.with_mag(Ok(transaction), |mut transaction: Result<_, ItemStateError>, entity_id, _mag| { - let character = character.clone(); - async move { - if let Ok(transaction) = &mut transaction { - transaction.gateway().change_mag_owner(&entity_id, &character).await?; - } - transaction - }}).await?; - - let add_result = inventory.add_floor_item(floor_item)?; - transaction.gateway().set_character_inventory(&character.id, &inventory.inventory.as_inventory_entity(&character.id)).await?; - item_state.set_inventory(inventory); - - Ok(((item_state, transaction), - match add_result { - AddItemResult::NewItem => TriggerCreateItem::Yes, - AddItemResult::AddToStack => TriggerCreateItem::No, - AddItemResult::Meseta => TriggerCreateItem::No, - })) - }) - } -} -*/ - -/* -fn take_item_from_inventory(character_id: CharacterEntityId, item_id: ClientItemId) - -> impl for<'a> Fn((ItemStateProxy<'a>, Box), ()) - -> Pin), InventoryItem), ItemStateError>> + Send + 'a>> -{ - move |(mut item_state, transaction), _| { - Box::pin(async move { - let mut inventory = item_state.inventory(&character_id)?; - let item = inventory.take_item(&item_id); - - transaction.gateway().set_character_inventory(&character_id, &inventory.inventory.as_inventory_entity(&character_id)).await?; - item_state.set_inventory(inventory); - Ok((item_state, transaction), item) - }) - } -} - - -fn add_inventory_item_to_shared_floor(character: &CharacterEntity) - -> impl for<'a> Fn((ItemStateProxy<'a>, Box), InventoryItem) - -> Pin), ()), ItemStateError>> + Send + 'a>> -{ - let character = character.clone(); - move |(mut item_state, transaction), inventory_item| { - let character = character.clone(); - Box::pin(async move { - - }) - } -} -*/ - -/* -pub async fn pick_up_item<'a, EG>( - item_state: &'a mut ItemState, - entity_gateway: &mut EG, - character: &CharacterEntity, - item_id: &ClientItemId) - -> Result -where - //'a: 'static, - EG: EntityGateway, -{ - let result: Result = entity_gateway.with_transaction(|transaction| async move { - let item_state_proxy = ItemStateProxy::new(item_state); - let ((item_state_proxy, transaction), result) = ItemStateAction::default() - .act(take_item_from_floor(character.id, *item_id)) - .act(add_floor_item_to_inventory(character)) - .commit((item_state_proxy, transaction)) - .await?; - item_state_proxy.commit(); - Ok((transaction, result)) - }).await; - Ok(result?) -} -*/ - pub async fn pick_up_item( item_state: &mut ItemState, entity_gateway: &mut EG, @@ -284,7 +90,7 @@ where item_state_proxy.commit(); Ok((transaction, result)) }).await; - Ok(result?) + result } pub async fn drop_item( @@ -294,7 +100,6 @@ pub async fn drop_item( item_id: &ClientItemId) -> Result<(), ItemStateError> where - //'a: 'static, EG: EntityGateway, { /* diff --git a/src/ship/items/state.rs b/src/ship/items/state.rs index 6b78a8a..7bdc42e 100644 --- a/src/ship/items/state.rs +++ b/src/ship/items/state.rs @@ -1,19 +1,13 @@ use std::collections::HashMap; use crate::ship::items::ClientItemId; -use crate::entity::item::{Meseta, ItemEntityId, ItemDetail, ItemEntity, ItemType, InventoryEntity, InventoryItemEntity, EquippedEntity, ItemNote}; -use std::cell::{RefMut, RefCell}; -use std::ops::{Deref, DerefMut}; -use std::convert::{From, Into}; +use crate::entity::item::{Meseta, ItemEntityId, ItemDetail, ItemEntity, InventoryEntity, InventoryItemEntity}; use std::future::Future; -use std::pin::Pin; -use async_std::sync::{Arc, Mutex}; -use std::borrow::BorrowMut; -use crate::ship::location::{AreaClient, RoomId}; -use crate::entity::character::{CharacterEntity, CharacterEntityId, TechLevel}; -use crate::entity::gateway::{EntityGateway, GatewayError}; +use crate::ship::location::RoomId; +use crate::entity::character::CharacterEntityId; +use crate::entity::gateway::GatewayError; use crate::entity::gateway::entitygateway::EntityGatewayTransaction; -use crate::entity::item::tool::{Tool, ToolType}; +use crate::entity::item::tool::Tool; use crate::entity::item::mag::Mag; use crate::ship::drops::ItemDrop; @@ -139,6 +133,7 @@ where O: Send + Sync, P::Error: Send + Sync, { + #[allow(clippy::type_complexity)] pub fn act(self, g: G) -> ItemActionStage, G, GFut, S, E> where S: Send + Sync, @@ -196,13 +191,13 @@ pub enum InventoryItemDetail { } impl InventoryItemDetail { - fn stacked<'a>(&'a self) -> Option<&'a StackedItemDetail> { + fn stacked(&self) -> Option<&StackedItemDetail> { match self { InventoryItemDetail::Stacked(sitem) => Some(sitem), _ => None, } } - fn stacked_mut <'a>(&'a mut self) -> Option<&'a mut StackedItemDetail> { + fn stacked_mut(&mut self) -> Option<&mut StackedItemDetail> { match self { InventoryItemDetail::Stacked(sitem) => Some(sitem), _ => None, @@ -225,11 +220,11 @@ impl InventoryItem { { match &self.item { InventoryItemDetail::Individual(individual_item) => { - param = func(param, individual_item.entity_id.clone()).await; + param = func(param, individual_item.entity_id).await; }, InventoryItemDetail::Stacked(stacked_item) => { for entity_id in &stacked_item.entity_ids { - param = func(param, entity_id.clone()).await; + param = func(param, *entity_id).await; } } } @@ -246,7 +241,7 @@ pub enum FloorItemDetail { } impl FloorItemDetail { - fn stacked<'a>(&'a self) -> Option<&'a StackedItemDetail> { + fn stacked(&self) -> Option<&StackedItemDetail> { match self { FloorItemDetail::Stacked(sitem) => Some(sitem), _ => None, @@ -268,11 +263,11 @@ impl FloorItem { { match &self.item { FloorItemDetail::Individual(individual_item) => { - param = func(param, individual_item.entity_id.clone()).await; + param = func(param, individual_item.entity_id).await; }, FloorItemDetail::Stacked(stacked_item) => { for entity_id in &stacked_item.entity_ids { - param = func(param, entity_id.clone()).await; + param = func(param, *entity_id).await; } }, FloorItemDetail::Meseta(_meseta) => {}, @@ -286,18 +281,11 @@ impl FloorItem { F: FnMut(T, ItemEntityId, Mag) -> Fut, Fut: Future, { - match &self.item { - FloorItemDetail::Individual(individual_item) => { - match &individual_item.item { - ItemDetail::Mag(mag) => { - param = func(param, individual_item.entity_id.clone(), mag.clone()).await; - } - _ => {} - } - }, - _ => {} + if let FloorItemDetail::Individual(individual_item) = &self.item { + if let ItemDetail::Mag(mag) = &individual_item.item { + param = func(param, individual_item.entity_id, mag.clone()).await; + } } - param } } @@ -370,7 +358,7 @@ impl InventoryState { match item.item { FloorItemDetail::Individual(iitem) => { if self.inventory.0.len() >= 30 { - return Err(InventoryError::InventoryFull) + Err(InventoryError::InventoryFull) } else { self.inventory.0.push(InventoryItem { @@ -390,7 +378,7 @@ impl InventoryState { match existing_stack { Some(existing_stack) => { if existing_stack.entity_ids.len() + sitem.entity_ids.len() > sitem.tool.max_stack() { - return Err(InventoryError::StackFull) + Err(InventoryError::StackFull) } else { existing_stack.entity_ids.append(&mut sitem.entity_ids.clone()); @@ -399,7 +387,7 @@ impl InventoryState { }, None => { if self.inventory.0.len() >= 30 { - return Err(InventoryError::InventoryFull) + Err(InventoryError::InventoryFull) } else { self.inventory.0.push(InventoryItem { @@ -477,6 +465,7 @@ impl Default for ItemState { } +#[derive(Default)] struct ProxiedItemState { character_inventory: HashMap, //character_bank: HashMap>, @@ -491,6 +480,7 @@ struct ProxiedItemState { } +/* impl Default for ProxiedItemState { fn default() -> Self { ProxiedItemState { @@ -505,6 +495,7 @@ impl Default for ProxiedItemState { } } } +*/ pub struct ItemStateProxy<'a> { item_state: &'a mut ItemState, @@ -529,7 +520,7 @@ where { let existing_element = master.get(&key).ok_or_else(|| err(key))?; Ok(proxy.entry(key) - .or_insert(existing_element.clone()).clone()) + .or_insert_with(|| existing_element.clone()).clone()) } @@ -544,8 +535,8 @@ impl<'a> ItemStateProxy<'a> { pub fn inventory(&mut self, character_id: &CharacterEntityId) -> Result { Ok(InventoryState { character_id: *character_id, - inventory: get_or_clone(&self.item_state.character_inventory, &mut self.proxied_state.character_inventory, *character_id, |c| ItemStateError::NoCharacter(c))?, - meseta: get_or_clone(&self.item_state.character_meseta, &mut self.proxied_state.character_meseta, *character_id, |c| ItemStateError::NoCharacter(c))?, + inventory: get_or_clone(&self.item_state.character_inventory, &mut self.proxied_state.character_inventory, *character_id, ItemStateError::NoCharacter)?, + meseta: get_or_clone(&self.item_state.character_meseta, &mut self.proxied_state.character_meseta, *character_id, ItemStateError::NoCharacter)?, }) } @@ -555,16 +546,16 @@ impl<'a> ItemStateProxy<'a> { } pub fn floor(&mut self, character_id: &CharacterEntityId) -> Result { - let room_id = get_or_clone(&self.item_state.character_room, &mut self.proxied_state.character_room, *character_id, |c| ItemStateError::NoCharacter(c))?; + let room_id = get_or_clone(&self.item_state.character_room, &mut self.proxied_state.character_room, *character_id, ItemStateError::NoCharacter)?; Ok(FloorState { character_id: *character_id, - local: get_or_clone(&self.item_state.character_floor, &mut self.proxied_state.character_floor, *character_id, |c| ItemStateError::NoCharacter(c))?, - shared: get_or_clone(&self.item_state.room_floor, &mut self.proxied_state.room_floor, room_id, |r| ItemStateError::NoRoom(r))?, + local: get_or_clone(&self.item_state.character_floor, &mut self.proxied_state.character_floor, *character_id, ItemStateError::NoCharacter)?, + shared: get_or_clone(&self.item_state.room_floor, &mut self.proxied_state.room_floor, room_id, ItemStateError::NoRoom)?, }) } pub fn set_floor(&mut self, floor: FloorState) { - let room_id = get_or_clone(&self.item_state.character_room, &mut self.proxied_state.character_room, floor.character_id, |c| ItemStateError::NoCharacter(c)).unwrap(); + let room_id = get_or_clone(&self.item_state.character_room, &mut self.proxied_state.character_room, floor.character_id, ItemStateError::NoCharacter).unwrap(); self.proxied_state.character_floor.insert(floor.character_id, floor.local); self.proxied_state.room_floor.insert(room_id, floor.shared); } -- 2.36.0 From eac0a31056d0289c6d7f336217335d4df1312513 Mon Sep 17 00:00:00 2001 From: jake Date: Wed, 27 Apr 2022 23:57:43 -0600 Subject: [PATCH 20/60] cool I can simplify this now --- src/ship/items/actions.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/ship/items/actions.rs b/src/ship/items/actions.rs index 4f2fdad..8675b9b 100644 --- a/src/ship/items/actions.rs +++ b/src/ship/items/actions.rs @@ -80,7 +80,7 @@ pub async fn pick_up_item( where EG: EntityGateway, { - let result: Result = entity_gateway.with_transaction(|transaction| async move { + entity_gateway.with_transaction(|transaction| async move { let item_state_proxy = ItemStateProxy::new(item_state); let ((item_state_proxy, transaction), result) = ItemStateAction::default() .act(take_item_from_floor(character.id, *item_id)) @@ -89,8 +89,7 @@ where .await?; item_state_proxy.commit(); Ok((transaction, result)) - }).await; - result + }).await } pub async fn drop_item( -- 2.36.0 From 1eae3795af3baca94d23a763d9b0258b91cfa64f Mon Sep 17 00:00:00 2001 From: jake Date: Fri, 29 Apr 2022 22:52:23 -0600 Subject: [PATCH 21/60] add character_id to ItemNote::PlayerDrop --- src/entity/gateway/postgres/models.rs | 7 +++++-- src/entity/item/mod.rs | 1 + src/ship/items/manager.rs | 3 +++ 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/entity/gateway/postgres/models.rs b/src/entity/gateway/postgres/models.rs index f2b8c94..89a219e 100644 --- a/src/entity/gateway/postgres/models.rs +++ b/src/entity/gateway/postgres/models.rs @@ -591,6 +591,7 @@ pub enum PgItemNoteDetail { character_id: u32, }, PlayerDrop { + character_id: u32, map_area: MapArea, x: f32, y: f32, @@ -625,7 +626,8 @@ impl From for PgItemNoteDetail { ItemNote::Pickup{character_id} => PgItemNoteDetail::Pickup { character_id: character_id.0, }, - ItemNote::PlayerDrop{map_area, x, y, z} => PgItemNoteDetail::PlayerDrop { + ItemNote::PlayerDrop{character_id, map_area, x, y, z} => PgItemNoteDetail::PlayerDrop { + character_id: character_id.0, map_area, x,y,z, }, @@ -660,7 +662,8 @@ impl From for ItemNote { PgItemNoteDetail::Pickup{character_id} => ItemNote::Pickup { character_id: CharacterEntityId(character_id as u32), }, - PgItemNoteDetail::PlayerDrop{map_area, x, y, z} => ItemNote::PlayerDrop { + PgItemNoteDetail::PlayerDrop{character_id, map_area, x, y, z} => ItemNote::PlayerDrop { + character_id: CharacterEntityId(character_id as u32), map_area, x,y,z, }, diff --git a/src/entity/item/mod.rs b/src/entity/item/mod.rs index c840034..220b810 100644 --- a/src/entity/item/mod.rs +++ b/src/entity/item/mod.rs @@ -40,6 +40,7 @@ pub enum ItemNote { character_id: CharacterEntityId, }, PlayerDrop { + character_id: CharacterEntityId, map_area: MapArea, x: f32, y: f32, diff --git a/src/ship/items/manager.rs b/src/ship/items/manager.rs index 950e088..7d9cd7c 100644 --- a/src/ship/items/manager.rs +++ b/src/ship/items/manager.rs @@ -492,6 +492,7 @@ impl ItemManager { entity_gateway.add_item_note( &individual_floor_item.entity_id, ItemNote::PlayerDrop { + character_id: character.id, map_area: item_drop_location.0, x: item_drop_location.1, y: item_drop_location.2, @@ -505,6 +506,7 @@ impl ItemManager { entity_gateway.add_item_note( entity_id, ItemNote::PlayerDrop { + character_id: character.id, map_area: item_drop_location.0, x: item_drop_location.1, y: item_drop_location.2, @@ -570,6 +572,7 @@ impl ItemManager { entity_gateway.add_item_note( entity_id, ItemNote::PlayerDrop { + character_id: character.id, map_area: drop_location.map_area, x: drop_location.x, y: 0.0, -- 2.36.0 From b8de2ea8e68b2f8ab2d8f149e20fc0fbb31d6ff3 Mon Sep 17 00:00:00 2001 From: jake Date: Sat, 30 Apr 2022 11:39:12 -0600 Subject: [PATCH 22/60] add item_state to ShipState --- Cargo.lock | 21 +++++++++++++++++++++ src/ship/ship.rs | 3 +++ 2 files changed, 24 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index f134f13..6ed5058 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -597,6 +597,7 @@ dependencies = [ "derive_more", "enum-utils", "fern", + "fix-hidden-lifetime-bug", "futures", "lazy_static", "libpso", @@ -679,6 +680,26 @@ dependencies = [ "log", ] +[[package]] +name = "fix-hidden-lifetime-bug" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4ae9c2016a663983d4e40a9ff967d6dcac59819672f0b47f2b17574e99c33c8" +dependencies = [ + "fix-hidden-lifetime-bug-proc_macros", +] + +[[package]] +name = "fix-hidden-lifetime-bug-proc_macros" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4c81935e123ab0741c4c4f0d9b8377e5fb21d3de7e062fa4b1263b1fbcba1ea" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "foreign-types" version = "0.3.2" diff --git a/src/ship/ship.rs b/src/ship/ship.rs index 11c5fb9..21295bf 100644 --- a/src/ship/ship.rs +++ b/src/ship/ship.rs @@ -62,6 +62,7 @@ pub enum ShipError { PickUpInvalidItemId(u32), DropInvalidItemId(u32), ItemManagerError(#[from] items::ItemManagerError), + ItemStateError(#[from] items::state::ItemStateError), #[error("")] ItemDropLocationNotSet, BoxAlreadyDroppedItem(ClientId, u16), @@ -406,6 +407,7 @@ impl ShipServerStateBuilder { level_table: CharacterLevelTable::default(), name: self.name.unwrap_or_else(|| "NAMENOTSET".into()), item_manager: items::ItemManager::default(), + item_state: items::state::ItemState::default(), ip: self.ip.unwrap_or_else(|| Ipv4Addr::new(127,0,0,1)), port: self.port.unwrap_or(SHIP_PORT), shops: Box::new(ItemShops::default()), @@ -450,6 +452,7 @@ pub struct ShipServerState { level_table: CharacterLevelTable, name: String, item_manager: items::ItemManager, + item_state: items::state::ItemState, shops: Box, pub blocks: Blocks, -- 2.36.0 From acdf960d4bdd13a808b620f28d045a65bd4a1100 Mon Sep 17 00:00:00 2001 From: jake Date: Sat, 30 Apr 2022 11:40:21 -0600 Subject: [PATCH 23/60] add/move stuff around for itemstate --- src/ship/items/actions.rs | 2 +- src/ship/items/state.rs | 85 +++++++++++++++++++++++++-------------- 2 files changed, 56 insertions(+), 31 deletions(-) diff --git a/src/ship/items/actions.rs b/src/ship/items/actions.rs index 8675b9b..09e6f1a 100644 --- a/src/ship/items/actions.rs +++ b/src/ship/items/actions.rs @@ -58,7 +58,7 @@ fn add_floor_item_to_inventory(character: &CharacterEntity) }}).await?; let add_result = inventory.add_floor_item(floor_item)?; - transaction.gateway().set_character_inventory(&character.id, &inventory.inventory.as_inventory_entity(&character.id)).await?; + transaction.gateway().set_character_inventory(&character.id, &inventory.as_inventory_entity(&character.id)).await?; item_state.set_inventory(inventory); Ok(((item_state, transaction), diff --git a/src/ship/items/state.rs b/src/ship/items/state.rs index 7bdc42e..40131a2 100644 --- a/src/ship/items/state.rs +++ b/src/ship/items/state.rs @@ -3,6 +3,7 @@ use crate::ship::items::ClientItemId; use crate::entity::item::{Meseta, ItemEntityId, ItemDetail, ItemEntity, InventoryEntity, InventoryItemEntity}; use std::future::Future; +use crate::ship::map::MapArea; use crate::ship::location::RoomId; use crate::entity::character::CharacterEntityId; use crate::entity::gateway::GatewayError; @@ -253,6 +254,10 @@ impl FloorItemDetail { pub struct FloorItem { item_id: ClientItemId, item: FloorItemDetail, + map_area: MapArea, + x: f32, + y: f32, + z: f32, } impl FloorItem { @@ -294,35 +299,6 @@ impl FloorItem { #[derive(Clone, Debug)] pub struct Inventory(Vec); -impl Inventory { - pub fn as_inventory_entity(&self, _character_id: &CharacterEntityId) -> InventoryEntity { - InventoryEntity { - items: self.0.iter() - .map(|item| { - match &item.item { - InventoryItemDetail::Individual(item) => { - InventoryItemEntity::Individual(ItemEntity { - id: item.entity_id, - item: item.item.clone(), - }) - }, - InventoryItemDetail::Stacked(items) => { - InventoryItemEntity::Stacked(items.entity_ids.iter() - .map(|id| { - ItemEntity { - id: *id, - item: ItemDetail::Tool(items.tool) - } - }) - .collect()) - }, - } - }) - .collect() - } - } -} - #[derive(thiserror::Error, Debug)] pub enum InventoryError { @@ -349,7 +325,7 @@ pub struct RoomFloorItems(Vec); pub struct InventoryState { character_id: CharacterEntityId, - pub inventory: Inventory, + inventory: Inventory, meseta: Meseta, } @@ -411,6 +387,39 @@ impl InventoryState { }, } } + + pub fn take_item(&mut self, item_id: &ClientItemId) -> Option { + self.inventory.0 + .drain_filter(|i| i.item_id == *item_id) + .next() + } + + pub fn as_inventory_entity(&self, _character_id: &CharacterEntityId) -> InventoryEntity { + InventoryEntity { + items: self.inventory.0.iter() + .map(|item| { + match &item.item { + InventoryItemDetail::Individual(item) => { + InventoryItemEntity::Individual(ItemEntity { + id: item.entity_id, + item: item.item.clone(), + }) + }, + InventoryItemDetail::Stacked(items) => { + InventoryItemEntity::Stacked(items.entity_ids.iter() + .map(|id| { + ItemEntity { + id: *id, + item: ItemDetail::Tool(items.tool) + } + }) + .collect()) + }, + } + }) + .collect() + } + } } pub struct FloorState { @@ -434,6 +443,22 @@ impl FloorState { .next() }) } + + pub fn add_inventory_item(&mut self, inventory_item: InventoryItem, map_area: MapArea, position: (f32, f32, f32)) { + let floor_item = FloorItem { + item_id: inventory_item.item_id, + item: match inventory_item.item { + InventoryItemDetail::Individual(individual_item) => FloorItemDetail::Individual(individual_item), + InventoryItemDetail::Stacked(stacked_item) => FloorItemDetail::Stacked(stacked_item), + }, + map_area: map_area, + x: position.0, + y: position.1, + z: position.2, + }; + + self.shared.0.push(floor_item); + } } -- 2.36.0 From 810d8269afd5519597cc70c17ea3e711dc92d90b Mon Sep 17 00:00:00 2001 From: jake Date: Sat, 30 Apr 2022 11:41:24 -0600 Subject: [PATCH 24/60] move drop item over to item_state --- src/ship/items/actions.rs | 69 +++++++++++++++++++++++++----- src/ship/packet/handler/message.rs | 16 ++++--- src/ship/ship.rs | 2 +- 3 files changed, 68 insertions(+), 19 deletions(-) diff --git a/src/ship/items/actions.rs b/src/ship/items/actions.rs index 09e6f1a..124b626 100644 --- a/src/ship/items/actions.rs +++ b/src/ship/items/actions.rs @@ -3,9 +3,10 @@ use crate::entity::item::ItemNote; use std::future::Future; use std::pin::Pin; +use crate::ship::map::MapArea; use crate::entity::character::{CharacterEntity, CharacterEntityId}; -use crate::entity::gateway::EntityGateway; -use crate::ship::items::state::{ItemState, ItemStateProxy, ItemStateAction, ItemAction, ItemStateError, FloorItem, AddItemResult}; +use crate::entity::gateway::{EntityGateway, EntityGatewayTransaction}; +use crate::ship::items::state::{ItemState, ItemStateProxy, ItemStateAction, ItemAction, ItemStateError, FloorItem, InventoryItem, AddItemResult}; pub enum TriggerCreateItem {ItemAction, Yes, @@ -21,7 +22,7 @@ fn take_item_from_floor(character_id: CharacterEntityId, item_id: ClientItemId) let mut floor = item_state.floor(&character_id)?; let item = floor.take_item(&item_id).ok_or(ItemStateError::NoFloorItem(item_id))?; item_state.set_floor(floor); - + Ok(((item_state, transaction), item)) }) } @@ -71,6 +72,54 @@ fn add_floor_item_to_inventory(character: &CharacterEntity) } } + +fn take_item_from_inventory(character_id: CharacterEntityId, item_id: ClientItemId) + -> impl for<'a> Fn((ItemStateProxy<'a>, Box), ()) + -> Pin), InventoryItem), ItemStateError>> + Send + 'a>> +{ + move |(mut item_state, mut transaction), _| { + Box::pin(async move { + let mut inventory = item_state.inventory(&character_id)?; + let item = inventory.take_item(&item_id).ok_or_else (|| ItemStateError::NoFloorItem(item_id))?; + + transaction.gateway().set_character_inventory(&character_id, &inventory.as_inventory_entity(&character_id)).await?; + item_state.set_inventory(inventory); + + Ok(((item_state, transaction), item)) + }) + } +} + + +fn add_inventory_item_to_shared_floor(character_id: CharacterEntityId, item_id: ClientItemId, map_area: MapArea, drop_position: (f32, f32, f32)) + -> impl for<'a> Fn((ItemStateProxy<'a>, Box), InventoryItem) + -> Pin), ()), ItemStateError>> + Send + 'a>> +{ + move |(mut item_state, transaction), inventory_item| { + Box::pin(async move { + let transaction = inventory_item.with_entity_id(Ok(transaction), |mut transaction: Result<_, ItemStateError>, entity_id| { + async move { + if let Ok(transaction) = &mut transaction { + transaction.gateway().add_item_note(&entity_id, ItemNote::PlayerDrop { + character_id, + map_area, + x: drop_position.0, + y: drop_position.1, + z: drop_position.2, + }).await?; + } + transaction + }}).await?; + + let mut floor = item_state.floor(&character_id)?; + floor.add_inventory_item(inventory_item, map_area, drop_position); + + Ok(((item_state, transaction), ())) + }) + } +} + + pub async fn pick_up_item( item_state: &mut ItemState, entity_gateway: &mut EG, @@ -96,23 +145,21 @@ pub async fn drop_item( item_state: &mut ItemState, entity_gateway: &mut EG, character: &CharacterEntity, - item_id: &ClientItemId) + item_id: &ClientItemId, + map_area: MapArea, + drop_position: (f32, f32, f32)) -> Result<(), ItemStateError> where EG: EntityGateway, { -/* - let result: Result = entity_gateway.with_transaction(|transaction| async move { + entity_gateway.with_transaction(|transaction| async move { let item_state_proxy = ItemStateProxy::new(item_state); let ((item_state_proxy, transaction), result) = ItemStateAction::default() .act(take_item_from_inventory(character.id, *item_id)) - .act(add_inventory_item_to_shared_floor(&character)) + .act(add_inventory_item_to_shared_floor(character.id, *item_id, map_area, drop_position)) .commit((item_state_proxy, transaction)) .await?; item_state_proxy.commit(); Ok((transaction, result)) - }).await; - Ok(result?) - */ - Ok(()) + }).await } diff --git a/src/ship/packet/handler/message.rs b/src/ship/packet/handler/message.rs index c39ba73..9ed1af9 100644 --- a/src/ship/packet/handler/message.rs +++ b/src/ship/packet/handler/message.rs @@ -7,6 +7,8 @@ use crate::ship::ship::{SendShipPacket, ShipError, Rooms, Clients, ItemDropLocat use crate::ship::location::{ClientLocation, ClientLocationError}; use crate::ship::items::{ItemManager, ClientItemId}; use crate::ship::packet::builder; +use crate::ship::items::state::ItemState; +use crate::ship::items::actions::{drop_item, pick_up_item}; pub async fn request_exp(id: ClientId, request_exp: &RequestExp, @@ -63,12 +65,12 @@ pub async fn request_exp(id: ClientId, } pub async fn player_drop_item(id: ClientId, - player_drop_item: &PlayerDropItem, - entity_gateway: &mut EG, - client_location: &ClientLocation, - clients: &mut Clients, - rooms: &mut Rooms, - item_manager: &mut ItemManager) + player_drop_item: &PlayerDropItem, + entity_gateway: &mut EG, + client_location: &ClientLocation, + clients: &mut Clients, + rooms: &mut Rooms, + item_state: &mut ItemState) -> Result + Send>, anyhow::Error> where EG: EntityGateway @@ -80,7 +82,7 @@ where .as_mut() .ok_or(ShipError::InvalidRoom(room_id.0 as u32))?; let area = room.map_areas.get_area_map(player_drop_item.map_area)?; - item_manager.player_drop_item_on_shared_floor(entity_gateway, &client.character, ClientItemId(player_drop_item.item_id), (*area, player_drop_item.x, player_drop_item.y, player_drop_item.z)).await?; + drop_item(item_state, entity_gateway, &client.character, &ClientItemId(player_drop_item.item_id), *area, (player_drop_item.x, player_drop_item.y, player_drop_item.z)).await?; let clients_in_area = client_location.get_clients_in_room(room_id).map_err(|err| -> ClientLocationError { err.into() })?; let pdi = player_drop_item.clone(); Ok(Box::new(clients_in_area.into_iter() diff --git a/src/ship/ship.rs b/src/ship/ship.rs index 21295bf..b16c6be 100644 --- a/src/ship/ship.rs +++ b/src/ship/ship.rs @@ -482,7 +482,7 @@ impl ShipServerState { }, GameMessage::PlayerDropItem(player_drop_item) => { let block = self.blocks.with_client(id, &self.clients)?; - handler::message::player_drop_item(id, player_drop_item, &mut self.entity_gateway, &block.client_location, &mut self.clients, &mut block.rooms, &mut self.item_manager).await? + handler::message::player_drop_item(id, player_drop_item, &mut self.entity_gateway, &block.client_location, &mut self.clients, &mut block.rooms, &mut self.item_state).await? }, GameMessage::DropCoordinates(drop_coordinates) => { let block = self.blocks.with_client(id, &self.clients)?; -- 2.36.0 From 8c403f0d0e3596321f8258618c804c80c1ad70cd Mon Sep 17 00:00:00 2001 From: jake Date: Sat, 30 Apr 2022 18:16:11 -0600 Subject: [PATCH 25/60] missed fixing these --- src/login/character.rs | 4 ++-- src/login/login.rs | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/login/character.rs b/src/login/character.rs index 595b261..420e933 100644 --- a/src/login/character.rs +++ b/src/login/character.rs @@ -812,7 +812,7 @@ mod test { #[async_trait::async_trait] impl EntityGateway for TestData { - async fn get_user_settings_by_user(&self, user: &UserAccountEntity) -> Result { + async fn get_user_settings_by_user(&mut self, user: &UserAccountEntity) -> Result { Ok(UserSettingsEntity { id: UserSettingsId(0), user_id: user.id, @@ -868,7 +868,7 @@ mod test { #[async_std::test] async fn test_character_create() { - let test_data = InMemoryGateway::default(); + let mut test_data = InMemoryGateway::default(); let mut fake_user = ClientState::new(); fake_user.user = Some(UserAccountEntity { id: UserAccountId(3), diff --git a/src/login/login.rs b/src/login/login.rs index 8e6020f..6d73817 100644 --- a/src/login/login.rs +++ b/src/login/login.rs @@ -211,7 +211,7 @@ mod test { #[async_trait::async_trait] impl EntityGateway for TestData { - async fn get_user_by_name(&self, name: String) -> Result { + async fn get_user_by_name(&mut self, name: String) -> Result { assert!(name == "testuser"); Ok(UserAccountEntity { id: UserAccountId(1), @@ -269,7 +269,7 @@ mod test { #[async_trait::async_trait] impl EntityGateway for TestData { - async fn get_user_by_name(&self, _name: String) -> Result { + async fn get_user_by_name(&mut self, _name: String) -> Result { Err(GatewayError::Error) } } @@ -303,7 +303,7 @@ mod test { #[async_trait::async_trait] impl EntityGateway for TestData { - async fn get_user_by_name(&self, name: String) -> Result { + async fn get_user_by_name(&mut self, name: String) -> Result { assert!(name == "testuser"); Ok(UserAccountEntity { id: UserAccountId(1), -- 2.36.0 From bd6b8effcdce9c340d5901b2a3ade5f03bf71468 Mon Sep 17 00:00:00 2001 From: jake Date: Sat, 30 Apr 2022 18:18:05 -0600 Subject: [PATCH 26/60] reorder function --- src/ship/items/actions.rs | 43 +++++++++++++++++++-------------------- 1 file changed, 21 insertions(+), 22 deletions(-) diff --git a/src/ship/items/actions.rs b/src/ship/items/actions.rs index 124b626..c8eb0d1 100644 --- a/src/ship/items/actions.rs +++ b/src/ship/items/actions.rs @@ -73,6 +73,27 @@ fn add_floor_item_to_inventory(character: &CharacterEntity) } +pub async fn pick_up_item( + item_state: &mut ItemState, + entity_gateway: &mut EG, + character: &CharacterEntity, + item_id: &ClientItemId) + -> Result +where + EG: EntityGateway, +{ + entity_gateway.with_transaction(|transaction| async move { + let item_state_proxy = ItemStateProxy::new(item_state); + let ((item_state_proxy, transaction), result) = ItemStateAction::default() + .act(take_item_from_floor(character.id, *item_id)) + .act(add_floor_item_to_inventory(character)) + .commit((item_state_proxy, transaction)) + .await?; + item_state_proxy.commit(); + Ok((transaction, result)) + }).await +} + fn take_item_from_inventory(character_id: CharacterEntityId, item_id: ClientItemId) -> impl for<'a> Fn((ItemStateProxy<'a>, Box), ()) -> Pin), InventoryItem), ItemStateError>> + Send + 'a>> @@ -119,28 +140,6 @@ fn add_inventory_item_to_shared_floor(character_id: CharacterEntityId, item_id: } } - -pub async fn pick_up_item( - item_state: &mut ItemState, - entity_gateway: &mut EG, - character: &CharacterEntity, - item_id: &ClientItemId) - -> Result -where - EG: EntityGateway, -{ - entity_gateway.with_transaction(|transaction| async move { - let item_state_proxy = ItemStateProxy::new(item_state); - let ((item_state_proxy, transaction), result) = ItemStateAction::default() - .act(take_item_from_floor(character.id, *item_id)) - .act(add_floor_item_to_inventory(character)) - .commit((item_state_proxy, transaction)) - .await?; - item_state_proxy.commit(); - Ok((transaction, result)) - }).await -} - pub async fn drop_item( item_state: &mut ItemState, entity_gateway: &mut EG, -- 2.36.0 From 9a2d1945af56782f5b6e8e77fb78b7715c6a68fa Mon Sep 17 00:00:00 2001 From: jake Date: Sat, 30 Apr 2022 18:19:11 -0600 Subject: [PATCH 27/60] I was wondering why nightly a week ago was letting me get away with this --- src/ship/items/actions.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/ship/items/actions.rs b/src/ship/items/actions.rs index c8eb0d1..ee2c64a 100644 --- a/src/ship/items/actions.rs +++ b/src/ship/items/actions.rs @@ -15,7 +15,7 @@ pub enum TriggerCreateItem {ItemAction, fn take_item_from_floor(character_id: CharacterEntityId, item_id: ClientItemId) -> impl for<'a> Fn((ItemStateProxy<'a>, Box), ()) - -> Pin), FloorItem), ItemStateError>> + Send + 'a>> + -> Pin, Box), FloorItem), ItemStateError>> + Send + 'a>> { move |(mut item_state, transaction), _| { Box::pin(async move { @@ -30,7 +30,7 @@ fn take_item_from_floor(character_id: CharacterEntityId, item_id: ClientItemId) fn add_floor_item_to_inventory(character: &CharacterEntity) -> impl for<'a> Fn((ItemStateProxy<'a>, Box), FloorItem) - -> Pin), TriggerCreateItem), ItemStateError>> + Send + 'a>> + -> Pin, Box), TriggerCreateItem), ItemStateError>> + Send + 'a>> { let character = character.clone(); move |(mut item_state, transaction), floor_item| { @@ -96,7 +96,7 @@ where fn take_item_from_inventory(character_id: CharacterEntityId, item_id: ClientItemId) -> impl for<'a> Fn((ItemStateProxy<'a>, Box), ()) - -> Pin), InventoryItem), ItemStateError>> + Send + 'a>> + -> Pin, Box), InventoryItem), ItemStateError>> + Send + 'a>> { move |(mut item_state, mut transaction), _| { Box::pin(async move { @@ -114,7 +114,7 @@ fn take_item_from_inventory(character_id: CharacterEntityId, item_id: ClientItem fn add_inventory_item_to_shared_floor(character_id: CharacterEntityId, item_id: ClientItemId, map_area: MapArea, drop_position: (f32, f32, f32)) -> impl for<'a> Fn((ItemStateProxy<'a>, Box), InventoryItem) - -> Pin), ()), ItemStateError>> + Send + 'a>> + -> Pin, Box), ()), ItemStateError>> + Send + 'a>> { move |(mut item_state, transaction), inventory_item| { Box::pin(async move { -- 2.36.0 From 646f75d00d17317d95dfa7f2cfdabe775bb6e939 Mon Sep 17 00:00:00 2001 From: jake Date: Sat, 30 Apr 2022 21:40:46 -0600 Subject: [PATCH 28/60] move partial drops over to item_state --- src/ship/items/actions.rs | 153 ++++++++++++++++++++++++++++- src/ship/items/state.rs | 89 ++++++++++++++--- src/ship/packet/builder/message.rs | 13 +-- src/ship/packet/handler/message.rs | 10 +- src/ship/ship.rs | 2 +- 5 files changed, 239 insertions(+), 28 deletions(-) diff --git a/src/ship/items/actions.rs b/src/ship/items/actions.rs index ee2c64a..220c899 100644 --- a/src/ship/items/actions.rs +++ b/src/ship/items/actions.rs @@ -1,12 +1,12 @@ use crate::ship::items::ClientItemId; -use crate::entity::item::ItemNote; +use crate::entity::item::{Meseta, ItemNote}; use std::future::Future; use std::pin::Pin; use crate::ship::map::MapArea; use crate::entity::character::{CharacterEntity, CharacterEntityId}; use crate::entity::gateway::{EntityGateway, EntityGatewayTransaction}; -use crate::ship::items::state::{ItemState, ItemStateProxy, ItemStateAction, ItemAction, ItemStateError, FloorItem, InventoryItem, AddItemResult}; +use crate::ship::items::state::{ItemState, ItemStateProxy, ItemStateAction, ItemAction, ItemStateError, FloorItem, InventoryItem, AddItemResult, FloorItemDetail, StackedItemDetail}; pub enum TriggerCreateItem {ItemAction, Yes, @@ -112,7 +112,7 @@ fn take_item_from_inventory(character_id: CharacterEntityId, item_id: ClientItem } -fn add_inventory_item_to_shared_floor(character_id: CharacterEntityId, item_id: ClientItemId, map_area: MapArea, drop_position: (f32, f32, f32)) +fn add_inventory_item_to_shared_floor(character_id: CharacterEntityId, map_area: MapArea, drop_position: (f32, f32, f32)) -> impl for<'a> Fn((ItemStateProxy<'a>, Box), InventoryItem) -> Pin, Box), ()), ItemStateError>> + Send + 'a>> { @@ -134,6 +134,7 @@ fn add_inventory_item_to_shared_floor(character_id: CharacterEntityId, item_id: let mut floor = item_state.floor(&character_id)?; floor.add_inventory_item(inventory_item, map_area, drop_position); + item_state.set_floor(floor); Ok(((item_state, transaction), ())) }) @@ -155,7 +156,151 @@ where let item_state_proxy = ItemStateProxy::new(item_state); let ((item_state_proxy, transaction), result) = ItemStateAction::default() .act(take_item_from_inventory(character.id, *item_id)) - .act(add_inventory_item_to_shared_floor(character.id, *item_id, map_area, drop_position)) + .act(add_inventory_item_to_shared_floor(character.id, map_area, drop_position)) + .commit((item_state_proxy, transaction)) + .await?; + item_state_proxy.commit(); + Ok((transaction, result)) + }).await +} + + +fn take_partial_item_from_inventory(character_id: CharacterEntityId, item_id: ClientItemId, amount: u32) + -> impl for<'a> Fn((ItemStateProxy<'a>, Box), ()) + -> Pin, Box), StackedItemDetail), ItemStateError>> + Send + 'a>> +{ + move |(mut item_state, mut transaction), _| { + Box::pin(async move { + let mut inventory = item_state.inventory(&character_id)?; + let item = inventory.take_partial_item(&item_id, amount).ok_or_else (|| ItemStateError::NoFloorItem(item_id))?; + + transaction.gateway().set_character_inventory(&character_id, &inventory.as_inventory_entity(&character_id)).await?; + item_state.set_inventory(inventory); + + Ok(((item_state, transaction), item)) + }) + } +} + +fn add_partial_inventory_item_to_shared_floor(character_id: CharacterEntityId, map_area: MapArea, drop_position: (f32, f32)) + -> impl for<'a> Fn((ItemStateProxy<'a>, Box), StackedItemDetail) + -> Pin, Box), FloorItem), ItemStateError>> + Send + 'a>> +{ + move |(mut item_state, transaction), stacked_item| { + Box::pin(async move { + let floor_item = FloorItem { + item_id: item_state.new_item_id()?, + item: FloorItemDetail::Stacked(stacked_item), + map_area, + x: drop_position.0, + y: 0.0, + z: drop_position.1, + }; + + let transaction = floor_item.with_entity_id(Ok(transaction), |mut transaction: Result<_, ItemStateError>, entity_id| { + async move { + if let Ok(transaction) = &mut transaction { + transaction.gateway().add_item_note(&entity_id, ItemNote::PlayerDrop { + character_id, + map_area, + x: drop_position.0, + y: 0.0, + z: drop_position.1, + }).await?; + } + transaction + }}).await?; + + let mut floor = item_state.floor(&character_id)?; + let floor_item = floor.add_item(floor_item).clone(); + item_state.set_floor(floor); + + Ok(((item_state, transaction), floor_item)) + }) + } +} + +pub async fn drop_partial_item<'a, EG>( + item_state: &'a mut ItemState, + entity_gateway: &mut EG, + character: &CharacterEntity, + item_id: &ClientItemId, + map_area: MapArea, + drop_position: (f32, f32), + amount: u32) + -> Result +where + EG: EntityGateway, +{ + entity_gateway.with_transaction(|transaction| async move { + let item_state_proxy = ItemStateProxy::new(item_state); + let ((item_state_proxy, transaction), result) = ItemStateAction::default() + .act(take_partial_item_from_inventory(character.id, *item_id, amount)) + .act(add_partial_inventory_item_to_shared_floor(character.id, map_area, drop_position)) + .commit((item_state_proxy, transaction)) + .await?; + item_state_proxy.commit(); + Ok((transaction, result)) + }).await +} + + +fn take_meseta_from_inventory(character_id: CharacterEntityId, amount: u32) + -> impl for<'a> Fn((ItemStateProxy<'a>, Box), ()) + -> Pin, Box), u32), ItemStateError>> + Send + 'a>> +{ + move |(mut item_state, mut transaction), _| { + Box::pin(async move { + let mut inventory = item_state.inventory(&character_id)?; + inventory.remove_meseta(amount)?; + transaction.gateway().set_character_meseta(&character_id, inventory.meseta).await?; + + Ok(((item_state, transaction), amount)) + }) + } +} + +fn add_meseta_to_shared_floor(character_id: CharacterEntityId, map_area: MapArea, drop_position: (f32, f32)) + -> impl for<'a> Fn((ItemStateProxy<'a>, Box), u32) + -> Pin, Box), FloorItem), ItemStateError>> + Send + 'a>> +{ + + move |(mut item_state, transaction), amount| { + Box::pin(async move { + let floor_item = FloorItem { + item_id: item_state.new_item_id()?, + item: FloorItemDetail::Meseta(Meseta(amount)), + map_area: map_area, + x: drop_position.0, + y: 0.0, + z: drop_position.1, + }; + + let mut floor = item_state.floor(&character_id)?; + let floor_item = floor.add_item(floor_item).clone(); + item_state.set_floor(floor); + + Ok(((item_state, transaction), floor_item)) + }) + } +} + +pub async fn drop_meseta<'a, EG>( + item_state: &'a mut ItemState, + entity_gateway: &mut EG, + character: &CharacterEntity, + map_area: MapArea, + drop_position: (f32, f32), + amount: u32) + -> Result +where + EG: EntityGateway, +{ + entity_gateway.with_transaction(|transaction| async move { + let item_state_proxy = ItemStateProxy::new(item_state); + let ((item_state_proxy, transaction), result) = ItemStateAction::default() + .act(take_meseta_from_inventory(character.id, amount)) + .act(add_meseta_to_shared_floor(character.id, map_area, drop_position)) .commit((item_state_proxy, transaction)) .await?; item_state_proxy.commit(); diff --git a/src/ship/items/state.rs b/src/ship/items/state.rs index 40131a2..ac07759 100644 --- a/src/ship/items/state.rs +++ b/src/ship/items/state.rs @@ -1,3 +1,4 @@ +use std::cmp::Ordering; use std::collections::HashMap; use crate::ship::items::ClientItemId; use crate::entity::item::{Meseta, ItemEntityId, ItemDetail, ItemEntity, InventoryEntity, InventoryItemEntity}; @@ -7,7 +8,6 @@ use crate::ship::map::MapArea; use crate::ship::location::RoomId; use crate::entity::character::CharacterEntityId; use crate::entity::gateway::GatewayError; -use crate::entity::gateway::entitygateway::EntityGatewayTransaction; use crate::entity::item::tool::Tool; use crate::entity::item::mag::Mag; use crate::ship::drops::ItemDrop; @@ -33,6 +33,9 @@ pub enum ItemStateError { #[error("gateway")] GatewayError(#[from] GatewayError), + + #[error("tried to drop more meseta than in inventory: {0}")] + InvalidMesetaDrop(u32), } @@ -163,7 +166,7 @@ where type Start = T; type Error = E; - async fn action(&self, s: Self::Start, i: Self::Input) -> Result<(Self::Start, Self::Output), Self::Error> { + async fn action(&self, s: Self::Start, _i: Self::Input) -> Result<(Self::Start, Self::Output), Self::Error> { Ok((s, ())) } @@ -252,12 +255,12 @@ impl FloorItemDetail { #[derive(Clone)] pub struct FloorItem { - item_id: ClientItemId, - item: FloorItemDetail, - map_area: MapArea, - x: f32, - y: f32, - z: f32, + pub item_id: ClientItemId, + pub item: FloorItemDetail, + pub map_area: MapArea, + pub x: f32, + pub y: f32, + pub z: f32, } impl FloorItem { @@ -293,6 +296,20 @@ impl FloorItem { } param } + + pub fn as_client_bytes(&self) -> [u8; 16] { + match &self.item { + FloorItemDetail::Individual(individual_floor_item) => { + individual_floor_item.item.as_client_bytes() + }, + FloorItemDetail::Stacked(stacked_floor_item) => { + stacked_floor_item.tool.as_stacked_bytes(stacked_floor_item.entity_ids.len()) + }, + FloorItemDetail::Meseta(meseta_floor_item) => { + meseta_floor_item.as_bytes() + } + } + } } @@ -326,7 +343,7 @@ pub struct RoomFloorItems(Vec); pub struct InventoryState { character_id: CharacterEntityId, inventory: Inventory, - meseta: Meseta, + pub meseta: Meseta, } impl InventoryState { @@ -374,13 +391,13 @@ impl InventoryState { } } } - + }, FloorItemDetail::Meseta(meseta) => { if self.meseta == Meseta(999999) { Err(InventoryError::MesetaFull) } - else { + else { self.meseta.0 = std::cmp::min(self.meseta.0 + meseta.0 ,999999); Ok(AddItemResult::Meseta) } @@ -394,6 +411,40 @@ impl InventoryState { .next() } + pub fn take_partial_item(&mut self, item_id: &ClientItemId, amount: u32) -> Option { + let amount = amount as usize; + let (idx, _, stacked_item) = self.inventory.0 + .iter_mut() + .enumerate() + .filter_map(|(k, item)| { + match item.item { + InventoryItemDetail::Stacked(ref mut stacked_item) => Some((k, item.item_id, stacked_item)), + _ => None + } + }) + .find(|(_, id, _)| *id == *item_id)?; + + + let remove_all = match stacked_item.entity_ids.len().cmp(&amount) { + Ordering::Equal => true, + Ordering::Greater => false, + Ordering::Less => return None, + }; + + if remove_all { + let stacked_item = stacked_item.clone(); + self.inventory.0.remove(idx); + Some(stacked_item) + } + else { + let entity_ids = stacked_item.entity_ids.drain(..amount).collect(); + Some(StackedItemDetail { + entity_ids: entity_ids, + tool: stacked_item.tool, + }) + } + } + pub fn as_inventory_entity(&self, _character_id: &CharacterEntityId) -> InventoryEntity { InventoryEntity { items: self.inventory.0.iter() @@ -420,6 +471,14 @@ impl InventoryState { .collect() } } + + pub fn remove_meseta(&mut self, amount: u32) -> Result<(), ItemStateError> { + if amount > self.meseta.0 { + return Err(ItemStateError::InvalidMesetaDrop(amount)) + } + self.meseta.0 -= amount; + Ok(()) + } } pub struct FloorState { @@ -444,7 +503,7 @@ impl FloorState { }) } - pub fn add_inventory_item(&mut self, inventory_item: InventoryItem, map_area: MapArea, position: (f32, f32, f32)) { + pub fn add_inventory_item(&mut self, inventory_item: InventoryItem, map_area: MapArea, position: (f32, f32, f32)) -> &FloorItem { let floor_item = FloorItem { item_id: inventory_item.item_id, item: match inventory_item.item { @@ -458,6 +517,12 @@ impl FloorState { }; self.shared.0.push(floor_item); + &self.shared.0[self.shared.0.len()-1] + } + + pub fn add_item(&mut self, floor_item: FloorItem) -> &FloorItem { + self.shared.0.push(floor_item); + &self.shared.0[self.shared.0.len()-1] } } diff --git a/src/ship/packet/builder/message.rs b/src/ship/packet/builder/message.rs index 186e637..23f3ea1 100644 --- a/src/ship/packet/builder/message.rs +++ b/src/ship/packet/builder/message.rs @@ -4,6 +4,7 @@ use crate::entity::item; use crate::common::leveltable::CharacterStats; use crate::ship::ship::{ShipError}; use crate::ship::items::{ClientItemId, InventoryItem, StackedFloorItem, FloorItem, CharacterBank}; +use crate::ship::items::state::FloorItem as FloorItem2; use crate::ship::location::AreaClient; use std::convert::TryInto; use crate::ship::shops::ShopItem; @@ -89,7 +90,7 @@ pub fn remove_item_from_floor(area_client: AreaClient, item: &FloorItem) -> Resu }) } -pub fn drop_split_stack(area_client: AreaClient, item: &StackedFloorItem) -> Result { +pub fn drop_split_stack(area_client: AreaClient, item: &FloorItem2) -> Result { let item_bytes = item.as_client_bytes(); Ok(DropSplitStack { client: area_client.local_client.id(), @@ -106,18 +107,18 @@ pub fn drop_split_stack(area_client: AreaClient, item: &StackedFloorItem) -> Res }) } -pub fn drop_split_meseta_stack(area_client: AreaClient, item: &FloorItem) -> Result { +pub fn drop_split_meseta_stack(area_client: AreaClient, item: &FloorItem2) -> Result { let item_bytes = item.as_client_bytes(); Ok(DropSplitStack { client: area_client.local_client.id(), target: 0, variety: 0, unknown1: 0, - map_area: item.map_area().area_value(), - x: item.x(), - z: item.z(), + map_area: item.map_area.area_value(), + x: item.x, + z: item.z, item_bytes: item_bytes[0..12].try_into()?, - item_id: item.item_id().0, + item_id: item.item_id.0, item_bytes2: item_bytes[12..16].try_into()?, unknown2: 0, }) diff --git a/src/ship/packet/handler/message.rs b/src/ship/packet/handler/message.rs index 9ed1af9..fbdd998 100644 --- a/src/ship/packet/handler/message.rs +++ b/src/ship/packet/handler/message.rs @@ -8,7 +8,7 @@ use crate::ship::location::{ClientLocation, ClientLocationError}; use crate::ship::items::{ItemManager, ClientItemId}; use crate::ship::packet::builder; use crate::ship::items::state::ItemState; -use crate::ship::items::actions::{drop_item, pick_up_item}; +use crate::ship::items::actions::{drop_item, drop_partial_item, drop_meseta}; pub async fn request_exp(id: ClientId, request_exp: &RequestExp, @@ -120,7 +120,7 @@ pub async fn no_longer_has_item(id: ClientId, entity_gateway: &mut EG, client_location: &ClientLocation, clients: &mut Clients, - item_manager: &mut ItemManager) + item_state: &mut ItemState) -> Result + Send>, anyhow::Error> where EG: EntityGateway @@ -134,7 +134,7 @@ where } if no_longer_has_item.item_id == 0xFFFFFFFF { - let dropped_meseta = item_manager.player_drops_meseta_on_shared_floor(entity_gateway, &mut client.character, drop_location, no_longer_has_item.amount as u32).await?; + let dropped_meseta = drop_meseta(item_state, entity_gateway, &client.character, drop_location.map_area, (drop_location.x, drop_location.z), no_longer_has_item.amount).await?; let dropped_meseta_pkt = builder::message::drop_split_meseta_stack(area_client, &dropped_meseta)?; let no_longer_has_meseta_pkt = builder::message::player_no_longer_has_meseta(area_client, no_longer_has_item.amount as u32); @@ -158,9 +158,9 @@ where )) } else { - let dropped_item = item_manager.player_drops_partial_stack_on_shared_floor(entity_gateway, &client.character, drop_location.item_id, drop_location, no_longer_has_item.amount as usize).await?; + let dropped_item = drop_partial_item(item_state, entity_gateway, &client.character, &drop_location.item_id, drop_location.map_area, (drop_location.x, drop_location.z), no_longer_has_item.amount).await?; - let dropped_item_pkt = builder::message::drop_split_stack(area_client, dropped_item)?; + let dropped_item_pkt = builder::message::drop_split_stack(area_client, &dropped_item)?; client.item_drop_location = None; let clients_in_area = client_location.get_clients_in_room(room_id).map_err(|err| -> ClientLocationError { err.into() })?; diff --git a/src/ship/ship.rs b/src/ship/ship.rs index b16c6be..698d62e 100644 --- a/src/ship/ship.rs +++ b/src/ship/ship.rs @@ -490,7 +490,7 @@ impl ShipServerState { }, GameMessage::PlayerNoLongerHasItem(no_longer_has_item) => { let block = self.blocks.with_client(id, &self.clients)?; - handler::message::no_longer_has_item(id, no_longer_has_item, &mut self.entity_gateway, &block.client_location, &mut self.clients, &mut self.item_manager).await? + handler::message::no_longer_has_item(id, no_longer_has_item, &mut self.entity_gateway, &block.client_location, &mut self.clients, &mut self.item_state).await? }, GameMessage::PlayerChangedMap(_) | GameMessage::PlayerChangedMap2(_) | GameMessage::TellOtherPlayerMyLocation(_) | GameMessage::PlayerWarpingToFloor(_) | GameMessage::PlayerTeleported(_) | GameMessage::PlayerStopped(_) | -- 2.36.0 From 1f7086396e0c40c47d58f5fac08a8c142420033d Mon Sep 17 00:00:00 2001 From: jake Date: Sat, 30 Apr 2022 23:51:26 -0600 Subject: [PATCH 29/60] wtf happened here --- src/ship/items/actions.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ship/items/actions.rs b/src/ship/items/actions.rs index 220c899..1a80b7a 100644 --- a/src/ship/items/actions.rs +++ b/src/ship/items/actions.rs @@ -8,7 +8,7 @@ use crate::entity::character::{CharacterEntity, CharacterEntityId}; use crate::entity::gateway::{EntityGateway, EntityGatewayTransaction}; use crate::ship::items::state::{ItemState, ItemStateProxy, ItemStateAction, ItemAction, ItemStateError, FloorItem, InventoryItem, AddItemResult, FloorItemDetail, StackedItemDetail}; -pub enum TriggerCreateItem {ItemAction, +pub enum TriggerCreateItem { Yes, No } -- 2.36.0 From bd35481c2824e6b8dcb421b430104325644b3a70 Mon Sep 17 00:00:00 2001 From: jake Date: Sun, 1 May 2022 21:59:34 -0600 Subject: [PATCH 30/60] the start of a long process of replacing ItemManager --- src/ship/character.rs | 11 +- src/ship/items/manager.rs | 44 +-- src/ship/items/state.rs | 353 ++++++++++++++++++++-- src/ship/packet/builder/message.rs | 6 +- src/ship/packet/handler/direct_message.rs | 23 +- src/ship/packet/handler/lobby.rs | 10 +- src/ship/ship.rs | 4 +- 7 files changed, 388 insertions(+), 63 deletions(-) diff --git a/src/ship/character.rs b/src/ship/character.rs index 39064aa..d2144cd 100644 --- a/src/ship/character.rs +++ b/src/ship/character.rs @@ -1,7 +1,8 @@ use libpso::character::character; use crate::common::leveltable::CharacterStats; use crate::entity::character::CharacterEntity; -use crate::ship::items::{CharacterInventory, CharacterBank}; +//use crate::ship::items::{CharacterInventory, CharacterBank}; +use crate::ship::items::state::{InventoryState, BankState}; use crate::entity::item::Meseta; @@ -88,8 +89,8 @@ pub struct FullCharacterBytesBuilder<'a> { stats: Option<&'a CharacterStats>, level: Option, meseta: Option, - inventory: Option<&'a CharacterInventory>, - bank: Option<&'a CharacterBank>, + inventory: Option<&'a InventoryState>, + bank: Option<&'a BankState>, keyboard_config: Option<&'a [u8; 0x16C]>, gamepad_config: Option<&'a [u8; 0x38]>, symbol_chat: Option<&'a [u8; 1248]>, @@ -131,7 +132,7 @@ impl<'a> FullCharacterBytesBuilder<'a> { } #[must_use] - pub fn inventory(self, inventory: &'a CharacterInventory) -> FullCharacterBytesBuilder<'a> { + pub fn inventory(self, inventory: &'a InventoryState) -> FullCharacterBytesBuilder<'a> { FullCharacterBytesBuilder { inventory: Some(inventory), ..self @@ -139,7 +140,7 @@ impl<'a> FullCharacterBytesBuilder<'a> { } #[must_use] - pub fn bank(self, bank: &'a CharacterBank) -> FullCharacterBytesBuilder<'a> { + pub fn bank(self, bank: &'a BankState) -> FullCharacterBytesBuilder<'a> { FullCharacterBytesBuilder { bank: Some(bank), ..self diff --git a/src/ship/items/manager.rs b/src/ship/items/manager.rs index 7d9cd7c..f1706a3 100644 --- a/src/ship/items/manager.rs +++ b/src/ship/items/manager.rs @@ -388,7 +388,7 @@ impl ItemManager { .map_err(|err| err.into()) } - pub async fn enemy_drop_item_on_local_floor(&mut self, entity_gateway: &mut EG, character: &CharacterEntity, item_drop: ItemDrop) -> Result<&FloorItem, anyhow::Error> { + pub async fn enemy_drop_item_on_local_floor<'a, EG: EntityGateway>(&'a mut self, entity_gateway: &'a mut EG, character: &CharacterEntity, item_drop: ItemDrop) -> Result<&'a FloorItem, anyhow::Error> { let room_id = self.character_room.get(&character.id).ok_or(ItemManagerError::NoCharacter(character.id))?; enum ItemOrMeseta { @@ -550,14 +550,14 @@ impl ItemManager { Ok(floor_item) } - pub async fn player_drops_partial_stack_on_shared_floor(&mut self, - entity_gateway: &mut EG, - character: &CharacterEntity, - //inventory_item: InventoryItem, - item_id: ClientItemId, - drop_location: ItemDropLocation, - amount: usize) - -> Result<&StackedFloorItem, anyhow::Error> { + pub async fn player_drops_partial_stack_on_shared_floor<'a, EG: EntityGateway>(&'a mut self, + entity_gateway: &'a mut EG, + character: &'a CharacterEntity, + //inventory_item: InventoryItem, + item_id: ClientItemId, + drop_location: ItemDropLocation, + amount: usize) + -> Result<&'a StackedFloorItem, anyhow::Error> { let inventory = self.character_inventory.get_mut(&character.id).ok_or(ItemManagerError::NoCharacter(character.id))?; let room_id = self.character_room.get(&character.id).ok_or(ItemManagerError::NoCharacter(character.id))?; let shared_floor = self.room_floor.get_mut(room_id).ok_or(ItemManagerError::NoCharacter(character.id))?; @@ -629,12 +629,12 @@ impl ItemManager { Ok(()) } - pub async fn player_withdraws_item(&mut self, - entity_gateway: &mut EG, - character: &CharacterEntity, - item_id: ClientItemId, - amount: usize) - -> Result<&InventoryItem, anyhow::Error> { + pub async fn player_withdraws_item<'a, EG: EntityGateway>(&'a mut self, + entity_gateway: &'a mut EG, + character: &'a CharacterEntity, + item_id: ClientItemId, + amount: usize) + -> Result<&'a InventoryItem, anyhow::Error> { let inventory = self.character_inventory.get_mut(&character.id).ok_or(ItemManagerError::NoCharacter(character.id))?; let bank = self.character_bank @@ -804,13 +804,13 @@ impl ItemManager { Ok(()) } - pub async fn player_buys_item(&mut self, - entity_gateway: &mut EG, - character: &CharacterEntity, - shop_item: &(dyn ShopItem + Send + Sync), - item_id: ClientItemId, - amount: usize) - -> Result<&InventoryItem, anyhow::Error> { + pub async fn player_buys_item<'a, EG: EntityGateway>(&'a mut self, + entity_gateway: &mut EG, + character: &'a CharacterEntity, + shop_item: &'a (dyn ShopItem + Send + Sync), + item_id: ClientItemId, + amount: usize) + -> Result<&'a InventoryItem, anyhow::Error> { let inventory = self.character_inventory.get_mut(&character.id).ok_or(ItemManagerError::NoCharacter(character.id))?; let item_detail = shop_item.as_item(); diff --git a/src/ship/items/state.rs b/src/ship/items/state.rs index ac07759..a92bc4b 100644 --- a/src/ship/items/state.rs +++ b/src/ship/items/state.rs @@ -1,13 +1,14 @@ use std::cmp::Ordering; use std::collections::HashMap; +use libpso::character::character; use crate::ship::items::ClientItemId; -use crate::entity::item::{Meseta, ItemEntityId, ItemDetail, ItemEntity, InventoryEntity, InventoryItemEntity}; +use crate::entity::item::{Meseta, ItemEntityId, ItemDetail, ItemEntity, InventoryEntity, InventoryItemEntity, BankItemEntity, BankName, EquippedEntity}; use std::future::Future; use crate::ship::map::MapArea; -use crate::ship::location::RoomId; -use crate::entity::character::CharacterEntityId; -use crate::entity::gateway::GatewayError; +use crate::ship::location::{AreaClient, RoomId}; +use crate::entity::character::{CharacterEntity, CharacterEntityId}; +use crate::entity::gateway::{EntityGateway, GatewayError}; use crate::entity::item::tool::Tool; use crate::entity::item::mag::Mag; use crate::ship::drops::ItemDrop; @@ -36,6 +37,14 @@ pub enum ItemStateError { #[error("tried to drop more meseta than in inventory: {0}")] InvalidMesetaDrop(u32), + + #[error("stacked item")] + StackedItemError(Vec), +} + +pub enum FloorType { + Local, + Shared, } @@ -178,8 +187,8 @@ where #[derive(Clone, Debug)] pub struct IndividualItemDetail { - entity_id: ItemEntityId, - item: ItemDetail, + pub entity_id: ItemEntityId, + pub item: ItemDetail, } #[derive(Clone, Debug)] @@ -188,6 +197,12 @@ pub struct StackedItemDetail { pub tool: Tool, } +impl StackedItemDetail { + pub fn count(&self) -> usize { + self.entity_ids.len() + } +} + #[derive(Clone, Debug)] pub enum InventoryItemDetail { Individual(IndividualItemDetail), @@ -207,6 +222,26 @@ impl InventoryItemDetail { _ => None, } } + + pub fn as_client_bytes(&self) -> [u8; 16] { + match self { + InventoryItemDetail::Individual(item) => { + match &item.item { + ItemDetail::Weapon(w) => w.as_bytes(), + ItemDetail::Armor(a) => a.as_bytes(), + ItemDetail::Shield(s) => s.as_bytes(), + ItemDetail::Unit(u) => u.as_bytes(), + ItemDetail::Tool(t) => t.as_individual_bytes(), + ItemDetail::TechniqueDisk(d) => d.as_bytes(), + ItemDetail::Mag(m) => m.as_bytes(), + ItemDetail::ESWeapon(e) => e.as_bytes(), + } + }, + InventoryItemDetail::Stacked(item) => { + item.tool.as_stacked_bytes(item.entity_ids.len()) + }, + } + } } @@ -237,6 +272,41 @@ impl InventoryItem { } } + +#[derive(Clone, Debug)] +pub enum BankItemDetail { + Individual(IndividualItemDetail), + Stacked(StackedItemDetail), +} + +impl BankItemDetail { + pub fn as_client_bytes(&self) -> [u8; 16] { + match self { + BankItemDetail::Individual(item) => { + match &item.item { + ItemDetail::Weapon(w) => w.as_bytes(), + ItemDetail::Armor(a) => a.as_bytes(), + ItemDetail::Shield(s) => s.as_bytes(), + ItemDetail::Unit(u) => u.as_bytes(), + ItemDetail::Tool(t) => t.as_individual_bytes(), + ItemDetail::TechniqueDisk(d) => d.as_bytes(), + ItemDetail::Mag(m) => m.as_bytes(), + ItemDetail::ESWeapon(e) => e.as_bytes(), + } + }, + BankItemDetail::Stacked(item) => { + item.tool.as_stacked_bytes(item.entity_ids.len()) + }, + } + } +} + +#[derive(Clone, Debug)] +pub struct BankItem { + item_id: ClientItemId, + item: BankItemDetail, +} + #[derive(Clone)] pub enum FloorItemDetail { Individual(IndividualItemDetail), @@ -327,26 +397,41 @@ pub enum InventoryError { MesetaFull, } +#[derive(Clone)] pub enum AddItemResult { NewItem, AddToStack, Meseta, } -#[derive(Clone)] +#[derive(Clone, Default)] pub struct LocalFloor(Vec); -#[derive(Clone)] +#[derive(Clone, Default)] pub struct SharedFloor(Vec); #[derive(Clone)] pub struct RoomFloorItems(Vec); +#[derive(Clone)] pub struct InventoryState { character_id: CharacterEntityId, - inventory: Inventory, + item_id_counter: u32, + pub inventory: Inventory, + equipped: EquippedEntity, pub meseta: Meseta, } impl InventoryState { + pub fn initialize_item_ids(&mut self, base_item_id: u32) { + for (i, item) in self.inventory.0.iter_mut().enumerate() { + item.item_id = ClientItemId(base_item_id + i as u32); + } + self.item_id_counter = base_item_id + self.inventory.0.len() as u32 + 1; + } + + pub fn count(&self) -> usize { + self.inventory.0.len() + } + pub fn add_floor_item(&mut self, item: FloorItem) -> Result { match item.item { FloorItemDetail::Individual(iitem) => { @@ -479,6 +564,68 @@ impl InventoryState { self.meseta.0 -= amount; Ok(()) } + + pub fn as_client_inventory_items(&self) -> [character::InventoryItem; 30] { + self.inventory.0.iter() + .enumerate() + .fold([character::InventoryItem::default(); 30], |mut inventory, (slot, item)| { + let bytes = item.item.as_client_bytes(); + inventory[slot].data1.copy_from_slice(&bytes[0..12]); + inventory[slot].data2.copy_from_slice(&bytes[12..16]); + inventory[slot].item_id = item.item_id.0; + inventory[slot].equipped = 0; + inventory[slot].flags = 0; + + if let InventoryItemDetail::Individual(individual_item) = &item.item { + if self.equipped.is_equipped(&individual_item.entity_id) { + if let ItemDetail::Unit(_) = individual_item.item { + inventory[slot].data1[4] = self.equipped.unit.iter() + .enumerate() + .find(|(_, u_id)| **u_id == Some(individual_item.entity_id)) + .map(|(a, _)| a) + .unwrap_or(0) as u8 + } + inventory[slot].equipped = 1; + inventory[slot].flags |= 8; + } + } + inventory + }) + } +} + +#[derive(Clone, Debug)] +pub struct Bank(Vec); + +#[derive(Clone, Debug)] +pub struct BankState { + character_id: CharacterEntityId, + item_id_counter: u32, + bank: Bank, + pub meseta: Meseta, +} + +impl BankState { + pub fn initialize_item_ids(&mut self, base_item_id: u32) { + for (i, item) in self.bank.0.iter_mut().enumerate() { + item.item_id = ClientItemId(base_item_id + i as u32); + } + self.item_id_counter = base_item_id + self.bank.0.len() as u32 + 1; + } + + pub fn as_client_bank_items(&self) -> character::Bank { + self.bank.0.iter() + .enumerate() + .fold(character::Bank::default(), |mut bank, (slot, item)| { + bank.item_count = (slot + 1) as u32; + let bytes = item.item.as_client_bytes(); + bank.items[slot].data1.copy_from_slice(&bytes[0..12]); + bank.items[slot].data2.copy_from_slice(&bytes[12..16]); + bank.items[slot].item_id = item.item_id.0; + + bank + }) + } } pub struct FloorState { @@ -528,9 +675,9 @@ impl FloorState { pub struct ItemState { - character_inventory: HashMap, - //character_bank: HashMap, - character_meseta: HashMap, + character_inventory: HashMap, + character_bank: HashMap, + //character_meseta: HashMap, //bank_meseta: HashMap, character_room: HashMap, @@ -545,7 +692,9 @@ impl Default for ItemState { fn default() -> ItemState { ItemState { character_inventory: HashMap::new(), - character_meseta: HashMap::new(), + character_bank: HashMap::new(), + //character_meseta: HashMap::new(), + //bank_meseta: HashMap::new(), character_room: HashMap::new(), character_floor: HashMap::new(), room_floor: HashMap::new(), @@ -554,13 +703,169 @@ impl Default for ItemState { } } +impl ItemState { + pub fn get_character_inventory(&self, character: &CharacterEntity) -> Result<&InventoryState, ItemStateError> { + Ok(self.character_inventory.get(&character.id) + .ok_or(ItemStateError::NoCharacter(character.id))?) + } + + pub fn get_character_bank(&self, character: &CharacterEntity) -> Result<&BankState, ItemStateError> { + Ok(self.character_bank.get(&character.id) + .ok_or(ItemStateError::NoCharacter(character.id))?) + } +} + +impl ItemState { + fn new_item_id(&mut self) -> Result { + self.room_item_id_counter += 1; + Ok(ClientItemId(self.room_item_id_counter)) + } + + pub async fn load_character(&mut self, entity_gateway: &mut EG, character: &CharacterEntity) -> Result<(), ItemStateError> { + let inventory = entity_gateway.get_character_inventory(&character.id).await?; + let bank = entity_gateway.get_character_bank(&character.id, BankName("".into())).await?; + let equipped = entity_gateway.get_character_equips(&character.id).await?; + + let inventory_items = inventory.items.into_iter() + .map(|item| -> Result { + Ok(match item { + InventoryItemEntity::Individual(item) => { + InventoryItem { + item_id: self.new_item_id()?, + item: InventoryItemDetail::Individual(IndividualItemDetail { + entity_id: item.id, + item: item.item, + }), + } + }, + InventoryItemEntity::Stacked(items) => { + InventoryItem { + item_id: self.new_item_id()?, + item: InventoryItemDetail::Stacked(StackedItemDetail { + entity_ids: items.iter().map(|i| i.id).collect(), + tool: items.get(0) + .ok_or_else(|| ItemStateError::StackedItemError(items.clone()))? + .item + .clone() + .as_tool() + .ok_or_else(|| ItemStateError::StackedItemError(items.clone()))? + }) + } + }, + }) + }) + .collect::, _>>()?; + + let character_meseta = entity_gateway.get_character_meseta(&character.id).await?; + let inventory_state = InventoryState { + character_id: character.id, + item_id_counter: 0, + inventory: Inventory(inventory_items), + equipped: equipped, + meseta: character_meseta, + }; + + let bank_items = bank.items.into_iter() + .map(|item| -> Result { + Ok(match item { + BankItemEntity::Individual(item) => { + BankItem { + item_id: self.new_item_id()?, + item: BankItemDetail::Individual(IndividualItemDetail { + entity_id: item.id, + item: item.item, + }) + } + }, + BankItemEntity::Stacked(items) => { + BankItem { + item_id: self.new_item_id()?, + item: BankItemDetail::Stacked(StackedItemDetail { + entity_ids: items.iter().map(|i| i.id).collect(), + tool: items.get(0) + .ok_or_else(|| ItemStateError::StackedItemError(items.clone()))? + .item + .clone() + .as_tool() + .ok_or_else(|| ItemStateError::StackedItemError(items.clone()))? + }) + } + }, + }) + }) + .collect::, _>>()?; + + let bank_meseta = entity_gateway.get_bank_meseta(&character.id, BankName("".into())).await?; + let bank_state = BankState { + character_id: character.id, + item_id_counter: 0, + bank: Bank(bank_items), + meseta: bank_meseta, + }; + + self.character_inventory.insert(character.id, inventory_state); + self.character_bank.insert(character.id, bank_state); + Ok(()) + } + + pub fn add_character_to_room(&mut self, room_id: RoomId, character: &CharacterEntity, area_client: AreaClient) { + let base_inventory_id = ((area_client.local_client.id() as u32) << 21) | 0x10000; + let inventory = self.character_inventory.get_mut(&character.id).unwrap(); + inventory.initialize_item_ids(base_inventory_id); + let base_bank_id = ((area_client.local_client.id() as u32) << 21) | 0x20000; + let default_bank = self.character_bank.get_mut(&character.id); + if let Some(default_bank ) = default_bank { + default_bank.initialize_item_ids(base_bank_id); + } + self.character_room.insert(character.id, room_id); + self.character_floor.insert(character.id, LocalFloor::default()); + self.room_floor.entry(room_id).or_insert_with(SharedFloor::default); + + /* + let mut inc = 0x00810000; + self.room_item_id_counter.borrow_mut().entry(room_id).or_insert_with(|| Box::new(move || { + inc += 1; + ClientItemId(inc) + })); + */ + } + + pub fn remove_character_from_room(&mut self, character: &CharacterEntity) { + self.character_inventory.remove(&character.id); + self.character_floor.remove(&character.id); + if let Some(room) = self.character_room.remove(&character.id).as_ref() { + if self.character_room.iter().any(|(_, r)| r == room) { + self.room_floor.remove(room); + } + } + } + + pub fn get_floor_item(&self, character_id: &CharacterEntityId, item_id: &ClientItemId) -> Result<(&FloorItem, FloorType), ItemStateError> { + let local_floor = self.character_floor.get(character_id).ok_or(ItemStateError::NoCharacter(*character_id))?; + let room = self.character_room.get(character_id).ok_or(ItemStateError::NoCharacter(*character_id))?; + let shared_floor = self.room_floor.get(room).ok_or(ItemStateError::NoCharacter(*character_id))?; + + local_floor.0 + .iter() + .find(|item| item.item_id == *item_id) + .map(|item| (item, FloorType::Local)) + .or_else(|| { + shared_floor.0 + .iter() + .find(|item| item.item_id == *item_id) + .map(|item| (item, FloorType::Shared)) + }) + .ok_or_else(|| ItemStateError::NoFloorItem(*item_id)) + } +} + #[derive(Default)] struct ProxiedItemState { - character_inventory: HashMap, - //character_bank: HashMap>, - character_meseta: HashMap, - //bank_meseta: HashMap>, + character_inventory: HashMap, + character_bank: HashMap, + //character_meseta: HashMap, + //bank_meseta: HashMap, character_room: HashMap, character_floor: HashMap, @@ -595,7 +900,8 @@ pub struct ItemStateProxy<'a> { impl<'a> ItemStateProxy<'a> { pub fn commit(self) { self.item_state.character_inventory.extend(self.proxied_state.character_inventory.clone()); - self.item_state.character_meseta.extend(self.proxied_state.character_meseta.clone()); + self.item_state.character_bank.extend(self.proxied_state.character_bank.clone()); + //self.item_state.character_meseta.extend(self.proxied_state.character_meseta.clone()); self.item_state.character_room.extend(self.proxied_state.character_room.clone()); self.item_state.character_floor.extend(self.proxied_state.character_floor.clone()); self.item_state.room_floor.extend(self.proxied_state.room_floor.clone()); @@ -623,16 +929,20 @@ impl<'a> ItemStateProxy<'a> { } pub fn inventory(&mut self, character_id: &CharacterEntityId) -> Result { + get_or_clone(&self.item_state.character_inventory, &mut self.proxied_state.character_inventory, *character_id, ItemStateError::NoCharacter) + /* Ok(InventoryState { character_id: *character_id, inventory: get_or_clone(&self.item_state.character_inventory, &mut self.proxied_state.character_inventory, *character_id, ItemStateError::NoCharacter)?, meseta: get_or_clone(&self.item_state.character_meseta, &mut self.proxied_state.character_meseta, *character_id, ItemStateError::NoCharacter)?, }) + */ } pub fn set_inventory(&mut self, inventory: InventoryState) { - self.proxied_state.character_inventory.insert(inventory.character_id, inventory.inventory); - self.proxied_state.character_meseta.insert(inventory.character_id, inventory.meseta); + self.proxied_state.character_inventory.insert(inventory.character_id, inventory); + //self.proxied_state.character_inventory.insert(inventory.character_id, inventory.inventory); + //self.proxied_state.character_meseta.insert(inventory.character_id, inventory.meseta); } pub fn floor(&mut self, character_id: &CharacterEntityId) -> Result { @@ -651,7 +961,6 @@ impl<'a> ItemStateProxy<'a> { } pub fn new_item_id(&mut self) -> Result { - self.item_state.room_item_id_counter += 1; - Ok(ClientItemId(self.item_state.room_item_id_counter)) + self.item_state.new_item_id() } } diff --git a/src/ship/packet/builder/message.rs b/src/ship/packet/builder/message.rs index 23f3ea1..a5303bc 100644 --- a/src/ship/packet/builder/message.rs +++ b/src/ship/packet/builder/message.rs @@ -78,15 +78,15 @@ pub fn create_withdrawn_inventory_item(area_client: AreaClient, item: &Inventory }) } -pub fn remove_item_from_floor(area_client: AreaClient, item: &FloorItem) -> Result { +pub fn remove_item_from_floor(area_client: AreaClient, item: &FloorItem2) -> Result { Ok(RemoveItemFromFloor { client: area_client.local_client.id(), target: 0, client_id: area_client.local_client.id(), unknown: 0, - map_area: item.map_area().area_value(), + map_area: item.map_area.area_value(), unknown2: 0, - item_id: item.item_id().0, + item_id: item.item_id.0, }) } diff --git a/src/ship/packet/handler/direct_message.rs b/src/ship/packet/handler/direct_message.rs index a417c6f..f5f4a09 100644 --- a/src/ship/packet/handler/direct_message.rs +++ b/src/ship/packet/handler/direct_message.rs @@ -8,12 +8,14 @@ use crate::common::serverstate::ClientId; use crate::ship::ship::{SendShipPacket, ShipError, Clients, Rooms, ItemShops}; use crate::ship::location::{ClientLocation, ClientLocationError}; use crate::ship::drops::ItemDrop; -use crate::ship::items::{ItemManager, ItemManagerError, ClientItemId, TriggerCreateItem, FloorItem, FloorType}; +use crate::ship::items::{ItemManager, ItemManagerError, ClientItemId, FloorItem}; use crate::entity::gateway::EntityGateway; use crate::entity::item; use libpso::utf8_to_utf16_array; use crate::ship::packet::builder; use crate::ship::shops::{ShopItem, ToolShopItem, ArmorShopItem}; +use crate::ship::items::state::{ItemState, FloorType, FloorItemDetail}; +use crate::ship::items::actions::{pick_up_item, TriggerCreateItem}; const BANK_ACTION_DEPOSIT: u8 = 0; const BANK_ACTION_WITHDRAW: u8 = 1; @@ -123,7 +125,7 @@ pub async fn pickup_item(id: ClientId, entity_gateway: &mut EG, client_location: &ClientLocation, clients: &mut Clients, - item_manager: &mut ItemManager) + item_state: &mut ItemState) -> Result + Send>, anyhow::Error> where EG: EntityGateway @@ -134,7 +136,9 @@ where let clients_in_area = client_location.get_clients_in_room(room_id).map_err(|err| -> ClientLocationError { err.into() })?; // TODO: should not need to fetch the item here to construct this packet - let (item, floor_type) = item_manager.get_floor_item_by_id(&client.character, ClientItemId(pickup_item.item_id))?; + //let (item, floor_type) = item_manager.get_floor_item_by_id(&client.character, ClientItemId(pickup_item.item_id))?; + /* + let (item, floor_type) = item_state.get_floor_item(&client.character, &ClientItemId(pickup_item.item_id))?; let remove_item = builder::message::remove_item_from_floor(area_client, item)?; let create_item = match item { FloorItem::Individual(individual_floor_item) => Some(builder::message::create_individual_item(area_client, item.item_id(), &individual_floor_item.item)?), @@ -142,8 +146,19 @@ where FloorItem::Meseta(_) => None, //_ => Some(builder::message::create_item(area_client, &item)?), }; + */ - match item_manager.character_picks_up_item(entity_gateway, &mut client.character, ClientItemId(pickup_item.item_id)).await { + let (item, floor_type) = item_state.get_floor_item(&client.character.id, &ClientItemId(pickup_item.item_id))?; + let remove_item = builder::message::remove_item_from_floor(area_client, item)?; + let create_item = match &item.item { + FloorItemDetail::Individual(individual_floor_item) => Some(builder::message::create_individual_item(area_client, item.item_id, &individual_floor_item.item)?), + FloorItemDetail::Stacked(stacked_floor_item) => Some(builder::message::create_stacked_item(area_client, item.item_id, &stacked_floor_item.tool, stacked_floor_item.count())?), + FloorItemDetail::Meseta(_) => None, + //_ => Some(builder::message::create_item(area_client, &item)?), + }; + + //match item_manager.character_picks_up_item(entity_gateway, &mut client.character, ClientItemId(pickup_item.item_id)).await { + match pick_up_item(item_state, entity_gateway, &mut client.character, &ClientItemId(pickup_item.item_id)).await { Ok(trigger_create_item) => { let remove_packets: Box + Send> = match floor_type { FloorType::Local => { diff --git a/src/ship/packet/handler/lobby.rs b/src/ship/packet/handler/lobby.rs index ff2a0c2..4113929 100644 --- a/src/ship/packet/handler/lobby.rs +++ b/src/ship/packet/handler/lobby.rs @@ -5,6 +5,7 @@ use crate::ship::ship::{SendShipPacket, ShipError, Clients, Rooms}; use crate::ship::character::{FullCharacterBytesBuilder}; use crate::ship::location::{ClientLocation, LobbyId, RoomLobby, ClientLocationError, RoomId}; use crate::ship::packet; +use crate::ship::items::state::ItemState; use crate::ship::items::ItemManager; use crate::entity::gateway::EntityGateway; use crate::ship::map::MapArea; @@ -13,7 +14,7 @@ use crate::ship::map::MapArea; pub fn block_selected(id: ClientId, pkt: &MenuSelect, clients: &mut Clients, - item_manager: &ItemManager, + item_state: &ItemState, level_table: &CharacterLevelTable) -> Result, anyhow::Error> { let client = clients.get_mut(&id).ok_or(ShipError::ClientNotFound(id))?; @@ -21,15 +22,14 @@ pub fn block_selected(id: ClientId, let (level, stats) = level_table.get_stats_from_exp(client.character.char_class, client.character.exp); - let inventory = item_manager.get_character_inventory(&client.character).unwrap(); - let meseta = item_manager.get_character_meseta(&client.character.id).unwrap(); - let bank = item_manager.get_character_bank(&client.character).unwrap(); + let inventory = item_state.get_character_inventory(&client.character).unwrap(); + let bank = item_state.get_character_bank(&client.character).unwrap(); let fc = FullCharacterBytesBuilder::default() .character(&client.character) .stats(&stats) .level(level) - .meseta(*meseta) + .meseta(inventory.meseta) .inventory(inventory) .bank(bank) .keyboard_config(&client.character.keyboard_config.as_bytes()) diff --git a/src/ship/ship.rs b/src/ship/ship.rs index 698d62e..8cd2fdc 100644 --- a/src/ship/ship.rs +++ b/src/ship/ship.rs @@ -547,7 +547,7 @@ impl ShipServerState { handler::direct_message::request_item(id, request_item, &mut self.entity_gateway, &block.client_location, &mut self.clients, &mut block.rooms, &mut self.item_manager).await? }, GameMessage::PickupItem(pickup_item) => { - handler::direct_message::pickup_item(id, pickup_item, &mut self.entity_gateway, &block.client_location, &mut self.clients, &mut self.item_manager).await? + handler::direct_message::pickup_item(id, pickup_item, &mut self.entity_gateway, &block.client_location, &mut self.clients, &mut self.item_state).await? }, GameMessage::BoxDropRequest(box_drop_request) => { handler::direct_message::request_box_item(id, box_drop_request, &mut self.entity_gateway, &block.client_location, &mut self.clients, &mut block.rooms, &mut self.item_manager).await? @@ -629,7 +629,7 @@ impl ServerState for ShipServerState { } BLOCK_MENU_ID => { let leave_lobby = handler::lobby::remove_from_lobby(id, &mut block.client_location).into_iter().into_iter().flatten(); - let select_block = handler::lobby::block_selected(id, menuselect, &mut self.clients, &self.item_manager, &self.level_table)?.into_iter(); + let select_block = handler::lobby::block_selected(id, menuselect, &mut self.clients, &self.item_state, &self.level_table)?.into_iter(); Box::new(leave_lobby.chain(select_block)) } ROOM_MENU_ID => handler::room::join_room(id, menuselect, &mut block.client_location, &mut self.clients, &mut self.item_manager, &self.level_table, &mut block.rooms)?, -- 2.36.0 From 94c50c011d5730434cad0b3a761a752599e89569 Mon Sep 17 00:00:00 2001 From: jake Date: Sat, 14 May 2022 13:02:23 -0600 Subject: [PATCH 31/60] entitygateway stuff should take &BankName not BankName --- src/bin/main.rs | 7 +- src/entity/gateway/entitygateway.rs | 8 +-- src/entity/gateway/inmemory.rs | 12 ++-- src/entity/gateway/postgres/postgres.rs | 32 ++++----- src/login/character.rs | 2 +- src/ship/items/manager.rs | 8 +-- src/ship/items/state.rs | 2 +- tests/common.rs | 2 +- tests/test_bank.rs | 92 +++++++++++++------------ 9 files changed, 86 insertions(+), 79 deletions(-) diff --git a/src/bin/main.rs b/src/bin/main.rs index d493ec2..b2d9dbe 100644 --- a/src/bin/main.rs +++ b/src/bin/main.rs @@ -69,14 +69,15 @@ fn main() { character.name = format!("Test Char {}", i*2); let character = entity_gateway.create_character(character).await.unwrap(); entity_gateway.set_character_meseta(&character.id, item::Meseta(999999)).await.unwrap(); - entity_gateway.set_bank_meseta(&character.id, item::BankName("".into()), item::Meseta(999999)).await.unwrap(); + let mut character = NewCharacterEntity::new(fake_user.id); + entity_gateway.set_bank_meseta(&character.id, &item::BankName("".into()), item::Meseta(999999)).await.unwrap(); let mut character = NewCharacterEntity::new(fake_user.id, 1); character.slot = 2; character.name = "ItemRefactor".into(); character.exp = 80000000; let character = entity_gateway.create_character(character).await.unwrap(); entity_gateway.set_character_meseta(&character.id, item::Meseta(999999)).await.unwrap(); - entity_gateway.set_bank_meseta(&character.id, item::BankName("".into()), item::Meseta(999999)).await.unwrap(); + entity_gateway.set_bank_meseta(&character.id, &item::BankName("".into()), item::Meseta(999999)).await.unwrap(); for _ in 0..3 { entity_gateway.create_item( @@ -327,7 +328,7 @@ fn main() { let inventory = item::InventoryEntity::new(vec![InventoryItemEntity::from(item0), item1.into(), item2_w.into(), item3.into(), item4.into(), item5_m.into(), item6.into(), item6_1.into(), item7_a.into(), item8_s.into(), item9_u0.into(), item10_u1.into(), item11_u2.into(), item12_u3.into(), item13.into(), item14.into(), monomates.into()]); entity_gateway.set_character_inventory(&character.id, &inventory).await.unwrap(); - entity_gateway.set_character_bank(&character.id, &item::BankEntity::default(), item::BankName("".into())).await.unwrap(); + entity_gateway.set_character_bank(&character.id, &item::BankEntity::default(), &item::BankName("".into())).await.unwrap(); } info!("[patch] starting server"); diff --git a/src/entity/gateway/entitygateway.rs b/src/entity/gateway/entitygateway.rs index cf5599c..fa04d3b 100644 --- a/src/entity/gateway/entitygateway.rs +++ b/src/entity/gateway/entitygateway.rs @@ -108,7 +108,7 @@ pub trait EntityGateway: Send + Sync { unimplemented!(); } - async fn get_character_bank(&mut self, _char_id: &CharacterEntityId, _bank_name: BankName) -> Result { + async fn get_character_bank(&mut self, _char_id: &CharacterEntityId, _bank_name: &BankName) -> Result { unimplemented!(); } @@ -116,7 +116,7 @@ pub trait EntityGateway: Send + Sync { unimplemented!(); } - async fn set_character_bank(&mut self, _char_id: &CharacterEntityId, _inventory: &BankEntity, _bank_name: BankName) -> Result<(), GatewayError> { + async fn set_character_bank(&mut self, _char_id: &CharacterEntityId, _inventory: &BankEntity, _bank_name: &BankName) -> Result<(), GatewayError> { unimplemented!(); } @@ -136,11 +136,11 @@ pub trait EntityGateway: Send + Sync { unimplemented!(); } - async fn get_bank_meseta(&mut self, _char_id: &CharacterEntityId, _bank: BankName) -> Result { + async fn get_bank_meseta(&mut self, _char_id: &CharacterEntityId, _bank: &BankName) -> Result { unimplemented!(); } - async fn set_bank_meseta(&mut self, _char_id: &CharacterEntityId, _bank: BankName, _amount: Meseta) -> Result<(), GatewayError> { + async fn set_bank_meseta(&mut self, _char_id: &CharacterEntityId, _bank: &BankName, _amount: Meseta) -> Result<(), GatewayError> { unimplemented!(); } } diff --git a/src/entity/gateway/inmemory.rs b/src/entity/gateway/inmemory.rs index 0e3d02e..9b8d1ab 100644 --- a/src/entity/gateway/inmemory.rs +++ b/src/entity/gateway/inmemory.rs @@ -392,7 +392,7 @@ impl EntityGateway for InMemoryGateway { .unwrap_or_default()) } - async fn get_character_bank(&mut self, char_id: &CharacterEntityId, _bank_name: BankName) -> Result { + async fn get_character_bank(&mut self, char_id: &CharacterEntityId, _bank_name: &BankName) -> Result { let banks = self.banks.lock().unwrap(); Ok(banks .iter() @@ -408,7 +408,7 @@ impl EntityGateway for InMemoryGateway { } // TOOD: impl bank name - async fn set_character_bank(&mut self, char_id: &CharacterEntityId, bank: &BankEntity, _bank_name: BankName) -> Result<(), GatewayError> { + async fn set_character_bank(&mut self, char_id: &CharacterEntityId, bank: &BankEntity, _bank_name: &BankName) -> Result<(), GatewayError> { let mut banks = self.banks.lock().unwrap(); banks.insert(*char_id, bank.clone()); Ok(()) @@ -445,15 +445,15 @@ impl EntityGateway for InMemoryGateway { } } - async fn set_bank_meseta(&mut self, char_id: &CharacterEntityId, bank: BankName, meseta: Meseta) -> Result<(), GatewayError> { + async fn set_bank_meseta(&mut self, char_id: &CharacterEntityId, bank: &BankName, meseta: Meseta) -> Result<(), GatewayError> { let mut bank_meseta = self.bank_meseta.lock().unwrap(); - bank_meseta.insert((*char_id, bank), meseta); + bank_meseta.insert((*char_id, bank.clone()), meseta); Ok(()) } - async fn get_bank_meseta(&mut self, char_id: &CharacterEntityId, bank: BankName) -> Result { + async fn get_bank_meseta(&mut self, char_id: &CharacterEntityId, bank: &BankName) -> Result { let mut bank_meseta = self.bank_meseta.lock().unwrap(); - if let Some(meseta) = bank_meseta.get_mut(&(*char_id, bank)) { + if let Some(meseta) = bank_meseta.get_mut(&(*char_id, bank.clone())) { Ok(*meseta) } else { diff --git a/src/entity/gateway/postgres/postgres.rs b/src/entity/gateway/postgres/postgres.rs index b6f9cc9..266eab2 100644 --- a/src/entity/gateway/postgres/postgres.rs +++ b/src/entity/gateway/postgres/postgres.rs @@ -400,12 +400,12 @@ async fn get_character_inventory(conn: &mut sqlx::PgConnection, char_id: &Charac Ok(InventoryEntity::new(real_inventory)) } -async fn get_character_bank(conn: &mut sqlx::PgConnection, char_id: &CharacterEntityId, bank_name: BankName) -> Result +async fn get_character_bank(conn: &mut sqlx::PgConnection, char_id: &CharacterEntityId, bank_name: &BankName) -> Result { let mut t = conn.begin().await?; let bank = sqlx::query_as::<_, PgInventoryEntity>("select * from bank where pchar = $1 and name = $2") .bind(char_id.0) - .bind(bank_name.0) + .bind(bank_name.0.clone()) .fetch_one(&mut t).await?; // TODO: inefficient let mut real_bank = Vec::new(); @@ -461,7 +461,7 @@ async fn set_character_inventory(conn: &mut sqlx::PgConnection, char_id: &Charac Ok(()) } -async fn set_character_bank(conn: &mut sqlx::PgConnection, char_id: &CharacterEntityId, bank: &BankEntity, bank_name: BankName) -> Result<(), GatewayError> +async fn set_character_bank(conn: &mut sqlx::PgConnection, char_id: &CharacterEntityId, bank: &BankEntity, bank_name: &BankName) -> Result<(), GatewayError> { let bank = bank.items.iter() .map(|item| { @@ -479,7 +479,7 @@ async fn set_character_bank(conn: &mut sqlx::PgConnection, char_id: &CharacterEn sqlx::query("insert into bank (pchar, items, name) values ($1, $2, $3) on conflict (pchar, name) do update set items = $2") .bind(char_id.0) .bind(sqlx::types::Json(bank)) - .bind(bank_name.0) + .bind(bank_name.0.clone()) .execute(conn) .await?; Ok(()) @@ -534,24 +534,24 @@ async fn get_character_meseta(conn: &mut sqlx::PgConnection, char_id: &Character Ok(Meseta(meseta.0 as u32)) } -async fn set_bank_meseta(conn: &mut sqlx::PgConnection, char_id: &CharacterEntityId, bank: BankName, meseta: Meseta) -> Result<(), GatewayError> +async fn set_bank_meseta(conn: &mut sqlx::PgConnection, char_id: &CharacterEntityId, bank: &BankName, meseta: Meseta) -> Result<(), GatewayError> { sqlx::query("insert into bank_meseta values ($1, $2, $3) on conflict (pchar, bank) do update set items = $2") .bind(char_id.0) .bind(meseta.0 as i32) - .bind(bank.0) + .bind(bank.0.clone()) .execute(conn) .await?; Ok(()) } -async fn get_bank_meseta(conn: &mut sqlx::PgConnection, char_id: &CharacterEntityId, bank: BankName) -> Result +async fn get_bank_meseta(conn: &mut sqlx::PgConnection, char_id: &CharacterEntityId, bank: &BankName) -> Result { #[derive(sqlx::FromRow)] struct PgMeseta(i32); let meseta = sqlx::query_as::<_, PgMeseta>(r#"select meseta from character_meseta where id = $1 and bank = $2"#) .bind(char_id.0) - .bind(bank.0) + .bind(bank.0.clone()) .fetch_one(conn) .await?; Ok(Meseta(meseta.0 as u32)) @@ -657,7 +657,7 @@ impl EntityGateway for PostgresGateway { get_character_inventory(&mut *self.pool.acquire().await?, char_id).await } - async fn get_character_bank(&mut self, char_id: &CharacterEntityId, bank_name: BankName) -> Result { + async fn get_character_bank(&mut self, char_id: &CharacterEntityId, bank_name: &BankName) -> Result { get_character_bank(&mut *self.pool.acquire().await?, char_id, bank_name).await } @@ -665,7 +665,7 @@ impl EntityGateway for PostgresGateway { set_character_inventory(&mut *self.pool.acquire().await?, char_id, inventory).await } - async fn set_character_bank(&mut self, char_id: &CharacterEntityId, bank: &BankEntity, bank_name: BankName) -> Result<(), GatewayError> { + async fn set_character_bank(&mut self, char_id: &CharacterEntityId, bank: &BankEntity, bank_name: &BankName) -> Result<(), GatewayError> { set_character_bank(&mut *self.pool.acquire().await?, char_id, bank, bank_name).await } @@ -685,11 +685,11 @@ impl EntityGateway for PostgresGateway { get_character_meseta(&mut *self.pool.acquire().await?, char_id).await } - async fn set_bank_meseta(&mut self, char_id: &CharacterEntityId, bank: BankName, meseta: Meseta) -> Result<(), GatewayError> { + async fn set_bank_meseta(&mut self, char_id: &CharacterEntityId, bank: &BankName, meseta: Meseta) -> Result<(), GatewayError> { set_bank_meseta(&mut *self.pool.acquire().await?, char_id, bank, meseta).await } - async fn get_bank_meseta(&mut self, char_id: &CharacterEntityId, bank: BankName) -> Result { + async fn get_bank_meseta(&mut self, char_id: &CharacterEntityId, bank: &BankName) -> Result { get_bank_meseta(&mut *self.pool.acquire().await?, char_id, bank).await } } @@ -773,7 +773,7 @@ impl<'c> EntityGateway for PostgresTransaction<'c> { get_character_inventory(&mut *self.pgtransaction, char_id).await } - async fn get_character_bank(&mut self, char_id: &CharacterEntityId, bank_name: BankName) -> Result { + async fn get_character_bank(&mut self, char_id: &CharacterEntityId, bank_name: &BankName) -> Result { get_character_bank(&mut *self.pgtransaction, char_id, bank_name).await } @@ -781,7 +781,7 @@ impl<'c> EntityGateway for PostgresTransaction<'c> { set_character_inventory(&mut *self.pgtransaction, char_id, inventory).await } - async fn set_character_bank(&mut self, char_id: &CharacterEntityId, bank: &BankEntity, bank_name: BankName) -> Result<(), GatewayError> { + async fn set_character_bank(&mut self, char_id: &CharacterEntityId, bank: &BankEntity, bank_name: &BankName) -> Result<(), GatewayError> { set_character_bank(&mut *self.pgtransaction, char_id, bank, bank_name).await } @@ -801,11 +801,11 @@ impl<'c> EntityGateway for PostgresTransaction<'c> { get_character_meseta(&mut *self.pgtransaction, char_id).await } - async fn set_bank_meseta(&mut self, char_id: &CharacterEntityId, bank: BankName, meseta: Meseta) -> Result<(), GatewayError> { + async fn set_bank_meseta(&mut self, char_id: &CharacterEntityId, bank: &BankName, meseta: Meseta) -> Result<(), GatewayError> { set_bank_meseta(&mut *self.pgtransaction, char_id, bank, meseta).await } - async fn get_bank_meseta(&mut self, char_id: &CharacterEntityId, bank: BankName) -> Result { + async fn get_bank_meseta(&mut self, char_id: &CharacterEntityId, bank: &BankName) -> Result { get_bank_meseta(&mut *self.pgtransaction, char_id, bank).await } } diff --git a/src/login/character.rs b/src/login/character.rs index 420e933..ed2045a 100644 --- a/src/login/character.rs +++ b/src/login/character.rs @@ -294,7 +294,7 @@ async fn new_character(entity_gateway: &mut EG, user: &UserAc InventoryItemEntity::Stacked(monomates), InventoryItemEntity::Stacked(monofluids)], }; entity_gateway.set_character_inventory(&character.id, &inventory).await?; - entity_gateway.set_character_bank(&character.id, &BankEntity::default(), BankName("".into())).await?; + entity_gateway.set_character_bank(&character.id, &BankEntity::default(), &BankName("".into())).await?; let equipped = EquippedEntity { weapon: Some(weapon.id), armor: Some(armor.id), diff --git a/src/ship/items/manager.rs b/src/ship/items/manager.rs index f1706a3..f3b701a 100644 --- a/src/ship/items/manager.rs +++ b/src/ship/items/manager.rs @@ -147,7 +147,7 @@ impl ItemManager { pub async fn load_character(&mut self, entity_gateway: &mut EG, character: &CharacterEntity) -> Result<(), anyhow::Error> { let inventory = entity_gateway.get_character_inventory(&character.id).await?; - let bank = entity_gateway.get_character_bank(&character.id, BankName("".into())).await?; + let bank = entity_gateway.get_character_bank(&character.id, &BankName("".into())).await?; let equipped = entity_gateway.get_character_equips(&character.id).await?; let inventory_items = inventory.items.into_iter() @@ -205,7 +205,7 @@ impl ItemManager { let character_bank = CharacterBank::new(bank_items); let character_meseta = entity_gateway.get_character_meseta(&character.id).await?; - let bank_meseta = entity_gateway.get_bank_meseta(&character.id, BankName("".into())).await?; + let bank_meseta = entity_gateway.get_bank_meseta(&character.id, &BankName("".into())).await?; self.character_inventory.insert(character.id, character_inventory); self.character_bank.insert(character.id, character_bank); @@ -625,7 +625,7 @@ impl ItemManager { let _bank_item = bank.deposit_item(item_to_deposit, amount).ok_or(ItemManagerError::Idunnoman)?; entity_gateway.set_character_inventory(&character.id, &inventory.as_inventory_entity(&character.id)).await?; - entity_gateway.set_character_bank(&character.id, &bank.as_bank_entity(&character.id, &BankName("".into())), BankName("".into())).await?; + entity_gateway.set_character_bank(&character.id, &bank.as_bank_entity(&character.id, &BankName("".into())), &BankName("".into())).await?; Ok(()) } @@ -648,7 +648,7 @@ impl ItemManager { }; entity_gateway.set_character_inventory(&character.id, &inventory.as_inventory_entity(&character.id)).await?; - entity_gateway.set_character_bank(&character.id, &bank.as_bank_entity(&character.id, &BankName("".into())), BankName("".into())).await?; + entity_gateway.set_character_bank(&character.id, &bank.as_bank_entity(&character.id, &BankName("".into())), &BankName("".into())).await?; inventory.slot(inventory_item_slot).ok_or_else(|| ItemManagerError::Idunnoman.into()) } diff --git a/src/ship/items/state.rs b/src/ship/items/state.rs index a92bc4b..ec5b00c 100644 --- a/src/ship/items/state.rs +++ b/src/ship/items/state.rs @@ -723,7 +723,7 @@ impl ItemState { pub async fn load_character(&mut self, entity_gateway: &mut EG, character: &CharacterEntity) -> Result<(), ItemStateError> { let inventory = entity_gateway.get_character_inventory(&character.id).await?; - let bank = entity_gateway.get_character_bank(&character.id, BankName("".into())).await?; + let bank = entity_gateway.get_character_bank(&character.id, &BankName("".into())).await?; let equipped = entity_gateway.get_character_equips(&character.id).await?; let inventory_items = inventory.items.into_iter() diff --git a/tests/common.rs b/tests/common.rs index a44679f..cf958c8 100644 --- a/tests/common.rs +++ b/tests/common.rs @@ -29,7 +29,7 @@ pub async fn new_user_character(entity_gateway: &mut EG, user let new_character = NewCharacterEntity::new(user.id, kb_conf_preset); let character = entity_gateway.create_character(new_character).await.unwrap(); entity_gateway.set_character_meseta(&character.id, Meseta(0)).await.unwrap(); - entity_gateway.set_bank_meseta(&character.id, BankName("".into()), Meseta(0)).await.unwrap(); + entity_gateway.set_bank_meseta(&character.id, &BankName("".into()), Meseta(0)).await.unwrap(); (user, character) } diff --git a/tests/test_bank.rs b/tests/test_bank.rs index 75777ab..aad05b9 100644 --- a/tests/test_bank.rs +++ b/tests/test_bank.rs @@ -31,7 +31,7 @@ async fn test_bank_items_sent_in_character_login() { ), }).await.unwrap(); - entity_gateway.set_character_bank(&char1.id, &item::BankEntity::new(vec![item]), item::BankName("".into())).await.unwrap(); + entity_gateway.set_character_bank(&char1.id, &item::BankEntity::new(vec![item]), &item::BankName("".into())).await.unwrap(); let mut ship = Box::new(ShipServerState::builder() .gateway(entity_gateway.clone()) @@ -69,7 +69,7 @@ async fn test_request_bank_items() { }).await.unwrap()); } - entity_gateway.set_character_bank(&char1.id, &item::BankEntity::new(bank), item::BankName("".into())).await.unwrap(); + entity_gateway.set_character_bank(&char1.id, &item::BankEntity::new(bank), &item::BankName("".into())).await.unwrap(); let mut ship = Box::new(ShipServerState::builder() .gateway(entity_gateway.clone()) @@ -113,7 +113,7 @@ async fn test_request_stacked_bank_items() { }).await.unwrap()); } - entity_gateway.set_character_bank(&char1.id, &item::BankEntity::new(vec![monomates]), item::BankName("".into())).await.unwrap(); + entity_gateway.set_character_bank(&char1.id, &item::BankEntity::new(vec![monomates]), &item::BankName("".into())).await.unwrap(); let mut ship = Box::new(ShipServerState::builder() .gateway(entity_gateway.clone()) @@ -178,7 +178,7 @@ async fn test_request_bank_items_sorted() { }).await.unwrap(); let bank = vec![item::BankItemEntity::Individual(item1), vec![monomate].into(), item2.into()]; - entity_gateway.set_character_bank(&char1.id, &item::BankEntity::new(bank), item::BankName("".into())).await.unwrap(); + entity_gateway.set_character_bank(&char1.id, &item::BankEntity::new(bank), &item::BankName("".into())).await.unwrap(); let mut ship = Box::new(ShipServerState::builder() .gateway(entity_gateway.clone()) @@ -269,7 +269,13 @@ async fn test_deposit_individual_item() { && player_no_longer_has_item.amount == 0 )); - let bank_items = entity_gateway.get_character_bank(&char1.id, item::BankName("".into())).await.unwrap(); + let inventory_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap(); + assert_eq!(inventory_items.items.len(), 1); + inventory_items.items[0].with_individual(|item| { + assert_eq!(item.id, item::ItemEntityId(1)); + }).unwrap(); + + let bank_items = entity_gateway.get_character_bank(&char1.id, &item::BankName("".into())).await.unwrap(); assert_eq!(bank_items.items.len(), 1); bank_items.items[0].with_individual(|item| { assert_eq!(item.id, item::ItemEntityId(2)); @@ -329,7 +335,7 @@ async fn test_deposit_stacked_item() { && player_no_longer_has_item.amount == 3 )); - let bank_items = entity_gateway.get_character_bank(&char1.id, item::BankName("".into())).await.unwrap(); + let bank_items = entity_gateway.get_character_bank(&char1.id, &item::BankName("".into())).await.unwrap(); assert_eq!(bank_items.items.len(), 1); bank_items.items[0].with_stacked(|items| { assert_eq!(items.iter().map(|i| i.id).collect::>(), @@ -391,7 +397,7 @@ async fn test_deposit_partial_stacked_item() { )); - let bank_items = entity_gateway.get_character_bank(&char1.id, item::BankName("".into())).await.unwrap(); + let bank_items = entity_gateway.get_character_bank(&char1.id, &item::BankName("".into())).await.unwrap(); assert_eq!(bank_items.items.len(), 1); bank_items.items[0].with_stacked(|items| { assert_eq!(items.iter().map(|i| i.id).collect::>(), @@ -437,7 +443,7 @@ async fn test_deposit_stacked_item_with_stack_already_in_bank() { } entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(vec![inventory_monomates])).await.unwrap(); - entity_gateway.set_character_bank(&char1.id, &item::BankEntity::new(vec![bank_monomates]), item::BankName("".into())).await.unwrap(); + entity_gateway.set_character_bank(&char1.id, &item::BankEntity::new(vec![bank_monomates]), &item::BankName("".into())).await.unwrap(); let mut ship = Box::new(ShipServerState::builder() .gateway(entity_gateway.clone()) @@ -471,7 +477,7 @@ async fn test_deposit_stacked_item_with_stack_already_in_bank() { && player_no_longer_has_item.amount == 2 )); - let bank_items = entity_gateway.get_character_bank(&char1.id, item::BankName("".into())).await.unwrap(); + let bank_items = entity_gateway.get_character_bank(&char1.id, &item::BankName("".into())).await.unwrap(); assert_eq!(bank_items.items.len(), 1); bank_items.items[0].with_stacked(|items| { assert_eq!(items.iter().map(|i| i.id).collect::>(), @@ -510,7 +516,7 @@ async fn test_deposit_stacked_item_with_full_stack_in_bank() { } entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(vec![inventory_monomates])).await.unwrap(); - entity_gateway.set_character_bank(&char1.id, &item::BankEntity::new(vec![bank_monomates]), item::BankName("".into())).await.unwrap(); + entity_gateway.set_character_bank(&char1.id, &item::BankEntity::new(vec![bank_monomates]), &item::BankName("".into())).await.unwrap(); let mut ship = Box::new(ShipServerState::builder() .gateway(entity_gateway.clone()) @@ -537,7 +543,7 @@ async fn test_deposit_stacked_item_with_full_stack_in_bank() { assert!(packets.is_err()); - let bank_items = entity_gateway.get_character_bank(&char1.id, item::BankName("".into())).await.unwrap(); + let bank_items = entity_gateway.get_character_bank(&char1.id, &item::BankName("".into())).await.unwrap(); assert_eq!(bank_items.items.len(), 1); bank_items.items[0].with_stacked(|items| { assert_eq!(items.len(), 10); @@ -588,7 +594,7 @@ async fn test_deposit_individual_item_in_full_bank() { } entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(inventory)).await.unwrap(); - entity_gateway.set_character_bank(&char1.id, &item::BankEntity::new(bank), item::BankName("".into())).await.unwrap(); + entity_gateway.set_character_bank(&char1.id, &item::BankEntity::new(bank), &item::BankName("".into())).await.unwrap(); let mut ship = Box::new(ShipServerState::builder() .gateway(entity_gateway.clone()) @@ -615,7 +621,7 @@ async fn test_deposit_individual_item_in_full_bank() { assert!(packets.is_err()); - let bank_items = entity_gateway.get_character_bank(&char1.id, item::BankName("".into())).await.unwrap(); + let bank_items = entity_gateway.get_character_bank(&char1.id, &item::BankName("".into())).await.unwrap(); assert_eq!(bank_items.items.len(), 200); let inventory_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap(); @@ -660,7 +666,7 @@ async fn test_deposit_stacked_item_in_full_bank() { } entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(vec![monomates])).await.unwrap(); - entity_gateway.set_character_bank(&char1.id, &item::BankEntity::new(full_bank), item::BankName("".into())).await.unwrap(); + entity_gateway.set_character_bank(&char1.id, &item::BankEntity::new(full_bank), &item::BankName("".into())).await.unwrap(); let mut ship = Box::new(ShipServerState::builder() .gateway(entity_gateway.clone()) @@ -687,7 +693,7 @@ async fn test_deposit_stacked_item_in_full_bank() { assert!(packets.is_err()); - let bank_items = entity_gateway.get_character_bank(&char1.id, item::BankName("".into())).await.unwrap(); + let bank_items = entity_gateway.get_character_bank(&char1.id, &item::BankName("".into())).await.unwrap(); assert_eq!(bank_items.items.len(), 200); let inventory_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap(); @@ -746,7 +752,7 @@ async fn test_deposit_stacked_item_in_full_bank_with_partial_stack() { almost_full_bank.push(bank_monomates.into()); entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(vec![monomates])).await.unwrap(); - entity_gateway.set_character_bank(&char1.id, &item::BankEntity::new(almost_full_bank), item::BankName("".into())).await.unwrap(); + entity_gateway.set_character_bank(&char1.id, &item::BankEntity::new(almost_full_bank), &item::BankName("".into())).await.unwrap(); let mut ship = Box::new(ShipServerState::builder() .gateway(entity_gateway.clone()) @@ -771,7 +777,7 @@ async fn test_deposit_stacked_item_in_full_bank_with_partial_stack() { unknown: 0, })))).await.unwrap().for_each(drop); - let bank_items = entity_gateway.get_character_bank(&char1.id, item::BankName("".into())).await.unwrap(); + let bank_items = entity_gateway.get_character_bank(&char1.id, &item::BankName("".into())).await.unwrap(); assert_eq!(bank_items.items.len(), 200); bank_items.items[199].with_stacked(|items| { assert_eq!(items.len(), 4); @@ -812,7 +818,7 @@ async fn test_deposit_meseta() { })))).await.unwrap().for_each(drop); let c1_meseta = entity_gateway.get_character_meseta(&char1.id).await.unwrap(); - let c1_bank_meseta = entity_gateway.get_bank_meseta(&char1.id, item::BankName("".into())).await.unwrap(); + let c1_bank_meseta = entity_gateway.get_bank_meseta(&char1.id, &item::BankName("".into())).await.unwrap(); assert!(c1_meseta.0 == 277); assert!(c1_bank_meseta.0 == 23); } @@ -823,7 +829,7 @@ async fn test_deposit_too_much_meseta() { let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a", 1).await; entity_gateway.set_character_meseta(&char1.id, item::Meseta(300)).await.unwrap(); - entity_gateway.set_bank_meseta(&char1.id, item::BankName("".into()), item::Meseta(999980)).await.unwrap(); + entity_gateway.set_bank_meseta(&char1.id, &item::BankName("".into()), item::Meseta(999980)).await.unwrap(); let mut ship = Box::new(ShipServerState::builder() .gateway(entity_gateway.clone()) @@ -849,7 +855,7 @@ async fn test_deposit_too_much_meseta() { })))).await.unwrap().for_each(drop); let c1_meseta = entity_gateway.get_character_meseta(&char1.id).await.unwrap(); - let c1_bank_meseta = entity_gateway.get_bank_meseta(&char1.id, item::BankName("".into())).await.unwrap(); + let c1_bank_meseta = entity_gateway.get_bank_meseta(&char1.id, &item::BankName("".into())).await.unwrap(); assert!(c1_meseta.0 == 300); assert!(c1_bank_meseta.0 == 999980); } @@ -860,7 +866,7 @@ async fn test_deposit_meseta_when_bank_is_maxed() { let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a", 1).await; entity_gateway.set_character_meseta(&char1.id, item::Meseta(300)).await.unwrap(); - entity_gateway.set_bank_meseta(&char1.id, item::BankName("".into()), item::Meseta(999999)).await.unwrap(); + entity_gateway.set_bank_meseta(&char1.id, &item::BankName("".into()), item::Meseta(999999)).await.unwrap(); let mut ship = Box::new(ShipServerState::builder() .gateway(entity_gateway.clone()) @@ -886,7 +892,7 @@ async fn test_deposit_meseta_when_bank_is_maxed() { })))).await.unwrap().for_each(drop); let c1_meseta = entity_gateway.get_character_meseta(&char1.id).await.unwrap(); - let c1_bank_meseta = entity_gateway.get_bank_meseta(&char1.id, item::BankName("".into())).await.unwrap(); + let c1_bank_meseta = entity_gateway.get_bank_meseta(&char1.id, &item::BankName("".into())).await.unwrap(); assert!(c1_meseta.0 == 300); assert!(c1_bank_meseta.0 == 999999); } @@ -913,7 +919,7 @@ async fn test_withdraw_individual_item() { ), }).await.unwrap()); - entity_gateway.set_character_bank(&char1.id, &item::BankEntity::new(bank), item::BankName("".into())).await.unwrap(); + entity_gateway.set_character_bank(&char1.id, &item::BankEntity::new(bank), &item::BankName("".into())).await.unwrap(); let mut ship = Box::new(ShipServerState::builder() .gateway(entity_gateway.clone()) @@ -973,7 +979,7 @@ async fn test_withdraw_stacked_item() { }).await.unwrap()); } - entity_gateway.set_character_bank(&char1.id, &item::BankEntity::new(vec![monomates]), item::BankName("".into())).await.unwrap(); + entity_gateway.set_character_bank(&char1.id, &item::BankEntity::new(vec![monomates]), &item::BankName("".into())).await.unwrap(); let mut ship = Box::new(ShipServerState::builder() .gateway(entity_gateway.clone()) @@ -1032,7 +1038,7 @@ async fn test_withdraw_partial_stacked_item() { ), }).await.unwrap()); } - entity_gateway.set_character_bank(&char1.id, &item::BankEntity::new(vec![monomates]), item::BankName("".into())).await.unwrap(); + entity_gateway.set_character_bank(&char1.id, &item::BankEntity::new(vec![monomates]), &item::BankName("".into())).await.unwrap(); let mut ship = Box::new(ShipServerState::builder() .gateway(entity_gateway.clone()) @@ -1062,10 +1068,10 @@ async fn test_withdraw_partial_stacked_item() { assert!(packets.len() == 2); assert!(matches!(&packets[1], (ClientId(2), SendShipPacket::Message(Message {msg: GameMessage::CreateItem(create_item)})) - if create_item.item_id == 0x10002 + if create_item.item_id == 0x20002 )); - let bank_items = entity_gateway.get_character_bank(&char1.id, item::BankName("".into())).await.unwrap(); + let bank_items = entity_gateway.get_character_bank(&char1.id, &item::BankName("".into())).await.unwrap(); assert_eq!(bank_items.items.len(), 1); bank_items.items[0].with_stacked(|items| { assert_eq!(items.iter().map(|i| i.id).collect::>(), @@ -1110,7 +1116,7 @@ async fn test_withdraw_stacked_item_with_stack_already_in_inventory() { } entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(vec![inventory_monomates])).await.unwrap(); - entity_gateway.set_character_bank(&char1.id, &item::BankEntity::new(vec![bank_monomates]), item::BankName("".into())).await.unwrap(); + entity_gateway.set_character_bank(&char1.id, &item::BankEntity::new(vec![bank_monomates]), &item::BankName("".into())).await.unwrap(); let mut ship = Box::new(ShipServerState::builder() .gateway(entity_gateway.clone()) @@ -1143,7 +1149,7 @@ async fn test_withdraw_stacked_item_with_stack_already_in_inventory() { if create_item.item_id == 0x10000 )); - let bank_items = entity_gateway.get_character_bank(&char1.id, item::BankName("".into())).await.unwrap(); + let bank_items = entity_gateway.get_character_bank(&char1.id, &item::BankName("".into())).await.unwrap(); assert_eq!(bank_items.items.len(), 0); let inventory_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap(); @@ -1185,7 +1191,7 @@ async fn test_withdraw_stacked_item_with_full_stack_in_inventory() { } entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(vec![inventory_monomates])).await.unwrap(); - entity_gateway.set_character_bank(&char1.id, &item::BankEntity::new(vec![bank_monomates]), item::BankName("".into())).await.unwrap(); + entity_gateway.set_character_bank(&char1.id, &item::BankEntity::new(vec![bank_monomates]), &item::BankName("".into())).await.unwrap(); let mut ship = Box::new(ShipServerState::builder() .gateway(entity_gateway.clone()) @@ -1212,7 +1218,7 @@ async fn test_withdraw_stacked_item_with_full_stack_in_inventory() { assert!(packets.is_err()); - let bank_items = entity_gateway.get_character_bank(&char1.id, item::BankName("".into())).await.unwrap(); + let bank_items = entity_gateway.get_character_bank(&char1.id, &item::BankName("".into())).await.unwrap(); assert_eq!(bank_items.items.len(), 1); bank_items.items[0].with_stacked(|items| { assert_eq!(items.iter().map(|i| i.id).collect::>(), @@ -1263,7 +1269,7 @@ async fn test_withdraw_individual_item_in_full_inventory() { } entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(inventory)).await.unwrap(); - entity_gateway.set_character_bank(&char1.id, &item::BankEntity::new(bank), item::BankName("".into())).await.unwrap(); + entity_gateway.set_character_bank(&char1.id, &item::BankEntity::new(bank), &item::BankName("".into())).await.unwrap(); let mut ship = Box::new(ShipServerState::builder() .gateway(entity_gateway.clone()) @@ -1289,7 +1295,7 @@ async fn test_withdraw_individual_item_in_full_inventory() { })))).await; assert!(packets.is_err()); - let bank_items = entity_gateway.get_character_bank(&char1.id, item::BankName("".into())).await.unwrap(); + let bank_items = entity_gateway.get_character_bank(&char1.id, &item::BankName("".into())).await.unwrap(); assert_eq!(bank_items.items.len(), 1); let inventory_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap(); @@ -1331,7 +1337,7 @@ async fn test_withdraw_stacked_item_in_full_inventory() { } entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(inventory)).await.unwrap(); - entity_gateway.set_character_bank(&char1.id, &item::BankEntity::new(vec![monomates]), item::BankName("".into())).await.unwrap(); + entity_gateway.set_character_bank(&char1.id, &item::BankEntity::new(vec![monomates]), &item::BankName("".into())).await.unwrap(); let mut ship = Box::new(ShipServerState::builder() .gateway(entity_gateway.clone()) @@ -1359,7 +1365,7 @@ async fn test_withdraw_stacked_item_in_full_inventory() { assert!(packets.is_err()); - let bank_items = entity_gateway.get_character_bank(&char1.id, item::BankName("".into())).await.unwrap(); + let bank_items = entity_gateway.get_character_bank(&char1.id, &item::BankName("".into())).await.unwrap(); assert_eq!(bank_items.items.len(), 1); bank_items.items[0].with_stacked(|items| { assert_eq!(items.iter().map(|i| i.id).collect::>(), @@ -1387,7 +1393,7 @@ async fn test_withdraw_stacked_item_in_full_inventory_with_partial_stack() { ), }).await.unwrap()); } - entity_gateway.set_character_bank(&char1.id, &item::BankEntity::new(vec![bank_item]), item::BankName("".into())).await.unwrap(); + entity_gateway.set_character_bank(&char1.id, &item::BankEntity::new(vec![bank_item]), &item::BankName("".into())).await.unwrap(); let mut items = Vec::new(); for _i in 0..29usize { @@ -1443,7 +1449,7 @@ async fn test_withdraw_stacked_item_in_full_inventory_with_partial_stack() { unknown: 0, })))).await.unwrap().for_each(drop); - let bank_items = entity_gateway.get_character_bank(&char1.id, item::BankName("".into())).await.unwrap(); + let bank_items = entity_gateway.get_character_bank(&char1.id, &item::BankName("".into())).await.unwrap(); assert!(bank_items.items.len() == 0); let inventory_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap(); @@ -1461,7 +1467,7 @@ async fn test_withdraw_meseta() { let mut entity_gateway = InMemoryGateway::default(); let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a", 1).await; - entity_gateway.set_bank_meseta(&char1.id, item::BankName("".into()), item::Meseta(300)).await.unwrap(); + entity_gateway.set_bank_meseta(&char1.id, &item::BankName("".into()), item::Meseta(300)).await.unwrap(); let mut ship = Box::new(ShipServerState::builder() .gateway(entity_gateway.clone()) @@ -1487,7 +1493,7 @@ async fn test_withdraw_meseta() { })))).await.unwrap().for_each(drop); let c1_meseta = entity_gateway.get_character_meseta(&char1.id).await.unwrap(); - let c1_bank_meseta = entity_gateway.get_bank_meseta(&char1.id, item::BankName("".into())).await.unwrap(); + let c1_bank_meseta = entity_gateway.get_bank_meseta(&char1.id, &item::BankName("".into())).await.unwrap(); assert!(c1_meseta.0 == 23); assert!(c1_bank_meseta.0 == 277); } @@ -1498,7 +1504,7 @@ async fn test_withdraw_too_much_meseta() { let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a", 1).await; entity_gateway.set_character_meseta(&char1.id, item::Meseta(999980)).await.unwrap(); - entity_gateway.set_bank_meseta(&char1.id, item::BankName("".into()), item::Meseta(300)).await.unwrap(); + entity_gateway.set_bank_meseta(&char1.id, &item::BankName("".into()), item::Meseta(300)).await.unwrap(); let mut ship = Box::new(ShipServerState::builder() .gateway(entity_gateway.clone()) @@ -1524,7 +1530,7 @@ async fn test_withdraw_too_much_meseta() { })))).await.unwrap().for_each(drop); let c1_meseta = entity_gateway.get_character_meseta(&char1.id).await.unwrap(); - let c1_bank_meseta = entity_gateway.get_bank_meseta(&char1.id, item::BankName("".into())).await.unwrap(); + let c1_bank_meseta = entity_gateway.get_bank_meseta(&char1.id, &item::BankName("".into())).await.unwrap(); assert!(c1_meseta.0 == 999980); assert!(c1_bank_meseta.0 == 300); } @@ -1535,7 +1541,7 @@ async fn test_withdraw_meseta_inventory_is_maxed() { let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a", 1).await; entity_gateway.set_character_meseta(&char1.id, item::Meseta(999999)).await.unwrap(); - entity_gateway.set_bank_meseta(&char1.id, item::BankName("".into()), item::Meseta(300)).await.unwrap(); + entity_gateway.set_bank_meseta(&char1.id, &item::BankName("".into()), item::Meseta(300)).await.unwrap(); let mut ship = Box::new(ShipServerState::builder() .gateway(entity_gateway.clone()) @@ -1561,7 +1567,7 @@ async fn test_withdraw_meseta_inventory_is_maxed() { })))).await.unwrap().for_each(drop); let c1_meseta = entity_gateway.get_character_meseta(&char1.id).await.unwrap(); - let c1_bank_meseta = entity_gateway.get_bank_meseta(&char1.id, item::BankName("".into())).await.unwrap(); + let c1_bank_meseta = entity_gateway.get_bank_meseta(&char1.id, &item::BankName("".into())).await.unwrap(); assert!(c1_meseta.0 == 999999); assert!(c1_bank_meseta.0 == 300); } -- 2.36.0 From 4ccdf579bd3762eac9709dc54974cdd285c88de5 Mon Sep 17 00:00:00 2001 From: jake Date: Sat, 14 May 2022 13:02:42 -0600 Subject: [PATCH 32/60] make inmemorytransaction actually work --- src/entity/gateway/inmemory.rs | 45 +++++++++++++++++++++++++--------- 1 file changed, 33 insertions(+), 12 deletions(-) diff --git a/src/entity/gateway/inmemory.rs b/src/entity/gateway/inmemory.rs index 9b8d1ab..3fd7cc6 100644 --- a/src/entity/gateway/inmemory.rs +++ b/src/entity/gateway/inmemory.rs @@ -21,18 +21,39 @@ impl<'a> EntityGatewayTransaction for InMemoryGatewayTransaction<'a> { &mut self.working_gateway } - async fn commit(self: Box) -> Result<(), GatewayError> { - self.original_gateway.users = self.working_gateway.users.clone(); - self.original_gateway.user_settings = self.working_gateway.user_settings.clone(); - self.original_gateway.characters = self.working_gateway.characters.clone(); - self.original_gateway.character_meseta = self.working_gateway.character_meseta.clone(); - self.original_gateway.bank_meseta = self.working_gateway.bank_meseta.clone(); - self.original_gateway.items = self.working_gateway.items.clone(); - self.original_gateway.inventories = self.working_gateway.inventories.clone(); - self.original_gateway.banks = self.working_gateway.banks.clone(); - self.original_gateway.equips = self.working_gateway.equips.clone(); - self.original_gateway.mag_modifiers = self.working_gateway.mag_modifiers.clone(); - self.original_gateway.weapon_modifiers = self.working_gateway.weapon_modifiers.clone(); + async fn commit(mut self: Box) -> Result<(), GatewayError> { + self.original_gateway.users.lock().unwrap().clear(); + self.original_gateway.users.lock().unwrap().extend(self.working_gateway.users.lock().unwrap().clone()); + + self.original_gateway.user_settings.lock().unwrap().clear(); + self.original_gateway.user_settings.lock().unwrap().extend(self.working_gateway.user_settings.lock().unwrap().clone()); + + self.original_gateway.characters.lock().unwrap().clear(); + self.original_gateway.characters.lock().unwrap().extend(self.working_gateway.characters.lock().unwrap().clone()); + + self.original_gateway.character_meseta.lock().unwrap().clear(); + self.original_gateway.character_meseta.lock().unwrap().extend(self.working_gateway.character_meseta.lock().unwrap().clone()); + + self.original_gateway.bank_meseta.lock().unwrap().clear(); + self.original_gateway.bank_meseta.lock().unwrap().extend(self.working_gateway.bank_meseta.lock().unwrap().clone()); + + self.original_gateway.items.lock().unwrap().clear(); + self.original_gateway.items.lock().unwrap().extend(self.working_gateway.items.lock().unwrap().clone()); + + self.original_gateway.inventories.lock().unwrap().clear(); + self.original_gateway.inventories.lock().unwrap().extend(self.working_gateway.inventories.lock().unwrap().clone()); + + self.original_gateway.banks.lock().unwrap().clear(); + self.original_gateway.banks.lock().unwrap().extend(self.working_gateway.banks.lock().unwrap().clone()); + + self.original_gateway.equips.lock().unwrap().clear(); + self.original_gateway.equips.lock().unwrap().extend(self.working_gateway.equips.lock().unwrap().clone()); + + self.original_gateway.mag_modifiers.lock().unwrap().clear(); + self.original_gateway.mag_modifiers.lock().unwrap().extend(self.working_gateway.mag_modifiers.lock().unwrap().clone()); + + self.original_gateway.weapon_modifiers.lock().unwrap().clear(); + self.original_gateway.weapon_modifiers.lock().unwrap().extend(self.working_gateway.weapon_modifiers.lock().unwrap().clone()); Ok(()) } -- 2.36.0 From 03df819a0453ad9c4eecdcd0395c81e7d4f90493 Mon Sep 17 00:00:00 2001 From: jake Date: Sat, 14 May 2022 13:06:40 -0600 Subject: [PATCH 33/60] bank itemstate stuff --- src/entity/gateway/inmemory.rs | 1 + src/entity/gateway/postgres/models.rs | 30 +- src/entity/item/mod.rs | 8 + src/ship/items/actions.rs | 309 +++++++++++--- src/ship/items/manager.rs | 4 +- src/ship/items/state.rs | 478 +++++++++++++++++----- src/ship/packet/builder/lobby.rs | 9 +- src/ship/packet/builder/message.rs | 18 +- src/ship/packet/builder/mod.rs | 9 +- src/ship/packet/builder/room.rs | 7 +- src/ship/packet/handler/auth.rs | 6 +- src/ship/packet/handler/direct_message.rs | 39 +- src/ship/packet/handler/lobby.rs | 17 +- src/ship/packet/handler/room.rs | 8 +- src/ship/ship.rs | 18 +- tests/test_bank.rs | 28 +- 16 files changed, 747 insertions(+), 242 deletions(-) diff --git a/src/entity/gateway/inmemory.rs b/src/entity/gateway/inmemory.rs index 3fd7cc6..93d10d7 100644 --- a/src/entity/gateway/inmemory.rs +++ b/src/entity/gateway/inmemory.rs @@ -9,6 +9,7 @@ use crate::entity::item::*; use std::sync::{Arc, Mutex}; +// TODO: implement multiple banks pub struct InMemoryGatewayTransaction<'a> { working_gateway: InMemoryGateway, diff --git a/src/entity/gateway/postgres/models.rs b/src/entity/gateway/postgres/models.rs index 89a219e..b54de2b 100644 --- a/src/entity/gateway/postgres/models.rs +++ b/src/entity/gateway/postgres/models.rs @@ -610,6 +610,14 @@ pub enum PgItemNoteDetail { character_to: u32, character_from: u32, }, + Withdraw { + character_id: u32, + bank: String, + }, + Deposit { + character_id: u32, + bank: String, + } } impl From for PgItemNoteDetail { @@ -643,6 +651,18 @@ impl From for PgItemNoteDetail { id: id.0, character_to: character_to.0, character_from: character_from.0, + }, + ItemNote::Withdraw{character_id, bank} => { + PgItemNoteDetail::Withdraw { + character_id: character_id.0, + bank: bank.0, + } + }, + ItemNote::Deposit{character_id, bank} => { + PgItemNoteDetail::Deposit { + character_id: character_id.0, + bank: bank.0, + } } } } @@ -679,7 +699,15 @@ impl From for ItemNote { id: TradeId(id as u32), character_to: CharacterEntityId(character_to as u32), character_from: CharacterEntityId(character_from as u32), - } + }, + PgItemNoteDetail::Withdraw{character_id, bank} => ItemNote::Withdraw { + character_id: CharacterEntityId(character_id as u32), + bank: BankName(bank), + }, + PgItemNoteDetail::Deposit{character_id, bank} => ItemNote::Deposit { + character_id: CharacterEntityId(character_id as u32), + bank: BankName(bank), + }, } } } diff --git a/src/entity/item/mod.rs b/src/entity/item/mod.rs index 220b810..203d819 100644 --- a/src/entity/item/mod.rs +++ b/src/entity/item/mod.rs @@ -59,6 +59,14 @@ pub enum ItemNote { character_to: CharacterEntityId, character_from: CharacterEntityId, }, + Withdraw { + character_id: CharacterEntityId, + bank: BankName, + }, + Deposit { + character_id: CharacterEntityId, + bank: BankName, + }, } #[derive(Debug, Copy, Clone, PartialEq)] diff --git a/src/ship/items/actions.rs b/src/ship/items/actions.rs index 1a80b7a..555403c 100644 --- a/src/ship/items/actions.rs +++ b/src/ship/items/actions.rs @@ -6,7 +6,10 @@ use std::pin::Pin; use crate::ship::map::MapArea; use crate::entity::character::{CharacterEntity, CharacterEntityId}; use crate::entity::gateway::{EntityGateway, EntityGatewayTransaction}; -use crate::ship::items::state::{ItemState, ItemStateProxy, ItemStateAction, ItemAction, ItemStateError, FloorItem, InventoryItem, AddItemResult, FloorItemDetail, StackedItemDetail}; +use crate::ship::items::state::{ItemState, ItemStateProxy, ItemStateAction, ItemAction, ItemStateError, FloorItem, InventoryItem, AddItemResult, FloorItemDetail, + StackedItemDetail, BankItem, BankItemDetail, InventoryItemDetail}; + + pub enum TriggerCreateItem { Yes, @@ -94,14 +97,14 @@ where }).await } -fn take_item_from_inventory(character_id: CharacterEntityId, item_id: ClientItemId) +fn take_item_from_inventory(character_id: CharacterEntityId, item_id: ClientItemId, amount: u32) -> impl for<'a> Fn((ItemStateProxy<'a>, Box), ()) -> Pin, Box), InventoryItem), ItemStateError>> + Send + 'a>> { move |(mut item_state, mut transaction), _| { Box::pin(async move { let mut inventory = item_state.inventory(&character_id)?; - let item = inventory.take_item(&item_id).ok_or_else (|| ItemStateError::NoFloorItem(item_id))?; + let item = inventory.take_item(&item_id, amount).ok_or_else (|| ItemStateError::NoFloorItem(item_id))?; transaction.gateway().set_character_inventory(&character_id, &inventory.as_inventory_entity(&character_id)).await?; item_state.set_inventory(inventory); @@ -114,7 +117,7 @@ fn take_item_from_inventory(character_id: CharacterEntityId, item_id: ClientItem fn add_inventory_item_to_shared_floor(character_id: CharacterEntityId, map_area: MapArea, drop_position: (f32, f32, f32)) -> impl for<'a> Fn((ItemStateProxy<'a>, Box), InventoryItem) - -> Pin, Box), ()), ItemStateError>> + Send + 'a>> + -> Pin, Box), FloorItem), ItemStateError>> + Send + 'a>> { move |(mut item_state, transaction), inventory_item| { Box::pin(async move { @@ -133,10 +136,10 @@ fn add_inventory_item_to_shared_floor(character_id: CharacterEntityId, map_area: }}).await?; let mut floor = item_state.floor(&character_id)?; - floor.add_inventory_item(inventory_item, map_area, drop_position); + let floor_item = floor.add_inventory_item(inventory_item, map_area, drop_position).clone(); item_state.set_floor(floor); - Ok(((item_state, transaction), ())) + Ok(((item_state, transaction), floor_item)) }) } } @@ -148,14 +151,14 @@ pub async fn drop_item( item_id: &ClientItemId, map_area: MapArea, drop_position: (f32, f32, f32)) - -> Result<(), ItemStateError> + -> Result where EG: EntityGateway, { entity_gateway.with_transaction(|transaction| async move { let item_state_proxy = ItemStateProxy::new(item_state); let ((item_state_proxy, transaction), result) = ItemStateAction::default() - .act(take_item_from_inventory(character.id, *item_id)) + .act(take_item_from_inventory(character.id, *item_id, 1)) .act(add_inventory_item_to_shared_floor(character.id, map_area, drop_position)) .commit((item_state_proxy, transaction)) .await?; @@ -164,53 +167,62 @@ where }).await } +pub async fn drop_partial_item<'a, EG>( + item_state: &'a mut ItemState, + entity_gateway: &mut EG, + character: &CharacterEntity, + item_id: &ClientItemId, + map_area: MapArea, + drop_position: (f32, f32), + amount: u32) + -> Result +where + EG: EntityGateway, +{ + entity_gateway.with_transaction(|transaction| async move { + let item_state_proxy = ItemStateProxy::new(item_state); + let ((item_state_proxy, transaction), result) = ItemStateAction::default() + .act(take_item_from_inventory(character.id, *item_id, amount)) + .act(add_inventory_item_to_shared_floor(character.id, map_area, (drop_position.0, 0.0, drop_position.1))) + .commit((item_state_proxy, transaction)) + .await?; + item_state_proxy.commit(); + Ok((transaction, result)) + }).await +} + -fn take_partial_item_from_inventory(character_id: CharacterEntityId, item_id: ClientItemId, amount: u32) - -> impl for<'a> Fn((ItemStateProxy<'a>, Box), ()) - -> Pin, Box), StackedItemDetail), ItemStateError>> + Send + 'a>> +fn take_meseta_from_inventory(character_id: CharacterEntityId, amount: u32) + -> impl for<'a> Fn((ItemStateProxy<'a>, Box), ()) + -> Pin, Box), ()), ItemStateError>> + Send + 'a>> { move |(mut item_state, mut transaction), _| { Box::pin(async move { let mut inventory = item_state.inventory(&character_id)?; - let item = inventory.take_partial_item(&item_id, amount).ok_or_else (|| ItemStateError::NoFloorItem(item_id))?; - - transaction.gateway().set_character_inventory(&character_id, &inventory.as_inventory_entity(&character_id)).await?; - item_state.set_inventory(inventory); + inventory.remove_meseta(amount)?; + transaction.gateway().set_character_meseta(&character_id, inventory.meseta).await?; - Ok(((item_state, transaction), item)) + Ok(((item_state, transaction), ())) }) } } -fn add_partial_inventory_item_to_shared_floor(character_id: CharacterEntityId, map_area: MapArea, drop_position: (f32, f32)) - -> impl for<'a> Fn((ItemStateProxy<'a>, Box), StackedItemDetail) - -> Pin, Box), FloorItem), ItemStateError>> + Send + 'a>> +fn add_meseta_to_shared_floor(character_id: CharacterEntityId, amount: u32, map_area: MapArea, drop_position: (f32, f32)) + -> impl for<'a> Fn((ItemStateProxy<'a>, Box), ()) + -> Pin, Box), FloorItem), ItemStateError>> + Send + 'a>> { - move |(mut item_state, transaction), stacked_item| { + + move |(mut item_state, transaction), _| { Box::pin(async move { let floor_item = FloorItem { item_id: item_state.new_item_id()?, - item: FloorItemDetail::Stacked(stacked_item), - map_area, + item: FloorItemDetail::Meseta(Meseta(amount)), + map_area: map_area, x: drop_position.0, y: 0.0, z: drop_position.1, }; - let transaction = floor_item.with_entity_id(Ok(transaction), |mut transaction: Result<_, ItemStateError>, entity_id| { - async move { - if let Ok(transaction) = &mut transaction { - transaction.gateway().add_item_note(&entity_id, ItemNote::PlayerDrop { - character_id, - map_area, - x: drop_position.0, - y: 0.0, - z: drop_position.1, - }).await?; - } - transaction - }}).await?; - let mut floor = item_state.floor(&character_id)?; let floor_item = floor.add_item(floor_item).clone(); item_state.set_floor(floor); @@ -220,11 +232,10 @@ fn add_partial_inventory_item_to_shared_floor(character_id: CharacterEntityId, m } } -pub async fn drop_partial_item<'a, EG>( +pub async fn drop_meseta<'a, EG>( item_state: &'a mut ItemState, entity_gateway: &mut EG, character: &CharacterEntity, - item_id: &ClientItemId, map_area: MapArea, drop_position: (f32, f32), amount: u32) @@ -235,8 +246,8 @@ where entity_gateway.with_transaction(|transaction| async move { let item_state_proxy = ItemStateProxy::new(item_state); let ((item_state_proxy, transaction), result) = ItemStateAction::default() - .act(take_partial_item_from_inventory(character.id, *item_id, amount)) - .act(add_partial_inventory_item_to_shared_floor(character.id, map_area, drop_position)) + .act(take_meseta_from_inventory(character.id, amount)) + .act(add_meseta_to_shared_floor(character.id, amount, map_area, drop_position)) .commit((item_state_proxy, transaction)) .await?; item_state_proxy.commit(); @@ -245,62 +256,228 @@ where } -fn take_meseta_from_inventory(character_id: CharacterEntityId, amount: u32) - -> impl for<'a> Fn((ItemStateProxy<'a>, Box), ()) - -> Pin, Box), u32), ItemStateError>> + Send + 'a>> +fn take_meseta_from_bank(character_id: CharacterEntityId, amount: u32) + -> impl for<'a> Fn((ItemStateProxy<'a>, Box), ()) + -> Pin, Box), ()), ItemStateError>> + Send + 'a>> +{ + move |(mut item_state, mut transaction), _| { + Box::pin(async move { + let mut bank = item_state.bank(&character_id)?; + bank.remove_meseta(amount)?; + transaction.gateway().set_bank_meseta(&character_id, &bank.name, bank.meseta).await?; + + Ok(((item_state, transaction), ())) + }) + } +} + +fn add_meseta_from_bank_to_inventory(character_id: CharacterEntityId, amount: u32) + -> impl for<'a> Fn((ItemStateProxy<'a>, Box), ()) + -> Pin, Box), ()), ItemStateError>> + Send + 'a>> { move |(mut item_state, mut transaction), _| { Box::pin(async move { let mut inventory = item_state.inventory(&character_id)?; - inventory.remove_meseta(amount)?; + inventory.add_meseta_no_overflow(amount)?; transaction.gateway().set_character_meseta(&character_id, inventory.meseta).await?; - Ok(((item_state, transaction), amount)) + Ok(((item_state, transaction), ())) }) } } -fn add_meseta_to_shared_floor(character_id: CharacterEntityId, map_area: MapArea, drop_position: (f32, f32)) - -> impl for<'a> Fn((ItemStateProxy<'a>, Box), u32) - -> Pin, Box), FloorItem), ItemStateError>> + Send + 'a>> + +pub async fn withdraw_meseta<'a, EG>( + item_state: &'a mut ItemState, + entity_gateway: &mut EG, + character: &CharacterEntity, + amount: u32) + -> Result<(), ItemStateError> +where + EG: EntityGateway, { + entity_gateway.with_transaction(|transaction| async move { + let item_state_proxy = ItemStateProxy::new(item_state); + let ((item_state_proxy, transaction), result) = ItemStateAction::default() + .act(take_meseta_from_bank(character.id, amount)) + .act(add_meseta_from_bank_to_inventory(character.id, amount)) + .commit((item_state_proxy, transaction)) + .await?; + item_state_proxy.commit(); + Ok((transaction, result)) + }).await +} - move |(mut item_state, transaction), amount| { +fn add_meseta_to_bank(character_id: CharacterEntityId, amount: u32) + -> impl for<'a> Fn((ItemStateProxy<'a>, Box), ()) + -> Pin, Box), ()), ItemStateError>> + Send + 'a>> +{ + move |(mut item_state, mut transaction), _| { Box::pin(async move { - let floor_item = FloorItem { - item_id: item_state.new_item_id()?, - item: FloorItemDetail::Meseta(Meseta(amount)), - map_area: map_area, - x: drop_position.0, - y: 0.0, - z: drop_position.1, + let mut bank = item_state.bank(&character_id)?; + bank.add_meseta(amount)?; + transaction.gateway().set_bank_meseta(&character_id, &bank.name, bank.meseta).await?; + + Ok(((item_state, transaction), ())) + }) + } +} + +pub async fn deposit_meseta<'a, EG>( + item_state: &'a mut ItemState, + entity_gateway: &mut EG, + character: &CharacterEntity, + amount: u32) + -> Result<(), ItemStateError> +where + EG: EntityGateway, +{ + entity_gateway.with_transaction(|transaction| async move { + let item_state_proxy = ItemStateProxy::new(item_state); + let ((item_state_proxy, transaction), result) = ItemStateAction::default() + .act(take_meseta_from_inventory(character.id, amount)) + .act(add_meseta_to_bank(character.id, amount)) + .commit((item_state_proxy, transaction)) + .await?; + item_state_proxy.commit(); + Ok((transaction, ())) + }).await +} + +fn take_item_from_bank(character_id: CharacterEntityId, item_id: ClientItemId, amount: u32) + -> impl for<'a> Fn((ItemStateProxy<'a>, Box), ()) + -> Pin, Box), BankItem), ItemStateError>> + Send + 'a>> +{ + move |(mut item_state, mut transaction), _| { + Box::pin(async move { + let mut bank = item_state.bank(&character_id)?; + let item = bank.take_item(&item_id, amount).ok_or_else(|| ItemStateError::NoBankItem(item_id))?; + transaction.gateway().set_character_bank(&character_id, &bank.as_bank_entity(), &bank.name).await?; + item_state.set_bank(bank); + + Ok(((item_state, transaction), item)) + }) + } +} + +fn add_bank_item_to_inventory(character: &CharacterEntity) + -> impl for<'a> Fn((ItemStateProxy<'a>, Box), BankItem) + -> Pin, Box), InventoryItem), ItemStateError>> + Send + 'a>> +{ + let character = character.clone(); + move |(mut item_state, transaction), bank_item| { + let character = character.clone(); + Box::pin(async move { + let bank_name = item_state.bank(&character.id)?.name; + let mut inventory = item_state.inventory(&character.id)?; + + let character_id = character.id; + let transaction = bank_item.with_entity_id(Ok(transaction), |mut transaction: Result<_, ItemStateError>, entity_id| { + let bank_name = bank_name.clone(); + async move { + if let Ok(transaction) = &mut transaction { + transaction.gateway().add_item_note(&entity_id, ItemNote::Withdraw { + character_id, + bank: bank_name, + }).await?; + } + transaction + }}).await?; + + let inventory_item = InventoryItem { + item_id: bank_item.item_id, + item: match bank_item.item { + BankItemDetail::Individual(individual_item) => InventoryItemDetail::Individual(individual_item), + BankItemDetail::Stacked(stacked_item) => InventoryItemDetail::Stacked(stacked_item), + }, }; - let mut floor = item_state.floor(&character_id)?; - let floor_item = floor.add_item(floor_item).clone(); - item_state.set_floor(floor); + let mut transaction = inventory_item.with_mag(Ok(transaction), |mut transaction: Result<_, ItemStateError>, entity_id, _mag| { + let character = character.clone(); + async move { + if let Ok(transaction) = &mut transaction { + transaction.gateway().change_mag_owner(&entity_id, &character).await?; + } + transaction + }}).await?; - Ok(((item_state, transaction), floor_item)) + inventory.add_item(inventory_item.clone())?; + transaction.gateway().set_character_inventory(&character.id, &inventory.as_inventory_entity(&character.id)).await?; + item_state.set_inventory(inventory); + + Ok(((item_state, transaction), inventory_item)) }) } } -pub async fn drop_meseta<'a, EG>( +pub async fn withdraw_item<'a, EG>( item_state: &'a mut ItemState, entity_gateway: &mut EG, character: &CharacterEntity, - map_area: MapArea, - drop_position: (f32, f32), + item_id: &ClientItemId, amount: u32) - -> Result + -> Result where EG: EntityGateway, { entity_gateway.with_transaction(|transaction| async move { let item_state_proxy = ItemStateProxy::new(item_state); let ((item_state_proxy, transaction), result) = ItemStateAction::default() - .act(take_meseta_from_inventory(character.id, amount)) - .act(add_meseta_to_shared_floor(character.id, map_area, drop_position)) + .act(take_item_from_bank(character.id, *item_id, amount)) + .act(add_bank_item_to_inventory(&character)) + .commit((item_state_proxy, transaction)) + .await?; + item_state_proxy.commit(); + Ok((transaction, result)) + }).await +} + +fn add_inventory_item_to_bank(character_id: CharacterEntityId) + -> impl for<'a> Fn((ItemStateProxy<'a>, Box), InventoryItem) + -> Pin, Box), ()), ItemStateError>> + Send + 'a>> +{ + move |(mut item_state, transaction), inventory_item| { + Box::pin(async move { + let mut bank = item_state.bank(&character_id)?; + let bank_name = bank.name.clone(); + let mut transaction = inventory_item.with_entity_id(Ok(transaction), move |mut transaction: Result<_, ItemStateError>, entity_id| { + let bank_name = bank_name.clone(); + async move { + if let Ok(transaction) = &mut transaction { + transaction.gateway().add_item_note(&entity_id, ItemNote::Deposit { + character_id, + bank: bank_name, + }).await?; + } + transaction + }}).await?; + + bank.add_inventory_item(inventory_item)?; + + transaction.gateway().set_character_bank(&character_id, &bank.as_bank_entity(), &bank.name).await?; + item_state.set_bank(bank); + + + Ok(((item_state, transaction), ())) + }) + } +} + +pub async fn deposit_item<'a, EG>( + item_state: &'a mut ItemState, + entity_gateway: &mut EG, + character: &CharacterEntity, + item_id: &ClientItemId, + amount: u32) + -> Result<(), ItemStateError> +where + EG: EntityGateway, +{ + entity_gateway.with_transaction(|transaction| async move { + let item_state_proxy = ItemStateProxy::new(item_state); + let ((item_state_proxy, transaction), result) = ItemStateAction::default() + .act(take_item_from_inventory(character.id, *item_id, amount)) + .act(add_inventory_item_to_bank(character.id)) .commit((item_state_proxy, transaction)) .await?; item_state_proxy.commit(); diff --git a/src/ship/items/manager.rs b/src/ship/items/manager.rs index f3b701a..c7b7fbf 100644 --- a/src/ship/items/manager.rs +++ b/src/ship/items/manager.rs @@ -388,7 +388,7 @@ impl ItemManager { .map_err(|err| err.into()) } - pub async fn enemy_drop_item_on_local_floor<'a, EG: EntityGateway>(&'a mut self, entity_gateway: &'a mut EG, character: &CharacterEntity, item_drop: ItemDrop) -> Result<&'a FloorItem, anyhow::Error> { + pub async fn enemy_drop_item_on_local_floor<'a, EG: EntityGateway>(&'a mut self, entity_gateway: &'a mut EG, character: &'a CharacterEntity, item_drop: ItemDrop) -> Result<&'a FloorItem, anyhow::Error> { let room_id = self.character_room.get(&character.id).ok_or(ItemManagerError::NoCharacter(character.id))?; enum ItemOrMeseta { @@ -805,7 +805,7 @@ impl ItemManager { } pub async fn player_buys_item<'a, EG: EntityGateway>(&'a mut self, - entity_gateway: &mut EG, + entity_gateway: &'a mut EG, character: &'a CharacterEntity, shop_item: &'a (dyn ShopItem + Send + Sync), item_id: ClientItemId, diff --git a/src/ship/items/state.rs b/src/ship/items/state.rs index ec5b00c..aef4692 100644 --- a/src/ship/items/state.rs +++ b/src/ship/items/state.rs @@ -2,7 +2,7 @@ use std::cmp::Ordering; use std::collections::HashMap; use libpso::character::character; use crate::ship::items::ClientItemId; -use crate::entity::item::{Meseta, ItemEntityId, ItemDetail, ItemEntity, InventoryEntity, InventoryItemEntity, BankItemEntity, BankName, EquippedEntity}; +use crate::entity::item::{Meseta, ItemEntityId, ItemDetail, ItemEntity, InventoryEntity, InventoryItemEntity, BankEntity, BankItemEntity, BankName, EquippedEntity}; use std::future::Future; use crate::ship::map::MapArea; @@ -13,6 +13,7 @@ use crate::entity::item::tool::Tool; use crate::entity::item::mag::Mag; use crate::ship::drops::ItemDrop; +// TODO: Commit trait that ItemStateProxy and EntityTransaction implement that .commit requires and acts on upon everything succeeding (like 3 less lines of code!) #[derive(thiserror::Error, Debug)] pub enum ItemStateError { @@ -23,9 +24,15 @@ pub enum ItemStateError { #[error("floor item {0} not found")] NoFloorItem(ClientItemId), + #[error("bank item {0} not found")] + NoBankItem(ClientItemId), + #[error("inventory error {0}")] InventoryError(#[from] InventoryError), + #[error("bank error {0}")] + BankError(#[from] BankError), + #[error("invalid drop? {0:?} (this shouldn't occur)")] BadItemDrop(ItemDrop), @@ -35,8 +42,11 @@ pub enum ItemStateError { #[error("gateway")] GatewayError(#[from] GatewayError), - #[error("tried to drop more meseta than in inventory: {0}")] - InvalidMesetaDrop(u32), + #[error("tried to remove more meseta than exists: {0}")] + InvalidMesetaRemoval(u32), + + #[error("tried to add meseta when there is no more room")] + FullOfMeseta, #[error("stacked item")] StackedItemError(Vec), @@ -247,8 +257,8 @@ impl InventoryItemDetail { #[derive(Clone, Debug)] pub struct InventoryItem { - item_id: ClientItemId, - item: InventoryItemDetail, + pub item_id: ClientItemId, + pub item: InventoryItemDetail, } impl InventoryItem { @@ -270,6 +280,19 @@ impl InventoryItem { param } + + pub async fn with_mag(&self, mut param: T, mut func: F) -> T + where + F: FnMut(T, ItemEntityId, Mag) -> Fut, + Fut: Future, + { + if let InventoryItemDetail::Individual(individual_item) = &self.item { + if let ItemDetail::Mag(mag) = &individual_item.item { + param = func(param, individual_item.entity_id, mag.clone()).await; + } + } + param + } } @@ -280,6 +303,13 @@ pub enum BankItemDetail { } impl BankItemDetail { + fn stacked_mut(&mut self) -> Option<&mut StackedItemDetail> { + match self { + BankItemDetail::Stacked(sitem) => Some(sitem), + _ => None, + } + } + pub fn as_client_bytes(&self) -> [u8; 16] { match self { BankItemDetail::Individual(item) => { @@ -303,8 +333,29 @@ impl BankItemDetail { #[derive(Clone, Debug)] pub struct BankItem { - item_id: ClientItemId, - item: BankItemDetail, + pub item_id: ClientItemId, + pub item: BankItemDetail, +} + +impl BankItem { + pub async fn with_entity_id(&self, mut param: T, mut func: F) -> T + where + F: FnMut(T, ItemEntityId) -> Fut, + Fut: Future, + { + match &self.item { + BankItemDetail::Individual(individual_item) => { + param = func(param, individual_item.entity_id).await; + }, + BankItemDetail::Stacked(stacked_item) => { + for entity_id in &stacked_item.entity_ids { + param = func(param, *entity_id).await; + } + } + } + + param + } } #[derive(Clone)] @@ -397,6 +448,16 @@ pub enum InventoryError { MesetaFull, } +#[derive(thiserror::Error, Debug)] +pub enum BankError { + #[error("bank full")] + BankFull, + #[error("stack full")] + StackFull, + #[error("meseta full")] + MesetaFull, +} + #[derive(Clone)] pub enum AddItemResult { NewItem, @@ -428,6 +489,11 @@ impl InventoryState { self.item_id_counter = base_item_id + self.inventory.0.len() as u32 + 1; } + pub fn new_item_id(&mut self) -> ClientItemId { + self.item_id_counter += 1; + ClientItemId(self.item_id_counter) + } + pub fn count(&self) -> usize { self.inventory.0.len() } @@ -490,43 +556,77 @@ impl InventoryState { } } - pub fn take_item(&mut self, item_id: &ClientItemId) -> Option { - self.inventory.0 - .drain_filter(|i| i.item_id == *item_id) - .next() + pub fn add_item(&mut self, item: InventoryItem) -> Result { + match &item.item { + InventoryItemDetail::Individual(_) => { + if self.inventory.0.len() >= 30 { + Err(InventoryError::InventoryFull) + } + else { + self.inventory.0.push(item); + Ok(AddItemResult::NewItem) + } + }, + InventoryItemDetail::Stacked(sitem) => { + let existing_stack = self.inventory.0 + .iter_mut() + .filter_map(|item| item.item.stacked_mut()) + .find(|item| { + item.tool == sitem.tool + }); + match existing_stack { + Some(existing_stack) => { + if existing_stack.entity_ids.len() + sitem.entity_ids.len() > sitem.tool.max_stack() { + Err(InventoryError::StackFull) + } + else { + existing_stack.entity_ids.append(&mut sitem.entity_ids.clone()); + Ok(AddItemResult::AddToStack) + } + }, + None => { + if self.inventory.0.len() >= 30 { + Err(InventoryError::InventoryFull) + } + else { + self.inventory.0.push(item); + Ok(AddItemResult::NewItem) + } + } + } + } + } } - pub fn take_partial_item(&mut self, item_id: &ClientItemId, amount: u32) -> Option { - let amount = amount as usize; - let (idx, _, stacked_item) = self.inventory.0 - .iter_mut() - .enumerate() - .filter_map(|(k, item)| { - match item.item { - InventoryItemDetail::Stacked(ref mut stacked_item) => Some((k, item.item_id, stacked_item)), - _ => None + pub fn take_item(&mut self, item_id: &ClientItemId, amount: u32) -> Option { + let idx = self.inventory.0 + .iter() + .position(|i| i.item_id == *item_id)?; + match &mut self.inventory.0[idx].item { + InventoryItemDetail::Individual(_individual_item) => { + Some(self.inventory.0.remove(idx)) + }, + InventoryItemDetail::Stacked(stacked_item) => { + let remove_all = match stacked_item.entity_ids.len().cmp(&(amount as usize)) { + Ordering::Equal => true, + Ordering::Greater => false, + Ordering::Less => return None, + }; + + if remove_all { + Some(self.inventory.0.remove(idx)) } - }) - .find(|(_, id, _)| *id == *item_id)?; - - - let remove_all = match stacked_item.entity_ids.len().cmp(&amount) { - Ordering::Equal => true, - Ordering::Greater => false, - Ordering::Less => return None, - }; - - if remove_all { - let stacked_item = stacked_item.clone(); - self.inventory.0.remove(idx); - Some(stacked_item) - } - else { - let entity_ids = stacked_item.entity_ids.drain(..amount).collect(); - Some(StackedItemDetail { - entity_ids: entity_ids, - tool: stacked_item.tool, - }) + else { + let entity_ids = stacked_item.entity_ids.drain(..(amount as usize)).collect(); + self.item_id_counter += 1; + Some(InventoryItem { + item_id: ClientItemId(self.item_id_counter), + item: InventoryItemDetail::Stacked(StackedItemDetail { + entity_ids: entity_ids, + tool: stacked_item.tool, + })}) + } + } } } @@ -557,9 +657,25 @@ impl InventoryState { } } + pub fn add_meseta(&mut self, amount: u32) -> Result<(), ItemStateError> { + if self.meseta.0 == 999999 { + return Err(ItemStateError::FullOfMeseta) + } + self.meseta.0 = std::cmp::min(self.meseta.0 + amount, 999999); + Ok(()) + } + + pub fn add_meseta_no_overflow(&mut self, amount: u32) -> Result<(), ItemStateError> { + if self.meseta.0 + amount > 999999 { + return Err(ItemStateError::FullOfMeseta) + } + self.meseta.0 += amount; + Ok(()) + } + pub fn remove_meseta(&mut self, amount: u32) -> Result<(), ItemStateError> { if amount > self.meseta.0 { - return Err(ItemStateError::InvalidMesetaDrop(amount)) + return Err(ItemStateError::InvalidMesetaRemoval(amount)) } self.meseta.0 -= amount; Ok(()) @@ -601,16 +717,130 @@ pub struct Bank(Vec); pub struct BankState { character_id: CharacterEntityId, item_id_counter: u32, + pub name: BankName, bank: Bank, pub meseta: Meseta, } impl BankState { + pub fn new(character_id: CharacterEntityId, name: BankName, mut bank: Bank, meseta: Meseta) -> BankState { + bank.0.sort(); + BankState { + character_id, + item_id_counter: 0, + name, + bank, + meseta, + } + } + + pub fn count(&self) -> usize { + self.bank.0.len() + } + pub fn initialize_item_ids(&mut self, base_item_id: u32) { for (i, item) in self.bank.0.iter_mut().enumerate() { item.item_id = ClientItemId(base_item_id + i as u32); } - self.item_id_counter = base_item_id + self.bank.0.len() as u32 + 1; + self.item_id_counter = base_item_id + self.bank.0.len() as u32; + } + + pub fn add_meseta(&mut self, amount: u32) -> Result<(), ItemStateError> { + if self.meseta.0 + amount > 999999 { + return Err(ItemStateError::FullOfMeseta) + } + self.meseta.0 += self.meseta.0 + amount; + Ok(()) + } + + pub fn remove_meseta(&mut self, amount: u32) -> Result<(), ItemStateError> { + if amount > self.meseta.0 { + return Err(ItemStateError::InvalidMesetaRemoval(amount)) + } + self.meseta.0 -= amount; + Ok(()) + } + + pub fn add_inventory_item(&mut self, item: InventoryItem) -> Result { + match item.item { + InventoryItemDetail::Individual(iitem) => { + if self.bank.0.len() >= 30 { + Err(BankError::BankFull) + } + else { + self.bank.0.push(BankItem { + item_id: item.item_id, + item: BankItemDetail::Individual(iitem) + }); + self.bank.0.sort(); + Ok(AddItemResult::NewItem) + } + }, + InventoryItemDetail::Stacked(sitem) => { + let existing_stack = self.bank.0 + .iter_mut() + .filter_map(|item| item.item.stacked_mut()) + .find(|item| { + item.tool == sitem.tool + }); + match existing_stack { + Some(existing_stack) => { + if existing_stack.entity_ids.len() + sitem.entity_ids.len() > sitem.tool.max_stack() { + Err(BankError::StackFull) + } + else { + existing_stack.entity_ids.append(&mut sitem.entity_ids.clone()); + Ok(AddItemResult::AddToStack) + } + }, + None => { + if self.bank.0.len() >= 30 { + Err(BankError::BankFull) + } + else { + self.bank.0.push(BankItem { + item_id: item.item_id, + item: BankItemDetail::Stacked(sitem) + }); + self.bank.0.sort(); + Ok(AddItemResult::NewItem) + } + } + } + } + } + } + + pub fn take_item(&mut self, item_id: &ClientItemId, amount: u32) -> Option { + let idx = self.bank.0 + .iter() + .position(|i| i.item_id == *item_id)?; + match &mut self.bank.0[idx].item { + BankItemDetail::Individual(_individual_item) => { + Some(self.bank.0.remove(idx)) + }, + BankItemDetail::Stacked(stacked_item) => { + let remove_all = match stacked_item.entity_ids.len().cmp(&(amount as usize)) { + Ordering::Equal => true, + Ordering::Greater => false, + Ordering::Less => return None, + }; + + if remove_all { + Some(self.bank.0.remove(idx)) + } + else { + let entity_ids = stacked_item.entity_ids.drain(..(amount as usize)).collect(); + self.item_id_counter += 1; + Some(BankItem { + item_id: ClientItemId(self.item_id_counter), + item: BankItemDetail::Stacked(StackedItemDetail { + entity_ids: entity_ids, + tool: stacked_item.tool, + })}) + } + } + } } pub fn as_client_bank_items(&self) -> character::Bank { @@ -626,6 +856,106 @@ impl BankState { bank }) } + + pub fn as_client_bank_request(&self) -> Vec { + self.bank.0.iter() + .map(|item| { + let bytes = item.item.as_client_bytes(); + let mut data1 = [0; 12]; + let mut data2 = [0; 4]; + data1.copy_from_slice(&bytes[0..12]); + data2.copy_from_slice(&bytes[12..16]); + let amount = match &item.item { + BankItemDetail::Individual(_individual_bank_item) => { + 1 + }, + BankItemDetail::Stacked(stacked_bank_item) => { + stacked_bank_item.count() + }, + }; + character::BankItem { + data1, + data2, + item_id: item.item_id.0, + amount: amount as u16, + flags: 1, + } + }) + .collect() + } + + pub fn as_bank_entity(&self) -> BankEntity { + BankEntity { + items: self.bank.0.iter() + .map(|item| { + match &item.item { + BankItemDetail::Individual(item) => { + BankItemEntity::Individual(ItemEntity { + id: item.entity_id, + item: item.item.clone(), + }) + }, + BankItemDetail::Stacked(items) => { + BankItemEntity::Stacked(items.entity_ids.iter() + .map(|id| { + ItemEntity { + id: *id, + item: ItemDetail::Tool(items.tool) + } + }) + .collect()) + }, + } + }) + .collect() + } + } +} + +impl std::cmp::PartialEq for BankItem { + fn eq(&self, other: &BankItem) -> bool { + let mut self_bytes = [0u8; 4]; + let mut other_bytes = [0u8; 4]; + self_bytes.copy_from_slice(&self.item.as_client_bytes()[0..4]); + other_bytes.copy_from_slice(&other.item.as_client_bytes()[0..4]); + + let self_value = u32::from_be_bytes(self_bytes); + let other_value = u32::from_be_bytes(other_bytes); + + self_value.eq(&other_value) + } +} + +impl std::cmp::Eq for BankItem {} + +impl std::cmp::PartialOrd for BankItem { + fn partial_cmp(&self, other: &BankItem) -> Option { + let mut self_bytes = [0u8; 4]; + let mut other_bytes = [0u8; 4]; + self_bytes.copy_from_slice(&self.item.as_client_bytes()[0..4]); + other_bytes.copy_from_slice(&other.item.as_client_bytes()[0..4]); + + + let self_value = u32::from_be_bytes(self_bytes); + let other_value = u32::from_be_bytes(other_bytes); + + self_value.partial_cmp(&other_value) + } +} + +impl std::cmp::Ord for BankItem { + fn cmp(&self, other: &BankItem) -> std::cmp::Ordering { + let mut self_bytes = [0u8; 4]; + let mut other_bytes = [0u8; 4]; + self_bytes.copy_from_slice(&self.item.as_client_bytes()[0..4]); + other_bytes.copy_from_slice(&other.item.as_client_bytes()[0..4]); + + + let self_value = u32::from_le_bytes(self_bytes); + let other_value = u32::from_le_bytes(other_bytes); + + self_value.cmp(&other_value) + } } pub struct FloorState { @@ -677,14 +1007,11 @@ impl FloorState { pub struct ItemState { character_inventory: HashMap, character_bank: HashMap, - //character_meseta: HashMap, - //bank_meseta: HashMap, character_room: HashMap, character_floor: HashMap, room_floor: HashMap, - //room_item_id_counter: Arc ClientItemId + Send + Sync>>>>, room_item_id_counter: u32, } @@ -693,8 +1020,6 @@ impl Default for ItemState { ItemState { character_inventory: HashMap::new(), character_bank: HashMap::new(), - //character_meseta: HashMap::new(), - //bank_meseta: HashMap::new(), character_room: HashMap::new(), character_floor: HashMap::new(), room_floor: HashMap::new(), @@ -795,13 +1120,8 @@ impl ItemState { }) .collect::, _>>()?; - let bank_meseta = entity_gateway.get_bank_meseta(&character.id, BankName("".into())).await?; - let bank_state = BankState { - character_id: character.id, - item_id_counter: 0, - bank: Bank(bank_items), - meseta: bank_meseta, - }; + let bank_meseta = entity_gateway.get_bank_meseta(&character.id, &BankName("".into())).await?; + let bank_state = BankState::new(character.id, BankName("".into()), Bank(bank_items), bank_meseta); self.character_inventory.insert(character.id, inventory_state); self.character_bank.insert(character.id, bank_state); @@ -820,14 +1140,6 @@ impl ItemState { self.character_room.insert(character.id, room_id); self.character_floor.insert(character.id, LocalFloor::default()); self.room_floor.entry(room_id).or_insert_with(SharedFloor::default); - - /* - let mut inc = 0x00810000; - self.room_item_id_counter.borrow_mut().entry(room_id).or_insert_with(|| Box::new(move || { - inc += 1; - ClientItemId(inc) - })); - */ } pub fn remove_character_from_room(&mut self, character: &CharacterEntity) { @@ -864,34 +1176,12 @@ impl ItemState { struct ProxiedItemState { character_inventory: HashMap, character_bank: HashMap, - //character_meseta: HashMap, - //bank_meseta: HashMap, character_room: HashMap, character_floor: HashMap, room_floor: HashMap, - - //room_item_id_counter: HashMap ClientItemId + Send>>, - } -/* -impl Default for ProxiedItemState { - fn default() -> Self { - ProxiedItemState { - character_inventory: HashMap::new(), - //character_bank: HashMap::new(), - character_meseta: HashMap::new(), - //bank_meseta: HashMap::new(), - character_floor: HashMap::new(), - character_room: HashMap::new(), - room_floor: HashMap::new(), - //room_item_id_counter: HashMap::new(), - } - } -} -*/ - pub struct ItemStateProxy<'a> { item_state: &'a mut ItemState, proxied_state: ProxiedItemState, @@ -901,7 +1191,6 @@ impl<'a> ItemStateProxy<'a> { pub fn commit(self) { self.item_state.character_inventory.extend(self.proxied_state.character_inventory.clone()); self.item_state.character_bank.extend(self.proxied_state.character_bank.clone()); - //self.item_state.character_meseta.extend(self.proxied_state.character_meseta.clone()); self.item_state.character_room.extend(self.proxied_state.character_room.clone()); self.item_state.character_floor.extend(self.proxied_state.character_floor.clone()); self.item_state.room_floor.extend(self.proxied_state.room_floor.clone()); @@ -930,19 +1219,18 @@ impl<'a> ItemStateProxy<'a> { pub fn inventory(&mut self, character_id: &CharacterEntityId) -> Result { get_or_clone(&self.item_state.character_inventory, &mut self.proxied_state.character_inventory, *character_id, ItemStateError::NoCharacter) - /* - Ok(InventoryState { - character_id: *character_id, - inventory: get_or_clone(&self.item_state.character_inventory, &mut self.proxied_state.character_inventory, *character_id, ItemStateError::NoCharacter)?, - meseta: get_or_clone(&self.item_state.character_meseta, &mut self.proxied_state.character_meseta, *character_id, ItemStateError::NoCharacter)?, - }) - */ } pub fn set_inventory(&mut self, inventory: InventoryState) { self.proxied_state.character_inventory.insert(inventory.character_id, inventory); - //self.proxied_state.character_inventory.insert(inventory.character_id, inventory.inventory); - //self.proxied_state.character_meseta.insert(inventory.character_id, inventory.meseta); + } + + pub fn bank(&mut self, character_id: &CharacterEntityId) -> Result { + get_or_clone(&self.item_state.character_bank, &mut self.proxied_state.character_bank, *character_id, ItemStateError::NoCharacter) + } + + pub fn set_bank(&mut self, bank: BankState) { + self.proxied_state.character_bank.insert(bank.character_id, bank); } pub fn floor(&mut self, character_id: &CharacterEntityId) -> Result { diff --git a/src/ship/packet/builder/lobby.rs b/src/ship/packet/builder/lobby.rs index 56213dd..aefe96f 100644 --- a/src/ship/packet/builder/lobby.rs +++ b/src/ship/packet/builder/lobby.rs @@ -4,6 +4,7 @@ use crate::common::leveltable::CharacterLevelTable; use crate::ship::ship::{ShipError, Clients}; use crate::ship::location::{ClientLocation, LobbyId, ClientLocationError}; use crate::ship::packet::builder::{player_info}; +use crate::ship::items::state::ItemState; use crate::ship::items::ItemManager; @@ -11,14 +12,14 @@ pub fn join_lobby(id: ClientId, lobby: LobbyId, client_location: &ClientLocation, clients: &Clients, - item_manager: &ItemManager, + item_state: &ItemState, level_table: &CharacterLevelTable) -> Result { let lobby_clients = client_location.get_clients_in_lobby(lobby).map_err(|err| -> ClientLocationError { err.into() })?; let playerinfo = lobby_clients.iter() .map(|area_client| { let client = clients.get(&area_client.client).ok_or(ShipError::ClientNotFound(area_client.client)).unwrap(); - player_info(0x100, client, area_client, item_manager, level_table) + player_info(0x100, client, area_client, item_state, level_table) }); let client = clients.get(&id).ok_or(ShipError::ClientNotFound(id)).unwrap(); @@ -40,7 +41,7 @@ pub fn add_to_lobby(id: ClientId, lobby: LobbyId, client_location: &ClientLocation, clients: &Clients, - item_manager: &ItemManager, + item_state: &ItemState, level_table: &CharacterLevelTable) -> Result { let client = clients.get(&id).ok_or(ShipError::ClientNotFound(id)).unwrap(); @@ -55,7 +56,7 @@ pub fn add_to_lobby(id: ClientId, block: client.block as u16, event: 0, padding: 0, - playerinfo: player_info(0x100, client, &area_client, item_manager, level_table), + playerinfo: player_info(0x100, client, &area_client, item_state, level_table), }) } diff --git a/src/ship/packet/builder/message.rs b/src/ship/packet/builder/message.rs index a5303bc..cf91fe9 100644 --- a/src/ship/packet/builder/message.rs +++ b/src/ship/packet/builder/message.rs @@ -5,6 +5,8 @@ use crate::common::leveltable::CharacterStats; use crate::ship::ship::{ShipError}; use crate::ship::items::{ClientItemId, InventoryItem, StackedFloorItem, FloorItem, CharacterBank}; use crate::ship::items::state::FloorItem as FloorItem2; +use crate::ship::items::state::InventoryItem as InventoryItem2; +use crate::ship::items::state::{BankState}; use crate::ship::location::AreaClient; use std::convert::TryInto; use crate::ship::shops::ShopItem; @@ -78,6 +80,18 @@ pub fn create_withdrawn_inventory_item(area_client: AreaClient, item: &Inventory }) } +pub fn create_withdrawn_inventory_item2(area_client: AreaClient, item: &InventoryItem2) -> Result { + let bytes = item.item.as_client_bytes(); + Ok(CreateItem { + client: area_client.local_client.id(), + target: 0, + item_data: bytes[0..12].try_into()?, + item_id: item.item_id.0, + item_data2: bytes[12..16].try_into()?, + unknown: 0, + }) +} + pub fn remove_item_from_floor(area_client: AreaClient, item: &FloorItem2) -> Result { Ok(RemoveItemFromFloor { client: area_client.local_client.id(), @@ -147,7 +161,7 @@ pub fn character_leveled_up(area_client: AreaClient, level: u32, before_stats: C } // TOOD: checksum? -pub fn bank_item_list(bank: &CharacterBank, char_bank_meseta: u32) -> BankItemList { +pub fn bank_item_list(bank: &BankState) -> BankItemList { BankItemList { aflag: 0, cmd: 0xBC, @@ -155,7 +169,7 @@ pub fn bank_item_list(bank: &CharacterBank, char_bank_meseta: u32) -> BankItemLi size: bank.count() as u32 * 0x18 + 0x14, checksum: 0x123434, item_count: bank.count() as u32, - meseta: char_bank_meseta, + meseta: bank.meseta.0, items: bank.as_client_bank_request() } } diff --git a/src/ship/packet/builder/mod.rs b/src/ship/packet/builder/mod.rs index f5d9f3c..80d9506 100644 --- a/src/ship/packet/builder/mod.rs +++ b/src/ship/packet/builder/mod.rs @@ -10,7 +10,7 @@ use crate::common::leveltable::CharacterLevelTable; use crate::ship::character::CharacterBytesBuilder; use crate::ship::ship::ClientState; use crate::ship::location::AreaClient; -use crate::ship::items::ItemManager; +use crate::ship::items::state::ItemState; pub fn player_header(tag: u32, client: &ClientState, area_client: &AreaClient) -> PlayerHeader { PlayerHeader { @@ -23,15 +23,14 @@ pub fn player_header(tag: u32, client: &ClientState, area_client: &AreaClient) - } } -pub fn player_info(tag: u32, client: &ClientState, area_client: &AreaClient, item_manager: &ItemManager, level_table: &CharacterLevelTable) -> PlayerInfo { +pub fn player_info(tag: u32, client: &ClientState, area_client: &AreaClient, item_state: &ItemState, level_table: &CharacterLevelTable) -> PlayerInfo { let (level, stats) = level_table.get_stats_from_exp(client.character.char_class, client.character.exp); - let inventory = item_manager.get_character_inventory(&client.character).unwrap(); - let meseta = item_manager.get_character_meseta(&client.character.id).unwrap(); + let inventory = item_state.get_character_inventory(&client.character).unwrap(); let character = CharacterBytesBuilder::default() .character(&client.character) .stats(&stats) .level(level - 1) - .meseta(*meseta) + .meseta(inventory.meseta) .build(); PlayerInfo { header: player_header(tag, client, area_client), diff --git a/src/ship/packet/builder/room.rs b/src/ship/packet/builder/room.rs index c5bd11c..a97d8b8 100644 --- a/src/ship/packet/builder/room.rs +++ b/src/ship/packet/builder/room.rs @@ -4,6 +4,7 @@ use crate::common::leveltable::CharacterLevelTable; use crate::ship::ship::{ShipError, ClientState, Clients}; use crate::ship::location::{ClientLocation, RoomId, AreaClient, ClientLocationError}; use crate::ship::room::RoomState; +use crate::ship::items::state::ItemState; use crate::ship::items::ItemManager; use crate::ship::packet::builder::{player_header, player_info}; use std::convert::TryInto; @@ -53,7 +54,7 @@ pub fn add_to_room(_id: ClientId, client: &ClientState, area_client: &AreaClient, leader: &AreaClient, - item_manager: &ItemManager, + item_state: &ItemState, level_table: &CharacterLevelTable, _room_id: RoomId, ) @@ -68,7 +69,7 @@ pub fn add_to_room(_id: ClientId, block: 0, event: 0, padding: 0, - playerinfo: player_info(0x10000, client, area_client, item_manager, level_table), + playerinfo: player_info(0x10000, client, area_client, item_state, level_table), }) } @@ -76,4 +77,4 @@ pub fn build_rare_monster_list(rare_monster_vec: Vec) -> RareMonsterList { RareMonsterList { ids: rare_monster_vec.try_into().unwrap_or([0xFFFFu16; 16]), } -} \ No newline at end of file +} diff --git a/src/ship/packet/handler/auth.rs b/src/ship/packet/handler/auth.rs index c9e6987..50be751 100644 --- a/src/ship/packet/handler/auth.rs +++ b/src/ship/packet/handler/auth.rs @@ -4,7 +4,7 @@ use crate::common::serverstate::ClientId; use crate::ship::ship::{SendShipPacket, ShipError, ClientState, Clients}; use crate::login::login::get_login_status; use crate::entity::gateway::EntityGateway; -use crate::ship::items::ItemManager; +use crate::ship::items::state::ItemState; use crate::common::interserver::ShipMessage; #[allow(clippy::too_many_arguments)] @@ -12,7 +12,7 @@ pub async fn validate_login(id: ClientId, pkt: &Login, entity_gateway: &mut EG, clients: &mut Clients, - item_manager: &mut ItemManager, + item_state: &mut ItemState, shipgate_sender: &Option>, ship_name: &str, num_blocks: usize) @@ -30,7 +30,7 @@ pub async fn validate_login(id: ClientId, .clone(); let settings = entity_gateway.get_user_settings_by_user(&user).await?; - item_manager.load_character(entity_gateway, &character).await?; + item_state.load_character(entity_gateway, &character).await?; if let Some(shipgate_sender) = shipgate_sender.as_ref() { shipgate_sender(ShipMessage::AddUser(user.id)); diff --git a/src/ship/packet/handler/direct_message.rs b/src/ship/packet/handler/direct_message.rs index f5f4a09..38968a3 100644 --- a/src/ship/packet/handler/direct_message.rs +++ b/src/ship/packet/handler/direct_message.rs @@ -15,7 +15,7 @@ use libpso::utf8_to_utf16_array; use crate::ship::packet::builder; use crate::ship::shops::{ShopItem, ToolShopItem, ArmorShopItem}; use crate::ship::items::state::{ItemState, FloorType, FloorItemDetail}; -use crate::ship::items::actions::{pick_up_item, TriggerCreateItem}; +use crate::ship::items::actions::{pick_up_item, withdraw_meseta, deposit_meseta, withdraw_item, deposit_item, TriggerCreateItem}; const BANK_ACTION_DEPOSIT: u8 = 0; const BANK_ACTION_WITHDRAW: u8 = 1; @@ -242,13 +242,12 @@ EG: EntityGateway // item_manager is not mutable in this, but for reasons I don't quite understand it requires the unique access of it to compile here pub async fn send_bank_list(id: ClientId, clients: &Clients, - item_manager: &mut ItemManager) + item_state: &mut ItemState) -> Result + Send>, anyhow::Error> { let client = clients.get(&id).ok_or(ShipError::ClientNotFound(id))?; - let bank_items = item_manager.get_character_bank(&client.character)?; - let bank_meseta = item_manager.get_bank_meseta(&client.character.id)?; - let bank_items_pkt = builder::message::bank_item_list(bank_items, bank_meseta.0); + let bank = item_state.get_character_bank(&client.character)?; + let bank_items_pkt = builder::message::bank_item_list(bank); Ok(Box::new(vec![(id, SendShipPacket::BankItemList(bank_items_pkt))].into_iter())) } @@ -257,7 +256,7 @@ pub async fn bank_interaction(id: ClientId, entity_gateway: &mut EG, client_location: &ClientLocation, clients: &mut Clients, - item_manager: &mut ItemManager) + item_state: &mut ItemState) -> Result + Send>, anyhow::Error> where EG: EntityGateway @@ -267,42 +266,24 @@ where let other_clients_in_area = client_location.get_all_clients_by_client(id).map_err(|err| -> ClientLocationError { err.into() })?; let bank_action_pkts = match bank_interaction.action { BANK_ACTION_DEPOSIT => { - let character_meseta = item_manager.get_character_meseta(&client.character.id)?; - let bank_meseta = item_manager.get_bank_meseta(&client.character.id)?; if bank_interaction.item_id == 0xFFFFFFFF { - if character_meseta.0 >= bank_interaction.meseta_amount && (bank_interaction.meseta_amount + bank_meseta.0) <= BANK_MESETA_CAPACITY { - let (character_meseta, bank_meseta) = item_manager.get_character_and_bank_meseta_mut(&client.character.id)?; - character_meseta.0 -= bank_interaction.meseta_amount; - bank_meseta.0 += bank_interaction.meseta_amount; - entity_gateway.set_character_meseta(&client.character.id, *character_meseta).await?; - // TODO: BankName - entity_gateway.set_bank_meseta(&client.character.id, item::BankName("".into()), *bank_meseta).await?; - } + deposit_meseta(item_state, entity_gateway, &client.character, bank_interaction.meseta_amount).await?; Vec::new() } else { - item_manager.player_deposits_item(entity_gateway, &client.character, ClientItemId(bank_interaction.item_id), bank_interaction.item_amount as usize).await?; + deposit_item(item_state, entity_gateway, &client.character, &ClientItemId(bank_interaction.item_id), bank_interaction.item_amount as u32).await?; let player_no_longer_has_item = builder::message::player_no_longer_has_item(area_client, ClientItemId(bank_interaction.item_id), bank_interaction.item_amount as u32); vec![SendShipPacket::Message(Message::new(GameMessage::PlayerNoLongerHasItem(player_no_longer_has_item)))] } }, BANK_ACTION_WITHDRAW => { - let character_meseta = item_manager.get_character_meseta(&client.character.id)?; - let bank_meseta = item_manager.get_bank_meseta(&client.character.id)?; if bank_interaction.item_id == 0xFFFFFFFF { - if (bank_meseta.0 >= bank_interaction.meseta_amount) && (character_meseta.0 + bank_interaction.meseta_amount <= INVENTORY_MESETA_CAPACITY) { - let (character_meseta, bank_meseta) = item_manager.get_character_and_bank_meseta_mut(&client.character.id)?; - character_meseta.0 += bank_interaction.meseta_amount; - bank_meseta.0 -= bank_interaction.meseta_amount; - entity_gateway.set_character_meseta(&client.character.id, *character_meseta).await?; - // TODO: BankName - entity_gateway.set_bank_meseta(&client.character.id, item::BankName("".into()), *bank_meseta).await?; - } + withdraw_meseta(item_state, entity_gateway, &client.character, bank_interaction.meseta_amount).await?; Vec::new() } else { - let item_added_to_inventory = item_manager.player_withdraws_item(entity_gateway, &client.character, ClientItemId(bank_interaction.item_id), bank_interaction.item_amount as usize).await?; - let item_created = builder::message::create_withdrawn_inventory_item(area_client, item_added_to_inventory)?; + let item_added_to_inventory = withdraw_item(item_state, entity_gateway, &client.character, &ClientItemId(bank_interaction.item_id), bank_interaction.item_amount as u32).await?; + let item_created = builder::message::create_withdrawn_inventory_item2(area_client, &item_added_to_inventory)?; vec![SendShipPacket::Message(Message::new(GameMessage::CreateItem(item_created)))] } }, diff --git a/src/ship/packet/handler/lobby.rs b/src/ship/packet/handler/lobby.rs index 4113929..dd0e6d9 100644 --- a/src/ship/packet/handler/lobby.rs +++ b/src/ship/packet/handler/lobby.rs @@ -6,7 +6,6 @@ use crate::ship::character::{FullCharacterBytesBuilder}; use crate::ship::location::{ClientLocation, LobbyId, RoomLobby, ClientLocationError, RoomId}; use crate::ship::packet; use crate::ship::items::state::ItemState; -use crate::ship::items::ItemManager; use crate::entity::gateway::EntityGateway; use crate::ship::map::MapArea; @@ -52,12 +51,12 @@ pub fn send_player_to_lobby(id: ClientId, _pkt: &CharData, client_location: &mut ClientLocation, clients: &Clients, - item_manager: &ItemManager, + item_state: &ItemState, level_table: &CharacterLevelTable) -> Result, anyhow::Error> { let lobby = client_location.add_client_to_next_available_lobby(id, LobbyId(0)).map_err(|_| ShipError::TooManyClients)?; - let join_lobby = packet::builder::lobby::join_lobby(id, lobby, client_location, clients, item_manager, level_table)?; - let addto = packet::builder::lobby::add_to_lobby(id, lobby, client_location, clients, item_manager, level_table)?; + let join_lobby = packet::builder::lobby::join_lobby(id, lobby, client_location, clients, item_state, level_table)?; + let addto = packet::builder::lobby::add_to_lobby(id, lobby, client_location, clients, item_state, level_table)?; let neighbors = client_location.get_client_neighbors(id).unwrap(); Ok(vec![(id, SendShipPacket::JoinLobby(join_lobby))] .into_iter() @@ -70,7 +69,7 @@ pub async fn change_lobby(id: ClientId, requested_lobby: u32, client_location: &mut ClientLocation, clients: &Clients, - item_manager: &mut ItemManager, + item_state: &mut ItemState, level_table: &CharacterLevelTable, ship_rooms: &mut Rooms, entity_gateway: &mut EG) @@ -87,7 +86,7 @@ pub async fn change_lobby(id: ClientId, if client_location.get_client_neighbors(id)?.is_empty() { ship_rooms[old_room.0] = None; } - item_manager.remove_character_from_room(&client.character); + item_state.remove_character_from_room(&client.character); }, } let leave_lobby = packet::builder::lobby::remove_from_lobby(id, client_location)?; @@ -104,9 +103,9 @@ pub async fn change_lobby(id: ClientId, } } } - item_manager.load_character(entity_gateway, &client.character).await?; - let join_lobby = packet::builder::lobby::join_lobby(id, lobby, client_location, clients, item_manager, level_table)?; - let addto = packet::builder::lobby::add_to_lobby(id, lobby, client_location, clients, item_manager, level_table)?; + item_state.load_character(entity_gateway, &client.character).await?; + let join_lobby = packet::builder::lobby::join_lobby(id, lobby, client_location, clients, item_state, level_table)?; + let addto = packet::builder::lobby::add_to_lobby(id, lobby, client_location, clients, item_state, level_table)?; let neighbors = client_location.get_client_neighbors(id).unwrap(); Ok(vec![(id, SendShipPacket::JoinLobby(join_lobby))] .into_iter() diff --git a/src/ship/packet/handler/room.rs b/src/ship/packet/handler/room.rs index 9e89469..96688f5 100644 --- a/src/ship/packet/handler/room.rs +++ b/src/ship/packet/handler/room.rs @@ -6,14 +6,14 @@ use crate::ship::ship::{SendShipPacket, ShipError, Rooms, Clients}; use crate::ship::location::{ClientLocation, RoomId, RoomLobby, ClientLocationError}; use crate::ship::packet::builder; use crate::ship::room; -use crate::ship::items::ItemManager; +use crate::ship::items::state::ItemState; use std::convert::{TryFrom}; pub fn create_room(id: ClientId, create_room: &CreateRoom, client_location: &mut ClientLocation, clients: &mut Clients, - item_manager: &mut ItemManager, + item_state: &mut ItemState, level_table: &CharacterLevelTable, rooms: &mut Rooms) -> Result + Send>, anyhow::Error> { @@ -45,7 +45,7 @@ pub fn create_room(id: ClientId, let mut room = room::RoomState::from_create_room(create_room, client.character.section_id).unwrap(); room.bursting = true; - item_manager.add_character_to_room(room_id, &client.character, area_client); + item_state.add_character_to_room(room_id, &client.character, area_client); let join_room = builder::room::join_room(id, clients, client_location, room_id, &room)?; rooms[room_id.0] = Some(room); @@ -80,7 +80,7 @@ pub fn join_room(id: ClientId, pkt: &MenuSelect, client_location: &mut ClientLocation, clients: &mut Clients, - item_manager: &mut ItemManager, + item_state: &mut ItemState, level_table: &CharacterLevelTable, rooms: &mut Rooms) -> Result + Send>, ShipError> { diff --git a/src/ship/ship.rs b/src/ship/ship.rs index 8cd2fdc..1584074 100644 --- a/src/ship/ship.rs +++ b/src/ship/ship.rs @@ -553,10 +553,10 @@ impl ShipServerState { handler::direct_message::request_box_item(id, box_drop_request, &mut self.entity_gateway, &block.client_location, &mut self.clients, &mut block.rooms, &mut self.item_manager).await? }, GameMessage::BankRequest(_bank_request) => { - handler::direct_message::send_bank_list(id, &self.clients, &mut self.item_manager).await? + handler::direct_message::send_bank_list(id, &self.clients, &mut self.item_state).await? }, GameMessage::BankInteraction(bank_interaction) => { - handler::direct_message::bank_interaction(id, bank_interaction, &mut self.entity_gateway, &block.client_location, &mut self.clients, &mut self.item_manager).await? + handler::direct_message::bank_interaction(id, bank_interaction, &mut self.entity_gateway, &block.client_location, &mut self.clients, &mut self.item_state).await? }, GameMessage::ShopRequest(shop_request) => { handler::direct_message::shop_request(id, shop_request, &block.client_location, &mut self.clients, &block.rooms, &self.level_table, &mut self.shops).await? @@ -609,7 +609,7 @@ impl ServerState for ShipServerState { -> Result + Send>, anyhow::Error> { Ok(match pkt { RecvShipPacket::Login(login) => { - Box::new(handler::auth::validate_login(id, login, &mut self.entity_gateway, &mut self.clients, &mut self.item_manager, &self.shipgate_sender, &self.name, self.blocks.0.len()) + Box::new(handler::auth::validate_login(id, login, &mut self.entity_gateway, &mut self.clients, &mut self.item_state, &self.shipgate_sender, &self.name, self.blocks.0.len()) .await?.into_iter().map(move |pkt| (id, pkt))) }, RecvShipPacket::QuestDetailRequest(questdetailrequest) => { @@ -632,7 +632,7 @@ impl ServerState for ShipServerState { let select_block = handler::lobby::block_selected(id, menuselect, &mut self.clients, &self.item_state, &self.level_table)?.into_iter(); Box::new(leave_lobby.chain(select_block)) } - ROOM_MENU_ID => handler::room::join_room(id, menuselect, &mut block.client_location, &mut self.clients, &mut self.item_manager, &self.level_table, &mut block.rooms)?, + ROOM_MENU_ID => handler::room::join_room(id, menuselect, &mut block.client_location, &mut self.clients, &mut self.item_state, &self.level_table, &mut block.rooms)?, QUEST_CATEGORY_MENU_ID => handler::quest::select_quest_category(id, menuselect, &block.client_location, &mut block.rooms)?, _ => unreachable!(), } @@ -654,7 +654,7 @@ impl ServerState for ShipServerState { menu: room_password_req.menu, item: room_password_req.item, }; - handler::room::join_room(id, &menuselect, &mut block.client_location, &mut self.clients, &mut self.item_manager, &self.level_table, &mut block.rooms)? + handler::room::join_room(id, &menuselect, &mut block.client_location, &mut self.clients, &mut self.item_state, &self.level_table, &mut block.rooms)? } else { Box::new(vec![(id, SendShipPacket::SmallDialog(SmallDialog::new("Incorrect password".into())))].into_iter()) @@ -662,7 +662,7 @@ impl ServerState for ShipServerState { }, RecvShipPacket::CharData(chardata) => { let block = self.blocks.with_client(id, &self.clients)?; - Box::new(handler::lobby::send_player_to_lobby(id, chardata, &mut block.client_location, &self.clients, &self.item_manager, &self.level_table)?.into_iter()) + Box::new(handler::lobby::send_player_to_lobby(id, chardata, &mut block.client_location, &self.clients, &self.item_state, &self.level_table)?.into_iter()) }, RecvShipPacket::Message(msg) => { self.message(id, msg).await? @@ -676,7 +676,7 @@ impl ServerState for ShipServerState { }, RecvShipPacket::CreateRoom(create_room) => { let block = self.blocks.with_client(id, &self.clients)?; - handler::room::create_room(id, create_room, &mut block.client_location, &mut self.clients, &mut self.item_manager, &self.level_table, &mut block.rooms)? + handler::room::create_room(id, create_room, &mut block.client_location, &mut self.clients, &mut self.item_state, &self.level_table, &mut block.rooms)? }, RecvShipPacket::RoomNameRequest(_req) => { let block = self.blocks.with_client(id, &self.clients)?; @@ -714,7 +714,7 @@ impl ServerState for ShipServerState { }, RecvShipPacket::LobbySelect(pkt) => { let block = self.blocks.with_client(id, &self.clients)?; - Box::new(handler::lobby::change_lobby(id, pkt.lobby, &mut block.client_location, &self.clients, &mut self.item_manager, &self.level_table, &mut block.rooms, &mut self.entity_gateway).await?.into_iter()) + Box::new(handler::lobby::change_lobby(id, pkt.lobby, &mut block.client_location, &self.clients, &mut self.item_state, &self.level_table, &mut block.rooms, &mut self.entity_gateway).await?.into_iter()) }, RecvShipPacket::RequestQuestList(rql) => { let block = self.blocks.with_client(id, &self.clients)?; @@ -786,7 +786,7 @@ impl ServerState for ShipServerState { } block.client_location.remove_client_from_area(id); - self.item_manager.remove_character_from_room(&client.character); + self.item_state.remove_character_from_room(&client.character); if let Some(mut client) = self.clients.remove(&id) { client.user.at_ship = false; diff --git a/tests/test_bank.rs b/tests/test_bank.rs index aad05b9..43a4364 100644 --- a/tests/test_bank.rs +++ b/tests/test_bank.rs @@ -844,7 +844,7 @@ async fn test_deposit_too_much_meseta() { unknown: 0, })))).await.unwrap().for_each(drop); - ship.handle(ClientId(1), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankInteraction(BankInteraction { + let packets = ship.handle(ClientId(1), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankInteraction(BankInteraction { client: 0, target: 0, item_id: 0xFFFFFFFF, @@ -852,7 +852,9 @@ async fn test_deposit_too_much_meseta() { item_amount: 0, meseta_amount: 23, unknown: 0, - })))).await.unwrap().for_each(drop); + })))).await; + + assert!(packets.is_err()); let c1_meseta = entity_gateway.get_character_meseta(&char1.id).await.unwrap(); let c1_bank_meseta = entity_gateway.get_bank_meseta(&char1.id, &item::BankName("".into())).await.unwrap(); @@ -881,7 +883,7 @@ async fn test_deposit_meseta_when_bank_is_maxed() { unknown: 0, })))).await.unwrap().for_each(drop); - ship.handle(ClientId(1), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankInteraction(BankInteraction { + let packets = ship.handle(ClientId(1), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankInteraction(BankInteraction { client: 0, target: 0, item_id: 0xFFFFFFFF, @@ -889,7 +891,9 @@ async fn test_deposit_meseta_when_bank_is_maxed() { item_amount: 0, meseta_amount: 23, unknown: 0, - })))).await.unwrap().for_each(drop); + })))).await; + + assert!(packets.is_err()); let c1_meseta = entity_gateway.get_character_meseta(&char1.id).await.unwrap(); let c1_bank_meseta = entity_gateway.get_bank_meseta(&char1.id, &item::BankName("".into())).await.unwrap(); @@ -1009,7 +1013,7 @@ async fn test_withdraw_stacked_item() { assert!(packets.len() == 2); assert!(matches!(&packets[1], (ClientId(2), SendShipPacket::Message(Message {msg: GameMessage::CreateItem(create_item)})) - if create_item.item_id == 0x10002 + if create_item.item_id == 0x20000 )); let inventory_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap(); @@ -1146,7 +1150,7 @@ async fn test_withdraw_stacked_item_with_stack_already_in_inventory() { assert!(packets.len() == 2); assert!(matches!(&packets[1], (ClientId(2), SendShipPacket::Message(Message {msg: GameMessage::CreateItem(create_item)})) - if create_item.item_id == 0x10000 + if create_item.item_id == 0x20000 )); let bank_items = entity_gateway.get_character_bank(&char1.id, &item::BankName("".into())).await.unwrap(); @@ -1519,7 +1523,7 @@ async fn test_withdraw_too_much_meseta() { unknown: 0, })))).await.unwrap().for_each(drop); - ship.handle(ClientId(1), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankInteraction(BankInteraction { + let packet = ship.handle(ClientId(1), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankInteraction(BankInteraction { client: 0, target: 0, item_id: 0xFFFFFFFF, @@ -1527,7 +1531,9 @@ async fn test_withdraw_too_much_meseta() { item_amount: 0, meseta_amount: 23, unknown: 0, - })))).await.unwrap().for_each(drop); + })))).await; + + assert!(packet.is_err()); let c1_meseta = entity_gateway.get_character_meseta(&char1.id).await.unwrap(); let c1_bank_meseta = entity_gateway.get_bank_meseta(&char1.id, &item::BankName("".into())).await.unwrap(); @@ -1556,7 +1562,7 @@ async fn test_withdraw_meseta_inventory_is_maxed() { unknown: 0, })))).await.unwrap().for_each(drop); - ship.handle(ClientId(1), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankInteraction(BankInteraction { + let packet = ship.handle(ClientId(1), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankInteraction(BankInteraction { client: 0, target: 0, item_id: 0xFFFFFFFF, @@ -1564,7 +1570,9 @@ async fn test_withdraw_meseta_inventory_is_maxed() { item_amount: 0, meseta_amount: 23, unknown: 0, - })))).await.unwrap().for_each(drop); + })))).await; + + assert!(packet.is_err()); let c1_meseta = entity_gateway.get_character_meseta(&char1.id).await.unwrap(); let c1_bank_meseta = entity_gateway.get_bank_meseta(&char1.id, &item::BankName("".into())).await.unwrap(); -- 2.36.0 From 7807cf2b278d1c85141bf085802456ea2cbbea9e Mon Sep 17 00:00:00 2001 From: jake Date: Sun, 15 May 2022 00:33:34 -0600 Subject: [PATCH 34/60] spacing --- src/ship/items/actions.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ship/items/actions.rs b/src/ship/items/actions.rs index 555403c..2d7e95c 100644 --- a/src/ship/items/actions.rs +++ b/src/ship/items/actions.rs @@ -463,7 +463,7 @@ fn add_inventory_item_to_bank(character_id: CharacterEntityId) } } -pub async fn deposit_item<'a, EG>( +pub async fn deposit_item<'a, EG> ( item_state: &'a mut ItemState, entity_gateway: &mut EG, character: &CharacterEntity, -- 2.36.0 From cf09433df24022399cdb035a751ab61bc08e8998 Mon Sep 17 00:00:00 2001 From: jake Date: Sun, 15 May 2022 00:34:06 -0600 Subject: [PATCH 35/60] equipping! --- src/ship/items/actions.rs | 74 +++++++++++++++++++++++ src/ship/items/state.rs | 94 +++++++++++++++++++++++------- src/ship/packet/handler/message.rs | 10 ++-- src/ship/ship.rs | 4 +- 4 files changed, 154 insertions(+), 28 deletions(-) diff --git a/src/ship/items/actions.rs b/src/ship/items/actions.rs index 2d7e95c..2b3f815 100644 --- a/src/ship/items/actions.rs +++ b/src/ship/items/actions.rs @@ -484,3 +484,77 @@ where Ok((transaction, result)) }).await } + +fn equip_inventory_item(character_id: CharacterEntityId, item_id: ClientItemId, equip_slot: u8) + -> impl for<'a> Fn((ItemStateProxy<'a>, Box), ()) + -> Pin, Box), ()), ItemStateError>> + Send + 'a>> +{ + move |(mut item_state, mut transaction), _| { + Box::pin(async move { + let mut inventory = item_state.inventory(&character_id)?; + inventory.equip(&item_id, equip_slot); + transaction.gateway().set_character_equips(&character_id, &inventory.as_equipped_entity()).await?; + + Ok(((item_state, transaction), ())) + }) + } +} + +pub async fn equip_item<'a, EG> ( + item_state: &'a mut ItemState, + entity_gateway: &mut EG, + character: &CharacterEntity, + item_id: &ClientItemId, + equip_slot: u8, +) -> Result<(), ItemStateError> +where + EG: EntityGateway, +{ + entity_gateway.with_transaction(|transaction| async move { + let item_state_proxy = ItemStateProxy::new(item_state); + let ((item_state_proxy, transaction), result) = ItemStateAction::default() + .act(equip_inventory_item(character.id, *item_id, equip_slot)) + .commit((item_state_proxy, transaction)) + .await?; + item_state_proxy.commit(); + Ok((transaction, result)) + }).await +} + + + +fn unequip_inventory_item(character_id: CharacterEntityId, item_id: ClientItemId) + -> impl for<'a> Fn((ItemStateProxy<'a>, Box), ()) + -> Pin, Box), ()), ItemStateError>> + Send + 'a>> +{ + move |(mut item_state, mut transaction), _| { + Box::pin(async move { + let mut inventory = item_state.inventory(&character_id)?; + inventory.unequip(&item_id); + transaction.gateway().set_character_equips(&character_id, &inventory.as_equipped_entity()).await?; + + Ok(((item_state, transaction), ())) + }) + } +} + +pub async fn unequip_item<'a, EG> ( + item_state: &'a mut ItemState, + entity_gateway: &mut EG, + character: &CharacterEntity, + item_id: &ClientItemId, +) -> Result<(), ItemStateError> +where + EG: EntityGateway, +{ + entity_gateway.with_transaction(|transaction| async move { + let item_state_proxy = ItemStateProxy::new(item_state); + let ((item_state_proxy, transaction), result) = ItemStateAction::default() + .act(unequip_inventory_item(character.id, *item_id)) + .commit((item_state_proxy, transaction)) + .await?; + item_state_proxy.commit(); + Ok((transaction, result)) + }).await +} + diff --git a/src/ship/items/state.rs b/src/ship/items/state.rs index aef4692..4905c05 100644 --- a/src/ship/items/state.rs +++ b/src/ship/items/state.rs @@ -630,6 +630,77 @@ impl InventoryState { } } + pub fn add_meseta(&mut self, amount: u32) -> Result<(), ItemStateError> { + if self.meseta.0 == 999999 { + return Err(ItemStateError::FullOfMeseta) + } + self.meseta.0 = std::cmp::min(self.meseta.0 + amount, 999999); + Ok(()) + } + + pub fn add_meseta_no_overflow(&mut self, amount: u32) -> Result<(), ItemStateError> { + if self.meseta.0 + amount > 999999 { + return Err(ItemStateError::FullOfMeseta) + } + self.meseta.0 += amount; + Ok(()) + } + + pub fn remove_meseta(&mut self, amount: u32) -> Result<(), ItemStateError> { + if amount > self.meseta.0 { + return Err(ItemStateError::InvalidMesetaRemoval(amount)) + } + self.meseta.0 -= amount; + Ok(()) + } + + pub fn equip(&mut self, item_id: &ClientItemId, equip_slot: u8) { + for item in &self.inventory.0 { + if let InventoryItemDetail::Individual(inventory_item) = &item.item { + if item.item_id == *item_id { + match inventory_item.item { + ItemDetail::Weapon(_) => self.equipped.weapon = Some(inventory_item.entity_id), + ItemDetail::Armor(_) => self.equipped.armor = Some(inventory_item.entity_id), + ItemDetail::Shield(_) => self.equipped.shield = Some(inventory_item.entity_id), + ItemDetail::Unit(_) => { + if let Some(unit) = self.equipped.unit.get_mut(equip_slot as usize) { + *unit = Some(inventory_item.entity_id) + } + } + ItemDetail::Mag(_) => self.equipped.mag = Some(inventory_item.entity_id), + _ => {} + } + } + } + } + } + + pub fn unequip(&mut self, item_id: &ClientItemId) { + for item in &self.inventory.0 { + if let InventoryItemDetail::Individual(inventory_item) = &item.item { + if item.item_id == *item_id { + match inventory_item.item { + ItemDetail::Weapon(_) => self.equipped.weapon = None, + ItemDetail::Armor(_) => { + self.equipped.armor = None; + self.equipped.unit = [None; 4]; + } + ItemDetail::Shield(_) => self.equipped.shield = None, + ItemDetail::Unit(_) => { + for unit in self.equipped.unit.iter_mut() { + if *unit == Some(inventory_item.entity_id) { + *unit = None + } + } + } + ItemDetail::Mag(_) => self.equipped.mag = Some(inventory_item.entity_id), + _ => {} + } + } + } + } + } + pub fn as_inventory_entity(&self, _character_id: &CharacterEntityId) -> InventoryEntity { InventoryEntity { items: self.inventory.0.iter() @@ -657,29 +728,10 @@ impl InventoryState { } } - pub fn add_meseta(&mut self, amount: u32) -> Result<(), ItemStateError> { - if self.meseta.0 == 999999 { - return Err(ItemStateError::FullOfMeseta) - } - self.meseta.0 = std::cmp::min(self.meseta.0 + amount, 999999); - Ok(()) - } - - pub fn add_meseta_no_overflow(&mut self, amount: u32) -> Result<(), ItemStateError> { - if self.meseta.0 + amount > 999999 { - return Err(ItemStateError::FullOfMeseta) - } - self.meseta.0 += amount; - Ok(()) + pub fn as_equipped_entity(&self) -> EquippedEntity { + self.equipped.clone() } - pub fn remove_meseta(&mut self, amount: u32) -> Result<(), ItemStateError> { - if amount > self.meseta.0 { - return Err(ItemStateError::InvalidMesetaRemoval(amount)) - } - self.meseta.0 -= amount; - Ok(()) - } pub fn as_client_inventory_items(&self) -> [character::InventoryItem; 30] { self.inventory.0.iter() diff --git a/src/ship/packet/handler/message.rs b/src/ship/packet/handler/message.rs index fbdd998..9686afb 100644 --- a/src/ship/packet/handler/message.rs +++ b/src/ship/packet/handler/message.rs @@ -8,7 +8,7 @@ use crate::ship::location::{ClientLocation, ClientLocationError}; use crate::ship::items::{ItemManager, ClientItemId}; use crate::ship::packet::builder; use crate::ship::items::state::ItemState; -use crate::ship::items::actions::{drop_item, drop_partial_item, drop_meseta}; +use crate::ship::items::actions::{drop_item, drop_partial_item, drop_meseta, equip_item, unequip_item}; pub async fn request_exp(id: ClientId, request_exp: &RequestExp, @@ -341,7 +341,7 @@ pub async fn player_equips_item(id: ClientId, pkt: &PlayerEquipItem, entity_gateway: &mut EG, clients: &Clients, - item_manager: &mut ItemManager) + item_state: &mut ItemState) -> Result + Send>, anyhow::Error> where EG: EntityGateway @@ -353,7 +353,7 @@ where else { 0 }; - item_manager.player_equips_item(entity_gateway, &client.character, ClientItemId(pkt.item_id), equip_slot).await?; + equip_item(item_state, entity_gateway, &client.character, &ClientItemId(pkt.item_id), equip_slot).await?; Ok(Box::new(None.into_iter())) // TODO: tell other players you equipped an item } @@ -361,13 +361,13 @@ pub async fn player_unequips_item(id: ClientId, pkt: &PlayerUnequipItem, entity_gateway: &mut EG, clients: &Clients, - item_manager: &mut ItemManager) + item_state: &mut ItemState) -> Result + Send>, anyhow::Error> where EG: EntityGateway { let client = clients.get(&id).ok_or(ShipError::ClientNotFound(id))?; - item_manager.player_unequips_item(entity_gateway, &client.character, ClientItemId(pkt.item_id)).await?; + unequip_item(item_state, entity_gateway, &client.character, &ClientItemId(pkt.item_id)).await?; Ok(Box::new(None.into_iter())) // TODO: tell other players if you unequip an item } diff --git a/src/ship/ship.rs b/src/ship/ship.rs index 1584074..6b1c356 100644 --- a/src/ship/ship.rs +++ b/src/ship/ship.rs @@ -514,10 +514,10 @@ impl ShipServerState { handler::message::player_feed_mag(id, player_feed_mag, &mut self.entity_gateway, &block.client_location, &self.clients, &mut self.item_manager).await? }, GameMessage::PlayerEquipItem(player_equip_item) => { - handler::message::player_equips_item(id, player_equip_item, &mut self.entity_gateway, &self.clients, &mut self.item_manager).await? + handler::message::player_equips_item(id, player_equip_item, &mut self.entity_gateway, &self.clients, &mut self.item_state).await? }, GameMessage::PlayerUnequipItem(player_unequip_item) => { - handler::message::player_unequips_item(id, player_unequip_item, &mut self.entity_gateway, &self.clients, &mut self.item_manager).await? + handler::message::player_unequips_item(id, player_unequip_item, &mut self.entity_gateway, &self.clients, &mut self.item_state).await? }, GameMessage::SortItems(sort_items) => { handler::message::player_sorts_items(id, sort_items, &mut self.entity_gateway, &self.clients, &mut self.item_manager).await? -- 2.36.0 From b473af76808d5419d95691be1577f264f24512f4 Mon Sep 17 00:00:00 2001 From: jake Date: Sun, 15 May 2022 00:55:09 -0600 Subject: [PATCH 36/60] I am dumb --- src/ship/items/actions.rs | 3 ++- tests/test_item_actions.rs | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/ship/items/actions.rs b/src/ship/items/actions.rs index 2b3f815..4decfa8 100644 --- a/src/ship/items/actions.rs +++ b/src/ship/items/actions.rs @@ -494,6 +494,7 @@ fn equip_inventory_item(character_id: CharacterEntityId, item_id: ClientItemId, let mut inventory = item_state.inventory(&character_id)?; inventory.equip(&item_id, equip_slot); transaction.gateway().set_character_equips(&character_id, &inventory.as_equipped_entity()).await?; + item_state.set_inventory(inventory); Ok(((item_state, transaction), ())) }) @@ -522,7 +523,6 @@ where } - fn unequip_inventory_item(character_id: CharacterEntityId, item_id: ClientItemId) -> impl for<'a> Fn((ItemStateProxy<'a>, Box), ()) -> Pin, Box), ()), ItemStateError>> + Send + 'a>> @@ -532,6 +532,7 @@ fn unequip_inventory_item(character_id: CharacterEntityId, item_id: ClientItemId let mut inventory = item_state.inventory(&character_id)?; inventory.unequip(&item_id); transaction.gateway().set_character_equips(&character_id, &inventory.as_equipped_entity()).await?; + item_state.set_inventory(inventory); Ok(((item_state, transaction), ())) }) diff --git a/tests/test_item_actions.rs b/tests/test_item_actions.rs index ab91b2f..ec216da 100644 --- a/tests/test_item_actions.rs +++ b/tests/test_item_actions.rs @@ -47,8 +47,8 @@ async fn test_equip_unit_from_equip_menu() { }).await.unwrap()); let equipped = item::EquippedEntity { - weapon: Some(p1_inv[0].id), - armor: None, + weapon: None, + armor: Some(p1_inv[0].id), shield: None, unit: [None; 4], mag: None, -- 2.36.0 From 95195d07e408369e91e7bb7c55ad7076106e1415 Mon Sep 17 00:00:00 2001 From: jake Date: Sun, 15 May 2022 18:59:49 -0600 Subject: [PATCH 37/60] sort inventory --- src/ship/items/actions.rs | 38 ++++++++++++++++++++++++++++++ src/ship/items/state.rs | 15 ++++++++++++ src/ship/packet/handler/message.rs | 17 ++++++++++--- src/ship/ship.rs | 2 +- 4 files changed, 68 insertions(+), 4 deletions(-) diff --git a/src/ship/items/actions.rs b/src/ship/items/actions.rs index 4decfa8..4055a5c 100644 --- a/src/ship/items/actions.rs +++ b/src/ship/items/actions.rs @@ -559,3 +559,41 @@ where }).await } +fn sort_inventory_items(character_id: CharacterEntityId, item_ids: Vec) + -> impl for<'a> Fn((ItemStateProxy<'a>, Box), ()) + -> Pin, Box), ()), ItemStateError>> + Send + 'a>> +{ + move |(mut item_state, mut transaction), _| { + let item_ids = item_ids.clone(); + Box::pin(async move { + let mut inventory = item_state.inventory(&character_id)?; + inventory.sort(&item_ids); + transaction.gateway().set_character_inventory(&character_id, &inventory.as_inventory_entity(&character_id)).await?; + item_state.set_inventory(inventory); + + Ok(((item_state, transaction), ())) + }) + } +} + + + +pub async fn sort_inventory<'a, EG> ( + item_state: &'a mut ItemState, + entity_gateway: &mut EG, + character: &CharacterEntity, + item_ids: Vec, +) -> Result<(), ItemStateError> +where + EG: EntityGateway, +{ + entity_gateway.with_transaction(|transaction| async move { + let item_state_proxy = ItemStateProxy::new(item_state); + let ((item_state_proxy, transaction), result) = ItemStateAction::default() + .act(sort_inventory_items(character.id, item_ids)) + .commit((item_state_proxy, transaction)) + .await?; + item_state_proxy.commit(); + Ok((transaction, result)) + }).await +} diff --git a/src/ship/items/state.rs b/src/ship/items/state.rs index 4905c05..0284b2a 100644 --- a/src/ship/items/state.rs +++ b/src/ship/items/state.rs @@ -701,6 +701,21 @@ impl InventoryState { } } + pub fn sort(&mut self, item_ids: &Vec) { + self.inventory.0.sort_by(|a, b| { + let a_index = item_ids.iter().position(|item_id| *item_id == a.item_id); + let b_index = item_ids.iter().position(|item_id| *item_id == b.item_id); + + match (a_index, b_index) { + (Some(a_index), Some(b_index)) => { + dbg!("sort!", a.item_id, a_index, b.item_id, b_index); + a_index.cmp(&b_index) + }, + _ => Ordering::Equal + } + }); + } + pub fn as_inventory_entity(&self, _character_id: &CharacterEntityId) -> InventoryEntity { InventoryEntity { items: self.inventory.0.iter() diff --git a/src/ship/packet/handler/message.rs b/src/ship/packet/handler/message.rs index 9686afb..875ce66 100644 --- a/src/ship/packet/handler/message.rs +++ b/src/ship/packet/handler/message.rs @@ -8,7 +8,7 @@ use crate::ship::location::{ClientLocation, ClientLocationError}; use crate::ship::items::{ItemManager, ClientItemId}; use crate::ship::packet::builder; use crate::ship::items::state::ItemState; -use crate::ship::items::actions::{drop_item, drop_partial_item, drop_meseta, equip_item, unequip_item}; +use crate::ship::items::actions::{drop_item, drop_partial_item, drop_meseta, equip_item, unequip_item, sort_inventory}; pub async fn request_exp(id: ClientId, request_exp: &RequestExp, @@ -375,13 +375,24 @@ pub async fn player_sorts_items(id: ClientId, pkt: &SortItems, entity_gateway: &mut EG, clients: &Clients, - item_manager: &mut ItemManager) + item_state: &mut ItemState) -> Result + Send>, anyhow::Error> where EG: EntityGateway { let client = clients.get(&id).ok_or(ShipError::ClientNotFound(id))?; - item_manager.player_sorts_items(entity_gateway, &client.character, pkt.item_ids).await?; + let item_ids = pkt.item_ids + .iter() + .filter_map(|item_id| { + if *item_id != 0 { + Some(ClientItemId(*item_id)) + } + else { + None + } + }) + .collect(); + sort_inventory(item_state, entity_gateway, &client.character, item_ids).await?; Ok(Box::new(None.into_iter())) // TODO: clients probably care about each others item orders } diff --git a/src/ship/ship.rs b/src/ship/ship.rs index 6b1c356..3ea9a53 100644 --- a/src/ship/ship.rs +++ b/src/ship/ship.rs @@ -520,7 +520,7 @@ impl ShipServerState { handler::message::player_unequips_item(id, player_unequip_item, &mut self.entity_gateway, &self.clients, &mut self.item_state).await? }, GameMessage::SortItems(sort_items) => { - handler::message::player_sorts_items(id, sort_items, &mut self.entity_gateway, &self.clients, &mut self.item_manager).await? + handler::message::player_sorts_items(id, sort_items, &mut self.entity_gateway, &self.clients, &mut self.item_state).await? }, GameMessage::PlayerSoldItem(player_sold_item) => { handler::message::player_sells_item(id, player_sold_item, &mut self.entity_gateway, &mut self.clients, &mut self.item_manager).await? -- 2.36.0 From 6306120692bf8607db1e00d6aefcad7cc9badf8e Mon Sep 17 00:00:00 2001 From: jake Date: Sun, 15 May 2022 19:59:38 -0600 Subject: [PATCH 38/60] spacing --- src/ship/items/actions.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/ship/items/actions.rs b/src/ship/items/actions.rs index 4055a5c..85e8f34 100644 --- a/src/ship/items/actions.rs +++ b/src/ship/items/actions.rs @@ -559,6 +559,7 @@ where }).await } + fn sort_inventory_items(character_id: CharacterEntityId, item_ids: Vec) -> impl for<'a> Fn((ItemStateProxy<'a>, Box), ()) -> Pin, Box), ()), ItemStateError>> + Send + 'a>> @@ -576,8 +577,6 @@ fn sort_inventory_items(character_id: CharacterEntityId, item_ids: Vec ( item_state: &'a mut ItemState, entity_gateway: &mut EG, -- 2.36.0 From 657dabca27fc2ba4bbae8dd8ea560d019280fc27 Mon Sep 17 00:00:00 2001 From: jake Date: Thu, 19 May 2022 22:22:54 -0600 Subject: [PATCH 39/60] fix item pickup tests --- src/ship/items/actions.rs | 3 ++- src/ship/items/state.rs | 20 +++++++++----------- tests/test_item_pickup.rs | 2 +- 3 files changed, 12 insertions(+), 13 deletions(-) diff --git a/src/ship/items/actions.rs b/src/ship/items/actions.rs index 85e8f34..51e644c 100644 --- a/src/ship/items/actions.rs +++ b/src/ship/items/actions.rs @@ -63,6 +63,7 @@ fn add_floor_item_to_inventory(character: &CharacterEntity) let add_result = inventory.add_floor_item(floor_item)?; transaction.gateway().set_character_inventory(&character.id, &inventory.as_inventory_entity(&character.id)).await?; + transaction.gateway().set_character_meseta(&character_id, inventory.meseta).await?; item_state.set_inventory(inventory); Ok(((item_state, transaction), @@ -158,7 +159,7 @@ where entity_gateway.with_transaction(|transaction| async move { let item_state_proxy = ItemStateProxy::new(item_state); let ((item_state_proxy, transaction), result) = ItemStateAction::default() - .act(take_item_from_inventory(character.id, *item_id, 1)) + .act(take_item_from_inventory(character.id, *item_id, 0)) .act(add_inventory_item_to_shared_floor(character.id, map_area, drop_position)) .commit((item_state_proxy, transaction)) .await?; diff --git a/src/ship/items/state.rs b/src/ship/items/state.rs index 0284b2a..92f7a44 100644 --- a/src/ship/items/state.rs +++ b/src/ship/items/state.rs @@ -358,7 +358,7 @@ impl BankItem { } } -#[derive(Clone)] +#[derive(Debug, Clone)] pub enum FloorItemDetail { Individual(IndividualItemDetail), Stacked(StackedItemDetail), @@ -374,7 +374,7 @@ impl FloorItemDetail { } } -#[derive(Clone)] +#[derive(Debug, Clone)] pub struct FloorItem { pub item_id: ClientItemId, pub item: FloorItemDetail, @@ -465,12 +465,10 @@ pub enum AddItemResult { Meseta, } -#[derive(Clone, Default)] +#[derive(Debug, Clone, Default)] pub struct LocalFloor(Vec); -#[derive(Clone, Default)] +#[derive(Debug, Clone, Default)] pub struct SharedFloor(Vec); -#[derive(Clone)] -pub struct RoomFloorItems(Vec); #[derive(Clone)] pub struct InventoryState { @@ -549,7 +547,7 @@ impl InventoryState { Err(InventoryError::MesetaFull) } else { - self.meseta.0 = std::cmp::min(self.meseta.0 + meseta.0 ,999999); + self.meseta.0 = std::cmp::min(self.meseta.0 + meseta.0, 999999); Ok(AddItemResult::Meseta) } }, @@ -607,7 +605,7 @@ impl InventoryState { Some(self.inventory.0.remove(idx)) }, InventoryItemDetail::Stacked(stacked_item) => { - let remove_all = match stacked_item.entity_ids.len().cmp(&(amount as usize)) { + let remove_all = (amount == 0) || match stacked_item.entity_ids.len().cmp(&(amount as usize)) { Ordering::Equal => true, Ordering::Greater => false, Ordering::Less => return None, @@ -708,7 +706,6 @@ impl InventoryState { match (a_index, b_index) { (Some(a_index), Some(b_index)) => { - dbg!("sort!", a.item_id, a_index, b.item_id, b_index); a_index.cmp(&b_index) }, _ => Ordering::Equal @@ -1025,6 +1022,7 @@ impl std::cmp::Ord for BankItem { } } +#[derive(Debug)] pub struct FloorState { character_id: CharacterEntityId, local: LocalFloor, @@ -1123,7 +1121,7 @@ impl ItemState { Ok(match item { InventoryItemEntity::Individual(item) => { InventoryItem { - item_id: self.new_item_id()?, + item_id: ClientItemId(0), item: InventoryItemDetail::Individual(IndividualItemDetail { entity_id: item.id, item: item.item, @@ -1132,7 +1130,7 @@ impl ItemState { }, InventoryItemEntity::Stacked(items) => { InventoryItem { - item_id: self.new_item_id()?, + item_id: ClientItemId(0), item: InventoryItemDetail::Stacked(StackedItemDetail { entity_ids: items.iter().map(|i| i.id).collect(), tool: items.get(0) diff --git a/tests/test_item_pickup.rs b/tests/test_item_pickup.rs index 9520527..b2d2ce3 100644 --- a/tests/test_item_pickup.rs +++ b/tests/test_item_pickup.rs @@ -734,7 +734,7 @@ async fn test_player_drops_partial_stack_and_other_player_picks_it_up() { ship.handle(ClientId(2), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::PickupItem(PickupItem { client: 0, target: 0, - item_id: 0x00810001, + item_id: 0x10003, map_area: 0, unknown: [0; 3] })))).await.unwrap().for_each(drop); -- 2.36.0 From 4e88f684c552fd03da0db2058034c61587f01ff8 Mon Sep 17 00:00:00 2001 From: jake Date: Fri, 27 May 2022 01:38:49 -0600 Subject: [PATCH 40/60] bare minimum item usage stuff --- src/ship/items/actions.rs | 52 ++++++- src/ship/items/apply_item.rs | 219 +++++++++++++++++++++++++++++ src/ship/items/mod.rs | 1 + src/ship/items/state.rs | 6 + src/ship/packet/handler/message.rs | 10 +- src/ship/ship.rs | 2 +- tests/test_item_use.rs | 6 +- 7 files changed, 287 insertions(+), 9 deletions(-) create mode 100644 src/ship/items/apply_item.rs diff --git a/src/ship/items/actions.rs b/src/ship/items/actions.rs index 51e644c..5fdd398 100644 --- a/src/ship/items/actions.rs +++ b/src/ship/items/actions.rs @@ -7,7 +7,10 @@ use crate::ship::map::MapArea; use crate::entity::character::{CharacterEntity, CharacterEntityId}; use crate::entity::gateway::{EntityGateway, EntityGatewayTransaction}; use crate::ship::items::state::{ItemState, ItemStateProxy, ItemStateAction, ItemAction, ItemStateError, FloorItem, InventoryItem, AddItemResult, FloorItemDetail, - StackedItemDetail, BankItem, BankItemDetail, InventoryItemDetail}; + StackedItemDetail, BankItem, BankItemDetail, InventoryItemDetail, IndividualItemDetail}; +use crate::ship::items::apply_item::apply_item; +use crate::entity::item::ItemDetail; +use crate::entity::item::tool::Tool; @@ -597,3 +600,50 @@ where Ok((transaction, result)) }).await } + + +fn use_consumed_item(character: CharacterEntity) + -> impl for<'a> Fn((ItemStateProxy<'a>, Box), InventoryItem) + -> Pin, Box), CharacterEntity), ItemStateError>> + Send + 'a>> +{ + move |(mut item_state, mut transaction), inventory_item| { + let mut character = character.clone(); + Box::pin(async move { + let mut transaction = inventory_item.with_entity_id(Ok(transaction), |mut transaction: Result<_, ItemStateError>, entity_id| { + async move { + if let Ok(transaction) = &mut transaction { + transaction.gateway().add_item_note(&entity_id, ItemNote::Consumed).await?; + } + transaction + }}).await?; + + apply_item(&mut item_state, transaction.gateway(), &mut character, inventory_item).await?; + + Ok(((item_state, transaction), character)) + }) + } +} + +pub async fn use_item<'a, EG> ( + item_state: &'a mut ItemState, + entity_gateway: &mut EG, + character: &mut CharacterEntity, + item_id: &ClientItemId, + amount: u32, +) -> Result<(), ItemStateError> +where + EG: EntityGateway, +{ + entity_gateway.with_transaction(|transaction| async move { + let item_state_proxy = ItemStateProxy::new(item_state); + let ((item_state_proxy, transaction), new_character) = ItemStateAction::default() + //.act(consume_inventory_tool(character.id, *item_id, 1)) + .act(take_item_from_inventory(character.id, *item_id, amount)) + .act(use_consumed_item(character.clone())) + .commit((item_state_proxy, transaction)) + .await?; + item_state_proxy.commit(); + *character = new_character; + Ok((transaction, ())) + }).await +} diff --git a/src/ship/items/apply_item.rs b/src/ship/items/apply_item.rs new file mode 100644 index 0000000..90a1211 --- /dev/null +++ b/src/ship/items/apply_item.rs @@ -0,0 +1,219 @@ +use thiserror::Error; +use crate::entity::gateway::{EntityGateway, GatewayError}; +use crate::entity::character::CharacterEntity; +use crate::entity::item::mag::MagCell; +use crate::entity::item::tool::ToolType; +use crate::entity::item::ItemDetail; +use crate::ship::items::state::{ItemStateProxy, InventoryState, InventoryItem, InventoryItemDetail}; + + +#[derive(Error, Debug)] +pub enum ApplyItemError { + #[error("no character")] + NoCharacter, + #[error("item not equipped")] + ItemNotEquipped, + #[error("invalid item")] + InvalidItem, + #[error("gateway error {0}")] + GatewayError(#[from] GatewayError), +} + +// TODO: make all these functions not-pub +pub async fn power_material(entity_gateway: &mut EG, character: &mut CharacterEntity) -> Result<(), ApplyItemError> { + character.materials.power += 1; + entity_gateway.save_character(character).await?; + Ok(()) +} + +pub async fn mind_material(entity_gateway: &mut EG, character: &mut CharacterEntity) -> Result<(), ApplyItemError> { + character.materials.mind += 1; + entity_gateway.save_character(character).await.unwrap(); + Ok(()) +} + +pub async fn evade_material(entity_gateway: &mut EG, character: &mut CharacterEntity) -> Result<(), ApplyItemError> { + character.materials.evade += 1; + entity_gateway.save_character(character).await.unwrap(); + Ok(()) +} + +pub async fn def_material(entity_gateway: &mut EG, character: &mut CharacterEntity) -> Result<(), ApplyItemError> { + character.materials.def += 1; + entity_gateway.save_character(character).await.unwrap(); + Ok(()) +} + +pub async fn luck_material(entity_gateway: &mut EG, character: &mut CharacterEntity) -> Result<(), ApplyItemError> { + character.materials.luck += 1; + entity_gateway.save_character(character).await.unwrap(); + Ok(()) +} + +pub async fn hp_material(entity_gateway: &mut EG, character: &mut CharacterEntity) -> Result<(), ApplyItemError> { + character.materials.hp += 1; + entity_gateway.save_character(character).await.unwrap(); + Ok(()) +} + +pub async fn tp_material(entity_gateway: &mut EG, character: &mut CharacterEntity) -> Result<(), ApplyItemError> { + character.materials.tp += 1; + entity_gateway.save_character(character).await.unwrap(); + Ok(()) +} + +/* +async fn mag_cell(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory, mag_cell_type: MagCell) -> Result<(), ApplyItemError> { + let mut mag_handle = inventory.get_equipped_mag_handle().ok_or(ApplyItemError::ItemNotEquipped)?; + let mag_item = mag_handle.item_mut() + .ok_or(ApplyItemError::InvalidItem)?; + let actual_mag = mag_item + .individual_mut() + .ok_or(ApplyItemError::InvalidItem)? + .mag_mut() + .ok_or(ApplyItemError::InvalidItem)?; + actual_mag.apply_mag_cell(mag_cell_type); + for mag_entity_id in mag_item.entity_ids() { + for cell_entity_id in used_cell.entity_ids() { + entity_gateway.use_mag_cell(&mag_entity_id, &cell_entity_id).await.unwrap(); + } + } + + Ok(()) +} + +pub async fn cell_of_mag_502(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), ApplyItemError> { + mag_cell(entity_gateway, used_cell, inventory, MagCell::CellOfMag502).await +} + +pub async fn cell_of_mag_213(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), ApplyItemError> { + mag_cell(entity_gateway, used_cell, inventory, MagCell::CellOfMag213).await +} + +pub async fn parts_of_robochao(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), ApplyItemError> { + mag_cell(entity_gateway, used_cell, inventory, MagCell::PartsOfRobochao).await +} + +pub async fn heart_of_opaopa(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), ApplyItemError> { + mag_cell(entity_gateway, used_cell, inventory, MagCell::HeartOfOpaOpa).await +} + +pub async fn heart_of_pian(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), ApplyItemError> { + mag_cell(entity_gateway, used_cell, inventory, MagCell::HeartOfPian).await +} + +pub async fn heart_of_chao(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), ApplyItemError> { + mag_cell(entity_gateway, used_cell, inventory, MagCell::HeartOfChao).await +} + +pub async fn heart_of_angel(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), ApplyItemError> { + mag_cell(entity_gateway, used_cell, inventory, MagCell::HeartOfAngel).await +} + +pub async fn kit_of_hamburger(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), ApplyItemError> { + mag_cell(entity_gateway, used_cell, inventory, MagCell::KitOfHamburger).await +} + +pub async fn panthers_spirit(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), ApplyItemError> { + mag_cell(entity_gateway, used_cell, inventory, MagCell::PanthersSpirit).await +} + +pub async fn kit_of_mark3(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), ApplyItemError> { + mag_cell(entity_gateway, used_cell, inventory, MagCell::KitOfMark3).await +} + +pub async fn kit_of_master_system(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), ApplyItemError> { + mag_cell(entity_gateway, used_cell, inventory, MagCell::KitOfMasterSystem).await +} + +pub async fn kit_of_genesis(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), ApplyItemError> { + mag_cell(entity_gateway, used_cell, inventory, MagCell::KitOfGenesis).await +} + +pub async fn kit_of_sega_saturn(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), ApplyItemError> { + mag_cell(entity_gateway, used_cell, inventory, MagCell::KitOfSegaSaturn).await +} + +pub async fn kit_of_dreamcast(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), ApplyItemError> { + mag_cell(entity_gateway, used_cell, inventory, MagCell::KitOfDreamcast).await +} + +pub async fn tablet(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), ApplyItemError> { + mag_cell(entity_gateway, used_cell, inventory, MagCell::Tablet).await +} + +pub async fn dragon_scale(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), ApplyItemError> { + mag_cell(entity_gateway, used_cell, inventory, MagCell::DragonScale).await +} + +pub async fn heaven_striker_coat(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), ApplyItemError> { + mag_cell(entity_gateway, used_cell, inventory, MagCell::HeavenStrikerCoat).await +} + +pub async fn pioneer_parts(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), ApplyItemError> { + mag_cell(entity_gateway, used_cell, inventory, MagCell::PioneerParts).await +} + +pub async fn amities_memo(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), ApplyItemError> { + mag_cell(entity_gateway, used_cell, inventory, MagCell::AmitiesMemo).await +} + +pub async fn heart_of_morolian(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), ApplyItemError> { + mag_cell(entity_gateway, used_cell, inventory, MagCell::HeartOfMorolian).await +} + +pub async fn rappys_beak(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), ApplyItemError> { + mag_cell(entity_gateway, used_cell, inventory, MagCell::RappysBeak).await +} + +pub async fn yahoos_engine(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), ApplyItemError> { + mag_cell(entity_gateway, used_cell, inventory, MagCell::YahoosEngine).await +} + +pub async fn d_photon_core(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), ApplyItemError> { + mag_cell(entity_gateway, used_cell, inventory, MagCell::DPhotonCore).await +} + +pub async fn liberta_kit(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), ApplyItemError> { + mag_cell(entity_gateway, used_cell, inventory, MagCell::LibertaKit).await +} +*/ + +async fn apply_tool<'a, EG: EntityGateway + ?Sized>(item_state: &mut ItemStateProxy<'a>, entity_gateway: &mut EG, character: &mut CharacterEntity, tool: ToolType) -> Result<(), ApplyItemError> { + match tool { + ToolType::PowerMaterial => power_material(entity_gateway, character).await, + ToolType::MindMaterial => mind_material(entity_gateway, character).await, + ToolType::EvadeMaterial => evade_material(entity_gateway, character).await, + ToolType::DefMaterial => def_material(entity_gateway, character).await, + ToolType::LuckMaterial => luck_material(entity_gateway, character).await, + ToolType::HpMaterial => hp_material(entity_gateway, character).await, + ToolType::TpMaterial => tp_material(entity_gateway, character).await, + ToolType::Monomate => Ok(()), + ToolType::Dimate => Ok(()), + ToolType::Trimate => Ok(()), + ToolType::Monofluid => Ok(()), + ToolType::Difluid => Ok(()), + ToolType::Trifluid => Ok(()), + ToolType::HuntersReport => Ok(()), + // TODO: rest of these + _ => Err(ApplyItemError::InvalidItem) + } + +} + + +pub async fn apply_item<'a, EG: EntityGateway + ?Sized>(item_state: &mut ItemStateProxy<'a>, entity_gateway: &mut EG, character: &mut CharacterEntity, item: InventoryItem) -> Result<(), ApplyItemError> { + let item_detail = match item.item { + InventoryItemDetail::Individual(individual_item) => { + individual_item.item + }, + InventoryItemDetail::Stacked(stacked_item) => { + ItemDetail::Tool(stacked_item.tool) + }, + }; + + match item_detail { + ItemDetail::Tool(tool) => apply_tool(item_state, entity_gateway, character, tool.tool).await, + _ => Err(ApplyItemError::InvalidItem) + } +} diff --git a/src/ship/items/mod.rs b/src/ship/items/mod.rs index b37db38..a3956ba 100644 --- a/src/ship/items/mod.rs +++ b/src/ship/items/mod.rs @@ -6,6 +6,7 @@ pub mod transaction; pub mod use_tool; pub mod state; pub mod actions; +pub mod apply_item; use serde::{Serialize, Deserialize}; #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, Serialize, Deserialize, derive_more::Display)] diff --git a/src/ship/items/state.rs b/src/ship/items/state.rs index 92f7a44..df226f1 100644 --- a/src/ship/items/state.rs +++ b/src/ship/items/state.rs @@ -24,6 +24,9 @@ pub enum ItemStateError { #[error("floor item {0} not found")] NoFloorItem(ClientItemId), + #[error("expected {0} to be a tool")] + NotATool(ClientItemId), + #[error("bank item {0} not found")] NoBankItem(ClientItemId), @@ -50,6 +53,9 @@ pub enum ItemStateError { #[error("stacked item")] StackedItemError(Vec), + + #[error("apply item {0}")] + ApplyItemError(#[from] crate::ship::items::apply_item::ApplyItemError), } pub enum FloorType { diff --git a/src/ship/packet/handler/message.rs b/src/ship/packet/handler/message.rs index 875ce66..60dd075 100644 --- a/src/ship/packet/handler/message.rs +++ b/src/ship/packet/handler/message.rs @@ -8,7 +8,7 @@ use crate::ship::location::{ClientLocation, ClientLocationError}; use crate::ship::items::{ItemManager, ClientItemId}; use crate::ship::packet::builder; use crate::ship::items::state::ItemState; -use crate::ship::items::actions::{drop_item, drop_partial_item, drop_meseta, equip_item, unequip_item, sort_inventory}; +use crate::ship::items::actions::{drop_item, drop_partial_item, drop_meseta, equip_item, unequip_item, sort_inventory, use_item}; pub async fn request_exp(id: ClientId, request_exp: &RequestExp, @@ -278,20 +278,18 @@ where } } -pub async fn use_item(id: ClientId, +pub async fn player_uses_item(id: ClientId, player_use_tool: &PlayerUseItem, entity_gateway: &mut EG, _client_location: &ClientLocation, clients: &mut Clients, - item_manager: &mut ItemManager) + item_state: &mut ItemState) -> Result + Send>, anyhow::Error> where EG: EntityGateway { let client = clients.get_mut(&id).ok_or(ShipError::ClientNotFound(id))?; - let item_used_type = item_manager.player_consumes_tool(entity_gateway, &mut client.character, ClientItemId(player_use_tool.item_id), 1).await?; - - item_manager.use_item(item_used_type, entity_gateway, &mut client.character).await?; + use_item(item_state, entity_gateway, &mut client.character, &ClientItemId(player_use_tool.item_id), 1).await?; Ok(Box::new(None.into_iter())) // TODO: should probably tell other players we used an item } diff --git a/src/ship/ship.rs b/src/ship/ship.rs index 3ea9a53..9958226 100644 --- a/src/ship/ship.rs +++ b/src/ship/ship.rs @@ -504,7 +504,7 @@ impl ShipServerState { }, GameMessage::PlayerUseItem(player_use_item) => { let block = self.blocks.with_client(id, &self.clients)?; - handler::message::use_item(id, player_use_item, &mut self.entity_gateway, &block.client_location, &mut self.clients, &mut self.item_manager).await? + handler::message::player_uses_item(id, player_use_item, &mut self.entity_gateway, &block.client_location, &mut self.clients, &mut self.item_state).await? }, GameMessage::PlayerUsedMedicalCenter(player_used_medical_center) => { handler::message::player_used_medical_center(id, player_used_medical_center, &mut self.entity_gateway, &mut self.clients, &mut self.item_manager).await? diff --git a/tests/test_item_use.rs b/tests/test_item_use.rs index 9151a0c..199dce7 100644 --- a/tests/test_item_use.rs +++ b/tests/test_item_use.rs @@ -164,7 +164,7 @@ async fn test_use_nonstackable_tool() { item::NewItemEntity { item: item::ItemDetail::Tool( item::tool::Tool { - tool: item::tool::ToolType::MagicStoneIritista, + tool: item::tool::ToolType::HuntersReport, } ), }).await.unwrap()); @@ -251,6 +251,9 @@ async fn test_use_materials() { assert!(char.materials.luck == 2); } +// TODO: tests for ALL ITEMS WOW + +/* #[async_std::test] pub async fn test_learn_new_tech() {} @@ -268,3 +271,4 @@ pub async fn test_char_cannot_learn_high_level_tech() {} #[async_std::test] pub async fn test_android_cannot_learn_tech() {} +*/ -- 2.36.0 From 5be796cb5ca4c30e0372eb728ad6d64c3a82e892 Mon Sep 17 00:00:00 2001 From: jake Date: Mon, 30 May 2022 23:47:58 -0600 Subject: [PATCH 41/60] clean these functions up a bit --- src/ship/items/actions.rs | 84 ++++++++++++++++----------------------- src/ship/items/state.rs | 47 +++++++++++----------- 2 files changed, 58 insertions(+), 73 deletions(-) diff --git a/src/ship/items/actions.rs b/src/ship/items/actions.rs index 5fdd398..f01c7b5 100644 --- a/src/ship/items/actions.rs +++ b/src/ship/items/actions.rs @@ -45,23 +45,19 @@ fn add_floor_item_to_inventory(character: &CharacterEntity) let mut inventory = item_state.inventory(&character.id)?; let character_id = character.id; - let transaction = floor_item.with_entity_id(Ok(transaction), |mut transaction: Result<_, ItemStateError>, entity_id| { + let transaction = floor_item.with_entity_id(transaction, |mut transaction, entity_id| { async move { - if let Ok(transaction) = &mut transaction { - transaction.gateway().add_item_note(&entity_id, ItemNote::Pickup { - character_id - }).await?; - } - transaction + transaction.gateway().add_item_note(&entity_id, ItemNote::Pickup { + character_id + }).await?; + Ok(transaction) }}).await?; - let mut transaction = floor_item.with_mag(Ok(transaction), |mut transaction: Result<_, ItemStateError>, entity_id, _mag| { + let mut transaction = floor_item.with_mag(transaction, |mut transaction, entity_id, _mag| { let character = character.clone(); async move { - if let Ok(transaction) = &mut transaction { - transaction.gateway().change_mag_owner(&entity_id, &character).await?; - } - transaction + transaction.gateway().change_mag_owner(&entity_id, &character).await?; + Ok(transaction) }}).await?; let add_result = inventory.add_floor_item(floor_item)?; @@ -125,18 +121,16 @@ fn add_inventory_item_to_shared_floor(character_id: CharacterEntityId, map_area: { move |(mut item_state, transaction), inventory_item| { Box::pin(async move { - let transaction = inventory_item.with_entity_id(Ok(transaction), |mut transaction: Result<_, ItemStateError>, entity_id| { + let transaction = inventory_item.with_entity_id(transaction, |mut transaction, entity_id| { async move { - if let Ok(transaction) = &mut transaction { - transaction.gateway().add_item_note(&entity_id, ItemNote::PlayerDrop { - character_id, - map_area, - x: drop_position.0, - y: drop_position.1, - z: drop_position.2, - }).await?; - } - transaction + transaction.gateway().add_item_note(&entity_id, ItemNote::PlayerDrop { + character_id, + map_area, + x: drop_position.0, + y: drop_position.1, + z: drop_position.2, + }).await?; + Ok(transaction) }}).await?; let mut floor = item_state.floor(&character_id)?; @@ -376,16 +370,14 @@ fn add_bank_item_to_inventory(character: &CharacterEntity) let mut inventory = item_state.inventory(&character.id)?; let character_id = character.id; - let transaction = bank_item.with_entity_id(Ok(transaction), |mut transaction: Result<_, ItemStateError>, entity_id| { + let transaction = bank_item.with_entity_id(transaction, |mut transaction, entity_id| { let bank_name = bank_name.clone(); async move { - if let Ok(transaction) = &mut transaction { - transaction.gateway().add_item_note(&entity_id, ItemNote::Withdraw { - character_id, - bank: bank_name, - }).await?; - } - transaction + transaction.gateway().add_item_note(&entity_id, ItemNote::Withdraw { + character_id, + bank: bank_name, + }).await?; + Ok(transaction) }}).await?; let inventory_item = InventoryItem { @@ -396,13 +388,11 @@ fn add_bank_item_to_inventory(character: &CharacterEntity) }, }; - let mut transaction = inventory_item.with_mag(Ok(transaction), |mut transaction: Result<_, ItemStateError>, entity_id, _mag| { + let mut transaction = inventory_item.with_mag(transaction, |mut transaction, entity_id, _mag| { let character = character.clone(); async move { - if let Ok(transaction) = &mut transaction { - transaction.gateway().change_mag_owner(&entity_id, &character).await?; - } - transaction + transaction.gateway().change_mag_owner(&entity_id, &character).await?; + Ok(transaction) }}).await?; inventory.add_item(inventory_item.clone())?; @@ -444,16 +434,14 @@ fn add_inventory_item_to_bank(character_id: CharacterEntityId) Box::pin(async move { let mut bank = item_state.bank(&character_id)?; let bank_name = bank.name.clone(); - let mut transaction = inventory_item.with_entity_id(Ok(transaction), move |mut transaction: Result<_, ItemStateError>, entity_id| { + let mut transaction = inventory_item.with_entity_id(transaction, move |mut transaction, entity_id| { let bank_name = bank_name.clone(); async move { - if let Ok(transaction) = &mut transaction { - transaction.gateway().add_item_note(&entity_id, ItemNote::Deposit { - character_id, - bank: bank_name, - }).await?; - } - transaction + transaction.gateway().add_item_note(&entity_id, ItemNote::Deposit { + character_id, + bank: bank_name, + }).await?; + Ok(transaction) }}).await?; bank.add_inventory_item(inventory_item)?; @@ -609,12 +597,10 @@ fn use_consumed_item(character: CharacterEntity) move |(mut item_state, mut transaction), inventory_item| { let mut character = character.clone(); Box::pin(async move { - let mut transaction = inventory_item.with_entity_id(Ok(transaction), |mut transaction: Result<_, ItemStateError>, entity_id| { + let mut transaction = inventory_item.with_entity_id(transaction, |mut transaction, entity_id| { async move { - if let Ok(transaction) = &mut transaction { - transaction.gateway().add_item_note(&entity_id, ItemNote::Consumed).await?; - } - transaction + transaction.gateway().add_item_note(&entity_id, ItemNote::Consumed).await?; + Ok(transaction) }}).await?; apply_item(&mut item_state, transaction.gateway(), &mut character, inventory_item).await?; diff --git a/src/ship/items/state.rs b/src/ship/items/state.rs index df226f1..f65a6d9 100644 --- a/src/ship/items/state.rs +++ b/src/ship/items/state.rs @@ -268,36 +268,36 @@ pub struct InventoryItem { } impl InventoryItem { - pub async fn with_entity_id(&self, mut param: T, mut func: F) -> T + pub async fn with_entity_id(&self, mut param: T, mut func: F) -> Result where F: FnMut(T, ItemEntityId) -> Fut, - Fut: Future, + Fut: Future>, { match &self.item { InventoryItemDetail::Individual(individual_item) => { - param = func(param, individual_item.entity_id).await; + param = func(param, individual_item.entity_id).await?; }, InventoryItemDetail::Stacked(stacked_item) => { for entity_id in &stacked_item.entity_ids { - param = func(param, *entity_id).await; + param = func(param, *entity_id).await?; } } } - param + Ok(param) } - pub async fn with_mag(&self, mut param: T, mut func: F) -> T + pub async fn with_mag(&self, mut param: T, mut func: F) -> Result where F: FnMut(T, ItemEntityId, Mag) -> Fut, - Fut: Future, + Fut: Future>, { if let InventoryItemDetail::Individual(individual_item) = &self.item { if let ItemDetail::Mag(mag) = &individual_item.item { - param = func(param, individual_item.entity_id, mag.clone()).await; + param = func(param, individual_item.entity_id, mag.clone()).await?; } } - param + Ok(param) } } @@ -344,23 +344,22 @@ pub struct BankItem { } impl BankItem { - pub async fn with_entity_id(&self, mut param: T, mut func: F) -> T + pub async fn with_entity_id(&self, mut param: T, mut func: F) -> Result where F: FnMut(T, ItemEntityId) -> Fut, - Fut: Future, + Fut: Future>, { match &self.item { BankItemDetail::Individual(individual_item) => { - param = func(param, individual_item.entity_id).await; + param = func(param, individual_item.entity_id).await?; }, BankItemDetail::Stacked(stacked_item) => { for entity_id in &stacked_item.entity_ids { - param = func(param, *entity_id).await; + param = func(param, *entity_id).await?; } } } - - param + Ok(param) } } @@ -391,37 +390,37 @@ pub struct FloorItem { } impl FloorItem { - pub async fn with_entity_id(&self, mut param: T, mut func: F) -> T + pub async fn with_entity_id(&self, mut param: T, mut func: F) -> Result where F: FnMut(T, ItemEntityId) -> Fut, - Fut: Future, + Fut: Future>, { match &self.item { FloorItemDetail::Individual(individual_item) => { - param = func(param, individual_item.entity_id).await; + param = func(param, individual_item.entity_id).await?; }, FloorItemDetail::Stacked(stacked_item) => { for entity_id in &stacked_item.entity_ids { - param = func(param, *entity_id).await; + param = func(param, *entity_id).await?; } }, FloorItemDetail::Meseta(_meseta) => {}, } - param + Ok(param) } - pub async fn with_mag(&self, mut param: T, mut func: F) -> T + pub async fn with_mag(&self, mut param: T, mut func: F) -> Result where F: FnMut(T, ItemEntityId, Mag) -> Fut, - Fut: Future, + Fut: Future>, { if let FloorItemDetail::Individual(individual_item) = &self.item { if let ItemDetail::Mag(mag) = &individual_item.item { - param = func(param, individual_item.entity_id, mag.clone()).await; + param = func(param, individual_item.entity_id, mag.clone()).await?; } } - param + Ok(param) } pub fn as_client_bytes(&self) -> [u8; 16] { -- 2.36.0 From d3d8d766b83a0a7c5957dff8643d5bdc513bc945 Mon Sep 17 00:00:00 2001 From: jake Date: Mon, 20 Jun 2022 15:40:30 -0600 Subject: [PATCH 42/60] magstuff --- src/entity/gateway/inmemory.rs | 2 +- src/entity/gateway/postgres/postgres.rs | 3 +- src/entity/item/mag.rs | 27 +++++-- src/entity/item/mod.rs | 3 +- src/ship/items/actions.rs | 72 ++++++++++++++++- src/ship/items/apply_item.rs | 100 +++++++++++++++++++++--- src/ship/items/state.rs | 69 +++++++++++++++- src/ship/packet/handler/message.rs | 6 +- src/ship/ship.rs | 2 +- 9 files changed, 253 insertions(+), 31 deletions(-) diff --git a/src/entity/gateway/inmemory.rs b/src/entity/gateway/inmemory.rs index 93d10d7..8180fe0 100644 --- a/src/entity/gateway/inmemory.rs +++ b/src/entity/gateway/inmemory.rs @@ -126,7 +126,7 @@ impl InMemoryGateway { mag::MagModifier::MagCell(mag_cell_id) => { if let Some(mag_cell) = items.get(mag_cell_id) { if let ItemDetail::Tool(mag_cell) = mag_cell.item { - mag.apply_mag_cell(mag_cell.tool.try_into().unwrap()) + mag.apply_mag_cell(mag_cell.tool.try_into().unwrap()).unwrap() } } }, diff --git a/src/entity/gateway/postgres/postgres.rs b/src/entity/gateway/postgres/postgres.rs index 266eab2..938c946 100644 --- a/src/entity/gateway/postgres/postgres.rs +++ b/src/entity/gateway/postgres/postgres.rs @@ -65,6 +65,7 @@ impl PostgresGateway { } +// TODO: remove unwraps, return Result async fn apply_item_modifications(conn: &mut sqlx::PgConnection, item: ItemEntity) -> ItemEntity { let ItemEntity {id, item} = item; @@ -108,7 +109,7 @@ async fn apply_item_modifications(conn: &mut sqlx::PgConnection, item: ItemEntit mag.bank() }, mag::MagModifier::MagCell(_) => { - mag.apply_mag_cell(mag::MagCell::try_from(Into::::into(cell.unwrap().0).tool).unwrap()) + mag.apply_mag_cell(mag::MagCell::try_from(Into::::into(cell.unwrap().0).tool).unwrap()).unwrap() }, mag::MagModifier::OwnerChange(class, section_id) => { mag.change_owner(class, section_id) diff --git a/src/entity/item/mag.rs b/src/entity/item/mag.rs index 64efc2e..e55491c 100644 --- a/src/entity/item/mag.rs +++ b/src/entity/item/mag.rs @@ -1,3 +1,4 @@ +use thiserror::Error; use std::collections::HashMap; use serde::{Serialize, Deserialize}; use crate::entity::item::tool::ToolType; @@ -419,9 +420,9 @@ pub enum MagCell { } impl std::convert::TryFrom for MagCell { - type Error = (); + type Error = MagCellError; - fn try_from(tool: ToolType) -> Result { + fn try_from(tool: ToolType) -> Result { match tool { ToolType::CellOfMag502 => Ok(MagCell::CellOfMag502), ToolType::CellOfMag213 => Ok(MagCell::CellOfMag213), @@ -448,7 +449,7 @@ impl std::convert::TryFrom for MagCell { ToolType::YahoosEngine => Ok(MagCell::YahoosEngine), ToolType::DPhotonCore => Ok(MagCell::DPhotonCore), ToolType::LibertaKit => Ok(MagCell::LibertaKit), - _ => Err(()), + _ => Err(MagCellError::IsNotMagCell), } } } @@ -509,6 +510,15 @@ impl MagAttributeOrdering { } } + +#[derive(Error, Debug)] +pub enum MagCellError { + #[error("not a mag cell")] + IsNotMagCell, + #[error("mag is rare")] + IsRareMag, +} + #[derive(Debug, Clone, PartialEq)] pub enum MagModifier { FeedMag{ @@ -1047,7 +1057,10 @@ impl Mag { } // TODO: this needs more checks on validity - pub fn apply_mag_cell(&mut self, mag_cell: MagCell) { + pub fn apply_mag_cell(&mut self, mag_cell: MagCell) -> Result<(), MagCellError> { + if self.is_rare_item() { + return Err(MagCellError::IsRareMag) + } self.mag = match mag_cell { MagCell::CellOfMag502 => { match self.id { @@ -1097,11 +1110,11 @@ impl Mag { MagCell::YahoosEngine => MagType::Yahoo, MagCell::DPhotonCore => MagType::GaelGiel, MagCell::LibertaKit => MagType::Agastya, - } + }; + Ok(()) } - // TODO: is this even needed? mags are not shop sellable...yet - pub fn is_rare_item(self) -> bool { + pub fn is_rare_item(&self) -> bool { matches!( self.mag, MagType::Pitri diff --git a/src/entity/item/mod.rs b/src/entity/item/mod.rs index 203d819..2a8dad4 100644 --- a/src/entity/item/mod.rs +++ b/src/entity/item/mod.rs @@ -46,8 +46,9 @@ pub enum ItemNote { y: f32, z: f32, }, - Consumed, + Consumed, // TODO: character_id FedToMag { + //character_id: CharacterEntityId, mag: ItemEntityId, }, BoughtAtShop { diff --git a/src/ship/items/actions.rs b/src/ship/items/actions.rs index f01c7b5..216c523 100644 --- a/src/ship/items/actions.rs +++ b/src/ship/items/actions.rs @@ -594,7 +594,7 @@ fn use_consumed_item(character: CharacterEntity) -> impl for<'a> Fn((ItemStateProxy<'a>, Box), InventoryItem) -> Pin, Box), CharacterEntity), ItemStateError>> + Send + 'a>> { - move |(mut item_state, mut transaction), inventory_item| { + move |(mut item_state, transaction), inventory_item| { let mut character = character.clone(); Box::pin(async move { let mut transaction = inventory_item.with_entity_id(transaction, |mut transaction, entity_id| { @@ -623,7 +623,6 @@ where entity_gateway.with_transaction(|transaction| async move { let item_state_proxy = ItemStateProxy::new(item_state); let ((item_state_proxy, transaction), new_character) = ItemStateAction::default() - //.act(consume_inventory_tool(character.id, *item_id, 1)) .act(take_item_from_inventory(character.id, *item_id, amount)) .act(use_consumed_item(character.clone())) .commit((item_state_proxy, transaction)) @@ -633,3 +632,72 @@ where Ok((transaction, ())) }).await } + +fn feed_mag_item(character: CharacterEntity, mag_item_id: ClientItemId) + -> impl for<'a> Fn((ItemStateProxy<'a>, Box), InventoryItem) + -> Pin, Box), CharacterEntity), ItemStateError>> + Send + 'a>> +{ + move |(mut item_state, transaction), tool| { + let character = character.clone(); + Box::pin(async move { + let mut inventory = item_state.inventory(&character.id)?; + let mag_entity = inventory.get_by_client_id_mut(&mag_item_id) + .ok_or_else(|| ItemStateError::InvalidItemId(mag_item_id))? + .item + .as_individual_mut() + .ok_or_else(|| ItemStateError::NotAMag(mag_item_id))?; + let mag_entity_id = mag_entity.entity_id; + + let mut transaction = tool.with_entity_id(transaction, |mut transaction, entity_id| { + async move { + transaction.gateway().add_item_note(&entity_id, ItemNote::FedToMag { + //character_id: character.id, + mag: mag_entity_id, + }).await?; + transaction.gateway().feed_mag(&mag_entity_id, &entity_id).await?; + Ok(transaction) + }}).await?; + + let food_tool = tool + .item + .stacked() + .ok_or_else(|| ItemStateError::NotMagFood(tool.item_id))? + .tool + .tool; + + let mag_entity = mag_entity + .as_mag_mut() + .ok_or_else(|| ItemStateError::NotAMag(mag_item_id))?; + + mag_entity.feed(food_tool); + + transaction.gateway().set_character_inventory(&character.id, &inventory.as_inventory_entity(&character.id)).await?; + item_state.set_inventory(inventory); + + Ok(((item_state, transaction), character)) + }) + } +} + + +pub async fn feed_mag<'a, EG> ( + item_state: &'a mut ItemState, + entity_gateway: &mut EG, + character: &CharacterEntity, + mag_item_id: &ClientItemId, + tool_item_id: &ClientItemId, +) -> Result<(), ItemStateError> +where + EG: EntityGateway, +{ + entity_gateway.with_transaction(|transaction| async move { + let item_state_proxy = ItemStateProxy::new(item_state); + let ((item_state_proxy, transaction), _) = ItemStateAction::default() + .act(take_item_from_inventory(character.id, *tool_item_id, 1)) + .act(feed_mag_item(character.clone(), *mag_item_id)) + .commit((item_state_proxy, transaction)) + .await?; + item_state_proxy.commit(); + Ok((transaction, ())) + }).await +} diff --git a/src/ship/items/apply_item.rs b/src/ship/items/apply_item.rs index 90a1211..1028ed7 100644 --- a/src/ship/items/apply_item.rs +++ b/src/ship/items/apply_item.rs @@ -1,10 +1,12 @@ use thiserror::Error; +use std::convert::TryFrom; +use std::convert::TryInto; use crate::entity::gateway::{EntityGateway, GatewayError}; use crate::entity::character::CharacterEntity; -use crate::entity::item::mag::MagCell; +use crate::entity::item::mag::{MagCell, MagCellError}; use crate::entity::item::tool::ToolType; -use crate::entity::item::ItemDetail; -use crate::ship::items::state::{ItemStateProxy, InventoryState, InventoryItem, InventoryItemDetail}; +use crate::entity::item::{ItemDetail, ItemEntityId}; +use crate::ship::items::state::{ItemStateProxy, InventoryState, InventoryItem, InventoryItemDetail, ItemStateError}; #[derive(Error, Debug)] @@ -17,6 +19,18 @@ pub enum ApplyItemError { InvalidItem, #[error("gateway error {0}")] GatewayError(#[from] GatewayError), + + #[error("itemstate error {0}")] + ItemStateError(Box), + + #[error("magcell error {0}")] + MagCellError(#[from] MagCellError), +} + +impl From for ApplyItemError { + fn from(other: ItemStateError) -> ApplyItemError { + ApplyItemError::ItemStateError(Box::new(other)) + } } // TODO: make all these functions not-pub @@ -81,9 +95,34 @@ async fn mag_cell(entity_gateway: &mut EG, used_cell: &Consum Ok(()) } + */ + + +async fn mag_cell<'a, EG>(item_state: &mut ItemStateProxy<'a>, + entity_gateway: &mut EG, + character: &CharacterEntity, + cell_entity_id: ItemEntityId, + mag_cell_type: MagCell) + -> Result<(), ApplyItemError> +where + EG: EntityGateway + ?Sized, +{ + let mut inventory = item_state.inventory(&character.id)?; + + let (mag_entity_id, mag) = inventory.equipped_mag_mut() + .ok_or_else(|| ApplyItemError::ItemNotEquipped)?; + mag.apply_mag_cell(mag_cell_type)?; + entity_gateway.use_mag_cell(&mag_entity_id, &cell_entity_id).await?; + entity_gateway.set_character_inventory(&character.id, &inventory.as_inventory_entity(&character.id)).await?; + item_state.set_inventory(inventory); + + Ok(()) +} + +/* pub async fn cell_of_mag_502(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), ApplyItemError> { - mag_cell(entity_gateway, used_cell, inventory, MagCell::CellOfMag502).await + mag_cell(entity_gateway, inventory, MagCell::CellOfMag502).await } pub async fn cell_of_mag_213(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), ApplyItemError> { @@ -179,7 +218,15 @@ pub async fn liberta_kit(entity_gateway: &mut EG, used_cell: } */ -async fn apply_tool<'a, EG: EntityGateway + ?Sized>(item_state: &mut ItemStateProxy<'a>, entity_gateway: &mut EG, character: &mut CharacterEntity, tool: ToolType) -> Result<(), ApplyItemError> { +async fn apply_tool<'a, EG>(item_state: &mut ItemStateProxy<'a>, + entity_gateway: &mut EG, + character: &mut CharacterEntity, + entity_id: ItemEntityId, + tool: ToolType) + -> Result<(), ApplyItemError> +where + EG: EntityGateway + ?Sized, +{ match tool { ToolType::PowerMaterial => power_material(entity_gateway, character).await, ToolType::MindMaterial => mind_material(entity_gateway, character).await, @@ -195,6 +242,32 @@ async fn apply_tool<'a, EG: EntityGateway + ?Sized>(item_state: &mut ItemStatePr ToolType::Difluid => Ok(()), ToolType::Trifluid => Ok(()), ToolType::HuntersReport => Ok(()), + ToolType::CellOfMag502 + | ToolType::CellOfMag213 + | ToolType::PartsOfRobochao + | ToolType::HeartOfOpaOpa + | ToolType::HeartOfPian + | ToolType::HeartOfChao + | ToolType::HeartOfAngel + | ToolType::KitOfHamburger + | ToolType::PanthersSpirit + | ToolType::KitOfMark3 + | ToolType::KitOfMasterSystem + | ToolType::KitOfGenesis + | ToolType::KitOfSegaSaturn + | ToolType::KitOfDreamcast + | ToolType::Tablet + | ToolType::DragonScale + | ToolType::HeavenStrikerCoat + | ToolType::PioneerParts + | ToolType::AmitiesMemo + | ToolType::HeartOfMorolian + | ToolType::RappysBeak + | ToolType::YahoosEngine + | ToolType::DPhotonCore + | ToolType::LibertaKit => { + mag_cell(item_state, entity_gateway, character, entity_id, tool.try_into()?).await + } // TODO: rest of these _ => Err(ApplyItemError::InvalidItem) } @@ -203,17 +276,18 @@ async fn apply_tool<'a, EG: EntityGateway + ?Sized>(item_state: &mut ItemStatePr pub async fn apply_item<'a, EG: EntityGateway + ?Sized>(item_state: &mut ItemStateProxy<'a>, entity_gateway: &mut EG, character: &mut CharacterEntity, item: InventoryItem) -> Result<(), ApplyItemError> { - let item_detail = match item.item { + match item.item { InventoryItemDetail::Individual(individual_item) => { - individual_item.item + match individual_item.item { + ItemDetail::Tool(tool) => apply_tool(item_state, entity_gateway, character, individual_item.entity_id, tool.tool).await, + _ => Err(ApplyItemError::InvalidItem) + } }, InventoryItemDetail::Stacked(stacked_item) => { - ItemDetail::Tool(stacked_item.tool) + for entity_id in stacked_item.entity_ids { + apply_tool(item_state, entity_gateway, character, entity_id, stacked_item.tool.tool).await? + } + Ok(()) }, - }; - - match item_detail { - ItemDetail::Tool(tool) => apply_tool(item_state, entity_gateway, character, tool.tool).await, - _ => Err(ApplyItemError::InvalidItem) } } diff --git a/src/ship/items/state.rs b/src/ship/items/state.rs index f65a6d9..7c33c3f 100644 --- a/src/ship/items/state.rs +++ b/src/ship/items/state.rs @@ -36,6 +36,9 @@ pub enum ItemStateError { #[error("bank error {0}")] BankError(#[from] BankError), + #[error("invalid item id {0}")] + InvalidItemId(ClientItemId), + #[error("invalid drop? {0:?} (this shouldn't occur)")] BadItemDrop(ItemDrop), @@ -56,6 +59,12 @@ pub enum ItemStateError { #[error("apply item {0}")] ApplyItemError(#[from] crate::ship::items::apply_item::ApplyItemError), + + #[error("item is not a mag {0}")] + NotAMag(ClientItemId), + + #[error("item is not mag food {0}")] + NotMagFood(ClientItemId), } pub enum FloorType { @@ -207,6 +216,22 @@ pub struct IndividualItemDetail { pub item: ItemDetail, } +impl IndividualItemDetail { + pub fn as_mag(&self) -> Option<&Mag> { + match &self.item { + ItemDetail::Mag(mag) => Some(mag), + _ => None + } + } + + pub fn as_mag_mut(&mut self) -> Option<&mut Mag> { + match &mut self.item { + ItemDetail::Mag(mag) => Some(mag), + _ => None + } + } +} + #[derive(Clone, Debug)] pub struct StackedItemDetail { pub entity_ids: Vec, @@ -226,19 +251,35 @@ pub enum InventoryItemDetail { } impl InventoryItemDetail { - fn stacked(&self) -> Option<&StackedItemDetail> { + // TODO: rename as_stacked for consistency + pub fn stacked(&self) -> Option<&StackedItemDetail> { match self { InventoryItemDetail::Stacked(sitem) => Some(sitem), _ => None, } } - fn stacked_mut(&mut self) -> Option<&mut StackedItemDetail> { + // TODO: rename as_stacked_mut for consistency + pub fn stacked_mut(&mut self) -> Option<&mut StackedItemDetail> { match self { InventoryItemDetail::Stacked(sitem) => Some(sitem), _ => None, } } + pub fn as_individual(&self) -> Option<&IndividualItemDetail> { + match self { + InventoryItemDetail::Individual(iitem) => Some(iitem), + _ => None, + } + } + + pub fn as_individual_mut(&mut self) -> Option<&mut IndividualItemDetail> { + match self { + InventoryItemDetail::Individual(iitem) => Some(iitem), + _ => None, + } + } + pub fn as_client_bytes(&self) -> [u8; 16] { match self { InventoryItemDetail::Individual(item) => { @@ -633,6 +674,18 @@ impl InventoryState { } } + pub fn get_by_client_id(&self, item_id: &ClientItemId) -> Option<&InventoryItem> { + self.inventory.0 + .iter() + .find(|i| i.item_id == *item_id) + } + + pub fn get_by_client_id_mut(&mut self, item_id: &ClientItemId) -> Option<&mut InventoryItem> { + self.inventory.0 + .iter_mut() + .find(|i| i.item_id == *item_id) + } + pub fn add_meseta(&mut self, amount: u32) -> Result<(), ItemStateError> { if self.meseta.0 == 999999 { return Err(ItemStateError::FullOfMeseta) @@ -704,6 +757,18 @@ impl InventoryState { } } + pub fn equipped_mag_mut(&mut self) -> Option<(ItemEntityId, &mut Mag)> { + let mag_id = self.equipped.mag?; + self.inventory.0 + .iter_mut() + .filter_map(|i| { + let individual = i.item.as_individual_mut()?; + let entity_id = individual.entity_id; + Some((entity_id, individual.as_mag_mut()?)) + }) + .find(|(entity_id, _)| *entity_id == mag_id) + } + pub fn sort(&mut self, item_ids: &Vec) { self.inventory.0.sort_by(|a, b| { let a_index = item_ids.iter().position(|item_id| *item_id == a.item_id); diff --git a/src/ship/packet/handler/message.rs b/src/ship/packet/handler/message.rs index 60dd075..f25e4cc 100644 --- a/src/ship/packet/handler/message.rs +++ b/src/ship/packet/handler/message.rs @@ -8,7 +8,7 @@ use crate::ship::location::{ClientLocation, ClientLocationError}; use crate::ship::items::{ItemManager, ClientItemId}; use crate::ship::packet::builder; use crate::ship::items::state::ItemState; -use crate::ship::items::actions::{drop_item, drop_partial_item, drop_meseta, equip_item, unequip_item, sort_inventory, use_item}; +use crate::ship::items::actions::{drop_item, drop_partial_item, drop_meseta, equip_item, unequip_item, sort_inventory, use_item, feed_mag}; pub async fn request_exp(id: ClientId, request_exp: &RequestExp, @@ -320,13 +320,13 @@ pub async fn player_feed_mag(id: ClientId, entity_gateway: &mut EG, client_location: &ClientLocation, clients: &Clients, - item_manager: &mut ItemManager) + item_state: &mut ItemState) -> Result + Send>, anyhow::Error> where EG: EntityGateway { let client = clients.get(&id).ok_or(ShipError::ClientNotFound(id))?; - item_manager.player_feeds_mag_item(entity_gateway, &client.character, ClientItemId(mag_feed.mag_id), ClientItemId(mag_feed.item_id)).await?; + feed_mag(item_state, entity_gateway, &client.character, &ClientItemId(mag_feed.mag_id), &ClientItemId(mag_feed.item_id)).await?; let mag_feed = mag_feed.clone(); Ok(Box::new(client_location.get_client_neighbors(id).unwrap().into_iter() diff --git a/src/ship/ship.rs b/src/ship/ship.rs index 9958226..c3e4090 100644 --- a/src/ship/ship.rs +++ b/src/ship/ship.rs @@ -511,7 +511,7 @@ impl ShipServerState { }, GameMessage::PlayerFeedMag(player_feed_mag) => { let block = self.blocks.with_client(id, &self.clients)?; - handler::message::player_feed_mag(id, player_feed_mag, &mut self.entity_gateway, &block.client_location, &self.clients, &mut self.item_manager).await? + handler::message::player_feed_mag(id, player_feed_mag, &mut self.entity_gateway, &block.client_location, &self.clients, &mut self.item_state).await? }, GameMessage::PlayerEquipItem(player_equip_item) => { handler::message::player_equips_item(id, player_equip_item, &mut self.entity_gateway, &self.clients, &mut self.item_state).await? -- 2.36.0 From eb9fdb485bf9934f6d4fca700aee1216b114ebec Mon Sep 17 00:00:00 2001 From: jake Date: Tue, 21 Jun 2022 00:09:52 -0600 Subject: [PATCH 43/60] buy items from shop --- src/ship/items/actions.rs | 88 ++++++++++++++++++++++- src/ship/items/state.rs | 28 ++++++-- src/ship/packet/builder/message.rs | 6 +- src/ship/packet/handler/direct_message.rs | 17 ++--- src/ship/packet/handler/message.rs | 13 ++-- src/ship/ship.rs | 4 +- 6 files changed, 126 insertions(+), 30 deletions(-) diff --git a/src/ship/items/actions.rs b/src/ship/items/actions.rs index 216c523..1f06d0f 100644 --- a/src/ship/items/actions.rs +++ b/src/ship/items/actions.rs @@ -9,8 +9,9 @@ use crate::entity::gateway::{EntityGateway, EntityGatewayTransaction}; use crate::ship::items::state::{ItemState, ItemStateProxy, ItemStateAction, ItemAction, ItemStateError, FloorItem, InventoryItem, AddItemResult, FloorItemDetail, StackedItemDetail, BankItem, BankItemDetail, InventoryItemDetail, IndividualItemDetail}; use crate::ship::items::apply_item::apply_item; -use crate::entity::item::ItemDetail; +use crate::entity::item::{ItemDetail, ItemEntity, NewItemEntity}; use crate::entity::item::tool::Tool; +use crate::ship::shops::ShopItem; @@ -701,3 +702,88 @@ where Ok((transaction, ())) }).await } + + +fn add_bought_item_to_inventory<'a>(character_id: CharacterEntityId, + shop_item: &'a (dyn ShopItem + Send + Sync), + item_id: ClientItemId, + amount: u32) + -> impl Fn((ItemStateProxy<'a>, Box), ()) + -> Pin, Box), InventoryItem), ItemStateError>> + Send + 'a>> +{ + move |(mut item_state, mut transaction), _| { + Box::pin(async move { + let mut inventory = item_state.inventory(&character_id)?; + let bought_item = shop_item.as_item(); + + let inventory_item = match bought_item { + ItemDetail::Tool(tool) if tool.is_stackable() => { + let mut item_entities = Vec::new(); + for _ in 0..amount { + let item_entity = transaction.gateway().create_item(NewItemEntity { + item: ItemDetail::Tool(tool), + }).await?; + transaction.gateway().add_item_note(&item_entity.id, ItemNote::BoughtAtShop { + character_id: character_id, + }).await?; + item_entities.push(item_entity); + } + + let inventory_item = InventoryItem { + item_id, + item: InventoryItemDetail::Stacked(StackedItemDetail { + entity_ids: item_entities.into_iter().map(|i| i.id).collect(), + tool: tool, + }) + }; + inventory.add_item(inventory_item)?.1 + }, + item_detail => { + let item_entity = transaction.gateway().create_item(NewItemEntity { + item: item_detail.clone(), + }).await?; + transaction.gateway().add_item_note(&item_entity.id, ItemNote::BoughtAtShop { + character_id: character_id, + }).await?; + + let inventory_item = InventoryItem { + item_id, + item: InventoryItemDetail::Individual(IndividualItemDetail { + entity_id: item_entity.id, + item: item_detail, + }) + }; + inventory.add_item(inventory_item)?.1 + }, + }; + + transaction.gateway().set_character_inventory(&character_id, &inventory.as_inventory_entity(&character_id)).await?; + item_state.set_inventory(inventory); + Ok(((item_state, transaction), inventory_item)) + }) + } +} + +pub async fn buy_shop_item<'a, EG> ( + item_state: &'a mut ItemState, + entity_gateway: &mut EG, + character: &CharacterEntity, + shop_item: &'a (dyn ShopItem + Send + Sync), + item_id: ClientItemId, + amount: u32, +) -> Result +where + EG: EntityGateway, +{ + let item_price = shop_item.price() as u32 * amount; + entity_gateway.with_transaction(|transaction| async move { + let item_state_proxy = ItemStateProxy::new(item_state); + let ((item_state_proxy, transaction), result) = ItemStateAction::default() + .act(take_meseta_from_inventory(character.id, item_price)) + .act(add_bought_item_to_inventory(character.id, shop_item, item_id, amount)) + .commit((item_state_proxy, transaction)) + .await?; + item_state_proxy.commit(); + Ok((transaction, result)) + }).await +} diff --git a/src/ship/items/state.rs b/src/ship/items/state.rs index 7c33c3f..3967182 100644 --- a/src/ship/items/state.rs +++ b/src/ship/items/state.rs @@ -600,7 +600,7 @@ impl InventoryState { } } - pub fn add_item(&mut self, item: InventoryItem) -> Result { + pub fn add_item(&mut self, item: InventoryItem) -> Result<(AddItemResult, InventoryItem), InventoryError> { match &item.item { InventoryItemDetail::Individual(_) => { if self.inventory.0.len() >= 30 { @@ -608,7 +608,13 @@ impl InventoryState { } else { self.inventory.0.push(item); - Ok(AddItemResult::NewItem) + Ok(( + AddItemResult::NewItem, + self.inventory.0 + .last() + .unwrap() + .clone() + )) } }, InventoryItemDetail::Stacked(sitem) => { @@ -625,7 +631,15 @@ impl InventoryState { } else { existing_stack.entity_ids.append(&mut sitem.entity_ids.clone()); - Ok(AddItemResult::AddToStack) + Ok(( + AddItemResult::AddToStack, + self.inventory.0[self.inventory.0 + .iter() + .filter_map(|item| item.item.stacked()) + .position(|item| item.tool == sitem.tool) + .unwrap()] + .clone() + )) } }, None => { @@ -634,7 +648,13 @@ impl InventoryState { } else { self.inventory.0.push(item); - Ok(AddItemResult::NewItem) + Ok(( + AddItemResult::NewItem, + self.inventory.0 + .last() + .unwrap() + .clone() + )) } } } diff --git a/src/ship/packet/builder/message.rs b/src/ship/packet/builder/message.rs index cf91fe9..a349b8f 100644 --- a/src/ship/packet/builder/message.rs +++ b/src/ship/packet/builder/message.rs @@ -68,13 +68,13 @@ pub fn create_meseta(area_client: AreaClient, amount: usize) -> CreateItem { } } -pub fn create_withdrawn_inventory_item(area_client: AreaClient, item: &InventoryItem) -> Result { - let bytes = item.as_client_bytes(); +pub fn create_withdrawn_inventory_item(area_client: AreaClient, item: &InventoryItem2) -> Result { + let bytes = item.item.as_client_bytes(); Ok(CreateItem { client: area_client.local_client.id(), target: 0, item_data: bytes[0..12].try_into()?, - item_id: item.item_id().0, + item_id: item.item_id.0, item_data2: bytes[12..16].try_into()?, unknown: 0, }) diff --git a/src/ship/packet/handler/direct_message.rs b/src/ship/packet/handler/direct_message.rs index 38968a3..1cc319a 100644 --- a/src/ship/packet/handler/direct_message.rs +++ b/src/ship/packet/handler/direct_message.rs @@ -15,7 +15,7 @@ use libpso::utf8_to_utf16_array; use crate::ship::packet::builder; use crate::ship::shops::{ShopItem, ToolShopItem, ArmorShopItem}; use crate::ship::items::state::{ItemState, FloorType, FloorItemDetail}; -use crate::ship::items::actions::{pick_up_item, withdraw_meseta, deposit_meseta, withdraw_item, deposit_item, TriggerCreateItem}; +use crate::ship::items::actions::{pick_up_item, withdraw_meseta, deposit_meseta, withdraw_item, deposit_item, buy_shop_item, TriggerCreateItem}; const BANK_ACTION_DEPOSIT: u8 = 0; const BANK_ACTION_WITHDRAW: u8 = 1; @@ -347,7 +347,7 @@ pub async fn buy_item(id: ClientId, entity_gateway: &mut EG, client_location: &ClientLocation, clients: &mut Clients, - item_manager: &mut ItemManager) + item_state: &mut ItemState) -> Result + Send>, anyhow::Error> where EG: EntityGateway @@ -355,7 +355,6 @@ where let client = clients.get_mut(&id).ok_or(ShipError::ClientNotFound(id))?; let area_client = client_location.get_local_client(id).map_err(|err| -> ClientLocationError { err.into() })?; - let (item, remove): (&(dyn ShopItem + Send + Sync), bool) = match buy_item.shop_type { SHOP_OPTION_WEAPON => { (client.weapon_shop.get(buy_item.shop_index as usize).ok_or(ShipError::ShopError)?, false) @@ -375,16 +374,8 @@ where } }; - let character_meseta = item_manager.get_character_meseta_mut(&client.character.id)?; - if character_meseta.0 < (item.price() * buy_item.amount as usize) as u32 { - return Err(ShipError::ShopError.into()) - } - - character_meseta.0 -= (item.price() * buy_item.amount as usize) as u32; - entity_gateway.set_character_meseta(&client.character.id, *character_meseta).await?; - - let inventory_item = item_manager.player_buys_item(entity_gateway, &client.character, item, ClientItemId(buy_item.item_id), buy_item.amount as usize).await?; - let create = builder::message::create_withdrawn_inventory_item(area_client, inventory_item)?; + let inventory_item = buy_shop_item(item_state, entity_gateway, &mut client.character, item, ClientItemId(buy_item.item_id), buy_item.amount as u32).await?; + let create = builder::message::create_withdrawn_inventory_item(area_client, &inventory_item)?; if remove { match buy_item.shop_type { diff --git a/src/ship/packet/handler/message.rs b/src/ship/packet/handler/message.rs index f25e4cc..0977f43 100644 --- a/src/ship/packet/handler/message.rs +++ b/src/ship/packet/handler/message.rs @@ -395,17 +395,16 @@ where } pub async fn player_sells_item (id: ClientId, - sold_item: &PlayerSoldItem, - entity_gateway: &mut EG, - // client_location: &ClientLocation, - clients: &mut Clients, - item_manager: &mut ItemManager) - -> Result + Send>, anyhow::Error> + sold_item: &PlayerSoldItem, + entity_gateway: &mut EG, + clients: &mut Clients, + item_state: &mut ItemState) + -> Result + Send>, anyhow::Error> where EG: EntityGateway { let client = clients.get_mut(&id).ok_or(ShipError::ClientNotFound(id))?; - item_manager.player_sells_item(entity_gateway, &mut client.character, ClientItemId(sold_item.item_id), sold_item.amount as usize).await?; + //item_manager.player_sells_item(entity_gateway, &mut client.character, ClientItemId(sold_item.item_id), sold_item.amount as usize).await?; // TODO: send the packet to other clients Ok(Box::new(None.into_iter())) } diff --git a/src/ship/ship.rs b/src/ship/ship.rs index c3e4090..e64d254 100644 --- a/src/ship/ship.rs +++ b/src/ship/ship.rs @@ -523,7 +523,7 @@ impl ShipServerState { handler::message::player_sorts_items(id, sort_items, &mut self.entity_gateway, &self.clients, &mut self.item_state).await? }, GameMessage::PlayerSoldItem(player_sold_item) => { - handler::message::player_sells_item(id, player_sold_item, &mut self.entity_gateway, &mut self.clients, &mut self.item_manager).await? + handler::message::player_sells_item(id, player_sold_item, &mut self.entity_gateway, &mut self.clients, &mut self.item_state).await? }, _ => { let cmsg = msg.clone(); @@ -562,7 +562,7 @@ impl ShipServerState { handler::direct_message::shop_request(id, shop_request, &block.client_location, &mut self.clients, &block.rooms, &self.level_table, &mut self.shops).await? }, GameMessage::BuyItem(buy_item) => { - handler::direct_message::buy_item(id, buy_item, &mut self.entity_gateway, &block.client_location, &mut self.clients, &mut self.item_manager).await? + handler::direct_message::buy_item(id, buy_item, &mut self.entity_gateway, &block.client_location, &mut self.clients, &mut self.item_state).await? }, GameMessage::TekRequest(tek_request) => { handler::direct_message::request_tek_item(id, tek_request, &mut self.entity_gateway, &mut self.clients, &mut self.item_manager).await? -- 2.36.0 From 6bacdd4fe37b08694afe63a3a02a2f22bf228c8c Mon Sep 17 00:00:00 2001 From: jake Date: Tue, 21 Jun 2022 00:57:46 -0600 Subject: [PATCH 44/60] selling items --- src/ship/items/actions.rs | 45 +++++++++++++++++++++ src/ship/items/state.rs | 64 +++++++++++++++++++++++++++++- src/ship/packet/handler/message.rs | 4 +- tests/test_shops.rs | 3 +- 4 files changed, 112 insertions(+), 4 deletions(-) diff --git a/src/ship/items/actions.rs b/src/ship/items/actions.rs index 1f06d0f..a717519 100644 --- a/src/ship/items/actions.rs +++ b/src/ship/items/actions.rs @@ -787,3 +787,48 @@ where Ok((transaction, result)) }).await } + + +fn sell_inventory_item<'a>(character_id: CharacterEntityId) + -> impl Fn((ItemStateProxy<'a>, Box), InventoryItem) + -> Pin, Box), InventoryItem), ItemStateError>> + Send + 'a>> +{ + move |(mut item_state, mut transaction), inventory_item| { + Box::pin(async move { + let mut inventory = item_state.inventory(&character_id)?; + let price = inventory_item.item.sell_price()?; + inventory.add_meseta_no_overflow(price)?; + + let mut transaction = inventory_item.with_entity_id(transaction, |mut transaction, entity_id| { + async move { + transaction.gateway().add_item_note(&entity_id, ItemNote::SoldToShop).await?; + Ok(transaction) + }}).await?; + transaction.gateway().set_character_meseta(&character_id, inventory.meseta).await?; + item_state.set_inventory(inventory); + Ok(((item_state, transaction), inventory_item)) + }) + } +} + +pub async fn sell_item<'a, EG> ( + item_state: &'a mut ItemState, + entity_gateway: &mut EG, + character: &CharacterEntity, + item_id: ClientItemId, + amount: u32, +) -> Result +where + EG: EntityGateway, +{ + entity_gateway.with_transaction(|transaction| async move { + let item_state_proxy = ItemStateProxy::new(item_state); + let ((item_state_proxy, transaction), result) = ItemStateAction::default() + .act(take_item_from_inventory(character.id, item_id, amount)) + .act(sell_inventory_item(character.id)) + .commit((item_state_proxy, transaction)) + .await?; + item_state_proxy.commit(); + Ok((transaction, result)) + }).await +} diff --git a/src/ship/items/state.rs b/src/ship/items/state.rs index 3967182..22ce757 100644 --- a/src/ship/items/state.rs +++ b/src/ship/items/state.rs @@ -9,9 +9,10 @@ use crate::ship::map::MapArea; use crate::ship::location::{AreaClient, RoomId}; use crate::entity::character::{CharacterEntity, CharacterEntityId}; use crate::entity::gateway::{EntityGateway, GatewayError}; -use crate::entity::item::tool::Tool; +use crate::entity::item::tool::{Tool, ToolType}; use crate::entity::item::mag::Mag; use crate::ship::drops::ItemDrop; +use crate::ship::shops::{ShopItem, ArmorShopItem, ToolShopItem, WeaponShopItem}; // TODO: Commit trait that ItemStateProxy and EntityTransaction implement that .commit requires and acts on upon everything succeeding (like 3 less lines of code!) @@ -65,6 +66,9 @@ pub enum ItemStateError { #[error("item is not mag food {0}")] NotMagFood(ClientItemId), + + #[error("item is not sellable")] + ItemNotSellable, } pub enum FloorType { @@ -299,6 +303,64 @@ impl InventoryItemDetail { }, } } + + // TODO: this should probably go somewhere a bit more fundamental like ItemDetail + pub fn sell_price(&self) -> Result { + match self { + InventoryItemDetail::Individual(individual_item) => { + match &individual_item.item { + // TODO: can wrapped items be sold? + ItemDetail::Weapon(w) => { + if !w.tekked { + return Ok(1u32) + } + if w.is_rare_item() { + return Ok(10u32) + } + Ok((WeaponShopItem::from(w).price() / 8) as u32) + }, + ItemDetail::Armor(a) => { + if a.is_rare_item() { + return Ok(10u32) + } + Ok((ArmorShopItem::from(a).price() / 8) as u32) + }, + ItemDetail::Shield(s) => { + if s.is_rare_item() { + return Ok(10u32) + } + Ok((ArmorShopItem::from(s).price() / 8) as u32) + }, + ItemDetail::Unit(u) => { + if u.is_rare_item() { + return Ok(10u32) + } + Ok((ArmorShopItem::from(u).price() / 8) as u32) + }, + ItemDetail::Tool(t) => { + if !matches!(t.tool, ToolType::PhotonDrop | ToolType::PhotonSphere | ToolType::PhotonCrystal) && t.is_rare_item() { + return Ok(10u32) + } + Ok((ToolShopItem::from(t).price() / 8) as u32) + }, + ItemDetail::TechniqueDisk(d) => { + Ok((ToolShopItem::from(d).price() / 8) as u32) + }, + ItemDetail::Mag(_m) => { + Err(ItemStateError::ItemNotSellable) + }, + ItemDetail::ESWeapon(_e) => { + Ok(10u32) + }, + } + }, + // the number of stacked items sold is handled by the caller. this is just the price of 1 + InventoryItemDetail::Stacked(stacked_item) => { + Ok(((ToolShopItem::from(&stacked_item.tool).price() / 8) as u32) * stacked_item.count() as u32) + }, + } + } + } diff --git a/src/ship/packet/handler/message.rs b/src/ship/packet/handler/message.rs index 0977f43..4680c6c 100644 --- a/src/ship/packet/handler/message.rs +++ b/src/ship/packet/handler/message.rs @@ -8,7 +8,7 @@ use crate::ship::location::{ClientLocation, ClientLocationError}; use crate::ship::items::{ItemManager, ClientItemId}; use crate::ship::packet::builder; use crate::ship::items::state::ItemState; -use crate::ship::items::actions::{drop_item, drop_partial_item, drop_meseta, equip_item, unequip_item, sort_inventory, use_item, feed_mag}; +use crate::ship::items::actions::{drop_item, drop_partial_item, drop_meseta, equip_item, unequip_item, sort_inventory, use_item, feed_mag, sell_item}; pub async fn request_exp(id: ClientId, request_exp: &RequestExp, @@ -404,7 +404,7 @@ where EG: EntityGateway { let client = clients.get_mut(&id).ok_or(ShipError::ClientNotFound(id))?; - //item_manager.player_sells_item(entity_gateway, &mut client.character, ClientItemId(sold_item.item_id), sold_item.amount as usize).await?; + sell_item(item_state, entity_gateway, &client.character, ClientItemId(sold_item.item_id), sold_item.amount as u32).await?; // TODO: send the packet to other clients Ok(Box::new(None.into_iter())) } diff --git a/tests/test_shops.rs b/tests/test_shops.rs index c4c4655..fa6da02 100644 --- a/tests/test_shops.rs +++ b/tests/test_shops.rs @@ -4,6 +4,7 @@ use elseware::entity::item; use elseware::ship::ship::{ShipServerState, RecvShipPacket, SendShipPacket}; use elseware::ship::room::Difficulty; use elseware::ship::items::manager::ItemManagerError; +use elseware::ship::items::state::ItemStateError; use libpso::packet::ship::*; use libpso::packet::messages::*; @@ -1142,7 +1143,7 @@ async fn test_player_cant_sell_if_meseta_would_go_over_max() { item_id: 0x10000, amount: 1, })))).await.err().unwrap(); - assert!(matches!(ack.downcast::().unwrap(), ItemManagerError::WalletFull)); + assert!(matches!(ack.downcast::().unwrap(), ItemStateError::FullOfMeseta)); let c1_meseta = entity_gateway.get_character_meseta(&char1.id).await.unwrap(); assert_eq!(c1_meseta.0, 999995); -- 2.36.0 From 5f4325429ec3940106257e84e8c62a7a63990f2b Mon Sep 17 00:00:00 2001 From: jake Date: Mon, 18 Jul 2022 18:42:44 -0600 Subject: [PATCH 45/60] trade refactor --- Cargo.toml | 1 + src/entity/gateway/entitygateway.rs | 4 + src/entity/gateway/inmemory.rs | 21 ++ src/entity/gateway/postgres/models.rs | 10 +- src/entity/item/mod.rs | 11 +- src/ship/items/actions.rs | 303 +++++++++++++++++- src/ship/packet/handler/trade.rs | 146 +++++---- src/ship/ship.rs | 10 +- src/ship/trade.rs | 10 +- tests/test_trade.rs | 433 +++++++++++++------------- 10 files changed, 649 insertions(+), 300 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 789571d..fb618d9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,6 +25,7 @@ derive_more = { version = "0.99.3", features = ["display"]} thiserror = "1.0.15" ages-prs = "0.1" async-trait = "0.1.51" +async-recursion= "1.0.0" lazy_static = "1.4.0" barrel = { version = "0.6.5", features = ["pg"] } refinery = { version = "0.5.0", features = ["postgres"] } diff --git a/src/entity/gateway/entitygateway.rs b/src/entity/gateway/entitygateway.rs index fa04d3b..53b6fc4 100644 --- a/src/entity/gateway/entitygateway.rs +++ b/src/entity/gateway/entitygateway.rs @@ -143,6 +143,10 @@ pub trait EntityGateway: Send + Sync { async fn set_bank_meseta(&mut self, _char_id: &CharacterEntityId, _bank: &BankName, _amount: Meseta) -> Result<(), GatewayError> { unimplemented!(); } + + async fn create_trade(&mut self, _char_id1: &CharacterEntityId, _char_id2: &CharacterEntityId) -> Result { + unimplemented!(); + } } diff --git a/src/entity/gateway/inmemory.rs b/src/entity/gateway/inmemory.rs index 8180fe0..f8b3c96 100644 --- a/src/entity/gateway/inmemory.rs +++ b/src/entity/gateway/inmemory.rs @@ -56,6 +56,9 @@ impl<'a> EntityGatewayTransaction for InMemoryGatewayTransaction<'a> { self.original_gateway.weapon_modifiers.lock().unwrap().clear(); self.original_gateway.weapon_modifiers.lock().unwrap().extend(self.working_gateway.weapon_modifiers.lock().unwrap().clone()); + self.original_gateway.trades.lock().unwrap().clear(); + self.original_gateway.trades.lock().unwrap().extend(self.working_gateway.trades.lock().unwrap().clone()); + Ok(()) } } @@ -73,6 +76,7 @@ pub struct InMemoryGateway { equips: Arc>>, mag_modifiers: Arc>>>, weapon_modifiers: Arc>>>, + trades: Arc>>, } impl Default for InMemoryGateway { @@ -89,6 +93,7 @@ impl Default for InMemoryGateway { equips: Arc::new(Mutex::new(BTreeMap::new())), mag_modifiers: Arc::new(Mutex::new(BTreeMap::new())), weapon_modifiers: Arc::new(Mutex::new(BTreeMap::new())), + trades: Arc::new(Mutex::new(Vec::new())), } } } @@ -165,6 +170,7 @@ impl EntityGateway for InMemoryGateway { let equips = self.equips.lock().unwrap().clone(); let mag_modifiers = self.mag_modifiers.lock().unwrap().clone(); let weapon_modifiers = self.weapon_modifiers.lock().unwrap().clone(); + let trades = self.trades.lock().unwrap().clone(); InMemoryGateway { users: Arc::new(Mutex::new(users)), @@ -178,6 +184,7 @@ impl EntityGateway for InMemoryGateway { equips: Arc::new(Mutex::new(equips)), mag_modifiers: Arc::new(Mutex::new(mag_modifiers)), weapon_modifiers: Arc::new(Mutex::new(weapon_modifiers)), + trades: Arc::new(Mutex::new(trades)), } }; @@ -206,6 +213,7 @@ impl EntityGateway for InMemoryGateway { let equips = self.equips.lock().unwrap().clone(); let mag_modifiers = self.mag_modifiers.lock().unwrap().clone(); let weapon_modifiers = self.weapon_modifiers.lock().unwrap().clone(); + let trades = self.trades.lock().unwrap().clone(); let working_gateway = InMemoryGateway { users: Arc::new(Mutex::new(users)), @@ -219,6 +227,7 @@ impl EntityGateway for InMemoryGateway { equips: Arc::new(Mutex::new(equips)), mag_modifiers: Arc::new(Mutex::new(mag_modifiers)), weapon_modifiers: Arc::new(Mutex::new(weapon_modifiers)), + trades: Arc::new(Mutex::new(trades)), }; let transaction = Box::new(InMemoryGatewayTransaction { @@ -482,4 +491,16 @@ impl EntityGateway for InMemoryGateway { Err(GatewayError::Error) } } + + async fn create_trade(&mut self, char_id1: &CharacterEntityId, char_id2: &CharacterEntityId) -> Result { + let mut trades = self.trades.lock().unwrap(); + let id = trades.len() as u32; + let new_trade = TradeEntity { + id: TradeId(id), + character1: *char_id1, + character2: *char_id2, + }; + trades.push(new_trade.clone()); + Ok(new_trade) + } } diff --git a/src/entity/gateway/postgres/models.rs b/src/entity/gateway/postgres/models.rs index b54de2b..81296ae 100644 --- a/src/entity/gateway/postgres/models.rs +++ b/src/entity/gateway/postgres/models.rs @@ -606,7 +606,7 @@ pub enum PgItemNoteDetail { }, SoldToShop, Trade { - id: u32, + trade_id: u32, character_to: u32, character_from: u32, }, @@ -647,8 +647,8 @@ impl From for PgItemNoteDetail { character_id: character_id.0, }, ItemNote::SoldToShop => PgItemNoteDetail::SoldToShop, - ItemNote::Trade{id, character_to, character_from} => PgItemNoteDetail::Trade { - id: id.0, + ItemNote::Trade{trade_id, character_to, character_from} => PgItemNoteDetail::Trade { + trade_id: trade_id.0, character_to: character_to.0, character_from: character_from.0, }, @@ -695,8 +695,8 @@ impl From for ItemNote { character_id: CharacterEntityId(character_id), }, PgItemNoteDetail::SoldToShop => ItemNote::SoldToShop, - PgItemNoteDetail::Trade {id, character_to, character_from} => ItemNote::Trade { - id: TradeId(id as u32), + PgItemNoteDetail::Trade {trade_id, character_to, character_from} => ItemNote::Trade { + trade_id: TradeId(trade_id as u32), character_to: CharacterEntityId(character_to as u32), character_from: CharacterEntityId(character_from as u32), }, diff --git a/src/entity/item/mod.rs b/src/entity/item/mod.rs index 2a8dad4..6e51550 100644 --- a/src/entity/item/mod.rs +++ b/src/entity/item/mod.rs @@ -19,7 +19,7 @@ pub struct ItemEntityId(pub u32); pub struct ItemId(u32); #[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize, derive_more::Display)] pub struct BankName(pub String); -#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)] pub struct TradeId(pub u32); #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] @@ -56,7 +56,7 @@ pub enum ItemNote { }, SoldToShop, Trade { - id: TradeId, + trade_id: TradeId, character_to: CharacterEntityId, character_from: CharacterEntityId, }, @@ -327,3 +327,10 @@ impl BankEntity { } } } + +#[derive(Clone, Debug)] +pub struct TradeEntity { + pub id: TradeId, + pub character1: CharacterEntityId, + pub character2: CharacterEntityId, +} diff --git a/src/ship/items/actions.rs b/src/ship/items/actions.rs index a717519..43f41b2 100644 --- a/src/ship/items/actions.rs +++ b/src/ship/items/actions.rs @@ -9,11 +9,10 @@ use crate::entity::gateway::{EntityGateway, EntityGatewayTransaction}; use crate::ship::items::state::{ItemState, ItemStateProxy, ItemStateAction, ItemAction, ItemStateError, FloorItem, InventoryItem, AddItemResult, FloorItemDetail, StackedItemDetail, BankItem, BankItemDetail, InventoryItemDetail, IndividualItemDetail}; use crate::ship::items::apply_item::apply_item; -use crate::entity::item::{ItemDetail, ItemEntity, NewItemEntity}; -use crate::entity::item::tool::Tool; +use crate::entity::item::{ItemDetail, NewItemEntity, TradeId}; use crate::ship::shops::ShopItem; - - +use crate::ship::trade::TradeItem; +use crate::ship::location::{AreaClient, RoomId}; pub enum TriggerCreateItem { Yes, @@ -22,9 +21,9 @@ pub enum TriggerCreateItem { fn take_item_from_floor(character_id: CharacterEntityId, item_id: ClientItemId) -> impl for<'a> Fn((ItemStateProxy<'a>, Box), ()) - -> Pin, Box), FloorItem), ItemStateError>> + Send + 'a>> + -> Pin, Box), FloorItem), ItemStateError>> + Send + 'a>> { - move |(mut item_state, transaction), _| { + move |(mut item_state, transaction): (ItemStateProxy<'_>, Box) , _| { Box::pin(async move { let mut floor = item_state.floor(&character_id)?; let item = floor.take_item(&item_id).ok_or(ItemStateError::NoFloorItem(item_id))?; @@ -200,6 +199,23 @@ fn take_meseta_from_inventory(character_id: CharacterEntityId, amount: u32) let mut inventory = item_state.inventory(&character_id)?; inventory.remove_meseta(amount)?; transaction.gateway().set_character_meseta(&character_id, inventory.meseta).await?; + item_state.set_inventory(inventory); + + Ok(((item_state, transaction), ())) + }) + } +} + +fn add_meseta_to_inventory(character_id: CharacterEntityId, amount: u32) + -> impl for<'a> Fn((ItemStateProxy<'a>, Box), ()) + -> Pin, Box), ()), ItemStateError>> + Send + 'a>> +{ + move |(mut item_state, mut transaction), _| { + Box::pin(async move { + let mut inventory = item_state.inventory(&character_id)?; + inventory.add_meseta(amount)?; + transaction.gateway().set_character_meseta(&character_id, inventory.meseta).await?; + item_state.set_inventory(inventory); Ok(((item_state, transaction), ())) }) @@ -419,6 +435,8 @@ where let item_state_proxy = ItemStateProxy::new(item_state); let ((item_state_proxy, transaction), result) = ItemStateAction::default() .act(take_item_from_bank(character.id, *item_id, amount)) + //.act(bank_item_to_inventory_item) + //.act(add_item_to_inventory) .act(add_bank_item_to_inventory(&character)) .commit((item_state_proxy, transaction)) .await?; @@ -780,6 +798,8 @@ where let item_state_proxy = ItemStateProxy::new(item_state); let ((item_state_proxy, transaction), result) = ItemStateAction::default() .act(take_meseta_from_inventory(character.id, item_price)) + //.act(bought_item_to_inventory_item) + //.act(add_item_to_inventory) .act(add_bought_item_to_inventory(character.id, shop_item, item_id, amount)) .commit((item_state_proxy, transaction)) .await?; @@ -832,3 +852,274 @@ where Ok((transaction, result)) }).await } + +#[async_recursion::async_recursion] +async fn iterate_inner<'a, I, O, T, F, FR>(state: (ItemStateProxy<'a>, Box), + mut input: Vec, + func: F, + arg: T) + -> Result<((ItemStateProxy<'a>, Box), Vec), ItemStateError> +where + 'a: 'async_recursion, + I: Send, + O: Send, + T: Clone + Send + Sync, + F: Fn(I) -> FR + Send + Sync + Clone + 'static, + FR: Fn((ItemStateProxy<'a>, Box), T) + -> Pin, Box), O), ItemStateError>> + Send + 'a>> + Send + Sync, +{ + let item = match input.pop() { + Some(item) => item, + None => return Ok((state, Vec::new())) + }; + + let (state, mut output) = iterate_inner(state, input, func.clone(), arg.clone()).await.unwrap(); + let rfunc = func(item); + let (state, result) = rfunc(state, arg.clone()).await.unwrap(); + + output.push(result); + + Ok((state, output)) +} + +pub fn iterate<'k, I, O, T, F, FR>( + input: Vec, + func: F) + -> impl for<'a> Fn((ItemStateProxy<'a>, Box), T) + -> Pin, Box), Vec), ItemStateError>> + Send + 'a>> +where + O: Send, + I: Send + Clone + 'static + std::fmt::Debug, + T: Send + Clone + 'static + std::fmt::Debug, + F: Fn(I) -> FR + Send + Sync + Clone + 'static, + FR: for<'a> Fn((ItemStateProxy<'a>, Box), T) + -> Pin, Box), O), ItemStateError>> + Send + 'a>> + Send + Sync, + T: Clone + Send + Sync, +{ + move |(mut item_state, mut transaction), arg| { + let input = input.clone(); + let func = func.clone(); + println!("i {:?} {:?}", input, arg); + Box::pin(async move { + let (state, result) = iterate_inner((item_state, transaction), input, func, arg.clone()).await?; + Ok((state, result)) + }) + } +} + + +#[async_recursion::async_recursion] +async fn foreach_inner<'a, O, T, F>(state: (ItemStateProxy<'a>, Box), + mut input: Vec, + func: F) + -> Result<((ItemStateProxy<'a>, Box), Vec), ItemStateError> +where + 'a: 'async_recursion, + O: Send, + T: Clone + Send, + F: Fn((ItemStateProxy<'a>, Box), T) + -> Pin, Box), O), ItemStateError>> + Send + 'a>> + Send + Sync, + F: Clone, +{ + let item = match input.pop() { + Some(item) => item, + None => return Ok((state, Vec::new())) + }; + + let (state, mut output) = foreach_inner(state, input, func.clone()).await?; + let (state, result) = func(state, item).await?; + + output.push(result); + + Ok((state, output)) +} + +pub fn foreach<'k, O, T, F>(func: F) + -> impl for<'a> Fn((ItemStateProxy<'a>, Box), Vec) + -> Pin, Box), Vec), ItemStateError>> + Send + 'a>> +where + O: Send, + T: Send + Clone + 'static + std::fmt::Debug, + F: for<'a> Fn((ItemStateProxy<'a>, Box), T) + -> Pin, Box), O), ItemStateError>> + Send + 'a>> + Send + Sync + 'static, + F: Clone, + T: Clone + Send + Sync, +{ + move |(item_state, transaction), items| { + println!("fe {:?}", items); + let func = func.clone(); + Box::pin(async move { + let (state, result) = foreach_inner((item_state, transaction), items, func).await?; + Ok((state, result)) + }) + } +} + + +fn clear<'a, T: Send + Clone + 'a>() + -> impl Fn((ItemStateProxy<'a>, Box), T) + -> Pin, Box), ()), ItemStateError>> + Send + 'a>> +{ + move |state, _| { + Box::pin(async move { + Ok((state, ())) + }) + } +} + +fn insert<'a, T: Send + Clone + 'a>(element: T) + -> impl Fn((ItemStateProxy<'a>, Box), ()) + -> Pin, Box), T), ItemStateError>> + Send + 'a>> +{ + move |state, _| { + let element = element.clone(); + Box::pin(async move { + Ok((state, element)) + }) + } +} + +fn add_item_to_inventory(character: CharacterEntity) + -> impl for<'a> Fn((ItemStateProxy<'a>, Box), InventoryItem) + -> Pin, Box), InventoryItem), ItemStateError>> + Send + 'a>> + Clone +{ + let character = character.clone(); + move |(mut item_state, transaction), inventory_item| { + let character = character.clone(); + Box::pin(async move { + //let bank_name = item_state.bank(&character.id)?.name; + let mut inventory = item_state.inventory(&character.id)?; + + let character_id = character.id; + /* + let transaction = bank_item.with_entity_id(transaction, |mut transaction, entity_id| { + let bank_name = bank_name.clone(); + async move { + transaction.gateway().add_item_note(&entity_id, ItemNote::Withdraw { + character_id, + bank: bank_name, + }).await?; + Ok(transaction) + }}).await?; + */ + + let mut transaction = inventory_item.with_mag(transaction, |mut transaction, entity_id, _mag| { + let character = character.clone(); + async move { + transaction.gateway().change_mag_owner(&entity_id, &character).await?; + Ok(transaction) + }}).await?; + + inventory.add_item(inventory_item.clone())?; + transaction.gateway().set_character_inventory(&character.id, &inventory.as_inventory_entity(&character.id)).await?; + item_state.set_inventory(inventory); + + Ok(((item_state, transaction), inventory_item)) + }) + } +} + +fn record_trade(trade_id: TradeId, character_to: CharacterEntityId, character_from: CharacterEntityId) + -> impl for<'a> Fn((ItemStateProxy<'a>, Box), Vec) + -> Pin, Box), Vec), ItemStateError>> + Send + 'a>> + Clone +{ + move |(item_state, mut transaction), traded_items| { + Box::pin(async move { + for item in &traded_items { + transaction = item.with_entity_id(transaction, |mut transaction, entity_id| { + async move { + transaction.gateway().add_item_note(&entity_id, ItemNote::Trade { + trade_id, + character_to, + character_from, + }).await?; + Ok(transaction) + }}).await?; + } + Ok(((item_state, transaction), traded_items)) + }) + } +} + + +fn assign_new_item_id() + -> impl for<'a> Fn((ItemStateProxy<'a>, Box), InventoryItem) + -> Pin, Box), InventoryItem), ItemStateError>> + Send + 'a>> + Clone +{ + move |(mut item_state, transaction), mut inventory_item| { + Box::pin(async move { + inventory_item.item_id = item_state.new_item_id()?; + Ok(((item_state, transaction), inventory_item)) + }) + } +} + +pub async fn trade_items<'a, EG> ( + item_state: &'a mut ItemState, + entity_gateway: &mut EG, + room_id: RoomId, + p1: (&AreaClient, &CharacterEntity, &Vec, Meseta), + p2: (&AreaClient, &CharacterEntity, &Vec, Meseta)) + -> Result<(Vec, Vec), ItemStateError> +where + EG: EntityGateway, +{ + let p1_trade_items = p1.2 + .iter() + .map(|item| { + match item { + TradeItem::Individual(item_id) => (*item_id, 1), + TradeItem::Stacked(item_id, amount) => (*item_id, *amount as u32), + } + }) + .collect(); + let p2_trade_items = p2.2 + .iter() + .map(|item| { + match item { + TradeItem::Individual(item_id) => (*item_id, 1), + TradeItem::Stacked(item_id, amount) => (*item_id, *amount as u32), + } + }) + .collect(); + entity_gateway.with_transaction(|mut transaction| async move { + let p1_id = p1.1.id; + let p2_id = p2.1.id; + let trade = transaction.gateway().create_trade(&p1_id, &p2_id).await?; + let item_state_proxy = ItemStateProxy::new(item_state); + let ((item_state_proxy, transaction), p1_removed_items) = ItemStateAction::default() + .act(iterate(p1_trade_items, move |p1_trade_item| take_item_from_inventory(p1_id, p1_trade_item.0, p1_trade_item.1) )) + .act(foreach(assign_new_item_id())) + .commit((item_state_proxy, transaction)) + .await?; + let ((item_state_proxy, transaction), p2_removed_items) = ItemStateAction::default() + .act(iterate(p2_trade_items, move |p2_trade_item| take_item_from_inventory(p2_id, p2_trade_item.0, p2_trade_item.1) )) + .act(foreach(assign_new_item_id())) + .commit((item_state_proxy, transaction)) + .await?; + + let ((item_state_proxy, transaction), p2_new_items) = ItemStateAction::default() + .act(insert(p1_removed_items)) + .act(foreach(add_item_to_inventory(p2.1.clone()))) + .act(record_trade(trade.id, p1_id, p2_id)) + .commit((item_state_proxy, transaction)) + .await?; + let ((item_state_proxy, transaction), p1_new_items) = ItemStateAction::default() + .act(insert(p2_removed_items)) + .act(foreach(add_item_to_inventory(p1.1.clone()))) + .act(record_trade(trade.id, p2_id, p1_id)) + .commit((item_state_proxy, transaction)) + .await?; + + let ((item_state_proxy, transaction), _) = ItemStateAction::default() + .act(take_meseta_from_inventory(p1_id, p1.3.0)) + .act(take_meseta_from_inventory(p2_id, p2.3.0)) + .act(add_meseta_to_inventory(p1_id, p2.3.0)) + .act(add_meseta_to_inventory(p2_id, p1.3.0)) + .commit((item_state_proxy, transaction)) + .await?; + + item_state_proxy.commit(); + Ok((transaction, (p1_new_items, p2_new_items))) + }).await +} diff --git a/src/ship/packet/handler/trade.rs b/src/ship/packet/handler/trade.rs index 040bc29..050241f 100644 --- a/src/ship/packet/handler/trade.rs +++ b/src/ship/packet/handler/trade.rs @@ -4,16 +4,20 @@ use libpso::packet::messages::*; use crate::common::serverstate::ClientId; use crate::ship::ship::{SendShipPacket, ShipError, Clients}; use crate::ship::location::{ClientLocation, ClientLocationError}; -use crate::ship::items::{ItemManager, ItemManagerError, ClientItemId, ItemToTradeDetail}; -use crate::ship::items::inventory::InventoryItem; +use crate::ship::items::ClientItemId; +use crate::ship::items::state::{ItemState, ItemStateError, InventoryItemDetail}; use crate::ship::trade::{TradeItem, TradeState, TradeStatus}; use crate::entity::gateway::EntityGateway; use crate::ship::packet::builder; +use crate::ship::items::actions::trade_items; +use crate::entity::item::ItemDetail; +use crate::entity::item::tool::Tool; +use crate::ship::location::AreaClient; +use crate::entity::item::{Meseta, ItemNote}; pub const MESETA_ITEM_ID: ClientItemId = ClientItemId(0xFFFFFF01); pub const OTHER_MESETA_ITEM_ID: ClientItemId = ClientItemId(0xFFFFFFFF); - #[derive(thiserror::Error, Debug, PartialEq, Eq)] pub enum TradeError { #[error("no partner")] @@ -51,9 +55,9 @@ pub async fn trade_request(id: ClientId, target: u32, client_location: &ClientLocation, clients: &mut Clients, - item_manager: &mut ItemManager, + item_state: &mut ItemState, trades: &mut TradeState) - -> Result + Send>, anyhow::Error> + -> Result + Send>, ShipError> { let trade_request = trade_request.clone(); // TODO: make this function take ownership of packet match trade_request.trade { @@ -114,18 +118,18 @@ pub async fn trade_request(id: ClientId, .with(&id, |this, other| -> Result + Send>, anyhow::Error> { if this.status == TradeStatus::Trading && other.status == TradeStatus::Trading { let client = clients.get(&this.client()).ok_or_else(|| ShipError::ClientNotFound(this.client()))?; - let inventory = item_manager.get_character_inventory(&client.character)?; + let inventory = item_state.get_character_inventory(&client.character)?; if ClientItemId(item_id) == MESETA_ITEM_ID { this.meseta += amount as usize; } else { - let item = inventory.get_item_by_id(ClientItemId(item_id)).ok_or(ItemManagerError::NoSuchItemId(ClientItemId(item_id)))?; + let item = inventory.get_by_client_id(&ClientItemId(item_id)).ok_or(ItemStateError::InvalidItemId(ClientItemId(item_id)))?; - match item { - InventoryItem::Individual(_) => { + match &item.item { + InventoryItemDetail::Individual(_) => { this.items.push(TradeItem::Individual(ClientItemId(item_id))); }, - InventoryItem::Stacked(stacked_item) => { + InventoryItemDetail::Stacked(stacked_item) => { if stacked_item.count() < amount as usize { return Err(TradeError::InvalidStackAmount(ClientItemId(item_id), amount as usize).into()); } @@ -160,20 +164,20 @@ pub async fn trade_request(id: ClientId, .with(&id, |this, other| -> Option + Send>> { if this.status == TradeStatus::Trading && other.status == TradeStatus::Trading { let client = clients.get(&this.client())?; //.ok_or(ShipError::ClientNotFound(id)).ok()?; - let inventory = item_manager.get_character_inventory(&client.character).ok()?; + let inventory = item_state.get_character_inventory(&client.character).ok()?; if ClientItemId(item_id) == MESETA_ITEM_ID { this.meseta -= amount as usize; } else { - let item = inventory.get_item_by_id(ClientItemId(item_id))?; + let item = inventory.get_by_client_id(&ClientItemId(item_id))?; - match item { - InventoryItem::Individual(_) => { + match &item.item { + InventoryItemDetail::Individual(_) => { this.items.retain(|item| { item.item_id() != ClientItemId(item_id) }) }, - InventoryItem::Stacked(_stacked_item) => { + InventoryItemDetail::Stacked(_stacked_item) => { let trade_item_index = this.items.iter() .position(|item| { item.item_id() == ClientItemId(item_id) @@ -293,7 +297,7 @@ pub async fn inner_items_to_trade(id: ClientId, items_to_trade: &ItemsToTrade, client_location: &ClientLocation, clients: &mut Clients, - item_manager: &mut ItemManager, + item_state: &mut ItemState, trades: &mut TradeState) -> Result + Send>, anyhow::Error> { @@ -305,7 +309,7 @@ pub async fn inner_items_to_trade(id: ClientId, let client = clients.get(&this.client()).ok_or_else(|| ShipError::ClientNotFound(this.client()))?; let other_client = clients.get(&other.client()).ok_or_else(|| ShipError::ClientNotFound(other.client()))?; - let inventory = item_manager.get_character_inventory(&client.character)?; + let inventory = item_state.get_character_inventory(&client.character)?; if items_to_trade.count as usize != (this.items.len() + (if this.meseta != 0 { 1 } else { 0 })) { return Err(TradeError::MismatchedTradeItems.into()) @@ -320,8 +324,8 @@ pub async fn inner_items_to_trade(id: ClientId, return Err(TradeError::InvalidItemId(ClientItemId(item.item_id)).into()) } let amount = u32::from_le_bytes(item.item_data2); - let character_meseta = item_manager.get_character_meseta(&client.character.id).map_err(|_| TradeError::InvalidMeseta)?; - let other_character_meseta = item_manager.get_character_meseta(&other_client.character.id).map_err(|_| TradeError::InvalidMeseta)?; + let character_meseta = item_state.get_character_inventory(&client.character).map_err(|_| TradeError::InvalidMeseta)?.meseta; + let other_character_meseta = item_state.get_character_inventory(&other_client.character).map_err(|_| TradeError::InvalidMeseta)?.meseta; if amount > character_meseta.0 { return Err(TradeError::InvalidMeseta.into()) } @@ -334,8 +338,8 @@ pub async fn inner_items_to_trade(id: ClientId, Ok(()) } else { - let real_item = inventory.get_item_by_id(ClientItemId(item.item_id)) - .ok_or(ItemManagerError::NoSuchItemId(ClientItemId(item.item_id)))?; + let real_item = inventory.get_by_client_id(&ClientItemId(item.item_id)) + .ok_or(ItemStateError::InvalidItemId(ClientItemId(item.item_id)))?; let real_trade_item = this.items .iter() .find(|i| i.item_id() == ClientItemId(item.item_id)) @@ -345,28 +349,28 @@ pub async fn inner_items_to_trade(id: ClientId, .cloned().collect::>() .try_into() .unwrap(); - match real_item { - InventoryItem::Individual(_individual_inventory_item) => { - if real_item.as_client_bytes() == trade_item_bytes { + match &real_item.item { + InventoryItemDetail::Individual(_individual_inventory_item) => { + if real_item.item.as_client_bytes() == trade_item_bytes { Ok(()) } else { Err(TradeError::ClientItemIdDidNotMatchItem(ClientItemId(item.item_id), trade_item_bytes).into()) } }, - InventoryItem::Stacked(stacked_inventory_item) => { - if real_item.as_client_bytes()[0..4] == trade_item_bytes[0..4] { + InventoryItemDetail::Stacked(stacked_inventory_item) => { + if real_item.item.as_client_bytes()[0..4] == trade_item_bytes[0..4] { let amount = trade_item_bytes[5] as usize; if amount <= stacked_inventory_item.entity_ids.len() { if real_trade_item.stacked().ok_or(TradeError::SketchyTrade)?.1 == amount { Ok(()) } else { - Err(TradeError::InvalidStackAmount(stacked_inventory_item.item_id, amount).into()) + Err(TradeError::InvalidStackAmount(real_item.item_id, amount).into()) } } else { - Err(TradeError::InvalidStackAmount(stacked_inventory_item.item_id, amount).into()) + Err(TradeError::InvalidStackAmount(real_item.item_id, amount).into()) } } else { @@ -405,11 +409,11 @@ pub async fn items_to_trade(id: ClientId, items_to_trade_pkt: &ItemsToTrade, client_location: &ClientLocation, clients: &mut Clients, - item_manager: &mut ItemManager, + item_state: &mut ItemState, trades: &mut TradeState) -> Result + Send>, anyhow::Error> { - let t = inner_items_to_trade(id, items_to_trade_pkt, client_location, clients, item_manager, trades).await; + let t = inner_items_to_trade(id, items_to_trade_pkt, client_location, clients, item_state, trades).await; match t { Ok(p) => Ok(p), Err(err) => { @@ -429,7 +433,7 @@ pub async fn trade_confirmed(id: ClientId, entity_gateway: &mut EG, client_location: &ClientLocation, clients: &mut Clients, - item_manager: &mut ItemManager, + item_state: &mut ItemState, trades: &mut TradeState) -> Result + Send>, anyhow::Error> where @@ -473,36 +477,63 @@ where Ok(Box::new(None.into_iter()) as Box + Send>) }, TradeReady::BothPlayers(room_id, (this_local_client, this_client, this), (other_local_client, other_client, other)) => { - let traded_items = item_manager.trade_items(entity_gateway, - room_id, - (&this_local_client, &this_client.character, &this.items, this.meseta), - (&other_local_client, &other_client.character, &other.items, other.meseta)).await?; + let remove_item_packets = this.items + .clone() + .into_iter() + .map(move |item| { + (this_local_client, item) + }) + .chain(other.items + .clone() + .into_iter() + .map(move |item| { + (other_local_client, item) + })) + .map(|(client, item)| { + GameMessage::PlayerNoLongerHasItem(builder::message::player_no_longer_has_item(client, item.item_id(), item.amount() as u32)) + }); - let clients_in_room = client_location.get_all_clients_by_client(id)?; - let traded_item_packets = traded_items + let (this_new_items, other_new_items) = trade_items(item_state, + entity_gateway, + room_id, + (&this_local_client, &this_client.character, &this.items, Meseta(this.meseta as u32)), + (&other_local_client, &other_client.character, &other.items, Meseta(other.meseta as u32))).await?; + + let create_item_packets = this_new_items .into_iter() - .flat_map(|item| { - match item.item_detail { - ItemToTradeDetail::Individual(item_detail) => { - [ - GameMessage::CreateItem(builder::message::create_individual_item(item.add_to, item.new_item_id, &item_detail).unwrap()), - GameMessage::PlayerNoLongerHasItem(builder::message::player_no_longer_has_item(item.remove_from, item.current_item_id, 1)) // TODO: amount = ? - ] - }, - ItemToTradeDetail::Stacked(tool, amount) => { - [ - GameMessage::CreateItem(builder::message::create_stacked_item(item.add_to, item.new_item_id, &tool, amount).unwrap()), - GameMessage::PlayerNoLongerHasItem(builder::message::player_no_longer_has_item(item.remove_from, item.current_item_id, amount as u32)) - ] - }, - ItemToTradeDetail::Meseta(amount) => { - [ - GameMessage::CreateItem(builder::message::create_meseta(item.add_to, amount)), - GameMessage::PlayerNoLongerHasItem(builder::message::player_no_longer_has_item(item.remove_from, item.current_item_id, amount as u32)) - ] + .map(move |item| { + (this_local_client, item) + }) + .chain(other_new_items + .into_iter() + .map(move |item| { + (other_local_client, item) + })) + .map(|(client, item)| { + match item.item { + InventoryItemDetail::Individual(individual_item) => { + GameMessage::CreateItem(builder::message::create_individual_item(client, item.item_id, &individual_item.item).unwrap()) }, + InventoryItemDetail::Stacked(stacked_item) => { + GameMessage::CreateItem(builder::message::create_stacked_item(client, item.item_id, &stacked_item.tool, stacked_item.count()).unwrap()) + } } - }) + }); + + let meseta_packets = vec![(this_local_client, other_local_client, this.meseta), (other_local_client, this_local_client, other.meseta)] + .into_iter() + .filter(|(_, _, meseta)| *meseta != 0) + .flat_map(|(this, other, meseta)| { + [ + GameMessage::PlayerNoLongerHasItem(builder::message::player_no_longer_has_item(this, MESETA_ITEM_ID, meseta as u32)), + GameMessage::CreateItem(builder::message::create_meseta(other, meseta)), + ] + }); + + let clients_in_room = client_location.get_all_clients_by_client(id)?; + let traded_item_packets = remove_item_packets + .chain(create_item_packets) + .chain(meseta_packets) .flat_map(move |packet| { clients_in_room .clone() @@ -521,6 +552,7 @@ where } }) }); + let close_trade = vec![ (this.client(), SendShipPacket::TradeSuccessful(TradeSuccessful::default())), (other.client(), SendShipPacket::TradeSuccessful(TradeSuccessful::default())) diff --git a/src/ship/ship.rs b/src/ship/ship.rs index e64d254..9909f63 100644 --- a/src/ship/ship.rs +++ b/src/ship/ship.rs @@ -78,8 +78,10 @@ pub enum ShipError { InvalidShip(usize), InvalidBlock(usize), InvalidItem(items::ClientItemId), - #[error("tradeerror {0}")] + #[error("trade error {0}")] TradeError(#[from] crate::ship::packet::handler::trade::TradeError), + #[error("trade state error {0}")] + TradeStateError(#[from] crate::ship::trade::TradeStateError), } #[derive(Debug)] @@ -571,7 +573,7 @@ impl ShipServerState { handler::direct_message::accept_tek_item(id, tek_accept, &mut self.entity_gateway, &block.client_location, &mut self.clients, &mut self.item_manager).await? }, GameMessage::TradeRequest(trade_request) => { - handler::trade::trade_request(id, trade_request, target, &block.client_location, &mut self.clients, &mut self.item_manager, &mut self.trades).await? + handler::trade::trade_request(id, trade_request, target, &block.client_location, &mut self.clients, &mut self.item_state, &mut self.trades).await? }, _ => { let cmsg = msg.clone(); @@ -746,11 +748,11 @@ impl ServerState for ShipServerState { }, RecvShipPacket::ItemsToTrade(items_to_trade) => { let block = self.blocks.with_client(id, &self.clients)?; - handler::trade::items_to_trade(id, items_to_trade, &block.client_location, &mut self.clients, &mut self.item_manager, &mut self.trades).await? + handler::trade::items_to_trade(id, items_to_trade, &block.client_location, &mut self.clients, &mut self.item_state, &mut self.trades).await? }, RecvShipPacket::TradeConfirmed(_) => { let block = self.blocks.with_client(id, &self.clients)?; - handler::trade::trade_confirmed(id, &mut self.entity_gateway, &block.client_location, &mut self.clients, &mut self.item_manager, &mut self.trades).await? + handler::trade::trade_confirmed(id, &mut self.entity_gateway, &block.client_location, &mut self.clients, &mut self.item_state, &mut self.trades).await? }, RecvShipPacket::KeyboardConfig(keyboard_config) => { handler::settings::keyboard_config(id, keyboard_config, &mut self.clients, &mut self.entity_gateway).await diff --git a/src/ship/trade.rs b/src/ship/trade.rs index 39d7d0e..6871179 100644 --- a/src/ship/trade.rs +++ b/src/ship/trade.rs @@ -31,6 +31,13 @@ impl TradeItem { TradeItem::Stacked(item_id, _) => *item_id, } } + + pub fn amount(&self) -> usize { + match self { + TradeItem::Individual(_) => 1, + TradeItem::Stacked(_, amount) => *amount, + } + } } @@ -67,9 +74,10 @@ impl ClientTradeState { } #[derive(thiserror::Error, Debug)] -#[error("")] pub enum TradeStateError { + #[error("client not in trade {0}")] ClientNotInTrade(ClientId), + #[error("mismatched trade {0} {1}")] MismatchedTrade(ClientId, ClientId), } diff --git a/tests/test_trade.rs b/tests/test_trade.rs index 65505b3..1da0049 100644 --- a/tests/test_trade.rs +++ b/tests/test_trade.rs @@ -2,10 +2,11 @@ use std::convert::TryInto; use elseware::common::serverstate::{ClientId, ServerState}; use elseware::entity::gateway::{EntityGateway, InMemoryGateway}; use elseware::entity::item; -use elseware::ship::ship::{ShipServerState, RecvShipPacket, SendShipPacket}; +use elseware::ship::ship::{ShipServerState, RecvShipPacket, SendShipPacket, ShipError}; use elseware::entity::item::{Meseta, ItemEntity}; use elseware::ship::items::transaction::TransactionError; use elseware::ship::packet::handler::trade::TradeError; +use elseware::ship::items::state::{ItemStateError, InventoryError}; use libpso::packet::ship::*; use libpso::packet::messages::*; @@ -190,16 +191,16 @@ async fn test_trade_one_individual_item() { let ack = ship.handle(ClientId(2), &RecvShipPacket::TradeConfirmed(TradeConfirmed { })).await.unwrap().collect::>(); assert_eq!(ack.len(), 5); - assert!(matches!(ack[0], (ClientId(1), SendShipPacket::Message(Message { - msg: GameMessage::CreateItem(CreateItem {..}), + assert!(matches!(ack[0], (ClientId(2), SendShipPacket::Message(Message { + msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem {..}), .. })))); - assert!(matches!(ack[1], (ClientId(2), SendShipPacket::Message(Message { + assert!(matches!(ack[1], (ClientId(1), SendShipPacket::Message(Message { msg: GameMessage::CreateItem(CreateItem {..}), .. })))); assert!(matches!(ack[2], (ClientId(2), SendShipPacket::Message(Message { - msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem {..}), + msg: GameMessage::CreateItem(CreateItem {..}), .. })))); assert!(matches!(ack[3], (ClientId(2), SendShipPacket::TradeSuccessful {..}))); @@ -292,15 +293,15 @@ async fn test_trade_player2_to_player1() { })).await.unwrap().collect::>(); assert_eq!(ack.len(), 5); assert!(matches!(ack[0], (ClientId(1), SendShipPacket::Message(Message { - msg: GameMessage::CreateItem(CreateItem {..}), + msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem {..}), .. })))); - assert!(matches!(ack[1], (ClientId(2), SendShipPacket::Message(Message { + assert!(matches!(ack[1], (ClientId(1), SendShipPacket::Message(Message { msg: GameMessage::CreateItem(CreateItem {..}), .. })))); - assert!(matches!(ack[2], (ClientId(1), SendShipPacket::Message(Message { - msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem {..}), + assert!(matches!(ack[2], (ClientId(2), SendShipPacket::Message(Message { + msg: GameMessage::CreateItem(CreateItem {..}), .. })))); assert!(matches!(ack[3], (ClientId(2), SendShipPacket::TradeSuccessful {..}))); @@ -392,16 +393,16 @@ async fn test_reverse_trade_ack_order() { let ack = ship.handle(ClientId(2), &RecvShipPacket::TradeConfirmed(TradeConfirmed { })).await.unwrap().collect::>(); assert_eq!(ack.len(), 5); - assert!(matches!(ack[0], (ClientId(1), SendShipPacket::Message(Message { - msg: GameMessage::CreateItem(CreateItem {..}), + assert!(matches!(ack[0], (ClientId(2), SendShipPacket::Message(Message { + msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem {..}), .. })))); - assert!(matches!(ack[1], (ClientId(2), SendShipPacket::Message(Message { + assert!(matches!(ack[1], (ClientId(1), SendShipPacket::Message(Message { msg: GameMessage::CreateItem(CreateItem {..}), .. })))); assert!(matches!(ack[2], (ClientId(2), SendShipPacket::Message(Message { - msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem {..}), + msg: GameMessage::CreateItem(CreateItem {..}), .. })))); assert!(matches!(ack[3], (ClientId(2), SendShipPacket::TradeSuccessful {..}))); @@ -496,16 +497,16 @@ async fn test_trade_one_stacked_item() { let ack = ship.handle(ClientId(2), &RecvShipPacket::TradeConfirmed(TradeConfirmed { })).await.unwrap().collect::>(); assert_eq!(ack.len(), 5); - assert!(matches!(ack[0], (ClientId(1), SendShipPacket::Message(Message { - msg: GameMessage::CreateItem(CreateItem {..}), + assert!(matches!(ack[0], (ClientId(2), SendShipPacket::Message(Message { + msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem {..}), .. })))); - assert!(matches!(ack[1], (ClientId(2), SendShipPacket::Message(Message { + assert!(matches!(ack[1], (ClientId(1), SendShipPacket::Message(Message { msg: GameMessage::CreateItem(CreateItem {..}), .. })))); assert!(matches!(ack[2], (ClientId(2), SendShipPacket::Message(Message { - msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem {..}), + msg: GameMessage::CreateItem(CreateItem {..}), .. })))); assert!(matches!(ack[3], (ClientId(2), SendShipPacket::TradeSuccessful {..}))); @@ -601,16 +602,16 @@ async fn test_trade_partial_stacked_item() { let ack = ship.handle(ClientId(2), &RecvShipPacket::TradeConfirmed(TradeConfirmed { })).await.unwrap().collect::>(); assert_eq!(ack.len(), 5); - assert!(matches!(ack[0], (ClientId(1), SendShipPacket::Message(Message { - msg: GameMessage::CreateItem(CreateItem {..}), + assert!(matches!(ack[0], (ClientId(2), SendShipPacket::Message(Message { + msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem {..}), .. })))); - assert!(matches!(ack[1], (ClientId(2), SendShipPacket::Message(Message { + assert!(matches!(ack[1], (ClientId(1), SendShipPacket::Message(Message { msg: GameMessage::CreateItem(CreateItem {..}), .. })))); assert!(matches!(ack[2], (ClientId(2), SendShipPacket::Message(Message { - msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem {..}), + msg: GameMessage::CreateItem(CreateItem {..}), .. })))); assert!(matches!(ack[3], (ClientId(2), SendShipPacket::TradeSuccessful {..}))); @@ -727,32 +728,31 @@ async fn test_trade_individual_both() { })).await.unwrap().collect::>(); assert_eq!(ack.len(), 8); assert!(matches!(ack[0], (ClientId(1), SendShipPacket::Message(Message { - msg: GameMessage::CreateItem(CreateItem { - client: 0, - item_data: [0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], // handgun - item_id: 0x810001, + msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem { + client: 1, + item_id: 0x210000, .. }), .. })))); assert!(matches!(ack[1], (ClientId(2), SendShipPacket::Message(Message { - msg: GameMessage::CreateItem(CreateItem { + msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem { client: 0, - item_data: [0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], // handgun - item_id: 0x810001, + item_id: 0x10000, .. }), .. })))); assert!(matches!(ack[2], (ClientId(1), SendShipPacket::Message(Message { - msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem { + msg: GameMessage::CreateItem(CreateItem { client: 1, - item_id: 0x210000, + item_data: [0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], // saber + item_id: 0x810002, .. }), .. })))); - assert!(matches!(ack[3], (ClientId(1), SendShipPacket::Message(Message { + assert!(matches!(ack[3], (ClientId(2), SendShipPacket::Message(Message { msg: GameMessage::CreateItem(CreateItem { client: 1, item_data: [0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], // saber @@ -761,19 +761,20 @@ async fn test_trade_individual_both() { }), .. })))); - assert!(matches!(ack[4], (ClientId(2), SendShipPacket::Message(Message { + assert!(matches!(ack[4], (ClientId(1), SendShipPacket::Message(Message { msg: GameMessage::CreateItem(CreateItem { - client: 1, - item_data: [0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], // saber - item_id: 0x810002, + client: 0, + item_data: [0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], // handgun + item_id: 0x810001, .. }), .. })))); assert!(matches!(ack[5], (ClientId(2), SendShipPacket::Message(Message { - msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem { + msg: GameMessage::CreateItem(CreateItem { client: 0, - item_id: 0x10000, + item_data: [0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], // handgun + item_id: 0x810001, .. }), .. @@ -899,30 +900,30 @@ async fn test_trade_stacked_both() { })).await.unwrap().collect::>(); assert_eq!(ack.len(), 8); assert!(matches!(ack[0], (ClientId(1), SendShipPacket::Message(Message { - msg: GameMessage::CreateItem(CreateItem { - client: 0, - item_id: 0x810001, + msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem { + client: 1, + item_id: 0x210000, .. }), .. })))); assert!(matches!(ack[1], (ClientId(2), SendShipPacket::Message(Message { - msg: GameMessage::CreateItem(CreateItem { + msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem { client: 0, - item_id: 0x810001, + item_id: 0x10000, .. }), .. })))); assert!(matches!(ack[2], (ClientId(1), SendShipPacket::Message(Message { - msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem { + msg: GameMessage::CreateItem(CreateItem { client: 1, - item_id: 0x210000, + item_id: 0x810002, .. }), .. })))); - assert!(matches!(ack[3], (ClientId(1), SendShipPacket::Message(Message { + assert!(matches!(ack[3], (ClientId(2), SendShipPacket::Message(Message { msg: GameMessage::CreateItem(CreateItem { client: 1, item_id: 0x810002, @@ -930,18 +931,18 @@ async fn test_trade_stacked_both() { }), .. })))); - assert!(matches!(ack[4], (ClientId(2), SendShipPacket::Message(Message { + assert!(matches!(ack[4], (ClientId(1), SendShipPacket::Message(Message { msg: GameMessage::CreateItem(CreateItem { - client: 1, - item_id: 0x810002, + client: 0, + item_id: 0x810001, .. }), .. })))); assert!(matches!(ack[5], (ClientId(2), SendShipPacket::Message(Message { - msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem { + msg: GameMessage::CreateItem(CreateItem { client: 0, - item_id: 0x10000, + item_id: 0x810001, .. }), .. @@ -1069,31 +1070,32 @@ async fn test_trade_partial_stack_both() { })).await.unwrap().collect::>(); assert_eq!(ack.len(), 8); assert!(matches!(ack[0], (ClientId(1), SendShipPacket::Message(Message { - msg: GameMessage::CreateItem(CreateItem { - client: 0, - item_id: 0x810001, + msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem { + client: 1, + item_id: 0x210000, + amount: 2, .. }), .. })))); assert!(matches!(ack[1], (ClientId(2), SendShipPacket::Message(Message { - msg: GameMessage::CreateItem(CreateItem { + msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem { client: 0, - item_id: 0x810001, + item_id: 0x10000, + amount: 1, .. }), .. })))); assert!(matches!(ack[2], (ClientId(1), SendShipPacket::Message(Message { - msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem { + msg: GameMessage::CreateItem(CreateItem { client: 1, - item_id: 0x210000, - amount: 2, + item_id: 0x810002, .. }), .. })))); - assert!(matches!(ack[3], (ClientId(1), SendShipPacket::Message(Message { + assert!(matches!(ack[3], (ClientId(2), SendShipPacket::Message(Message { msg: GameMessage::CreateItem(CreateItem { client: 1, item_id: 0x810002, @@ -1101,19 +1103,18 @@ async fn test_trade_partial_stack_both() { }), .. })))); - assert!(matches!(ack[4], (ClientId(2), SendShipPacket::Message(Message { + assert!(matches!(ack[4], (ClientId(1), SendShipPacket::Message(Message { msg: GameMessage::CreateItem(CreateItem { - client: 1, - item_id: 0x810002, + client: 0, + item_id: 0x810001, .. }), .. })))); assert!(matches!(ack[5], (ClientId(2), SendShipPacket::Message(Message { - msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem { + msg: GameMessage::CreateItem(CreateItem { client: 0, - item_id: 0x10000, - amount: 1, + item_id: 0x810001, .. }), .. @@ -1245,31 +1246,32 @@ async fn test_trade_same_stacked_item_to_eachother() { })).await.unwrap().collect::>(); assert_eq!(ack.len(), 8); assert!(matches!(ack[0], (ClientId(1), SendShipPacket::Message(Message { - msg: GameMessage::CreateItem(CreateItem { - client: 0, - item_id: 0x810001, + msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem { + client: 1, + item_id: 0x210000, + amount: 3, .. }), .. })))); assert!(matches!(ack[1], (ClientId(2), SendShipPacket::Message(Message { - msg: GameMessage::CreateItem(CreateItem { + msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem { client: 0, - item_id: 0x810001, + item_id: 0x10000, + amount: 1, .. }), .. })))); assert!(matches!(ack[2], (ClientId(1), SendShipPacket::Message(Message { - msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem { + msg: GameMessage::CreateItem(CreateItem { client: 1, - item_id: 0x210000, - amount: 3, + item_id: 0x810002, .. }), .. })))); - assert!(matches!(ack[3], (ClientId(1), SendShipPacket::Message(Message { + assert!(matches!(ack[3], (ClientId(2), SendShipPacket::Message(Message { msg: GameMessage::CreateItem(CreateItem { client: 1, item_id: 0x810002, @@ -1277,19 +1279,18 @@ async fn test_trade_same_stacked_item_to_eachother() { }), .. })))); - assert!(matches!(ack[4], (ClientId(2), SendShipPacket::Message(Message { + assert!(matches!(ack[4], (ClientId(1), SendShipPacket::Message(Message { msg: GameMessage::CreateItem(CreateItem { - client: 1, - item_id: 0x810002, + client: 0, + item_id: 0x810001, .. }), .. })))); assert!(matches!(ack[5], (ClientId(2), SendShipPacket::Message(Message { - msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem { + msg: GameMessage::CreateItem(CreateItem { client: 0, - item_id: 0x10000, - amount: 1, + item_id: 0x810001, .. }), .. @@ -1407,16 +1408,16 @@ async fn test_trade_stacked_when_already_have_partial_stack() { let ack = ship.handle(ClientId(2), &RecvShipPacket::TradeConfirmed(TradeConfirmed { })).await.unwrap().collect::>(); assert_eq!(ack.len(), 5); - assert!(matches!(ack[0], (ClientId(1), SendShipPacket::Message(Message { - msg: GameMessage::CreateItem(CreateItem { - client: 1, - item_id: 0x810001, - item_data: [3, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0], + assert!(matches!(ack[0], (ClientId(2), SendShipPacket::Message(Message { + msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem { + client: 0, + item_id: 0x10000, + amount: 2, .. }), .. })))); - assert!(matches!(ack[1], (ClientId(2), SendShipPacket::Message(Message { + assert!(matches!(ack[1], (ClientId(1), SendShipPacket::Message(Message { msg: GameMessage::CreateItem(CreateItem { client: 1, item_id: 0x810001, @@ -1426,10 +1427,10 @@ async fn test_trade_stacked_when_already_have_partial_stack() { .. })))); assert!(matches!(ack[2], (ClientId(2), SendShipPacket::Message(Message { - msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem { - client: 0, - item_id: 0x10000, - amount: 2, + msg: GameMessage::CreateItem(CreateItem { + client: 1, + item_id: 0x810001, + item_data: [3, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0], .. }), .. @@ -1554,32 +1555,31 @@ async fn test_trade_individual_for_stacked() { })).await.unwrap().collect::>(); assert_eq!(ack.len(), 8); assert!(matches!(ack[0], (ClientId(1), SendShipPacket::Message(Message { - msg: GameMessage::CreateItem(CreateItem { - client: 0, - item_data: [3, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0], - item_id: 0x810001, + msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem { + client: 1, + item_id: 0x210000, .. }), .. })))); assert!(matches!(ack[1], (ClientId(2), SendShipPacket::Message(Message { - msg: GameMessage::CreateItem(CreateItem { + msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem { client: 0, - item_data: [3, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0], - item_id: 0x810001, + item_id: 0x10000, .. }), .. })))); assert!(matches!(ack[2], (ClientId(1), SendShipPacket::Message(Message { - msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem { + msg: GameMessage::CreateItem(CreateItem { client: 1, - item_id: 0x210000, + item_data: [0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + item_id: 0x810002, .. }), .. })))); - assert!(matches!(ack[3], (ClientId(1), SendShipPacket::Message(Message { + assert!(matches!(ack[3], (ClientId(2), SendShipPacket::Message(Message { msg: GameMessage::CreateItem(CreateItem { client: 1, item_data: [0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], @@ -1588,19 +1588,20 @@ async fn test_trade_individual_for_stacked() { }), .. })))); - assert!(matches!(ack[4], (ClientId(2), SendShipPacket::Message(Message { + assert!(matches!(ack[4], (ClientId(1), SendShipPacket::Message(Message { msg: GameMessage::CreateItem(CreateItem { - client: 1, - item_data: [0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - item_id: 0x810002, + client: 0, + item_data: [3, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0], + item_id: 0x810001, .. }), .. })))); assert!(matches!(ack[5], (ClientId(2), SendShipPacket::Message(Message { - msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem { + msg: GameMessage::CreateItem(CreateItem { client: 0, - item_id: 0x10000, + item_data: [3, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0], + item_id: 0x810001, .. }), .. @@ -1758,53 +1759,51 @@ async fn test_trade_multiple_individual() { })).await.unwrap().collect::>(); assert_eq!(ack.len(), 14); assert!(matches!(ack[0], (ClientId(1), SendShipPacket::Message(Message { - msg: GameMessage::CreateItem(CreateItem { - client: 0, - item_data: [0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], // handgun - item_id: 0x810001, + msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem { + client: 1, + item_id: 0x210000, .. }), .. })))); - assert!(matches!(ack[1], (ClientId(2), SendShipPacket::Message(Message { - msg: GameMessage::CreateItem(CreateItem { - client: 0, - item_data: [0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], // handgun - item_id: 0x810001, + assert!(matches!(ack[1], (ClientId(1), SendShipPacket::Message(Message { + msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem { + client: 1, + item_id: 0x210001, .. }), .. })))); - assert!(matches!(ack[2], (ClientId(1), SendShipPacket::Message(Message { + assert!(matches!(ack[2], (ClientId(2), SendShipPacket::Message(Message { msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem { - client: 1, - item_id: 0x210000, + client: 0, + item_id: 0x10000, .. }), .. })))); - assert!(matches!(ack[3], (ClientId(1), SendShipPacket::Message(Message { - msg: GameMessage::CreateItem(CreateItem { + assert!(matches!(ack[3], (ClientId(2), SendShipPacket::Message(Message { + msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem { client: 0, - item_data: [0, 6, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0], // handgun - item_id: 0x810002, + item_id: 0x10001, .. }), .. })))); - assert!(matches!(ack[4], (ClientId(2), SendShipPacket::Message(Message { + assert!(matches!(ack[4], (ClientId(1), SendShipPacket::Message(Message { msg: GameMessage::CreateItem(CreateItem { - client: 0, - item_data: [0, 6, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0], // handgun - item_id: 0x810002, + client: 1, + item_data: [0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], // saber + item_id: 0x810003, .. }), .. })))); - assert!(matches!(ack[5], (ClientId(1), SendShipPacket::Message(Message { - msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem { + assert!(matches!(ack[5], (ClientId(2), SendShipPacket::Message(Message { + msg: GameMessage::CreateItem(CreateItem { client: 1, - item_id: 0x210001, + item_data: [0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], // saber + item_id: 0x810003, .. }), .. @@ -1812,8 +1811,8 @@ async fn test_trade_multiple_individual() { assert!(matches!(ack[6], (ClientId(1), SendShipPacket::Message(Message { msg: GameMessage::CreateItem(CreateItem { client: 1, - item_data: [0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], // saber - item_id: 0x810003, + item_data: [0, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0], // saber + item_id: 0x810004, .. }), .. @@ -1821,42 +1820,44 @@ async fn test_trade_multiple_individual() { assert!(matches!(ack[7], (ClientId(2), SendShipPacket::Message(Message { msg: GameMessage::CreateItem(CreateItem { client: 1, - item_data: [0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], // saber - item_id: 0x810003, + item_data: [0, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0], // saber + item_id: 0x810004, .. }), .. })))); - assert!(matches!(ack[8], (ClientId(2), SendShipPacket::Message(Message { - msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem { + assert!(matches!(ack[8], (ClientId(1), SendShipPacket::Message(Message { + msg: GameMessage::CreateItem(CreateItem { client: 0, - item_id: 0x10000, + item_data: [0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], // handgun + item_id: 0x810001, .. }), .. })))); - assert!(matches!(ack[9], (ClientId(1), SendShipPacket::Message(Message { + assert!(matches!(ack[9], (ClientId(2), SendShipPacket::Message(Message { msg: GameMessage::CreateItem(CreateItem { - client: 1, - item_data: [0, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0], // saber - item_id: 0x810004, + client: 0, + item_data: [0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], // handgun + item_id: 0x810001, .. }), .. })))); - assert!(matches!(ack[10], (ClientId(2), SendShipPacket::Message(Message { + assert!(matches!(ack[10], (ClientId(1), SendShipPacket::Message(Message { msg: GameMessage::CreateItem(CreateItem { - client: 1, - item_data: [0, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0], // saber - item_id: 0x810004, + client: 0, + item_data: [0, 6, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0], // handgun + item_id: 0x810002, .. }), .. })))); assert!(matches!(ack[11], (ClientId(2), SendShipPacket::Message(Message { - msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem { + msg: GameMessage::CreateItem(CreateItem { client: 0, - item_id: 0x10001, + item_data: [0, 6, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0], // handgun + item_id: 0x810002, .. }), .. @@ -2030,49 +2031,49 @@ async fn test_trade_multiple_stacked() { })).await.unwrap().collect::>(); assert_eq!(ack.len(), 14); assert!(matches!(ack[0], (ClientId(1), SendShipPacket::Message(Message { - msg: GameMessage::CreateItem(CreateItem { - client: 0, - item_id: 0x810001, + msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem { + client: 1, + item_id: 0x210000, .. }), .. })))); - assert!(matches!(ack[1], (ClientId(2), SendShipPacket::Message(Message { - msg: GameMessage::CreateItem(CreateItem { - client: 0, - item_id: 0x810001, + assert!(matches!(ack[1], (ClientId(1), SendShipPacket::Message(Message { + msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem { + client: 1, + item_id: 0x210001, .. }), .. })))); - assert!(matches!(ack[2], (ClientId(1), SendShipPacket::Message(Message { + assert!(matches!(ack[2], (ClientId(2), SendShipPacket::Message(Message { msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem { - client: 1, - item_id: 0x210000, + client: 0, + item_id: 0x10000, .. }), .. })))); - assert!(matches!(ack[3], (ClientId(1), SendShipPacket::Message(Message { - msg: GameMessage::CreateItem(CreateItem { + assert!(matches!(ack[3], (ClientId(2), SendShipPacket::Message(Message { + msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem { client: 0, - item_id: 0x810002, + item_id: 0x10001, .. }), .. })))); - assert!(matches!(ack[4], (ClientId(2), SendShipPacket::Message(Message { + assert!(matches!(ack[4], (ClientId(1), SendShipPacket::Message(Message { msg: GameMessage::CreateItem(CreateItem { - client: 0, - item_id: 0x810002, + client: 1, + item_id: 0x810003, .. }), .. })))); - assert!(matches!(ack[5], (ClientId(1), SendShipPacket::Message(Message { - msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem { + assert!(matches!(ack[5], (ClientId(2), SendShipPacket::Message(Message { + msg: GameMessage::CreateItem(CreateItem { client: 1, - item_id: 0x210001, + item_id: 0x810003, .. }), .. @@ -2080,7 +2081,7 @@ async fn test_trade_multiple_stacked() { assert!(matches!(ack[6], (ClientId(1), SendShipPacket::Message(Message { msg: GameMessage::CreateItem(CreateItem { client: 1, - item_id: 0x810003, + item_id: 0x810004, .. }), .. @@ -2088,39 +2089,39 @@ async fn test_trade_multiple_stacked() { assert!(matches!(ack[7], (ClientId(2), SendShipPacket::Message(Message { msg: GameMessage::CreateItem(CreateItem { client: 1, - item_id: 0x810003, + item_id: 0x810004, .. }), .. })))); - assert!(matches!(ack[8], (ClientId(2), SendShipPacket::Message(Message { - msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem { + assert!(matches!(ack[8], (ClientId(1), SendShipPacket::Message(Message { + msg: GameMessage::CreateItem(CreateItem { client: 0, - item_id: 0x10000, + item_id: 0x810001, .. }), .. })))); - assert!(matches!(ack[9], (ClientId(1), SendShipPacket::Message(Message { + assert!(matches!(ack[9], (ClientId(2), SendShipPacket::Message(Message { msg: GameMessage::CreateItem(CreateItem { - client: 1, - item_id: 0x810004, + client: 0, + item_id: 0x810001, .. }), .. })))); - assert!(matches!(ack[10], (ClientId(2), SendShipPacket::Message(Message { + assert!(matches!(ack[10], (ClientId(1), SendShipPacket::Message(Message { msg: GameMessage::CreateItem(CreateItem { - client: 1, - item_id: 0x810004, + client: 0, + item_id: 0x810002, .. }), .. })))); assert!(matches!(ack[11], (ClientId(2), SendShipPacket::Message(Message { - msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem { + msg: GameMessage::CreateItem(CreateItem { client: 0, - item_id: 0x10001, + item_id: 0x810002, .. }), .. @@ -2251,12 +2252,7 @@ async fn test_trade_not_enough_inventory_space_individual() { let ack = ship.handle(ClientId(2), &RecvShipPacket::TradeConfirmed(TradeConfirmed { })).await.err().unwrap(); - match ack.downcast::>().unwrap() { - TransactionError::Action(a) => { - assert_eq!(a.downcast::().unwrap(), TradeError::NoInventorySpace); - }, - _ => panic!() - } + assert!(matches!(ack.downcast::().unwrap(), ItemStateError::InventoryError(InventoryError::InventoryFull))); let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap(); assert_eq!(p1_items.items.len(), 2); @@ -2368,12 +2364,7 @@ async fn test_trade_not_enough_inventory_space_stacked() { let ack = ship.handle(ClientId(2), &RecvShipPacket::TradeConfirmed(TradeConfirmed { })).await.err().unwrap(); - match ack.downcast::>().unwrap() { - TransactionError::Action(a) => { - assert_eq!(a.downcast::().unwrap(), TradeError::NoInventorySpace); - }, - _ => panic!() - } + assert!(matches!(ack.downcast::().unwrap(), ItemStateError::InventoryError(InventoryError::InventoryFull))); let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap(); assert_eq!(p1_items.items.len(), 1); @@ -2482,12 +2473,7 @@ async fn test_trade_stack_too_big() { let ack = ship.handle(ClientId(2), &RecvShipPacket::TradeConfirmed(TradeConfirmed { })).await.err().unwrap(); - match ack.downcast::>().unwrap() { - TransactionError::Action(a) => { - assert_eq!(a.downcast::().unwrap(), TradeError::NoStackSpace); - }, - _ => panic!() - } + assert!(matches!(ack.downcast::().unwrap(), ItemStateError::InventoryError(InventoryError::StackFull))); let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap(); assert_eq!(p1_items.items.len(), 1); @@ -2557,16 +2543,16 @@ async fn test_trade_meseta() { let ack = ship.handle(ClientId(2), &RecvShipPacket::TradeConfirmed(TradeConfirmed { })).await.unwrap().collect::>(); assert_eq!(ack.len(), 5); - assert!(matches!(ack[0], (ClientId(1), SendShipPacket::Message(Message { - msg: GameMessage::CreateItem(CreateItem {..}), + assert!(matches!(ack[0], (ClientId(2), SendShipPacket::Message(Message { + msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem {..}), .. })))); - assert!(matches!(ack[1], (ClientId(2), SendShipPacket::Message(Message { + assert!(matches!(ack[1], (ClientId(1), SendShipPacket::Message(Message { msg: GameMessage::CreateItem(CreateItem {..}), .. })))); assert!(matches!(ack[2], (ClientId(2), SendShipPacket::Message(Message { - msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem {..}), + msg: GameMessage::CreateItem(CreateItem {..}), .. })))); assert!(matches!(ack[3], (ClientId(2), SendShipPacket::TradeSuccessful {..}))); @@ -3109,12 +3095,7 @@ async fn test_invalid_trade_when_both_inventories_are_full() { let ack = ship.handle(ClientId(2), &RecvShipPacket::TradeConfirmed(TradeConfirmed { })).await.err().unwrap(); - match ack.downcast::>().unwrap() { - TransactionError::Action(a) => { - assert_eq!(a.downcast::().unwrap(), TradeError::NoInventorySpace); - }, - _ => panic!() - } + assert!(matches!(ack.downcast::().unwrap(), ItemStateError::InventoryError(InventoryError::InventoryFull))); let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap(); assert_eq!(p1_items.items.len(), 30); @@ -3124,6 +3105,7 @@ async fn test_invalid_trade_when_both_inventories_are_full() { assert_eq!(p2_items.items.iter().filter(|i| matches!(i.individual().unwrap().item, item::ItemDetail::Weapon(item::weapon::Weapon { weapon: item::weapon::WeaponType::Handgun, ..}, ..))).count(), 30); } + #[async_std::test] async fn test_client_tries_to_start_two_trades() { let mut entity_gateway = InMemoryGateway::default(); @@ -3153,7 +3135,8 @@ async fn test_client_tries_to_start_two_trades() { target: 0, trade: TradeRequestCommand::Initialize(TradeRequestInitializeCommand::Initialize, 0) })))).await.err().unwrap(); - assert_eq!(ack.downcast::().unwrap(), TradeError::ClientAlreadyInTrade); + + assert!(matches!(ack.downcast::().unwrap(), ShipError::TradeError(TradeError::ClientAlreadyInTrade))); } #[async_std::test] @@ -3185,14 +3168,14 @@ async fn test_client_tries_trading_with_client_already_trading() { target: 0, trade: TradeRequestCommand::Initialize(TradeRequestInitializeCommand::Initialize, 0) })))).await.err().unwrap(); - assert_eq!(ack.downcast::().unwrap(), TradeError::OtherAlreadyInTrade); + assert!(matches!(ack.downcast::().unwrap(), ShipError::TradeError(TradeError::OtherAlreadyInTrade))); let ack = ship.handle(ClientId(3), &RecvShipPacket::DirectMessage(DirectMessage::new(1, GameMessage::TradeRequest(TradeRequest { client: 2, target: 0, trade: TradeRequestCommand::Initialize(TradeRequestInitializeCommand::Initialize, 1) })))).await.err().unwrap(); - assert_eq!(ack.downcast::().unwrap(), TradeError::OtherAlreadyInTrade); + assert!(matches!(ack.downcast::().unwrap(), ShipError::TradeError(TradeError::OtherAlreadyInTrade))); } #[async_std::test] @@ -3287,16 +3270,16 @@ async fn test_add_then_remove_individual_item() { let ack = ship.handle(ClientId(2), &RecvShipPacket::TradeConfirmed(TradeConfirmed { })).await.unwrap().collect::>(); assert_eq!(ack.len(), 5); - assert!(matches!(ack[0], (ClientId(1), SendShipPacket::Message(Message { - msg: GameMessage::CreateItem(CreateItem {..}), + assert!(matches!(ack[0], (ClientId(2), SendShipPacket::Message(Message { + msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem {..}), .. })))); - assert!(matches!(ack[1], (ClientId(2), SendShipPacket::Message(Message { + assert!(matches!(ack[1], (ClientId(1), SendShipPacket::Message(Message { msg: GameMessage::CreateItem(CreateItem {..}), .. })))); assert!(matches!(ack[2], (ClientId(2), SendShipPacket::Message(Message { - msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem {..}), + msg: GameMessage::CreateItem(CreateItem {..}), .. })))); assert!(matches!(ack[3], (ClientId(2), SendShipPacket::TradeSuccessful {..}))); @@ -3418,16 +3401,16 @@ async fn test_add_then_remove_stacked_item() { let ack = ship.handle(ClientId(2), &RecvShipPacket::TradeConfirmed(TradeConfirmed { })).await.unwrap().collect::>(); assert_eq!(ack.len(), 5); - assert!(matches!(ack[0], (ClientId(1), SendShipPacket::Message(Message { - msg: GameMessage::CreateItem(CreateItem {..}), + assert!(matches!(ack[0], (ClientId(2), SendShipPacket::Message(Message { + msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem {..}), .. })))); - assert!(matches!(ack[1], (ClientId(2), SendShipPacket::Message(Message { + assert!(matches!(ack[1], (ClientId(1), SendShipPacket::Message(Message { msg: GameMessage::CreateItem(CreateItem {..}), .. })))); assert!(matches!(ack[2], (ClientId(2), SendShipPacket::Message(Message { - msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem {..}), + msg: GameMessage::CreateItem(CreateItem {..}), .. })))); assert!(matches!(ack[3], (ClientId(2), SendShipPacket::TradeSuccessful {..}))); @@ -3632,16 +3615,16 @@ async fn test_add_then_remove_meseta() { let ack = ship.handle(ClientId(2), &RecvShipPacket::TradeConfirmed(TradeConfirmed { })).await.unwrap().collect::>(); assert_eq!(ack.len(), 5); - assert!(matches!(ack[0], (ClientId(1), SendShipPacket::Message(Message { - msg: GameMessage::CreateItem(CreateItem {..}), + assert!(matches!(ack[0], (ClientId(2), SendShipPacket::Message(Message { + msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem {..}), .. })))); - assert!(matches!(ack[1], (ClientId(2), SendShipPacket::Message(Message { + assert!(matches!(ack[1], (ClientId(1), SendShipPacket::Message(Message { msg: GameMessage::CreateItem(CreateItem {..}), .. })))); assert!(matches!(ack[2], (ClientId(2), SendShipPacket::Message(Message { - msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem {..}), + msg: GameMessage::CreateItem(CreateItem {..}), .. })))); assert!(matches!(ack[3], (ClientId(2), SendShipPacket::TradeSuccessful {..}))); @@ -4349,16 +4332,16 @@ async fn test_dropping_item_after_trade() { let ack = ship.handle(ClientId(2), &RecvShipPacket::TradeConfirmed(TradeConfirmed { })).await.unwrap().collect::>(); assert_eq!(ack.len(), 5); - assert!(matches!(ack[0], (ClientId(1), SendShipPacket::Message(Message { - msg: GameMessage::CreateItem(CreateItem {..}), + assert!(matches!(ack[0], (ClientId(2), SendShipPacket::Message(Message { + msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem {..}), .. })))); - assert!(matches!(ack[1], (ClientId(2), SendShipPacket::Message(Message { + assert!(matches!(ack[1], (ClientId(1), SendShipPacket::Message(Message { msg: GameMessage::CreateItem(CreateItem {..}), .. })))); assert!(matches!(ack[2], (ClientId(2), SendShipPacket::Message(Message { - msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem {..}), + msg: GameMessage::CreateItem(CreateItem {..}), .. })))); assert!(matches!(ack[3], (ClientId(2), SendShipPacket::TradeSuccessful {..}))); -- 2.36.0 From 2775f81cc97282191d66934de4105b06e7c2982a Mon Sep 17 00:00:00 2001 From: jake Date: Mon, 18 Jul 2022 19:25:47 -0600 Subject: [PATCH 46/60] fix some meseta-taking things --- src/ship/items/actions.rs | 22 ++++++++++++ src/ship/packet/handler/message.rs | 57 +++++++++++++++--------------- src/ship/ship.rs | 6 ++-- 3 files changed, 55 insertions(+), 30 deletions(-) diff --git a/src/ship/items/actions.rs b/src/ship/items/actions.rs index 43f41b2..544e17a 100644 --- a/src/ship/items/actions.rs +++ b/src/ship/items/actions.rs @@ -1123,3 +1123,25 @@ where Ok((transaction, (p1_new_items, p2_new_items))) }).await } + + +pub async fn take_meseta<'a, EG> ( + item_state: &'a mut ItemState, + entity_gateway: &mut EG, + character_id: &CharacterEntityId, + meseta: Meseta) + -> Result<(), ItemStateError> +where + EG: EntityGateway, +{ + entity_gateway.with_transaction(|mut transaction| async move { + let item_state_proxy = ItemStateProxy::new(item_state); + let ((item_state_proxy, transaction), p1_removed_items) = ItemStateAction::default() + .act(take_meseta_from_inventory(*character_id, meseta.0)) + .commit((item_state_proxy, transaction)) + .await?; + + item_state_proxy.commit(); + Ok((transaction, ())) + }).await +} diff --git a/src/ship/packet/handler/message.rs b/src/ship/packet/handler/message.rs index 4680c6c..dbb4695 100644 --- a/src/ship/packet/handler/message.rs +++ b/src/ship/packet/handler/message.rs @@ -1,14 +1,15 @@ use libpso::packet::ship::*; use libpso::packet::messages::*; use crate::entity::gateway::EntityGateway; +use crate::entity::item::Meseta; use crate::common::serverstate::ClientId; use crate::common::leveltable::CharacterLevelTable; use crate::ship::ship::{SendShipPacket, ShipError, Rooms, Clients, ItemDropLocation}; use crate::ship::location::{ClientLocation, ClientLocationError}; -use crate::ship::items::{ItemManager, ClientItemId}; +use crate::ship::items::ClientItemId; use crate::ship::packet::builder; use crate::ship::items::state::ItemState; -use crate::ship::items::actions::{drop_item, drop_partial_item, drop_meseta, equip_item, unequip_item, sort_inventory, use_item, feed_mag, sell_item}; +use crate::ship::items::actions::{drop_item, drop_partial_item, drop_meseta, equip_item, unequip_item, sort_inventory, use_item, feed_mag, sell_item, take_meseta}; pub async fn request_exp(id: ClientId, request_exp: &RequestExp, @@ -257,25 +258,25 @@ pub fn update_player_position(id: ClientId, } pub async fn charge_attack(id: ClientId, - charge: &ChargeAttack, - clients: &mut Clients, - entity_gateway: &mut EG, - item_manager: &mut ItemManager) - -> Result + Send>, anyhow::Error> + charge: &ChargeAttack, + entity_gateway: &mut EG, + client_location: &ClientLocation, + clients: &mut Clients, + item_state: &mut ItemState) + -> Result + Send>, anyhow::Error> where EG: EntityGateway { let client = clients.get_mut(&id).ok_or(ShipError::ClientNotFound(id))?; - let meseta = item_manager.get_character_meseta_mut(&client.character.id)?; - - if meseta.0 >= charge.meseta { - meseta.0 -= charge.meseta; - entity_gateway.set_character_meseta(&client.character.id, *meseta).await?; - // TODO: this should probably echo the packet - Ok(Box::new(None.into_iter())) - } else { - Err(ShipError::NotEnoughMeseta(id, meseta.0).into()) - } + + // TODO: should probably validate this to be a legit number, I'd just hardcode 200 but vjaya + take_meseta(item_state, entity_gateway, &client.character.id, Meseta(charge.meseta)).await?; + + let charge = charge.clone(); + Ok(Box::new(client_location.get_client_neighbors(id).unwrap().into_iter() + .map(move |client| { + (client.client, SendShipPacket::Message(Message::new(GameMessage::ChargeAttack(charge.clone())))) + }))) } pub async fn player_uses_item(id: ClientId, @@ -294,24 +295,24 @@ where } pub async fn player_used_medical_center(id: ClientId, - _pumc: &PlayerUsedMedicalCenter, // not needed? + pumc: &PlayerUsedMedicalCenter, entity_gateway: &mut EG, + client_location: &ClientLocation, clients: &mut Clients, - item_manager: &mut ItemManager) + item_state: &mut ItemState) -> Result + Send>, anyhow::Error> where EG: EntityGateway { let client = clients.get_mut(&id).ok_or(ShipError::ClientNotFound(id))?; - let meseta = item_manager.get_character_meseta_mut(&client.character.id)?; - if meseta.0 >= 10 { - meseta.0 -= 10; - entity_gateway.set_character_meseta(&client.character.id, *meseta).await?; - // TODO: this should probably echo the packet - Ok(Box::new(None.into_iter())) - } else { - Err(ShipError::NotEnoughMeseta(id, meseta.0).into()) - } + + take_meseta(item_state, entity_gateway, &client.character.id, Meseta(10)).await?; + + let pumc = pumc.clone(); + Ok(Box::new(client_location.get_client_neighbors(id).unwrap().into_iter() + .map(move |client| { + (client.client, SendShipPacket::Message(Message::new(GameMessage::PlayerUsedMedicalCenter(pumc.clone())))) + }))) } diff --git a/src/ship/ship.rs b/src/ship/ship.rs index 9909f63..9302fb7 100644 --- a/src/ship/ship.rs +++ b/src/ship/ship.rs @@ -502,14 +502,16 @@ impl ShipServerState { handler::message::update_player_position(id, msg, &mut self.clients, &block.client_location, &block.rooms)? }, GameMessage::ChargeAttack(charge_attack) => { - handler::message::charge_attack(id, charge_attack, &mut self.clients, &mut self.entity_gateway, &mut self.item_manager).await? + let block = self.blocks.with_client(id, &self.clients)?; + handler::message::charge_attack(id, charge_attack, &mut self.entity_gateway, &block.client_location, &mut self.clients, &mut self.item_state).await? }, GameMessage::PlayerUseItem(player_use_item) => { let block = self.blocks.with_client(id, &self.clients)?; handler::message::player_uses_item(id, player_use_item, &mut self.entity_gateway, &block.client_location, &mut self.clients, &mut self.item_state).await? }, GameMessage::PlayerUsedMedicalCenter(player_used_medical_center) => { - handler::message::player_used_medical_center(id, player_used_medical_center, &mut self.entity_gateway, &mut self.clients, &mut self.item_manager).await? + let block = self.blocks.with_client(id, &self.clients)?; + handler::message::player_used_medical_center(id, player_used_medical_center, &mut self.entity_gateway, &block.client_location, &mut self.clients, &mut self.item_state).await? }, GameMessage::PlayerFeedMag(player_feed_mag) => { let block = self.blocks.with_client(id, &self.clients)?; -- 2.36.0 From ff072ca02675739d8c6b361a1327e877c49e4dc0 Mon Sep 17 00:00:00 2001 From: jake Date: Mon, 18 Jul 2022 20:51:35 -0600 Subject: [PATCH 47/60] enemy drop refactor --- src/ship/items/actions.rs | 138 +++++++++++++++++++++- src/ship/items/state.rs | 7 +- src/ship/packet/builder/message.rs | 12 +- src/ship/packet/handler/direct_message.rs | 14 +-- src/ship/ship.rs | 4 +- 5 files changed, 158 insertions(+), 17 deletions(-) diff --git a/src/ship/items/actions.rs b/src/ship/items/actions.rs index 544e17a..bd9544a 100644 --- a/src/ship/items/actions.rs +++ b/src/ship/items/actions.rs @@ -10,9 +10,11 @@ use crate::ship::items::state::{ItemState, ItemStateProxy, ItemStateAction, Item StackedItemDetail, BankItem, BankItemDetail, InventoryItemDetail, IndividualItemDetail}; use crate::ship::items::apply_item::apply_item; use crate::entity::item::{ItemDetail, NewItemEntity, TradeId}; +use crate::entity::item::tool::Tool; use crate::ship::shops::ShopItem; use crate::ship::trade::TradeItem; use crate::ship::location::{AreaClient, RoomId}; +use crate::ship::drops::{ItemDrop, ItemDropType}; pub enum TriggerCreateItem { Yes, @@ -239,7 +241,7 @@ fn add_meseta_to_shared_floor(character_id: CharacterEntityId, amount: u32, map_ }; let mut floor = item_state.floor(&character_id)?; - let floor_item = floor.add_item(floor_item).clone(); + let floor_item = floor.add_shared_item(floor_item).clone(); item_state.set_floor(floor); Ok(((item_state, transaction), floor_item)) @@ -1145,3 +1147,137 @@ where Ok((transaction, ())) }).await } + +fn convert_item_drop_to_floor_item(character_id: CharacterEntityId, item_drop: ItemDrop) + -> impl for<'a> Fn((ItemStateProxy<'a>, Box), ()) + -> Pin, Box), FloorItem), ItemStateError>> + Send + 'a>> + Clone +{ + move |(mut item_state, mut transaction), _| { + let item_drop = item_drop.clone(); + Box::pin(async move { + enum ItemOrMeseta { + Individual(ItemDetail), + Stacked(Tool), + Meseta(Meseta) + } + + let item = match item_drop.item { + ItemDropType::Weapon(w) => ItemOrMeseta::Individual(ItemDetail::Weapon(w)), + ItemDropType::Armor(w) => ItemOrMeseta::Individual(ItemDetail::Armor(w)), + ItemDropType::Shield(w) => ItemOrMeseta::Individual(ItemDetail::Shield(w)), + ItemDropType::Unit(w) => ItemOrMeseta::Individual(ItemDetail::Unit(w)), + ItemDropType::TechniqueDisk(w) => ItemOrMeseta::Individual(ItemDetail::TechniqueDisk(w)), + ItemDropType::Mag(w) => ItemOrMeseta::Individual(ItemDetail::Mag(w)), + ItemDropType::Tool(t) => { + if t.tool.is_stackable() { + ItemOrMeseta::Stacked(t) + } + else { + ItemOrMeseta::Individual(ItemDetail::Tool(t)) + } + }, + ItemDropType::Meseta(m) => ItemOrMeseta::Meseta(Meseta(m)), + }; + + let item_id = item_state.new_item_id()?; + + let floor_item = match item { + ItemOrMeseta::Individual(item_detail) => { + let entity = transaction.gateway().create_item(NewItemEntity { + item: item_detail.clone(), + }).await?; + transaction.gateway().add_item_note(&entity.id, ItemNote::EnemyDrop { + character_id, + map_area: item_drop.map_area, + x: item_drop.x, + y: item_drop.y, + z: item_drop.z, + }).await?; + FloorItem { + item_id, + item: FloorItemDetail::Individual(IndividualItemDetail { + entity_id: entity.id, + item: item_detail, + }), + map_area: item_drop.map_area, + x: item_drop.x, + y: item_drop.y, + z: item_drop.z, + } + }, + ItemOrMeseta::Stacked(tool) => { + let entity = transaction.gateway().create_item(NewItemEntity { + item: ItemDetail::Tool(tool), + }).await?; + transaction.gateway().add_item_note(&entity.id, ItemNote::EnemyDrop { + character_id, + map_area: item_drop.map_area, + x: item_drop.x, + y: item_drop.y, + z: item_drop.z, + }).await?; + FloorItem { + item_id, + item: FloorItemDetail::Stacked(StackedItemDetail{ + entity_ids: vec![entity.id], + tool, + }), + map_area: item_drop.map_area, + x: item_drop.x, + y: item_drop.y, + z: item_drop.z, + } + }, + ItemOrMeseta::Meseta(meseta) => { + FloorItem { + item_id, + item: FloorItemDetail::Meseta(meseta), + map_area: item_drop.map_area, + x: item_drop.x, + y: item_drop.y, + z: item_drop.z, + } + }, + }; + + Ok(((item_state, transaction), floor_item)) + }) + } +} + +fn add_item_to_local_floor(character_id: CharacterEntityId) + -> impl for<'a> Fn((ItemStateProxy<'a>, Box), FloorItem) + -> Pin, Box), FloorItem), ItemStateError>> + Send + 'a>> +{ + move |(mut item_state, transaction) , floor_item| { + Box::pin(async move { + let mut floor = item_state.floor(&character_id)?; + let item = floor.add_local_item(floor_item).clone(); + item_state.set_floor(floor); + + Ok(((item_state, transaction), item)) + }) + } +} + +pub async fn enemy_drops_item<'a, EG> ( + item_state: &'a mut ItemState, + entity_gateway: &mut EG, + character_id: CharacterEntityId, + item_drop: ItemDrop) + -> Result +where + EG: EntityGateway, +{ + entity_gateway.with_transaction(|mut transaction| async move { + let item_state_proxy = ItemStateProxy::new(item_state); + let ((item_state_proxy, transaction), floor_item) = ItemStateAction::default() + .act(convert_item_drop_to_floor_item(character_id, item_drop)) + .act(add_item_to_local_floor(character_id)) + .commit((item_state_proxy, transaction)) + .await?; + + item_state_proxy.commit(); + Ok((transaction, floor_item)) + }).await +} diff --git a/src/ship/items/state.rs b/src/ship/items/state.rs index 22ce757..0a69991 100644 --- a/src/ship/items/state.rs +++ b/src/ship/items/state.rs @@ -1214,10 +1214,15 @@ impl FloorState { &self.shared.0[self.shared.0.len()-1] } - pub fn add_item(&mut self, floor_item: FloorItem) -> &FloorItem { + pub fn add_shared_item(&mut self, floor_item: FloorItem) -> &FloorItem { self.shared.0.push(floor_item); &self.shared.0[self.shared.0.len()-1] } + + pub fn add_local_item(&mut self, floor_item: FloorItem) -> &FloorItem { + self.local.0.push(floor_item); + &self.local.0[self.local.0.len()-1] + } } diff --git a/src/ship/packet/builder/message.rs b/src/ship/packet/builder/message.rs index a349b8f..b149508 100644 --- a/src/ship/packet/builder/message.rs +++ b/src/ship/packet/builder/message.rs @@ -12,19 +12,19 @@ use std::convert::TryInto; use crate::ship::shops::ShopItem; -pub fn item_drop(client: u8, target: u8, item_drop: &FloorItem) -> Result { +pub fn item_drop(client: u8, target: u8, item_drop: &FloorItem2) -> Result { let item_bytes = item_drop.as_client_bytes(); Ok(ItemDrop { client, target, - map_area: item_drop.map_area().area_value(), + map_area: item_drop.map_area.area_value(), variety: 0, unknown: 0, - x: item_drop.x(), - z: item_drop.z(), - y: item_drop.y(), + x: item_drop.x, + z: item_drop.z, + y: item_drop.y, item_bytes: item_bytes[0..12].try_into()?, - item_id: item_drop.item_id().0, + item_id: item_drop.item_id.0, item_bytes2: item_bytes[12..16].try_into()?, unknown2: 0, }) diff --git a/src/ship/packet/handler/direct_message.rs b/src/ship/packet/handler/direct_message.rs index 1cc319a..511f218 100644 --- a/src/ship/packet/handler/direct_message.rs +++ b/src/ship/packet/handler/direct_message.rs @@ -15,7 +15,7 @@ use libpso::utf8_to_utf16_array; use crate::ship::packet::builder; use crate::ship::shops::{ShopItem, ToolShopItem, ArmorShopItem}; use crate::ship::items::state::{ItemState, FloorType, FloorItemDetail}; -use crate::ship::items::actions::{pick_up_item, withdraw_meseta, deposit_meseta, withdraw_item, deposit_item, buy_shop_item, TriggerCreateItem}; +use crate::ship::items::actions::{pick_up_item, withdraw_meseta, deposit_meseta, withdraw_item, deposit_item, buy_shop_item, enemy_drops_item, TriggerCreateItem}; const BANK_ACTION_DEPOSIT: u8 = 0; const BANK_ACTION_WITHDRAW: u8 = 1; @@ -76,7 +76,7 @@ pub async fn request_item(id: ClientId, client_location: &ClientLocation, clients: &mut Clients, rooms: &mut Rooms, - item_manager: &mut ItemManager) + item_state: &mut ItemState) -> Result + Send>, anyhow::Error> where EG: EntityGateway @@ -111,8 +111,8 @@ where item: item_drop, }; let client = clients.get_mut(&area_client.client).ok_or(ShipError::ClientNotFound(area_client.client))?; - let floor_item = item_manager.enemy_drop_item_on_local_floor(entity_gateway, &client.character, item_drop).await?; - let item_drop_msg = builder::message::item_drop(request_item.client, request_item.target, floor_item)?; + let floor_item = enemy_drops_item(item_state, entity_gateway, client.character.id, item_drop).await?; + let item_drop_msg = builder::message::item_drop(request_item.client, request_item.target, &floor_item)?; item_drop_packets.push((area_client.client, SendShipPacket::Message(Message::new(GameMessage::ItemDrop(item_drop_msg))))); } @@ -195,7 +195,7 @@ pub async fn request_box_item(id: ClientId, client_location: &ClientLocation, clients: &mut Clients, rooms: &mut Rooms, - item_manager: &mut ItemManager) + item_state: &mut ItemState) -> Result + Send>, anyhow::Error> where EG: EntityGateway @@ -230,8 +230,8 @@ EG: EntityGateway item: item_drop, }; let client = clients.get_mut(&area_client.client).ok_or(ShipError::ClientNotFound(area_client.client))?; - let floor_item = item_manager.enemy_drop_item_on_local_floor(entity_gateway, &client.character, item_drop).await?; // TODO: unwrap - let item_drop_msg = builder::message::item_drop(box_drop_request.client, box_drop_request.target, floor_item)?; + let floor_item = enemy_drops_item(item_state, entity_gateway, client.character.id, item_drop).await?; + let item_drop_msg = builder::message::item_drop(box_drop_request.client, box_drop_request.target, &floor_item)?; item_drop_packets.push((area_client.client, SendShipPacket::Message(Message::new(GameMessage::ItemDrop(item_drop_msg))))) } diff --git a/src/ship/ship.rs b/src/ship/ship.rs index 9302fb7..d7d61a0 100644 --- a/src/ship/ship.rs +++ b/src/ship/ship.rs @@ -548,13 +548,13 @@ impl ShipServerState { handler::direct_message::guildcard_send(id, guildcard_send, target, &block.client_location, &self.clients) }, GameMessage::RequestItem(request_item) => { - handler::direct_message::request_item(id, request_item, &mut self.entity_gateway, &block.client_location, &mut self.clients, &mut block.rooms, &mut self.item_manager).await? + handler::direct_message::request_item(id, request_item, &mut self.entity_gateway, &block.client_location, &mut self.clients, &mut block.rooms, &mut self.item_state).await? }, GameMessage::PickupItem(pickup_item) => { handler::direct_message::pickup_item(id, pickup_item, &mut self.entity_gateway, &block.client_location, &mut self.clients, &mut self.item_state).await? }, GameMessage::BoxDropRequest(box_drop_request) => { - handler::direct_message::request_box_item(id, box_drop_request, &mut self.entity_gateway, &block.client_location, &mut self.clients, &mut block.rooms, &mut self.item_manager).await? + handler::direct_message::request_box_item(id, box_drop_request, &mut self.entity_gateway, &block.client_location, &mut self.clients, &mut block.rooms, &mut self.item_state).await? }, GameMessage::BankRequest(_bank_request) => { handler::direct_message::send_bank_list(id, &self.clients, &mut self.item_state).await? -- 2.36.0 From b0b34b6616e10e3f146899903818da34cc0bc6f1 Mon Sep 17 00:00:00 2001 From: jake Date: Mon, 18 Jul 2022 21:03:07 -0600 Subject: [PATCH 48/60] add postgres trade stuff --- .../postgres/migrations/V0005__trade.sql | 5 +++++ src/entity/gateway/postgres/models.rs | 16 ++++++++++++++++ src/entity/gateway/postgres/postgres.rs | 18 ++++++++++++++++++ 3 files changed, 39 insertions(+) create mode 100644 src/entity/gateway/postgres/migrations/V0005__trade.sql diff --git a/src/entity/gateway/postgres/migrations/V0005__trade.sql b/src/entity/gateway/postgres/migrations/V0005__trade.sql new file mode 100644 index 0000000..16d3b84 --- /dev/null +++ b/src/entity/gateway/postgres/migrations/V0005__trade.sql @@ -0,0 +1,5 @@ +create table trades ( + id serial primary key not null, + character1 integer references character (id) not null, + character2 integer references character (id) not null, +); diff --git a/src/entity/gateway/postgres/models.rs b/src/entity/gateway/postgres/models.rs index 81296ae..c1257ee 100644 --- a/src/entity/gateway/postgres/models.rs +++ b/src/entity/gateway/postgres/models.rs @@ -858,3 +858,19 @@ impl From<(CharacterEntityId, EquippedEntity)> for PgEquipped { } } +#[derive(Debug, sqlx::FromRow)] +pub struct PgTradeEntity { + id: i32, + character1: i32, + character2: i32, +} + +impl From for TradeEntity { + fn from(other: PgTradeEntity) -> TradeEntity { + TradeEntity { + id: TradeId(other.id as u32), + character1: CharacterEntityId(other.character1 as u32), + character2: CharacterEntityId(other.character2 as u32), + } + } +} diff --git a/src/entity/gateway/postgres/postgres.rs b/src/entity/gateway/postgres/postgres.rs index 938c946..81ae972 100644 --- a/src/entity/gateway/postgres/postgres.rs +++ b/src/entity/gateway/postgres/postgres.rs @@ -558,6 +558,16 @@ async fn get_bank_meseta(conn: &mut sqlx::PgConnection, char_id: &CharacterEntit Ok(Meseta(meseta.0 as u32)) } +async fn create_trade(conn: &mut sqlx::PgConnection, char_id1: &CharacterEntityId, char_id2: &CharacterEntityId) -> Result +{ + let trade = sqlx::query_as::<_, PgTradeEntity>(r#"insert into trades (character1, character2) values ($1, $2) returning *;"#) + .bind(char_id1.0) + .bind(char_id2.0) + .fetch_one(conn) + .await?; + Ok(trade.into()) +} + #[async_trait::async_trait] impl EntityGateway for PostgresGateway { async fn transaction<'a>(&'a mut self) -> Result, GatewayError> @@ -693,6 +703,10 @@ impl EntityGateway for PostgresGateway { async fn get_bank_meseta(&mut self, char_id: &CharacterEntityId, bank: &BankName) -> Result { get_bank_meseta(&mut *self.pool.acquire().await?, char_id, bank).await } + + async fn create_trade(&mut self, char_id1: &CharacterEntityId, char_id2: &CharacterEntityId) -> Result { + create_trade(&mut *self.pool.acquire().await?, char_id1, char_id2).await + } } @@ -809,5 +823,9 @@ impl<'c> EntityGateway for PostgresTransaction<'c> { async fn get_bank_meseta(&mut self, char_id: &CharacterEntityId, bank: &BankName) -> Result { get_bank_meseta(&mut *self.pgtransaction, char_id, bank).await } + + async fn create_trade(&mut self, char_id1: &CharacterEntityId, char_id2: &CharacterEntityId) -> Result { + create_trade(&mut *self.pgtransaction, char_id1, char_id2).await + } } -- 2.36.0 From 9c878ed484d563b9c2e21a8b94d16296192081d5 Mon Sep 17 00:00:00 2001 From: jake Date: Mon, 18 Jul 2022 23:57:54 -0600 Subject: [PATCH 49/60] refactor tekking --- src/entity/item/mod.rs | 5 ++ src/ship/items/actions.rs | 64 +++++++++++++++++++++++ src/ship/items/state.rs | 39 ++++++++++---- src/ship/packet/builder/message.rs | 4 +- src/ship/packet/handler/direct_message.rs | 27 +++++----- src/ship/packet/handler/trade.rs | 2 +- src/ship/ship.rs | 4 +- 7 files changed, 116 insertions(+), 29 deletions(-) diff --git a/src/entity/item/mod.rs b/src/entity/item/mod.rs index 6e51550..e89a7a4 100644 --- a/src/entity/item/mod.rs +++ b/src/entity/item/mod.rs @@ -334,3 +334,8 @@ pub struct TradeEntity { pub character1: CharacterEntityId, pub character2: CharacterEntityId, } + +#[derive(Clone, Debug)] +pub enum ItemModifier { + WeaponModifier(weapon::WeaponModifier), +} diff --git a/src/ship/items/actions.rs b/src/ship/items/actions.rs index bd9544a..49c9c7f 100644 --- a/src/ship/items/actions.rs +++ b/src/ship/items/actions.rs @@ -11,6 +11,8 @@ use crate::ship::items::state::{ItemState, ItemStateProxy, ItemStateAction, Item use crate::ship::items::apply_item::apply_item; use crate::entity::item::{ItemDetail, NewItemEntity, TradeId}; use crate::entity::item::tool::Tool; +use crate::entity::item::weapon::WeaponModifier; +use crate::entity::item::ItemModifier; use crate::ship::shops::ShopItem; use crate::ship::trade::TradeItem; use crate::ship::location::{AreaClient, RoomId}; @@ -1281,3 +1283,65 @@ where Ok((transaction, floor_item)) }).await } + +fn apply_modifier_to_inventory_item(character_id: CharacterEntityId, modifier: ItemModifier) + -> impl for<'a> Fn((ItemStateProxy<'a>, Box), InventoryItem) + -> Pin, Box), InventoryItem), ItemStateError>> + Send + 'a>> +{ + move |(item_state, mut transaction), mut inventory_item| { + let modifier = modifier.clone(); + Box::pin(async move { + match (&inventory_item.item, modifier) { + (InventoryItemDetail::Individual(IndividualItemDetail{entity_id, item: ItemDetail::Weapon(mut weapon), ..}), ItemModifier::WeaponModifier(modifier)) => { + weapon.apply_modifier(&modifier); + transaction.gateway().add_weapon_modifier(&entity_id, modifier).await?; + }, + _ => return Err(ItemStateError::InvalidModifier) + } + + Ok(((item_state, transaction), inventory_item)) + }) + } +} + +fn as_individual_item() + -> impl for<'a> Fn((ItemStateProxy<'a>, Box), InventoryItem) + -> Pin, Box), IndividualItemDetail), ItemStateError>> + Send + 'a>> +{ + move |(item_state, transaction), inventory_item| { + Box::pin(async move { + let item = match inventory_item.item { + InventoryItemDetail::Individual(individual_item) => individual_item, + _ => return Err(ItemStateError::WrongItemType(inventory_item.item_id)) + }; + + Ok(((item_state, transaction), item)) + }) + } +} + + +pub async fn apply_modifier<'a, EG> ( + item_state: &'a mut ItemState, + entity_gateway: &mut EG, + character: &CharacterEntity, + item_id: ClientItemId, + modifier: ItemModifier) + -> Result +where + EG: EntityGateway, +{ + entity_gateway.with_transaction(|transaction| async move { + let item_state_proxy = ItemStateProxy::new(item_state); + let ((item_state_proxy, transaction), item) = ItemStateAction::default() + .act(take_item_from_inventory(character.id, item_id, 1)) + .act(apply_modifier_to_inventory_item(character.id, modifier)) + .act(add_item_to_inventory(character.clone())) + .act(as_individual_item()) + .commit((item_state_proxy, transaction)) + .await?; + + item_state_proxy.commit(); + Ok((transaction, item)) + }).await +} diff --git a/src/ship/items/state.rs b/src/ship/items/state.rs index 0a69991..dd39b83 100644 --- a/src/ship/items/state.rs +++ b/src/ship/items/state.rs @@ -10,6 +10,7 @@ use crate::ship::location::{AreaClient, RoomId}; use crate::entity::character::{CharacterEntity, CharacterEntityId}; use crate::entity::gateway::{EntityGateway, GatewayError}; use crate::entity::item::tool::{Tool, ToolType}; +use crate::entity::item::weapon::Weapon; use crate::entity::item::mag::Mag; use crate::ship::drops::ItemDrop; use crate::ship::shops::{ShopItem, ArmorShopItem, ToolShopItem, WeaponShopItem}; @@ -69,6 +70,12 @@ pub enum ItemStateError { #[error("item is not sellable")] ItemNotSellable, + + #[error("could not modify item")] + InvalidModifier, + + #[error("wrong item type ")] + WrongItemType(ClientItemId), } pub enum FloorType { @@ -221,6 +228,13 @@ pub struct IndividualItemDetail { } impl IndividualItemDetail { + pub fn as_weapon(&self) -> Option<&Weapon> { + match &self.item { + ItemDetail::Weapon(weapon) => Some(weapon), + _ => None + } + } + pub fn as_mag(&self) -> Option<&Mag> { match &self.item { ItemDetail::Mag(mag) => Some(mag), @@ -234,6 +248,20 @@ impl IndividualItemDetail { _ => None } } + + pub fn as_client_bytes(&self) -> [u8; 16] { + match &self.item { + ItemDetail::Weapon(w) => w.as_bytes(), + ItemDetail::Armor(a) => a.as_bytes(), + ItemDetail::Shield(s) => s.as_bytes(), + ItemDetail::Unit(u) => u.as_bytes(), + ItemDetail::Tool(t) => t.as_individual_bytes(), + ItemDetail::TechniqueDisk(d) => d.as_bytes(), + ItemDetail::Mag(m) => m.as_bytes(), + ItemDetail::ESWeapon(e) => e.as_bytes(), + } + } + } #[derive(Clone, Debug)] @@ -287,16 +315,7 @@ impl InventoryItemDetail { pub fn as_client_bytes(&self) -> [u8; 16] { match self { InventoryItemDetail::Individual(item) => { - match &item.item { - ItemDetail::Weapon(w) => w.as_bytes(), - ItemDetail::Armor(a) => a.as_bytes(), - ItemDetail::Shield(s) => s.as_bytes(), - ItemDetail::Unit(u) => u.as_bytes(), - ItemDetail::Tool(t) => t.as_individual_bytes(), - ItemDetail::TechniqueDisk(d) => d.as_bytes(), - ItemDetail::Mag(m) => m.as_bytes(), - ItemDetail::ESWeapon(e) => e.as_bytes(), - } + item.as_client_bytes() }, InventoryItemDetail::Stacked(item) => { item.tool.as_stacked_bytes(item.entity_ids.len()) diff --git a/src/ship/packet/builder/message.rs b/src/ship/packet/builder/message.rs index b149508..827aec9 100644 --- a/src/ship/packet/builder/message.rs +++ b/src/ship/packet/builder/message.rs @@ -6,7 +6,7 @@ use crate::ship::ship::{ShipError}; use crate::ship::items::{ClientItemId, InventoryItem, StackedFloorItem, FloorItem, CharacterBank}; use crate::ship::items::state::FloorItem as FloorItem2; use crate::ship::items::state::InventoryItem as InventoryItem2; -use crate::ship::items::state::{BankState}; +use crate::ship::items::state::{BankState, IndividualItemDetail}; use crate::ship::location::AreaClient; use std::convert::TryInto; use crate::ship::shops::ShopItem; @@ -31,7 +31,7 @@ pub fn item_drop(client: u8, target: u8, item_drop: &FloorItem2) -> Result Result { +pub fn create_individual_item(area_client: AreaClient, item_id: ClientItemId, item: &IndividualItemDetail) -> Result { let bytes = item.as_client_bytes(); Ok(CreateItem { client: area_client.local_client.id(), diff --git a/src/ship/packet/handler/direct_message.rs b/src/ship/packet/handler/direct_message.rs index 511f218..e5df411 100644 --- a/src/ship/packet/handler/direct_message.rs +++ b/src/ship/packet/handler/direct_message.rs @@ -15,7 +15,7 @@ use libpso::utf8_to_utf16_array; use crate::ship::packet::builder; use crate::ship::shops::{ShopItem, ToolShopItem, ArmorShopItem}; use crate::ship::items::state::{ItemState, FloorType, FloorItemDetail}; -use crate::ship::items::actions::{pick_up_item, withdraw_meseta, deposit_meseta, withdraw_item, deposit_item, buy_shop_item, enemy_drops_item, TriggerCreateItem}; +use crate::ship::items::actions::{pick_up_item, withdraw_meseta, deposit_meseta, withdraw_item, deposit_item, buy_shop_item, enemy_drops_item, take_meseta, apply_modifier, TriggerCreateItem}; const BANK_ACTION_DEPOSIT: u8 = 0; const BANK_ACTION_WITHDRAW: u8 = 1; @@ -151,7 +151,7 @@ where let (item, floor_type) = item_state.get_floor_item(&client.character.id, &ClientItemId(pickup_item.item_id))?; let remove_item = builder::message::remove_item_from_floor(area_client, item)?; let create_item = match &item.item { - FloorItemDetail::Individual(individual_floor_item) => Some(builder::message::create_individual_item(area_client, item.item_id, &individual_floor_item.item)?), + FloorItemDetail::Individual(individual_floor_item) => Some(builder::message::create_individual_item(area_client, item.item_id, individual_floor_item)?), FloorItemDetail::Stacked(stacked_floor_item) => Some(builder::message::create_stacked_item(area_client, item.item_id, &stacked_floor_item.tool, stacked_floor_item.count())?), FloorItemDetail::Meseta(_) => None, //_ => Some(builder::message::create_item(area_client, &item)?), @@ -411,7 +411,7 @@ pub async fn request_tek_item(id: ClientId, tek_request: &TekRequest, entity_gateway: &mut EG, clients: &mut Clients, - item_manager: &mut ItemManager) + item_state: &mut ItemState) -> Result + Send>, anyhow::Error> where EG: EntityGateway @@ -429,13 +429,14 @@ where client.tek = Some((ClientItemId(tek_request.item_id), special_mod, percent_mod, grind_mod)); - let inventory = item_manager.get_character_inventory(&client.character)?; - let item = inventory.get_item_by_id(ClientItemId(tek_request.item_id)) + let inventory = item_state.get_character_inventory(&client.character)?; + let item = inventory.get_by_client_id(&ClientItemId(tek_request.item_id)) .ok_or(ItemManagerError::WrongItemType(ClientItemId(tek_request.item_id)))?; - let mut weapon = *item.individual() + let mut weapon = item.item.as_individual() .ok_or(ItemManagerError::WrongItemType(ClientItemId(tek_request.item_id)))? - .weapon() - .ok_or(ItemManagerError::WrongItemType(ClientItemId(tek_request.item_id)))?; + .as_weapon() + .ok_or(ItemManagerError::WrongItemType(ClientItemId(tek_request.item_id)))? + .clone(); weapon.apply_modifier(&item::weapon::WeaponModifier::Tekked { special: special_mod, @@ -443,9 +444,7 @@ where grind: grind_mod, }); - let character_meseta = item_manager.get_character_meseta_mut(&client.character.id)?; - character_meseta.0 -= 100; - entity_gateway.set_character_meseta(&client.character.id, *character_meseta).await?; + take_meseta(item_state, entity_gateway, &client.character.id, item::Meseta(100)).await?; let preview_pkt = builder::message::tek_preview(ClientItemId(tek_request.item_id), &weapon)?; @@ -457,7 +456,7 @@ pub async fn accept_tek_item(id: ClientId, entity_gateway: &mut EG, client_location: &ClientLocation, clients: &mut Clients, - item_manager: &mut ItemManager) + item_state: &mut ItemState) -> Result + Send>, anyhow::Error> where EG: EntityGateway @@ -475,9 +474,9 @@ where percent: percent_mod, grind: grind_mod, }; - let weapon = item_manager.replace_item_with_tekked(entity_gateway, &client.character, item_id, modifier).await?; + let weapon = apply_modifier(item_state, entity_gateway, &client.character, item_id, item::ItemModifier::WeaponModifier(modifier)).await?; - let create_item_pkt = builder::message::create_individual_item(area_client, item_id, &item::ItemDetail::Weapon(weapon))?; + let create_item_pkt = builder::message::create_individual_item(area_client, item_id, &weapon)?; let neighbors = client_location.get_client_neighbors(id).map_err(|err| -> ClientLocationError { err.into() })?; Ok(Box::new(neighbors.into_iter() diff --git a/src/ship/packet/handler/trade.rs b/src/ship/packet/handler/trade.rs index 050241f..b6b9faa 100644 --- a/src/ship/packet/handler/trade.rs +++ b/src/ship/packet/handler/trade.rs @@ -512,7 +512,7 @@ where .map(|(client, item)| { match item.item { InventoryItemDetail::Individual(individual_item) => { - GameMessage::CreateItem(builder::message::create_individual_item(client, item.item_id, &individual_item.item).unwrap()) + GameMessage::CreateItem(builder::message::create_individual_item(client, item.item_id, &individual_item).unwrap()) }, InventoryItemDetail::Stacked(stacked_item) => { GameMessage::CreateItem(builder::message::create_stacked_item(client, item.item_id, &stacked_item.tool, stacked_item.count()).unwrap()) diff --git a/src/ship/ship.rs b/src/ship/ship.rs index d7d61a0..6d821a0 100644 --- a/src/ship/ship.rs +++ b/src/ship/ship.rs @@ -569,10 +569,10 @@ impl ShipServerState { handler::direct_message::buy_item(id, buy_item, &mut self.entity_gateway, &block.client_location, &mut self.clients, &mut self.item_state).await? }, GameMessage::TekRequest(tek_request) => { - handler::direct_message::request_tek_item(id, tek_request, &mut self.entity_gateway, &mut self.clients, &mut self.item_manager).await? + handler::direct_message::request_tek_item(id, tek_request, &mut self.entity_gateway, &mut self.clients, &mut self.item_state).await? }, GameMessage::TekAccept(tek_accept) => { - handler::direct_message::accept_tek_item(id, tek_accept, &mut self.entity_gateway, &block.client_location, &mut self.clients, &mut self.item_manager).await? + handler::direct_message::accept_tek_item(id, tek_accept, &mut self.entity_gateway, &block.client_location, &mut self.clients, &mut self.item_state).await? }, GameMessage::TradeRequest(trade_request) => { handler::trade::trade_request(id, trade_request, target, &block.client_location, &mut self.clients, &mut self.item_state, &mut self.trades).await? -- 2.36.0 From 597603097094ce536250e2753d76a5043e2c411b Mon Sep 17 00:00:00 2001 From: jake Date: Tue, 19 Jul 2022 10:18:54 -0600 Subject: [PATCH 50/60] remove old item manager code --- Cargo.lock | 12 + src/ship/items/actions.rs | 1 - src/ship/items/apply_item.rs | 3 +- src/ship/items/bank.rs | 324 ------- src/ship/items/floor.rs | 253 ------ src/ship/items/inventory.rs | 988 ---------------------- src/ship/items/manager.rs | 1 - src/ship/items/mod.rs | 12 - src/ship/items/transaction.rs | 337 -------- src/ship/items/use_tool.rs | 163 ---- src/ship/packet/builder/lobby.rs | 1 - src/ship/packet/builder/message.rs | 18 +- src/ship/packet/builder/room.rs | 1 - src/ship/packet/handler/direct_message.rs | 10 +- src/ship/ship.rs | 3 - tests/test_shops.rs | 1 - tests/test_trade.rs | 1 - 17 files changed, 26 insertions(+), 2103 deletions(-) delete mode 100644 src/ship/items/bank.rs delete mode 100644 src/ship/items/floor.rs delete mode 100644 src/ship/items/inventory.rs delete mode 100644 src/ship/items/transaction.rs delete mode 100644 src/ship/items/use_tool.rs diff --git a/Cargo.lock b/Cargo.lock index 6ed5058..e85e554 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -172,6 +172,17 @@ dependencies = [ "winapi", ] +[[package]] +name = "async-recursion" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2cda8f4bcc10624c4e85bc66b3f452cca98cfa5ca002dc83a16aad2367641bea" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "async-std" version = "1.11.0" @@ -587,6 +598,7 @@ version = "0.1.0" dependencies = [ "ages-prs", "anyhow", + "async-recursion", "async-std", "async-trait", "barrel", diff --git a/src/ship/items/actions.rs b/src/ship/items/actions.rs index 49c9c7f..10af797 100644 --- a/src/ship/items/actions.rs +++ b/src/ship/items/actions.rs @@ -11,7 +11,6 @@ use crate::ship::items::state::{ItemState, ItemStateProxy, ItemStateAction, Item use crate::ship::items::apply_item::apply_item; use crate::entity::item::{ItemDetail, NewItemEntity, TradeId}; use crate::entity::item::tool::Tool; -use crate::entity::item::weapon::WeaponModifier; use crate::entity::item::ItemModifier; use crate::ship::shops::ShopItem; use crate::ship::trade::TradeItem; diff --git a/src/ship/items/apply_item.rs b/src/ship/items/apply_item.rs index 1028ed7..ce4a701 100644 --- a/src/ship/items/apply_item.rs +++ b/src/ship/items/apply_item.rs @@ -1,12 +1,11 @@ use thiserror::Error; -use std::convert::TryFrom; use std::convert::TryInto; use crate::entity::gateway::{EntityGateway, GatewayError}; use crate::entity::character::CharacterEntity; use crate::entity::item::mag::{MagCell, MagCellError}; use crate::entity::item::tool::ToolType; use crate::entity::item::{ItemDetail, ItemEntityId}; -use crate::ship::items::state::{ItemStateProxy, InventoryState, InventoryItem, InventoryItemDetail, ItemStateError}; +use crate::ship::items::state::{ItemStateProxy, InventoryItem, InventoryItemDetail, ItemStateError}; #[derive(Error, Debug)] diff --git a/src/ship/items/bank.rs b/src/ship/items/bank.rs deleted file mode 100644 index dab026f..0000000 --- a/src/ship/items/bank.rs +++ /dev/null @@ -1,324 +0,0 @@ -use crate::ship::items::ClientItemId; -use libpso::character::character;//::InventoryItem; -use crate::entity::item::{ItemEntityId, ItemEntity, ItemDetail, BankEntity, BankItemEntity, BankName}; -use crate::entity::character::CharacterEntityId; -use crate::entity::item::tool::Tool; -use crate::ship::items::inventory::{InventoryItemHandle, InventoryItem}; - -const BANK_CAPACITY: usize = 200; - -#[derive(Debug, Clone)] -pub struct IndividualBankItem { - pub entity_id: ItemEntityId, - pub item_id: ClientItemId, - pub item: ItemDetail, -} - -#[derive(Debug, Clone)] -pub struct StackedBankItem { - pub entity_ids: Vec, - pub item_id: ClientItemId, - pub tool: Tool, -} - -impl StackedBankItem { - pub fn count(&self) -> usize { - self.entity_ids.len() - } - - pub fn take_entity_ids(&mut self, amount: usize) -> Option> { - if amount <= self.count() { - Some(self.entity_ids.drain(..amount).collect()) - } - else { - None - } - } -} - -#[derive(Debug, Clone)] -pub enum BankItem { - Individual(IndividualBankItem), - Stacked(StackedBankItem), -} - - -impl std::cmp::PartialEq for BankItem { - fn eq(&self, other: &BankItem) -> bool { - let mut self_bytes = [0u8; 4]; - let mut other_bytes = [0u8; 4]; - self_bytes.copy_from_slice(&self.as_client_bytes()[0..4]); - other_bytes.copy_from_slice(&other.as_client_bytes()[0..4]); - - let self_value = u32::from_be_bytes(self_bytes); - let other_value = u32::from_be_bytes(other_bytes); - - self_value.eq(&other_value) - } -} - -impl std::cmp::Eq for BankItem {} - -impl std::cmp::PartialOrd for BankItem { - fn partial_cmp(&self, other: &BankItem) -> Option { - //let self_bytes = self.as_client_bytes(); - //let other_bytes = other.as_client_bytes(); - let mut self_bytes = [0u8; 4]; - let mut other_bytes = [0u8; 4]; - self_bytes.copy_from_slice(&self.as_client_bytes()[0..4]); - other_bytes.copy_from_slice(&other.as_client_bytes()[0..4]); - - - let self_value = u32::from_be_bytes(self_bytes); - let other_value = u32::from_be_bytes(other_bytes); - - self_value.partial_cmp(&other_value) - } -} - -impl std::cmp::Ord for BankItem { - fn cmp(&self, other: &BankItem) -> std::cmp::Ordering { - //let self_bytes = self.as_client_bytes(); - //let other_bytes = other.as_client_bytes(); - let mut self_bytes = [0u8; 4]; - let mut other_bytes = [0u8; 4]; - self_bytes.copy_from_slice(&self.as_client_bytes()[0..4]); - other_bytes.copy_from_slice(&other.as_client_bytes()[0..4]); - - - let self_value = u32::from_le_bytes(self_bytes); - let other_value = u32::from_le_bytes(other_bytes); - - self_value.cmp(&other_value) - } -} - -impl BankItem { - pub fn set_item_id(&mut self, item_id: ClientItemId) { - match self { - BankItem::Individual(individual_bank_item) => { - individual_bank_item.item_id = item_id - }, - BankItem::Stacked(stacked_bank_item) => { - stacked_bank_item.item_id = item_id - } - } - } - - pub fn item_id(&self) -> ClientItemId { - match self { - BankItem::Individual(individual_bank_item) => { - individual_bank_item.item_id - }, - BankItem::Stacked(stacked_bank_item) => { - stacked_bank_item.item_id - } - } - } - - pub fn as_client_bytes(&self) -> [u8; 16] { - match self { - BankItem::Individual(item) => { - match &item.item { - ItemDetail::Weapon(w) => w.as_bytes(), - ItemDetail::Armor(a) => a.as_bytes(), - ItemDetail::Shield(s) => s.as_bytes(), - ItemDetail::Unit(u) => u.as_bytes(), - ItemDetail::Tool(t) => t.as_individual_bytes(), - ItemDetail::TechniqueDisk(d) => d.as_bytes(), - ItemDetail::Mag(m) => m.as_bytes(), - ItemDetail::ESWeapon(e) => e.as_bytes(), - } - }, - BankItem::Stacked(item) => { - item.tool.as_stacked_bytes(item.entity_ids.len()) - }, - } - } -} - - -pub struct BankItemHandle<'a> { - bank: &'a mut CharacterBank, - index: usize -} - -impl<'a> BankItemHandle<'a> { - pub fn item(&'a self) -> Option<&'a BankItem> { - self.bank.items.get(self.index) - } - - pub fn item_mut(&mut self) -> Option<&mut BankItem> { - self.bank.items.get_mut(self.index) - } - - pub fn remove_from_bank(self) { - self.bank.items.remove(self.index); - } -} - -pub struct CharacterBank { - item_id_counter: u32, - items: Vec -} - -impl CharacterBank { - pub fn new(mut items: Vec) -> CharacterBank { - items.sort(); - CharacterBank { - item_id_counter: 0, - items, - } - } - - pub fn initialize_item_ids(&mut self, base_item_id: u32) { - for (i, item) in self.items.iter_mut().enumerate() { - item.set_item_id(ClientItemId(base_item_id + i as u32)); - } - self.item_id_counter = base_item_id + self.items.len() as u32 + 1; - } - - pub fn get_item_handle_by_id(&mut self, item_id: ClientItemId) -> Option { - let (index, _) = self.items.iter() - .enumerate() - .find(|(_, item)| { - item.item_id() == item_id - })?; - Some(BankItemHandle { - bank: self, - index, - }) - } - - pub fn as_client_bank_items(&self) -> character::Bank { - self.items.iter() - .enumerate() - .fold(character::Bank::default(), |mut bank, (slot, item)| { - bank.item_count = (slot + 1) as u32; - let bytes = item.as_client_bytes(); - bank.items[slot].data1.copy_from_slice(&bytes[0..12]); - bank.items[slot].data2.copy_from_slice(&bytes[12..16]); - bank.items[slot].item_id = item.item_id().0; - - bank - }) - } - - pub fn as_client_bank_request(&self) -> Vec { - self.items.iter() - .map(|item| { - let bytes = item.as_client_bytes(); - let mut data1 = [0; 12]; - let mut data2 = [0; 4]; - data1.copy_from_slice(&bytes[0..12]); - data2.copy_from_slice(&bytes[12..16]); - let amount = match item { - BankItem::Individual(_individual_bank_item) => { - 1 - }, - BankItem::Stacked(stacked_bank_item) => { - stacked_bank_item.count() - }, - }; - character::BankItem { - data1, - data2, - item_id: item.item_id().0, - amount: amount as u16, - flags: 1, - } - }) - .collect() - } - - pub fn count(&self) -> usize { - self.items.len() - } - - pub fn deposit_item(&mut self, mut inventory_item: InventoryItemHandle, amount: usize) -> Option<&BankItem> { - let remove = match inventory_item.item_mut()? { - InventoryItem::Individual(individual_inventory_item) => { - if self.items.len() >= BANK_CAPACITY { - return None - } - self.items.push(BankItem::Individual(IndividualBankItem { - entity_id: individual_inventory_item.entity_id, - item_id: individual_inventory_item.item_id, - item: individual_inventory_item.item.clone(), - })); - true - }, - InventoryItem::Stacked(stacked_inventory_item) => { - let existing_bank_item = self.items.iter_mut() - .find_map(|item| { - if let BankItem::Stacked(stacked_bank_item) = item { - if stacked_bank_item.tool == stacked_inventory_item.tool { - return Some(stacked_bank_item) - } - } - None - }); - - match existing_bank_item { - Some(stacked_bank_item) => { - if stacked_bank_item.count() + stacked_inventory_item.count() > stacked_inventory_item.tool.max_stack() { - return None - } - - let mut deposited_entity_ids = stacked_inventory_item.take_entity_ids(amount)?; - stacked_bank_item.entity_ids.append(&mut deposited_entity_ids); - } - None => { - if self.items.len() >= BANK_CAPACITY { - return None - } - let deposited_entity_ids = stacked_inventory_item.take_entity_ids(amount)?; - - self.item_id_counter += 1; - self.items.push(BankItem::Stacked(StackedBankItem { - entity_ids: deposited_entity_ids, - item_id: ClientItemId(self.item_id_counter), - tool: stacked_inventory_item.tool, - })) - } - } - stacked_inventory_item.count() == 0 - } - }; - - if remove { - inventory_item.remove_from_inventory(); - } - - self.items.last() - } - - pub fn as_bank_entity(&self, _character_id: &CharacterEntityId, _bank_name: &BankName) -> BankEntity { - BankEntity { - items: self.items.iter() - .map(|item| { - match item { - BankItem::Individual(item) => { - BankItemEntity::Individual(ItemEntity { - id: item.entity_id, - item: item.item.clone(), - }) - }, - BankItem::Stacked(items) => { - BankItemEntity::Stacked(items.entity_ids.iter() - .map(|id| { - ItemEntity { - id: *id, - item: ItemDetail::Tool(items.tool) - } - }) - .collect()) - }, - } - }) - .collect() - } - } -} - - diff --git a/src/ship/items/floor.rs b/src/ship/items/floor.rs deleted file mode 100644 index f2720c0..0000000 --- a/src/ship/items/floor.rs +++ /dev/null @@ -1,253 +0,0 @@ -use crate::ship::items::ClientItemId; -use crate::entity::item::{ItemEntityId, ItemDetail}; -use crate::entity::item::Meseta; -use crate::entity::item::tool::Tool; -use crate::ship::map::MapArea; -use crate::ship::items::inventory::{IndividualInventoryItem, StackedInventoryItem, InventoryItemHandle}; - - -#[derive(Debug, Clone)] -pub struct IndividualFloorItem { - pub entity_id: ItemEntityId, - pub item_id: ClientItemId, - pub item: ItemDetail, - pub map_area: MapArea, - pub x: f32, - pub y: f32, - pub z: f32, -} - -#[derive(Debug, Clone)] -pub struct StackedFloorItem { - pub entity_ids: Vec, - pub item_id: ClientItemId, - pub tool: Tool, - pub map_area: MapArea, - pub x: f32, - pub y: f32, - pub z: f32, -} - -impl StackedFloorItem { - pub fn count(&self) -> usize { - self.entity_ids.len() - } - - pub fn as_client_bytes(&self) -> [u8; 16] { - self.tool.as_stacked_bytes(self.count()) - } -} - -#[derive(Debug, Clone)] -pub struct MesetaFloorItem { - pub item_id: ClientItemId, - pub meseta: Meseta, - pub map_area: MapArea, - pub x: f32, - pub y: f32, - pub z: f32, -} - -#[derive(Debug, Clone)] -pub enum FloorItem { - Individual(IndividualFloorItem), - Stacked(StackedFloorItem), - Meseta(MesetaFloorItem), -} - -impl FloorItem { - pub fn item_id(&self) -> ClientItemId { - match self { - FloorItem::Individual(individual_floor_item) => { - individual_floor_item.item_id - }, - FloorItem::Stacked(stacked_floor_item) => { - stacked_floor_item.item_id - }, - FloorItem::Meseta(meseta_floor_item) => { - meseta_floor_item.item_id - } - } - } - - pub fn x(&self) -> f32 { - match self { - FloorItem::Individual(individual_floor_item) => { - individual_floor_item.x - }, - FloorItem::Stacked(stacked_floor_item) => { - stacked_floor_item.x - }, - FloorItem::Meseta(meseta_floor_item) => { - meseta_floor_item.x - } - } - } - - pub fn y(&self) -> f32 { - match self { - FloorItem::Individual(individual_floor_item) => { - individual_floor_item.y - }, - FloorItem::Stacked(stacked_floor_item) => { - stacked_floor_item.y - }, - FloorItem::Meseta(meseta_floor_item) => { - meseta_floor_item.y - } - } - } - - pub fn z(&self) -> f32 { - match self { - FloorItem::Individual(individual_floor_item) => { - individual_floor_item.z - }, - FloorItem::Stacked(stacked_floor_item) => { - stacked_floor_item.z - }, - FloorItem::Meseta(meseta_floor_item) => { - meseta_floor_item.z - } - } - } - - pub fn map_area(&self) -> MapArea { - match self { - FloorItem::Individual(individual_floor_item) => { - individual_floor_item.map_area - }, - FloorItem::Stacked(stacked_floor_item) => { - stacked_floor_item.map_area - }, - FloorItem::Meseta(meseta_floor_item) => { - meseta_floor_item.map_area - } - } - } - - pub fn as_client_bytes(&self) -> [u8; 16] { - match self { - FloorItem::Individual(individual_floor_item) => { - individual_floor_item.item.as_client_bytes() - }, - FloorItem::Stacked(stacked_floor_item) => { - stacked_floor_item.as_client_bytes() - }, - FloorItem::Meseta(meseta_floor_item) => { - meseta_floor_item.meseta.as_bytes() - } - } - } -} - - - -pub struct FloorItemHandle<'a> { - floor: &'a mut RoomFloorItems, - index: usize, -} - -impl<'a> FloorItemHandle<'a> { - pub fn item(&'a self) -> Option<&'a FloorItem> { - self.floor.0.get(self.index) - } - - pub fn remove_from_floor(self) { - self.floor.0.remove(self.index); - } -} - -// TODO: floors should keep track of their own item_ids -#[derive(Debug, Default)] -pub struct RoomFloorItems(Vec); - -impl RoomFloorItems { - pub fn add_item(&mut self, item: FloorItem) { - self.0.push(item); - } - - pub fn remove_item(&mut self, item_id: &ClientItemId) { - self.0.retain(|item| item.item_id() != *item_id); - } - - // TODO: &ClientItemId - pub fn get_item_by_id(&self, item_id: ClientItemId) -> Option<&FloorItem> { - self.0.iter().find(|item| item.item_id() == item_id) - } - - // TODO: &ClientItemId - pub fn get_item_handle_by_id(&mut self, item_id: ClientItemId) -> Option { - let index = self.0.iter().position(|item| item.item_id() == item_id)?; - Some(FloorItemHandle { - floor: self, - index, - }) - } - - pub fn take_item_by_id(&mut self, item_id: ClientItemId) -> Option { - self.0 - .drain_filter(|i| i.item_id() == item_id) - .next() - } - - pub fn drop_individual_inventory_item(&mut self, individual_inventory_item: IndividualInventoryItem, item_drop_location: (MapArea, f32, f32, f32)) -> &IndividualFloorItem { - self.0.push(FloorItem::Individual(IndividualFloorItem { - entity_id: individual_inventory_item.entity_id, - item_id: individual_inventory_item.item_id, - item: individual_inventory_item.item, - map_area: item_drop_location.0, - x: item_drop_location.1, - y: item_drop_location.2, - z: item_drop_location.3, - })); - - match self.0.last().unwrap() { - FloorItem::Individual(item) => item, - _ => unreachable!(), - } - } - - pub fn drop_stacked_inventory_item(&mut self, stacked_inventory_item: StackedInventoryItem, item_drop_location: (MapArea, f32, f32, f32)) -> &StackedFloorItem { - self.0.push(FloorItem::Stacked(StackedFloorItem { - entity_ids: stacked_inventory_item.entity_ids, - item_id: stacked_inventory_item.item_id, - tool: stacked_inventory_item.tool, - map_area: item_drop_location.0, - x: item_drop_location.1, - y: item_drop_location.2, - z: item_drop_location.3, - })); - - match self.0.last().unwrap() { - FloorItem::Stacked(item) => item, - _ => unreachable!(), - } - } - - // TODO: Result - // TODO: if consumed_item is not a tool items do not get placed back into inventory (should I care?) - pub fn drop_partial_stacked_inventory_item(&mut self, inventory_item: InventoryItemHandle, amount: usize, new_item_id: ClientItemId, item_drop_location: (MapArea, f32, f32, f32)) -> Option<&StackedFloorItem> { - let consumed_item = inventory_item.consume(amount).ok()?; - - if let ItemDetail::Tool(tool) = consumed_item.item() { - self.0.push(FloorItem::Stacked(StackedFloorItem { - entity_ids: consumed_item.entity_ids(), - item_id: new_item_id, - tool, - map_area: item_drop_location.0, - x: item_drop_location.1, - y: item_drop_location.2, - z: item_drop_location.3, - })) - } - else { - return None - } - - match self.0.last().unwrap() { - FloorItem::Stacked(item) => Some(item), - _ => unreachable!(), - } - } -} diff --git a/src/ship/items/inventory.rs b/src/ship/items/inventory.rs deleted file mode 100644 index da4c4b0..0000000 --- a/src/ship/items/inventory.rs +++ /dev/null @@ -1,988 +0,0 @@ -use std::cmp::Ordering; -use thiserror::Error; -use libpso::character::character; -use crate::entity::character::CharacterEntityId; -use crate::entity::item::{ItemEntityId, ItemDetail, ItemEntity, ItemType, InventoryEntity, InventoryItemEntity, EquippedEntity}; -use crate::entity::item::tool::{Tool, ToolType}; -use crate::entity::item::mag::Mag; -use crate::entity::item::weapon::Weapon; -use crate::ship::items::{ClientItemId, BankItem, BankItemHandle, ItemManagerError}; -use crate::ship::items::floor::{IndividualFloorItem, StackedFloorItem}; -use crate::ship::shops::{ShopItem, ArmorShopItem, ToolShopItem, WeaponShopItem}; - -const INVENTORY_CAPACITY: usize = 30; - - -#[derive(Debug, Clone)] -pub struct InventorySlot(pub usize); - - -#[derive(Debug, Clone)] -pub struct IndividualInventoryItem { - pub entity_id: ItemEntityId, - pub item_id: ClientItemId, - pub item: ItemDetail, -} - -impl IndividualInventoryItem { - pub fn mag(&self) -> Option<&Mag> { - match self.item { - ItemDetail::Mag(ref mag) => Some(mag), - _ => None - } - } - - pub fn weapon(&self) -> Option<&Weapon> { - match self.item { - ItemDetail::Weapon(ref weapon) => Some(weapon), - _ => None - } - } - - pub fn mag_mut(&mut self) -> Option<&mut Mag> { - match self.item { - ItemDetail::Mag(ref mut mag) => Some(mag), - _ => None - } - } -} - -#[derive(Debug, Clone)] -pub struct StackedInventoryItem { - pub entity_ids: Vec, - pub item_id: ClientItemId, - pub tool: Tool, -} - -impl StackedInventoryItem { - pub fn count(&self) -> usize { - self.entity_ids.len() - } - - pub fn take_entity_ids(&mut self, amount: usize) -> Option> { - if amount <= self.count() { - Some(self.entity_ids.drain(..amount).collect()) - } - else { - None - } - } -} - -#[derive(Debug, Clone)] -pub enum InventoryItem { - Individual(IndividualInventoryItem), - Stacked(StackedInventoryItem), -} - -#[derive(Error, Debug, Clone)] -#[error("")] -pub enum InventoryItemAddToError { - BothAreNotStacked, - DifferentTool, - ExceedsCapacity, -} - -#[derive(Error, Debug, Clone)] -#[error("")] -pub enum InventoryAddError { -} - -#[derive(Debug, Clone)] -pub enum YesThereIsSpace { - NewStack, - ExistingStack, -} - -#[derive(Debug, Clone)] -pub enum NoThereIsNotSpace { - FullStack, - FullInventory, -} - -#[derive(Debug, Clone)] -pub enum SpaceForStack { - Yes(YesThereIsSpace), - No(NoThereIsNotSpace), -} - -impl InventoryItem { - pub fn entity_ids(&self) -> Vec { - match self { - InventoryItem::Individual(individual_inventory_item) => { - vec![individual_inventory_item.entity_id] - }, - InventoryItem::Stacked(stacked_inventory_item) => { - stacked_inventory_item.entity_ids.clone() - } - } - } - - pub fn item_id(&self) -> ClientItemId { - match self { - InventoryItem::Individual(individual_inventory_item) => { - individual_inventory_item.item_id - }, - InventoryItem::Stacked(stacked_inventory_item) => { - stacked_inventory_item.item_id - } - } - } - - pub fn set_item_id(&mut self, item_id: ClientItemId) { - match self { - InventoryItem::Individual(individual_inventory_item) => { - individual_inventory_item.item_id = item_id - }, - InventoryItem::Stacked(stacked_inventory_item) => { - stacked_inventory_item.item_id = item_id - } - } - } - - pub fn item_type(&self) -> ItemType { - match self { - InventoryItem::Individual(individual_inventory_item) => { - individual_inventory_item.item.item_type() - }, - InventoryItem::Stacked(stacked_inventory_item) => { - ItemType::Tool(stacked_inventory_item.tool.tool) - } - } - } - - // TOOD: delete? - pub fn are_same_stackable_tool(&self, other_stacked_item: &StackedFloorItem) -> bool { - match self { - InventoryItem::Stacked(self_stacked_item) => { - self_stacked_item.tool == other_stacked_item.tool - && self_stacked_item.tool.is_stackable() && other_stacked_item.tool.is_stackable() - }, - _ => false - } - } - - // TOOD: delete? - pub fn can_combine_stacks(&self, other_stacked_item: &StackedFloorItem) -> bool { - match self { - InventoryItem::Stacked(self_stacked_item) => { - self_stacked_item.tool == other_stacked_item.tool - && self_stacked_item.tool.is_stackable() && other_stacked_item.tool.is_stackable() - && self_stacked_item.count() + other_stacked_item.count() <= self_stacked_item.tool.max_stack() - }, - _ => false - } - } - - // TODO: result - // TOOD: delete? - pub fn combine_stacks(&mut self, other_stacked_item: &mut StackedFloorItem) { - if let InventoryItem::Stacked(self_stacked_item) = self { - self_stacked_item.entity_ids.append(&mut other_stacked_item.entity_ids); - } - } - - pub fn as_client_bytes(&self) -> [u8; 16] { - match self { - InventoryItem::Individual(item) => { - match &item.item { - ItemDetail::Weapon(w) => w.as_bytes(), - ItemDetail::Armor(a) => a.as_bytes(), - ItemDetail::Shield(s) => s.as_bytes(), - ItemDetail::Unit(u) => u.as_bytes(), - ItemDetail::Tool(t) => t.as_individual_bytes(), - ItemDetail::TechniqueDisk(d) => d.as_bytes(), - ItemDetail::Mag(m) => m.as_bytes(), - ItemDetail::ESWeapon(e) => e.as_bytes(), - } - }, - InventoryItem::Stacked(item) => { - item.tool.as_stacked_bytes(item.entity_ids.len()) - }, - } - } - - pub fn can_add_to(&mut self, stacked_floor_item: &StackedFloorItem) -> Result<(), InventoryItemAddToError> { - if let InventoryItem::Stacked(stacked_inventory_item) = self { - if stacked_floor_item.tool != stacked_inventory_item.tool { - return Err(InventoryItemAddToError::DifferentTool) - } - - if stacked_floor_item.tool.tool.max_stack() < (stacked_floor_item.count() + stacked_inventory_item.count()) { - return Err(InventoryItemAddToError::ExceedsCapacity) - } - Ok(()) - } - else { - Err(InventoryItemAddToError::BothAreNotStacked) - } - } - - pub fn add_to(&mut self, mut stacked_floor_item: StackedFloorItem) -> Result<(), InventoryItemAddToError> { - self.can_add_to(&stacked_floor_item)?; - if let InventoryItem::Stacked(stacked_inventory_item) = self { - stacked_inventory_item.entity_ids.append(&mut stacked_floor_item.entity_ids); - } - Ok(()) - } - - pub fn individual(&self) -> Option<&IndividualInventoryItem> { - match self { - InventoryItem::Individual(ref individual_inventory_item) => Some(individual_inventory_item), - _ => None - } - } - - pub fn individual_mut(&mut self) -> Option<&mut IndividualInventoryItem> { - match self { - InventoryItem::Individual(ref mut individual_inventory_item) => Some(individual_inventory_item), - _ => None - } - } - - pub fn get_sell_price(&self) -> Result { - match self { - InventoryItem::Individual(individual_item) => { - match &individual_item.item { - // TODO: can wrapped items be sold? - ItemDetail::Weapon(w) => { - if !w.tekked { - return Ok(1u32) - } - if w.is_rare_item() { - return Ok(10u32) - } - Ok((WeaponShopItem::from(w).price() / 8) as u32) - }, - ItemDetail::Armor(a) => { - if a.is_rare_item() { - return Ok(10u32) - } - Ok((ArmorShopItem::from(a).price() / 8) as u32) - }, - ItemDetail::Shield(s) => { - if s.is_rare_item() { - return Ok(10u32) - } - Ok((ArmorShopItem::from(s).price() / 8) as u32) - }, - ItemDetail::Unit(u) => { - if u.is_rare_item() { - return Ok(10u32) - } - Ok((ArmorShopItem::from(u).price() / 8) as u32) - }, - ItemDetail::Tool(t) => { - if !matches!(t.tool, ToolType::PhotonDrop | ToolType::PhotonSphere | ToolType::PhotonCrystal) && t.is_rare_item() { - return Ok(10u32) - } - Ok((ToolShopItem::from(t).price() / 8) as u32) - }, - ItemDetail::TechniqueDisk(d) => { - Ok((ToolShopItem::from(d).price() / 8) as u32) - }, - ItemDetail::Mag(_m) => { - Err(ItemManagerError::ItemNotSellable(self.clone())) - }, - ItemDetail::ESWeapon(_e) => { - Ok(10u32) - }, - } - }, - // the number of stacked items sold is handled by the caller. this is just the price of 1 - InventoryItem::Stacked(stacked_item) => { - Ok((ToolShopItem::from(&stacked_item.tool).price() / 8) as u32) - }, - } - } - - pub fn stacked(&self) -> Option<&StackedInventoryItem> { - match self { - InventoryItem::Stacked(ref stacked_inventory_item) => Some(stacked_inventory_item), - _ => None - } - } - - pub fn stacked_mut(&mut self) -> Option<&mut StackedInventoryItem> { - match self { - InventoryItem::Stacked(ref mut stacked_inventory_item) => Some(stacked_inventory_item), - _ => None - } - } - - pub fn mag(&self) -> Option<&Mag> { - match self { - InventoryItem::Individual(individual_inventory_item) => individual_inventory_item.mag(), - _ => None - } - } -} - - - - -#[derive(Error, Debug, Clone)] -#[error("")] -pub enum InventoryItemConsumeError { - InconsistentState, - InvalidAmount, -} - -pub struct IndividualConsumedItem { - pub entity_id: ItemEntityId, - pub item: ItemDetail, -} - -pub struct StackedConsumedItem { - pub entity_ids: Vec, - pub tool: Tool -} - -pub enum ConsumedItem { - Individual(IndividualConsumedItem), - Stacked(StackedConsumedItem), -} - -impl ConsumedItem { - pub fn entity_ids(&self) -> Vec { - match self { - ConsumedItem::Individual(individual_consumed_item) => { - vec![individual_consumed_item.entity_id] - }, - ConsumedItem::Stacked(stacked_consumed_item) => { - stacked_consumed_item.entity_ids.clone() - } - } - } - - pub fn item(&self) -> ItemDetail { - match self { - ConsumedItem::Individual(individual_consumed_item) => { - individual_consumed_item.item.clone() - }, - ConsumedItem::Stacked(stacked_consumed_item) => { - ItemDetail::Tool(stacked_consumed_item.tool) - } - } - } -} - - -pub struct InventoryItemHandle<'a> { - inventory: &'a mut CharacterInventory, - slot: usize, -} - -impl<'a> InventoryItemHandle<'a> { - pub fn item(&'a self) -> Option<&'a InventoryItem> { - self.inventory.items.get(self.slot) - } - - pub fn item_mut(&mut self) -> Option<&mut InventoryItem> { - self.inventory.items.get_mut(self.slot) - } - - pub fn remove_from_inventory(self) { - self.inventory.items.remove(self.slot); - } - - pub fn consume(self, amount: usize) -> Result { - enum RemoveMethod { - EntireThing(ConsumedItem), - Partial(Tool), - } - - let inventory_item = self.inventory.items.get(self.slot).ok_or(InventoryItemConsumeError::InconsistentState)?; - let remove_method = match inventory_item { - InventoryItem::Individual(individual_inventory_item) => { - RemoveMethod::EntireThing(ConsumedItem::Individual(IndividualConsumedItem { - entity_id: individual_inventory_item.entity_id, - item: individual_inventory_item.item.clone() - })) - }, - InventoryItem::Stacked(stacked_inventory_item) => { - match stacked_inventory_item.count().cmp(&amount) { - Ordering::Equal => { - RemoveMethod::EntireThing(ConsumedItem::Stacked(StackedConsumedItem { - entity_ids: stacked_inventory_item.entity_ids.clone(), - tool: stacked_inventory_item.tool, - })) - }, - Ordering::Greater => { - RemoveMethod::Partial(stacked_inventory_item.tool) - }, - Ordering::Less => { - return Err(InventoryItemConsumeError::InvalidAmount) - } - } - }, - }; - - match remove_method { - RemoveMethod::EntireThing(consumed_item) => { - self.inventory.items.remove(self.slot); - Ok(consumed_item) - }, - RemoveMethod::Partial(tool) => { - let entity_ids = self.inventory.items.get_mut(self.slot) - .and_then(|item| { - if let InventoryItem::Stacked(stacked_inventory_item) = item { - Some(stacked_inventory_item.entity_ids.drain(..amount).collect::>()) - } - else { - None - } - }) - .ok_or(InventoryItemConsumeError::InvalidAmount)?; - Ok(ConsumedItem::Stacked(StackedConsumedItem { - entity_ids, - tool, - })) - } - } - } - - pub fn get_slot(&self) -> usize { - self.slot - } -} - - - - -#[derive(Debug)] -pub struct CharacterInventory { - item_id_counter: u32, - items: Vec, - equipped: EquippedEntity, -} - -impl CharacterInventory { - pub fn new(items: Vec, equipped: &EquippedEntity) -> CharacterInventory { - CharacterInventory{ - item_id_counter: 0, - items, - equipped: equipped.clone(), - } - } - - pub fn initialize_item_ids(&mut self, base_item_id: u32) { - for (i, item) in self.items.iter_mut().enumerate() { - item.set_item_id(ClientItemId(base_item_id + i as u32)); - } - self.item_id_counter = base_item_id + self.items.len() as u32 + 1; - } - - pub fn as_client_inventory_items(&self) -> [character::InventoryItem; 30] { - self.items.iter() - .enumerate() - .fold([character::InventoryItem::default(); 30], |mut inventory, (slot, item)| { - let bytes = item.as_client_bytes(); - inventory[slot].data1.copy_from_slice(&bytes[0..12]); - inventory[slot].data2.copy_from_slice(&bytes[12..16]); - inventory[slot].item_id = item.item_id().0; - inventory[slot].equipped = 0; - inventory[slot].flags = 0; - - if let InventoryItem::Individual(individual_item) = item { - if self.equipped.is_equipped(&individual_item.entity_id) { - if let ItemDetail::Unit(_) = individual_item.item { - inventory[slot].data1[4] = self.equipped.unit.iter() - .enumerate() - .find(|(_, u_id)| **u_id == Some(individual_item.entity_id)) - .map(|(a, _)| a) - .unwrap_or(0) as u8 - } - inventory[slot].equipped = 1; - inventory[slot].flags |= 8; - } - } - inventory - }) - } - - pub fn slot(&self, slot: usize) -> Option<&InventoryItem> { - self.items.get(slot) - } - - pub fn count(&self) -> usize { - self.items.len() - } - - pub fn space_for_individual_item(&self) -> bool { - self.count() < INVENTORY_CAPACITY - } - - pub fn space_for_stacked_item(&self, tool: &Tool, amount: usize) -> SpaceForStack { - let existing_item = self.items.iter() - .filter_map(|item| { - match item { - InventoryItem::Stacked(s_item) => { - Some(s_item) - }, - _ => None - } - }) - .find(|s_item| { - s_item.tool == *tool - }); - - match existing_item { - Some(item) => { - if item.count() + amount <= tool.tool.max_stack() { - SpaceForStack::Yes(YesThereIsSpace::ExistingStack) - } - else { - SpaceForStack::No(NoThereIsNotSpace::FullStack) - } - } - None => { - if self.count() < INVENTORY_CAPACITY { - SpaceForStack::Yes(YesThereIsSpace::NewStack) - } - else { - SpaceForStack::No(NoThereIsNotSpace::FullInventory) - } - } - } - } - - pub fn stack_item_id(&self, tool: &Tool) -> Option { - self.items.iter() - .filter_map(|item| { - match item { - InventoryItem::Stacked(s_item) => { - Some(s_item) - }, - _ => None - } - }) - .find(|s_item| { - s_item.tool == *tool - }) - .map(|item| { - item.item_id - }) - } - - pub fn get_item_handle_by_id(&mut self, item_id: ClientItemId) -> Option { - let (slot, _) = self.items.iter() - .enumerate() - .find(|(_, item)| { - item.item_id() == item_id - })?; - Some(InventoryItemHandle { - inventory: self, - slot, - }) - } - - pub fn get_equipped_mag_handle(&mut self) -> Option { - let (slot, _) = self.items.iter() - .enumerate() - .find(|(_, item)| { - if let InventoryItem::Individual(individual_inventory_item) = item { - if let ItemDetail::Mag(_) = &individual_inventory_item.item { - return self.equipped.is_equipped(&individual_inventory_item.entity_id) - } - } - false - })?; - Some(InventoryItemHandle { - inventory: self, - slot, - }) - } - - pub fn get_equipped_armor_handle(&mut self) -> Option { - let (slot, _) = self.items.iter() - .enumerate() - .find(|(_, item)| { - if let InventoryItem::Individual(individual_inventory_item) = item { - if let ItemDetail::Armor(_) = &individual_inventory_item.item { - return self.equipped.is_equipped(&individual_inventory_item.entity_id) - } - } - false - })?; - Some(InventoryItemHandle { - inventory: self, - slot, - }) - } - - pub fn get_equipped_shield_handle(&mut self) -> Option { - let (slot, _) = self.items.iter() - .enumerate() - .find(|(_, item)| { - if let InventoryItem::Individual(individual_inventory_item) = item { - if let ItemDetail::Shield(_) = &individual_inventory_item.item { - return self.equipped.is_equipped(&individual_inventory_item.entity_id) - } - } - false - })?; - Some(InventoryItemHandle { - inventory: self, - slot, - }) - } - - pub fn get_equipped_weapon_handle(&mut self) -> Option { - let (slot, _) = self.items.iter() - .enumerate() - .find(|(_, item)| { - if let InventoryItem::Individual(individual_inventory_item) = item { - if let ItemDetail::Weapon(_) = &individual_inventory_item.item { - return self.equipped.is_equipped(&individual_inventory_item.entity_id) - } - } - false - })?; - Some(InventoryItemHandle { - inventory: self, - slot, - }) - } - - pub fn get_item_by_id(&self, item_id: ClientItemId) -> Option<&InventoryItem> { - self.items.iter() - .find(|item| { - item.item_id() == item_id - }) - } - - pub fn take_item_by_id(&mut self, item_id: ClientItemId) -> Option { - self.items - .drain_filter(|i| i.item_id() == item_id) - .next() - } - - pub fn take_stacked_item_by_id(&mut self, item_id: ClientItemId, amount: usize) -> Option { - let idx = self.items - .iter_mut() - .position(|i| i.item_id() == item_id)?; - let item: &mut StackedInventoryItem = self.items.get_mut(idx)?.stacked_mut()?; - match item.entity_ids.len().cmp(&amount) { - Ordering::Equal => { - let item = self.items.remove(idx); - item.stacked().cloned() - }, - Ordering::Greater => { - let entity_ids = item.entity_ids.drain(..amount).collect(); - Some(StackedInventoryItem { - entity_ids, - tool: item.tool, - item_id: item.item_id, - }) - }, - Ordering::Less => { - None - } - } - } - - pub fn add_item(&mut self, item: InventoryItem) { - self.items.push(item); - } - - pub fn add_stacked_item(&mut self, mut item: StackedInventoryItem) { - let existing_item = self.items - .iter_mut() - .filter_map(|i| { - match i { - InventoryItem::Stacked(stacked) => { - Some(stacked) - }, - _ => None - } - }) - .find(|i| { - i.tool == item.tool - }); - - match existing_item { - Some(existing_item) => { - existing_item.entity_ids.append(&mut item.entity_ids) - }, - None => { - self.items.push(InventoryItem::Stacked(item)) - } - } - } - - pub fn add_item_with_new_item_id(&mut self, item: InventoryItem, item_id: ClientItemId) { - match item { - InventoryItem::Individual(mut individual_inventory_item) => { - individual_inventory_item.item_id = item_id; - self.add_item(InventoryItem::Individual(individual_inventory_item)); - }, - InventoryItem::Stacked(mut stacked_inventory_item) => { - stacked_inventory_item.item_id = item_id; - self.add_stacked_item(stacked_inventory_item) - } - } - } - - pub fn add_individual_floor_item(&mut self, floor_item: &IndividualFloorItem) -> &InventoryItem { - self.items.push(InventoryItem::Individual(IndividualInventoryItem { - entity_id: floor_item.entity_id, - item_id: floor_item.item_id, - item: floor_item.item.clone(), - })); - - self.items.last().unwrap() - } - - // TODO: should these pick up functions take floor_item as mut and remove the ids? - pub fn pick_up_individual_floor_item(&mut self, floor_item: &IndividualFloorItem) -> Option<(&IndividualInventoryItem, InventorySlot)> { - if self.count() >= 30 { - return None; - } - - self.items.push(InventoryItem::Individual(IndividualInventoryItem { - entity_id: floor_item.entity_id, - item_id: floor_item.item_id, - item: floor_item.item.clone(), - })); - - if let Some(InventoryItem::Individual(new_item)) = self.items.last() { - Some((new_item, InventorySlot(self.count()-1))) - } - else { - None - } - } - - pub fn add_stacked_floor_item(&mut self, floor_item: &StackedFloorItem) { - let existing_item = self.items.iter_mut() - .filter_map(|item| { - match item { - InventoryItem::Stacked(s_item) => Some(s_item), - _ => None, - } - - }) - .find(|item| { - item.tool == floor_item.tool - }); - - match existing_item { - Some(item) => { - item.entity_ids.append(&mut floor_item.entity_ids.clone()) - }, - None => { - self.items.push(InventoryItem::Stacked(StackedInventoryItem { - entity_ids: floor_item.entity_ids.clone(), - item_id: floor_item.item_id, - tool: floor_item.tool, - })); - } - } - } - - // TODO: can be simplified using find instead of position - pub fn pick_up_stacked_floor_item(&mut self, floor_item: &StackedFloorItem) -> Option<(&StackedInventoryItem, InventorySlot)> { - let existing_stack_position = self.items.iter() - .position(|inventory_item| { - if let InventoryItem::Stacked(stacked_inventory_item) = inventory_item { - if stacked_inventory_item.tool == floor_item.tool { - return true - } - } - false - }); - - if let Some(existing_stack_position) = existing_stack_position { - if let Some(InventoryItem::Stacked(stacked_item)) = self.items.get_mut(existing_stack_position) { - if stacked_item.count() + floor_item.count() <= stacked_item.tool.max_stack() { - stacked_item.entity_ids.append(&mut floor_item.entity_ids.clone()); - Some((stacked_item, InventorySlot(existing_stack_position))) - } - else { - None - } - } - else { - None - } - } - else { - let new_stacked_item = InventoryItem::Stacked(StackedInventoryItem { - entity_ids: floor_item.entity_ids.clone(), - item_id: floor_item.item_id, - tool: floor_item.tool, - }); - - self.items.push(new_stacked_item); - if let Some(InventoryItem::Stacked(new_item)) = self.items.last() { - Some((new_item, InventorySlot(self.count()-1))) - } - else { - None - } - } - } - - pub fn withdraw_item(&mut self, mut bank_item: BankItemHandle, amount: usize) -> Option<(&InventoryItem, usize)> { - let (remove, slot) = match bank_item.item_mut()? { - BankItem::Individual(individual_bank_item) => { - if self.items.len() >= INVENTORY_CAPACITY { - return None - } - self.items.push(InventoryItem::Individual(IndividualInventoryItem { - entity_id: individual_bank_item.entity_id, - item_id: individual_bank_item.item_id, - item: individual_bank_item.item.clone(), - })); - (true, self.count()-1) - }, - BankItem::Stacked(stacked_bank_item) => { - let existing_inventory_item = self.items.iter_mut() - .enumerate() - .find_map(|(index, item)| { - if let InventoryItem::Stacked(stacked_inventory_item) = item { - if stacked_bank_item.tool == stacked_inventory_item.tool { - return Some((index, stacked_inventory_item)) - } - } - None - }); - - let slot = match existing_inventory_item { - Some((slot, stacked_inventory_item)) => { - if stacked_inventory_item.count() + stacked_bank_item.count() > stacked_bank_item.tool.max_stack() { - return None - } - - let mut withdrawn_entity_ids = stacked_bank_item.take_entity_ids(amount)?; - stacked_inventory_item.entity_ids.append(&mut withdrawn_entity_ids); - slot - } - None => { - if self.items.len() >= INVENTORY_CAPACITY { - return None - } - let withdrawn_entity_ids = stacked_bank_item.take_entity_ids(amount)?; - - self.item_id_counter += 1; // oh no - self.items.push(InventoryItem::Stacked(StackedInventoryItem { - entity_ids: withdrawn_entity_ids, - item_id: ClientItemId(self.item_id_counter), - tool: stacked_bank_item.tool, - })); - self.count()-1 - } - }; - (stacked_bank_item.count() == 0, slot) - } - }; - - if remove { - bank_item.remove_from_bank(); - } - - self.items.last().map(|item| { - (item, slot) - }) - } - - pub fn iter(&self) -> impl Iterator { - self.items.iter() - } - - pub fn items(&self) -> &Vec { - &self.items - } - - pub fn set_items(&mut self, sorted_items: Vec) { - self.items = sorted_items; - } - - pub fn remove_by_id(&mut self, id: ClientItemId) -> Option { - self.items.iter() - .position(|i| i.item_id() == id) - .map(|position| { - self.items.remove(position) - }) - } - - pub fn equip(&mut self, id: &ClientItemId, equip_slot: u8) { - for item in &self.items { - if let InventoryItem::Individual(inventory_item) = item { - if inventory_item.item_id == *id { - match inventory_item.item { - ItemDetail::Weapon(_) => self.equipped.weapon = Some(inventory_item.entity_id), - ItemDetail::Armor(_) => self.equipped.armor = Some(inventory_item.entity_id), - ItemDetail::Shield(_) => self.equipped.shield = Some(inventory_item.entity_id), - ItemDetail::Unit(_) => { - if let Some(unit) = self.equipped.unit.get_mut(equip_slot as usize) { - *unit = Some(inventory_item.entity_id) - } - } - ItemDetail::Mag(_) => self.equipped.mag = Some(inventory_item.entity_id), - _ => {} - } - } - } - } - } - - pub fn unequip(&mut self, id: &ClientItemId) { - for item in &self.items { - if let InventoryItem::Individual(inventory_item) = item { - if inventory_item.item_id == *id { - match inventory_item.item { - ItemDetail::Weapon(_) => self.equipped.weapon = None, - ItemDetail::Armor(_) => { - self.equipped.armor = None; - self.equipped.unit = [None; 4]; - } - ItemDetail::Shield(_) => self.equipped.shield = None, - ItemDetail::Unit(_) => { - for unit in self.equipped.unit.iter_mut() { - if *unit == Some(inventory_item.entity_id) { - *unit = None - } - } - } - ItemDetail::Mag(_) => self.equipped.mag = Some(inventory_item.entity_id), - _ => {} - } - } - } - } - } - - pub fn as_inventory_entity(&self, _character_id: &CharacterEntityId) -> InventoryEntity { - InventoryEntity { - items: self.items.iter() - .map(|item| { - match item { - InventoryItem::Individual(item) => { - InventoryItemEntity::Individual(ItemEntity { - id: item.entity_id, - item: item.item.clone(), - }) - }, - InventoryItem::Stacked(items) => { - InventoryItemEntity::Stacked(items.entity_ids.iter() - .map(|id| { - ItemEntity { - id: *id, - item: ItemDetail::Tool(items.tool) - } - }) - .collect()) - }, - } - }) - .collect() - } - } - - pub fn as_equipped_entity(&self) -> EquippedEntity { - self.equipped.clone() - } -} - diff --git a/src/ship/items/manager.rs b/src/ship/items/manager.rs index c7b7fbf..e9251c4 100644 --- a/src/ship/items/manager.rs +++ b/src/ship/items/manager.rs @@ -20,7 +20,6 @@ use crate::ship::packet::handler::trade::{TradeError, OTHER_MESETA_ITEM_ID}; use crate::ship::items::bank::*; use crate::ship::items::floor::*; use crate::ship::items::inventory::*; -use crate::ship::items::use_tool; use crate::ship::items::transaction::{ItemTransaction, ItemAction, TransactionError, TransactionCommitError}; #[derive(PartialEq, Eq)] diff --git a/src/ship/items/mod.rs b/src/ship/items/mod.rs index a3956ba..f2a8da1 100644 --- a/src/ship/items/mod.rs +++ b/src/ship/items/mod.rs @@ -1,9 +1,3 @@ -mod bank; -mod floor; -pub mod inventory; -pub mod manager; -pub mod transaction; -pub mod use_tool; pub mod state; pub mod actions; pub mod apply_item; @@ -12,10 +6,4 @@ use serde::{Serialize, Deserialize}; #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, Serialize, Deserialize, derive_more::Display)] pub struct ClientItemId(pub u32); -// TODO: remove these and fix use statements in the rest of the codebase -pub use inventory::*; -pub use floor::*; -pub use bank::*; -pub use manager::*; - diff --git a/src/ship/items/transaction.rs b/src/ship/items/transaction.rs deleted file mode 100644 index bc0d0a9..0000000 --- a/src/ship/items/transaction.rs +++ /dev/null @@ -1,337 +0,0 @@ -use crate::entity::gateway::EntityGateway; -use thiserror::Error; -use crate::ship::items::manager::{ItemManager, ItemManagerError}; -use crate::entity::gateway::GatewayError; - -#[derive(Error, Debug)] -pub enum TransactionCommitError { - #[error("transaction commit gateway error {0}")] - Gateway(#[from] GatewayError), - #[error("transaction commit itemmanager error {0}")] - ItemManager(#[from] ItemManagerError), -} - -#[async_trait::async_trait] -pub trait ItemAction: std::marker::Send + std::marker::Sync + std::fmt::Debug { - async fn commit(&self, manager: &mut ItemManager, entity_gateway: &mut EG) -> Result<(), TransactionCommitError>; -} - -pub struct ItemTransactionActions<'a, EG: EntityGateway> { - action_queue: Vec>>, - pub manager: &'a ItemManager, -} - - -impl<'a, EG: EntityGateway> ItemTransactionActions<'a, EG> { - fn new(manager: &'a ItemManager) -> ItemTransactionActions<'a, EG> { - ItemTransactionActions { - action_queue: Vec::new(), - manager - } - } - - pub fn action(&mut self, action: Box>) { - self.action_queue.push(action) - } -} - - -pub struct ItemTransaction<'a, T, EG: EntityGateway> { - data: T, - actions: ItemTransactionActions<'a, EG>, -} - -impl<'a, T, EG: EntityGateway> ItemTransaction<'a, T, EG> { - pub fn new(manager: &'a ItemManager, arg: T) -> ItemTransaction<'a, T, EG> { - ItemTransaction { - data: arg, - actions: ItemTransactionActions::new(manager), - } - } - - pub fn act(mut self, action: fn(&mut ItemTransactionActions, &T) -> Result) -> FinalizedItemTransaction { - match action(&mut self.actions, &self.data) { - Ok(k) => { - FinalizedItemTransaction { - value: Ok(k), - action_queue: self.actions.action_queue, - } - }, - Err(err) => { - FinalizedItemTransaction { - value: Err(err), - action_queue: Vec::new(), - } - } - } - } -} - - -#[derive(Error, Debug)] -pub enum TransactionError { - #[error("transaction action error {0:?}")] - Action(E), - #[error("transaction commit error {0}")] - Commit(#[from] TransactionCommitError), - -} - -// this only exists to drop the ItemManager borrow of ItemTransaction so a mutable ItemTransaction can be passed in later -pub struct FinalizedItemTransaction { - value: Result, - action_queue: Vec>>, -} - -impl FinalizedItemTransaction { - pub async fn commit(self, item_manager: &mut ItemManager, entity_gateway: &mut EG) -> Result> { - match self.value { - Ok(value) => { - for action in self.action_queue.into_iter() { - // TODO: better handle rolling back if this ever errors out - action.commit(item_manager, entity_gateway).await.map_err(|err| TransactionError::Commit(err))?; - } - Ok(value) - }, - Err(err) => Err(TransactionError::Action(err)), - } - } -} - - -#[cfg(test)] -mod test { - use super::*; - use crate::entity::account::{UserAccountId, NewUserAccountEntity, UserAccountEntity}; - use crate::entity::character::{NewCharacterEntity, CharacterEntity}; - use crate::entity::gateway::GatewayError; - use thiserror::Error; - - #[async_std::test] - async fn test_item_transaction() { - #[derive(Debug)] - struct DummyAction1 { - name: String, - } - #[derive(Debug)] - struct DummyAction2 { - value: u32, - } - - #[derive(Error, Debug)] - #[error("")] - enum DummyError { - Error - } - - #[derive(Default, Clone)] - struct DummyGateway { - d1_set: String, - d2_inc: u32, - } - - #[async_trait::async_trait] - impl EntityGateway for DummyGateway { - async fn create_user(&mut self, user: NewUserAccountEntity) -> Result { - self.d1_set = user.username; - Ok(UserAccountEntity::default()) - } - - async fn create_character(&mut self, char: NewCharacterEntity) -> Result { - self.d2_inc += char.slot; - Ok(CharacterEntity::default()) - } - } - - - #[async_trait::async_trait] - impl ItemAction for DummyAction1 { - async fn commit(&self, item_manager: &mut ItemManager, entity_gateway: &mut EG) -> Result<(), TransactionCommitError> { - item_manager.id_counter = 55555; - entity_gateway.create_user(NewUserAccountEntity { - username: self.name.clone(), - ..NewUserAccountEntity::default() - }) - .await?; - Ok(()) - } - } - - #[async_trait::async_trait] - impl ItemAction for DummyAction2 { - async fn commit(&self, item_manager: &mut ItemManager, entity_gateway: &mut EG) -> Result<(), TransactionCommitError> { - item_manager.id_counter += self.value; - entity_gateway.create_character(NewCharacterEntity { - slot: self.value, - ..NewCharacterEntity::new(UserAccountId(0), 1) // TODO: handle different keyboard_config_presets - }) - .await?; - Ok(()) - } - } - - let mut item_manager = ItemManager::default(); - let mut entity_gateway = DummyGateway::default(); - - let result = ItemTransaction::new(&item_manager, 12) - .act(|it, k| { - it.action(Box::new(DummyAction1 {name: "asdf".into()})); - it.action(Box::new(DummyAction2 {value: 11})); - it.action(Box::new(DummyAction2 {value: *k})); - if *k == 99 { - return Err(DummyError::Error) - } - Ok(String::from("hello")) - }) - .commit(&mut item_manager, &mut entity_gateway) - .await; - - assert!(entity_gateway.d1_set == "asdf"); - assert!(entity_gateway.d2_inc == 23); - assert!(item_manager.id_counter == 55578); - assert!(result.unwrap() == "hello"); - } - - #[async_std::test] - async fn test_item_transaction_with_action_error() { - #[derive(Debug)] - struct DummyAction1 { - } - #[derive(Debug)] - struct DummyAction2 { - } - - #[derive(Error, Debug, PartialEq, Eq)] - #[error("")] - enum DummyError { - Error - } - - #[derive(Default, Clone)] - struct DummyGateway { - _d1_set: String, - d2_inc: u32, - } - - #[async_trait::async_trait] - impl EntityGateway for DummyGateway { - async fn create_character(&mut self, char: NewCharacterEntity) -> Result { - self.d2_inc += char.slot; - Ok(CharacterEntity::default()) - } - } - - - #[async_trait::async_trait] - impl ItemAction for DummyAction1 { - async fn commit(&self, _item_manager: &mut ItemManager, entity_gateway: &mut EG) -> Result<(), TransactionCommitError> { - entity_gateway.create_character(NewCharacterEntity { - slot: 1, - ..NewCharacterEntity::new(UserAccountId(0), 1) // TODO: handle different keyboard_config_presets - }) - .await?; - Ok(()) - } - } - - #[async_trait::async_trait] - impl ItemAction for DummyAction2 { - async fn commit(&self, _item_manager: &mut ItemManager, entity_gateway: &mut EG) -> Result<(), TransactionCommitError> { - entity_gateway.create_character(NewCharacterEntity { - slot: 1, - ..NewCharacterEntity::new(UserAccountId(0), 1) // TODO: handle different keyboard_config_presets - }) - .await?; - Ok(()) - } - } - - let mut item_manager = ItemManager::default(); - let mut entity_gateway = DummyGateway::default(); - - let result = ItemTransaction::new(&item_manager, 12) - .act(|it, _| -> Result<(), _> { - it.action(Box::new(DummyAction1 {})); - it.action(Box::new(DummyAction2 {})); - it.action(Box::new(DummyAction2 {})); - Err(DummyError::Error) - }) - .commit(&mut item_manager, &mut entity_gateway) - .await; - - assert!(entity_gateway.d2_inc == 0); - assert!(matches!(result, Err(TransactionError::Action(DummyError::Error)))); - } - - #[async_std::test] - async fn test_item_transaction_with_commit_error() { - #[derive(Debug)] - struct DummyAction1 { - } - #[derive(Debug)] - struct DummyAction2 { - } - - #[derive(Error, Debug, PartialEq, Eq)] - #[error("")] - enum DummyError { - } - - #[derive(Default, Clone)] - struct DummyGateway { - _d1_set: String, - d2_inc: u32, - } - - #[async_trait::async_trait] - impl EntityGateway for DummyGateway { - async fn create_character(&mut self, char: NewCharacterEntity) -> Result { - self.d2_inc += char.slot; - Ok(CharacterEntity::default()) - } - } - - - #[async_trait::async_trait] - impl ItemAction for DummyAction1 { - async fn commit(&self, _item_manager: &mut ItemManager, entity_gateway: &mut EG) -> Result<(), TransactionCommitError> { - entity_gateway.create_character(NewCharacterEntity { - slot: 1, - ..NewCharacterEntity::new(UserAccountId(0), 1) // TODO: handle different keyboard_config_presets - }) - .await?; - Err(GatewayError::Error.into()) - } - } - - #[async_trait::async_trait] - impl ItemAction for DummyAction2 { - async fn commit(&self, _item_manager: &mut ItemManager, entity_gateway: &mut EG) -> Result<(), TransactionCommitError> { - entity_gateway.create_character(NewCharacterEntity { - slot: 1, - ..NewCharacterEntity::new(UserAccountId(0), 1) // TODO: handle different keyboard_config_presets - }) - .await?; - Ok(()) - } - } - - let mut item_manager = ItemManager::default(); - let mut entity_gateway = DummyGateway::default(); - - let result = ItemTransaction::new(&item_manager, 12) - .act(|it, _| -> Result<_, DummyError> { - it.action(Box::new(DummyAction1 {})); - it.action(Box::new(DummyAction2 {})); - it.action(Box::new(DummyAction2 {})); - Ok(()) - }) - .commit(&mut item_manager, &mut entity_gateway) - .await; - - // in an ideal world this would be 0 as rollbacks would occur - assert!(entity_gateway.d2_inc == 1); - assert!(matches!(result, Err(TransactionError::Commit(TransactionCommitError::Gateway(GatewayError::Error))))); - } -} - diff --git a/src/ship/items/use_tool.rs b/src/ship/items/use_tool.rs deleted file mode 100644 index 6440b84..0000000 --- a/src/ship/items/use_tool.rs +++ /dev/null @@ -1,163 +0,0 @@ -use thiserror::Error; -use crate::entity::gateway::EntityGateway; -use crate::entity::character::CharacterEntity; -use crate::entity::item::mag::MagCell; -use crate::ship::items::{CharacterInventory, ConsumedItem}; - -#[derive(Error, Debug)] -#[error("")] -pub enum UseItemError { - NoCharacter, - ItemNotEquipped, - InvalidItem, -} - -pub async fn power_material(entity_gateway: &mut EG, character: &mut CharacterEntity) { - character.materials.power += 1; - entity_gateway.save_character(character).await.unwrap(); -} - -pub async fn mind_material(entity_gateway: &mut EG, character: &mut CharacterEntity) { - character.materials.mind += 1; - entity_gateway.save_character(character).await.unwrap(); -} - -pub async fn evade_material(entity_gateway: &mut EG, character: &mut CharacterEntity) { - character.materials.evade += 1; - entity_gateway.save_character(character).await.unwrap(); -} - -pub async fn def_material(entity_gateway: &mut EG, character: &mut CharacterEntity) { - character.materials.def += 1; - entity_gateway.save_character(character).await.unwrap(); -} - -pub async fn luck_material(entity_gateway: &mut EG, character: &mut CharacterEntity) { - character.materials.luck += 1; - entity_gateway.save_character(character).await.unwrap(); -} - -pub async fn hp_material(entity_gateway: &mut EG, character: &mut CharacterEntity) { - character.materials.hp += 1; - entity_gateway.save_character(character).await.unwrap(); -} - -pub async fn tp_material(entity_gateway: &mut EG, character: &mut CharacterEntity) { - character.materials.tp += 1; - entity_gateway.save_character(character).await.unwrap(); -} - -async fn mag_cell(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory, mag_cell_type: MagCell) -> Result<(), UseItemError> { - let mut mag_handle = inventory.get_equipped_mag_handle().ok_or(UseItemError::ItemNotEquipped)?; - let mag_item = mag_handle.item_mut() - .ok_or(UseItemError::InvalidItem)?; - let actual_mag = mag_item - .individual_mut() - .ok_or(UseItemError::InvalidItem)? - .mag_mut() - .ok_or(UseItemError::InvalidItem)?; - actual_mag.apply_mag_cell(mag_cell_type); - for mag_entity_id in mag_item.entity_ids() { - for cell_entity_id in used_cell.entity_ids() { - entity_gateway.use_mag_cell(&mag_entity_id, &cell_entity_id).await.unwrap(); - } - } - - Ok(()) -} - -pub async fn cell_of_mag_502(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), UseItemError> { - mag_cell(entity_gateway, used_cell, inventory, MagCell::CellOfMag502).await -} - -pub async fn cell_of_mag_213(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), UseItemError> { - mag_cell(entity_gateway, used_cell, inventory, MagCell::CellOfMag213).await -} - -pub async fn parts_of_robochao(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), UseItemError> { - mag_cell(entity_gateway, used_cell, inventory, MagCell::PartsOfRobochao).await -} - -pub async fn heart_of_opaopa(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), UseItemError> { - mag_cell(entity_gateway, used_cell, inventory, MagCell::HeartOfOpaOpa).await -} - -pub async fn heart_of_pian(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), UseItemError> { - mag_cell(entity_gateway, used_cell, inventory, MagCell::HeartOfPian).await -} - -pub async fn heart_of_chao(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), UseItemError> { - mag_cell(entity_gateway, used_cell, inventory, MagCell::HeartOfChao).await -} - -pub async fn heart_of_angel(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), UseItemError> { - mag_cell(entity_gateway, used_cell, inventory, MagCell::HeartOfAngel).await -} - -pub async fn kit_of_hamburger(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), UseItemError> { - mag_cell(entity_gateway, used_cell, inventory, MagCell::KitOfHamburger).await -} - -pub async fn panthers_spirit(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), UseItemError> { - mag_cell(entity_gateway, used_cell, inventory, MagCell::PanthersSpirit).await -} - -pub async fn kit_of_mark3(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), UseItemError> { - mag_cell(entity_gateway, used_cell, inventory, MagCell::KitOfMark3).await -} - -pub async fn kit_of_master_system(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), UseItemError> { - mag_cell(entity_gateway, used_cell, inventory, MagCell::KitOfMasterSystem).await -} - -pub async fn kit_of_genesis(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), UseItemError> { - mag_cell(entity_gateway, used_cell, inventory, MagCell::KitOfGenesis).await -} - -pub async fn kit_of_sega_saturn(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), UseItemError> { - mag_cell(entity_gateway, used_cell, inventory, MagCell::KitOfSegaSaturn).await -} - -pub async fn kit_of_dreamcast(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), UseItemError> { - mag_cell(entity_gateway, used_cell, inventory, MagCell::KitOfDreamcast).await -} - -pub async fn tablet(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), UseItemError> { - mag_cell(entity_gateway, used_cell, inventory, MagCell::Tablet).await -} - -pub async fn dragon_scale(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), UseItemError> { - mag_cell(entity_gateway, used_cell, inventory, MagCell::DragonScale).await -} - -pub async fn heaven_striker_coat(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), UseItemError> { - mag_cell(entity_gateway, used_cell, inventory, MagCell::HeavenStrikerCoat).await -} - -pub async fn pioneer_parts(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), UseItemError> { - mag_cell(entity_gateway, used_cell, inventory, MagCell::PioneerParts).await -} - -pub async fn amities_memo(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), UseItemError> { - mag_cell(entity_gateway, used_cell, inventory, MagCell::AmitiesMemo).await -} - -pub async fn heart_of_morolian(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), UseItemError> { - mag_cell(entity_gateway, used_cell, inventory, MagCell::HeartOfMorolian).await -} - -pub async fn rappys_beak(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), UseItemError> { - mag_cell(entity_gateway, used_cell, inventory, MagCell::RappysBeak).await -} - -pub async fn yahoos_engine(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), UseItemError> { - mag_cell(entity_gateway, used_cell, inventory, MagCell::YahoosEngine).await -} - -pub async fn d_photon_core(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), UseItemError> { - mag_cell(entity_gateway, used_cell, inventory, MagCell::DPhotonCore).await -} - -pub async fn liberta_kit(entity_gateway: &mut EG, used_cell: &ConsumedItem, inventory: &mut CharacterInventory) -> Result<(), UseItemError> { - mag_cell(entity_gateway, used_cell, inventory, MagCell::LibertaKit).await -} diff --git a/src/ship/packet/builder/lobby.rs b/src/ship/packet/builder/lobby.rs index aefe96f..13c9dd5 100644 --- a/src/ship/packet/builder/lobby.rs +++ b/src/ship/packet/builder/lobby.rs @@ -5,7 +5,6 @@ use crate::ship::ship::{ShipError, Clients}; use crate::ship::location::{ClientLocation, LobbyId, ClientLocationError}; use crate::ship::packet::builder::{player_info}; use crate::ship::items::state::ItemState; -use crate::ship::items::ItemManager; pub fn join_lobby(id: ClientId, diff --git a/src/ship/packet/builder/message.rs b/src/ship/packet/builder/message.rs index 827aec9..fb135f7 100644 --- a/src/ship/packet/builder/message.rs +++ b/src/ship/packet/builder/message.rs @@ -3,16 +3,14 @@ use libpso::packet::ship::*; use crate::entity::item; use crate::common::leveltable::CharacterStats; use crate::ship::ship::{ShipError}; -use crate::ship::items::{ClientItemId, InventoryItem, StackedFloorItem, FloorItem, CharacterBank}; -use crate::ship::items::state::FloorItem as FloorItem2; -use crate::ship::items::state::InventoryItem as InventoryItem2; -use crate::ship::items::state::{BankState, IndividualItemDetail}; +use crate::ship::items::ClientItemId; +use crate::ship::items::state::{InventoryItem, FloorItem, BankState, IndividualItemDetail}; use crate::ship::location::AreaClient; use std::convert::TryInto; use crate::ship::shops::ShopItem; -pub fn item_drop(client: u8, target: u8, item_drop: &FloorItem2) -> Result { +pub fn item_drop(client: u8, target: u8, item_drop: &FloorItem) -> Result { let item_bytes = item_drop.as_client_bytes(); Ok(ItemDrop { client, @@ -68,7 +66,7 @@ pub fn create_meseta(area_client: AreaClient, amount: usize) -> CreateItem { } } -pub fn create_withdrawn_inventory_item(area_client: AreaClient, item: &InventoryItem2) -> Result { +pub fn create_withdrawn_inventory_item(area_client: AreaClient, item: &InventoryItem) -> Result { let bytes = item.item.as_client_bytes(); Ok(CreateItem { client: area_client.local_client.id(), @@ -80,7 +78,7 @@ pub fn create_withdrawn_inventory_item(area_client: AreaClient, item: &Inventory }) } -pub fn create_withdrawn_inventory_item2(area_client: AreaClient, item: &InventoryItem2) -> Result { +pub fn create_withdrawn_inventory_item2(area_client: AreaClient, item: &InventoryItem) -> Result { let bytes = item.item.as_client_bytes(); Ok(CreateItem { client: area_client.local_client.id(), @@ -92,7 +90,7 @@ pub fn create_withdrawn_inventory_item2(area_client: AreaClient, item: &Inventor }) } -pub fn remove_item_from_floor(area_client: AreaClient, item: &FloorItem2) -> Result { +pub fn remove_item_from_floor(area_client: AreaClient, item: &FloorItem) -> Result { Ok(RemoveItemFromFloor { client: area_client.local_client.id(), target: 0, @@ -104,7 +102,7 @@ pub fn remove_item_from_floor(area_client: AreaClient, item: &FloorItem2) -> Res }) } -pub fn drop_split_stack(area_client: AreaClient, item: &FloorItem2) -> Result { +pub fn drop_split_stack(area_client: AreaClient, item: &FloorItem) -> Result { let item_bytes = item.as_client_bytes(); Ok(DropSplitStack { client: area_client.local_client.id(), @@ -121,7 +119,7 @@ pub fn drop_split_stack(area_client: AreaClient, item: &FloorItem2) -> Result Result { +pub fn drop_split_meseta_stack(area_client: AreaClient, item: &FloorItem) -> Result { let item_bytes = item.as_client_bytes(); Ok(DropSplitStack { client: area_client.local_client.id(), diff --git a/src/ship/packet/builder/room.rs b/src/ship/packet/builder/room.rs index a97d8b8..29dc0f2 100644 --- a/src/ship/packet/builder/room.rs +++ b/src/ship/packet/builder/room.rs @@ -5,7 +5,6 @@ use crate::ship::ship::{ShipError, ClientState, Clients}; use crate::ship::location::{ClientLocation, RoomId, AreaClient, ClientLocationError}; use crate::ship::room::RoomState; use crate::ship::items::state::ItemState; -use crate::ship::items::ItemManager; use crate::ship::packet::builder::{player_header, player_info}; use std::convert::TryInto; diff --git a/src/ship/packet/handler/direct_message.rs b/src/ship/packet/handler/direct_message.rs index e5df411..79ed06d 100644 --- a/src/ship/packet/handler/direct_message.rs +++ b/src/ship/packet/handler/direct_message.rs @@ -8,13 +8,13 @@ use crate::common::serverstate::ClientId; use crate::ship::ship::{SendShipPacket, ShipError, Clients, Rooms, ItemShops}; use crate::ship::location::{ClientLocation, ClientLocationError}; use crate::ship::drops::ItemDrop; -use crate::ship::items::{ItemManager, ItemManagerError, ClientItemId, FloorItem}; +use crate::ship::items::ClientItemId; use crate::entity::gateway::EntityGateway; use crate::entity::item; use libpso::utf8_to_utf16_array; use crate::ship::packet::builder; use crate::ship::shops::{ShopItem, ToolShopItem, ArmorShopItem}; -use crate::ship::items::state::{ItemState, FloorType, FloorItemDetail}; +use crate::ship::items::state::{ItemState, ItemStateError, FloorType, FloorItemDetail}; use crate::ship::items::actions::{pick_up_item, withdraw_meseta, deposit_meseta, withdraw_item, deposit_item, buy_shop_item, enemy_drops_item, take_meseta, apply_modifier, TriggerCreateItem}; const BANK_ACTION_DEPOSIT: u8 = 0; @@ -431,11 +431,11 @@ where let inventory = item_state.get_character_inventory(&client.character)?; let item = inventory.get_by_client_id(&ClientItemId(tek_request.item_id)) - .ok_or(ItemManagerError::WrongItemType(ClientItemId(tek_request.item_id)))?; + .ok_or(ItemStateError::WrongItemType(ClientItemId(tek_request.item_id)))?; let mut weapon = item.item.as_individual() - .ok_or(ItemManagerError::WrongItemType(ClientItemId(tek_request.item_id)))? + .ok_or(ItemStateError::WrongItemType(ClientItemId(tek_request.item_id)))? .as_weapon() - .ok_or(ItemManagerError::WrongItemType(ClientItemId(tek_request.item_id)))? + .ok_or(ItemStateError::WrongItemType(ClientItemId(tek_request.item_id)))? .clone(); weapon.apply_modifier(&item::weapon::WeaponModifier::Tekked { diff --git a/src/ship/ship.rs b/src/ship/ship.rs index 6d821a0..23af6a1 100644 --- a/src/ship/ship.rs +++ b/src/ship/ship.rs @@ -61,7 +61,6 @@ pub enum ShipError { ItemError, // TODO: refine this PickUpInvalidItemId(u32), DropInvalidItemId(u32), - ItemManagerError(#[from] items::ItemManagerError), ItemStateError(#[from] items::state::ItemStateError), #[error("")] ItemDropLocationNotSet, @@ -408,7 +407,6 @@ impl ShipServerStateBuilder { clients: HashMap::new(), level_table: CharacterLevelTable::default(), name: self.name.unwrap_or_else(|| "NAMENOTSET".into()), - item_manager: items::ItemManager::default(), item_state: items::state::ItemState::default(), ip: self.ip.unwrap_or_else(|| Ipv4Addr::new(127,0,0,1)), port: self.port.unwrap_or(SHIP_PORT), @@ -453,7 +451,6 @@ pub struct ShipServerState { pub clients: Clients, level_table: CharacterLevelTable, name: String, - item_manager: items::ItemManager, item_state: items::state::ItemState, shops: Box, pub blocks: Blocks, diff --git a/tests/test_shops.rs b/tests/test_shops.rs index fa6da02..c127c09 100644 --- a/tests/test_shops.rs +++ b/tests/test_shops.rs @@ -3,7 +3,6 @@ use elseware::entity::gateway::{EntityGateway, InMemoryGateway}; use elseware::entity::item; use elseware::ship::ship::{ShipServerState, RecvShipPacket, SendShipPacket}; use elseware::ship::room::Difficulty; -use elseware::ship::items::manager::ItemManagerError; use elseware::ship::items::state::ItemStateError; use libpso::packet::ship::*; diff --git a/tests/test_trade.rs b/tests/test_trade.rs index 1da0049..8e35222 100644 --- a/tests/test_trade.rs +++ b/tests/test_trade.rs @@ -4,7 +4,6 @@ use elseware::entity::gateway::{EntityGateway, InMemoryGateway}; use elseware::entity::item; use elseware::ship::ship::{ShipServerState, RecvShipPacket, SendShipPacket, ShipError}; use elseware::entity::item::{Meseta, ItemEntity}; -use elseware::ship::items::transaction::TransactionError; use elseware::ship::packet::handler::trade::TradeError; use elseware::ship::items::state::{ItemStateError, InventoryError}; -- 2.36.0 From fae1ffe55b1ebc128fd9f04e7b9b095eb66452fd Mon Sep 17 00:00:00 2001 From: jake Date: Tue, 19 Jul 2022 12:34:07 -0600 Subject: [PATCH 51/60] appease clippy, the tyrant --- src/common/leveltable.rs | 2 +- src/entity/gateway/postgres/postgres.rs | 6 +- src/entity/item/armor.rs | 4 +- src/entity/item/esweapon.rs | 4 +- src/entity/item/mag.rs | 6 +- src/entity/item/mod.rs | 10 +-- src/entity/item/shield.rs | 2 +- src/entity/item/tool.rs | 2 +- src/entity/item/unit.rs | 4 +- src/entity/item/weapon.rs | 14 ++--- src/ship/drops/mod.rs | 2 +- src/ship/items/actions.rs | 77 ++++++++--------------- src/ship/items/apply_item.rs | 2 +- src/ship/items/state.rs | 31 ++++----- src/ship/location.rs | 30 ++++----- src/ship/map/area.rs | 2 +- src/ship/map/variant.rs | 2 +- src/ship/packet/handler/direct_message.rs | 17 ++--- src/ship/packet/handler/trade.rs | 15 ++--- 19 files changed, 93 insertions(+), 139 deletions(-) diff --git a/src/common/leveltable.rs b/src/common/leveltable.rs index 94ad8ae..1ca6cd1 100644 --- a/src/common/leveltable.rs +++ b/src/common/leveltable.rs @@ -3,7 +3,7 @@ use std::fs::File; use serde_json::Value; use crate::entity::character::CharacterClass; -#[derive(Default, Copy, Clone, Debug, PartialEq)] +#[derive(Default, Copy, Clone, Debug, PartialEq, Eq)] pub struct CharacterStats { pub hp: u16, pub atp: u16, diff --git a/src/entity/gateway/postgres/postgres.rs b/src/entity/gateway/postgres/postgres.rs index 81ae972..8987ab1 100644 --- a/src/entity/gateway/postgres/postgres.rs +++ b/src/entity/gateway/postgres/postgres.rs @@ -51,11 +51,9 @@ impl PostgresGateway { embedded::migrations::runner().run(&mut conn).unwrap(); let pool = async_std::task::block_on(async move { - let pool = PgPoolOptions::new() + PgPoolOptions::new() .max_connections(5) - .connect(&format!("postgresql://{}:{}@{}:5432/{}", username, password, host, dbname)).await.unwrap(); - - pool + .connect(&format!("postgresql://{}:{}@{}:5432/{}", username, password, host, dbname)).await.unwrap() }); PostgresGateway { diff --git a/src/entity/item/armor.rs b/src/entity/item/armor.rs index 451852b..45e9ed4 100644 --- a/src/entity/item/armor.rs +++ b/src/entity/item/armor.rs @@ -289,7 +289,7 @@ impl ArmorType { } -#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub enum ArmorModifier { AddSlot { addslot: ItemEntityId, @@ -297,7 +297,7 @@ pub enum ArmorModifier { } -#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] pub struct Armor { pub armor: ArmorType, pub dfp: u8, diff --git a/src/entity/item/esweapon.rs b/src/entity/item/esweapon.rs index 3a88ccd..2997780 100644 --- a/src/entity/item/esweapon.rs +++ b/src/entity/item/esweapon.rs @@ -121,7 +121,7 @@ impl ESWeaponType { } } -#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize, strum_macros::EnumIter)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize, strum_macros::EnumIter)] pub enum ESWeaponSpecial { Jellen = 1, Zalure, @@ -169,7 +169,7 @@ impl ESWeaponSpecial { } } -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] pub struct ESWeapon { pub esweapon: ESWeaponType, pub special: Option, diff --git a/src/entity/item/mag.rs b/src/entity/item/mag.rs index e55491c..dbdb4a7 100644 --- a/src/entity/item/mag.rs +++ b/src/entity/item/mag.rs @@ -519,7 +519,7 @@ pub enum MagCellError { IsRareMag, } -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Eq)] pub enum MagModifier { FeedMag{ food: ItemEntityId, @@ -529,7 +529,7 @@ pub enum MagModifier { OwnerChange(CharacterClass, SectionID) } -#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize, enum_utils::FromStr)] +#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize, enum_utils::FromStr)] pub enum PhotonBlast { Farlla, Estlla, @@ -539,7 +539,7 @@ pub enum PhotonBlast { MyllaYoulla, } -#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct Mag { pub mag: MagType, def: u16, diff --git a/src/entity/item/mod.rs b/src/entity/item/mod.rs index e89a7a4..a1f78bd 100644 --- a/src/entity/item/mod.rs +++ b/src/entity/item/mod.rs @@ -13,7 +13,7 @@ use crate::entity::character::CharacterEntityId; use crate::ship::map::MapArea; use crate::ship::drops::ItemDropType; -#[derive(PartialEq, Copy, Clone, Debug, Hash, Eq, PartialOrd, Ord, Serialize, Deserialize)] +#[derive(PartialEq, Eq, Copy, Clone, Debug, Hash, PartialOrd, Ord, Serialize, Deserialize)] pub struct ItemEntityId(pub u32); #[derive(Hash, PartialEq, Eq, Debug, Clone)] pub struct ItemId(u32); @@ -70,7 +70,7 @@ pub enum ItemNote { }, } -#[derive(Debug, Copy, Clone, PartialEq)] +#[derive(Debug, Copy, Clone, PartialEq, Eq)] pub struct Meseta(pub u32); impl Meseta { @@ -95,12 +95,12 @@ pub enum ItemType { ESWeapon(esweapon::ESWeaponType), } -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Debug, PartialEq, Eq)] pub enum ItemParseError { InvalidBytes } -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] pub enum ItemDetail { Weapon(weapon::Weapon), Armor(armor::Armor), @@ -186,7 +186,7 @@ pub struct NewItemEntity { pub item: ItemDetail, } -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] pub struct ItemEntity { pub id: ItemEntityId, pub item: ItemDetail, diff --git a/src/entity/item/shield.rs b/src/entity/item/shield.rs index 5c42598..4161c19 100644 --- a/src/entity/item/shield.rs +++ b/src/entity/item/shield.rs @@ -519,7 +519,7 @@ impl ShieldType { } -#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)] +#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct Shield { pub shield: ShieldType, pub dfp: u8, diff --git a/src/entity/item/tool.rs b/src/entity/item/tool.rs index f24f596..3826bbd 100644 --- a/src/entity/item/tool.rs +++ b/src/entity/item/tool.rs @@ -642,7 +642,7 @@ impl ToolType { } -#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] pub struct Tool { pub tool: ToolType, } diff --git a/src/entity/item/unit.rs b/src/entity/item/unit.rs index 371f450..5dc73b6 100644 --- a/src/entity/item/unit.rs +++ b/src/entity/item/unit.rs @@ -323,7 +323,7 @@ impl UnitType { } } -#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)] +#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)] pub enum UnitModifier { PlusPlus, Plus, @@ -331,7 +331,7 @@ pub enum UnitModifier { MinusMinus, } -#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)] +#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct Unit { pub unit: UnitType, pub modifier: Option, diff --git a/src/entity/item/weapon.rs b/src/entity/item/weapon.rs index 11a8709..8e9db4b 100644 --- a/src/entity/item/weapon.rs +++ b/src/entity/item/weapon.rs @@ -10,7 +10,7 @@ pub enum ItemParseError { InvalidWeaponAttribute, } -#[derive(Debug, Copy, Clone, PartialEq, Hash, Eq, Ord, PartialOrd, Serialize, Deserialize)] +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)] pub enum Attribute { Native = 1, ABeast, @@ -32,7 +32,7 @@ impl Attribute { } } -#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)] +#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct WeaponAttribute { pub attr: Attribute, pub value: i8, @@ -45,7 +45,7 @@ impl WeaponAttribute { } -#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize, strum_macros::EnumIter)] +#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize, strum_macros::EnumIter)] pub enum WeaponSpecial { Draw = 1, Drain, @@ -1424,14 +1424,14 @@ impl WeaponType { } -#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)] +#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)] pub enum TekSpecialModifier { Plus, Neutral, Minus, } -#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)] +#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)] pub enum TekPercentModifier { PlusPlus, Plus, @@ -1440,7 +1440,7 @@ pub enum TekPercentModifier { MinusMinus, } -#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub enum WeaponModifier { AddPercents { attr: WeaponAttribute, @@ -1457,7 +1457,7 @@ pub enum WeaponModifier { }, } -#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] pub struct Weapon { pub weapon: WeaponType, pub special: Option, diff --git a/src/ship/drops/mod.rs b/src/ship/drops/mod.rs index e5037af..5e9747f 100644 --- a/src/ship/drops/mod.rs +++ b/src/ship/drops/mod.rs @@ -89,7 +89,7 @@ pub struct MonsterDropStats { pub max_meseta: u32, } -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Debug, PartialEq, Eq)] pub enum ItemDropType { Weapon(weapon::Weapon), Armor(armor::Armor), diff --git a/src/ship/items/actions.rs b/src/ship/items/actions.rs index 10af797..bc0180e 100644 --- a/src/ship/items/actions.rs +++ b/src/ship/items/actions.rs @@ -1,3 +1,5 @@ +// TODO: replace various u32s and usizes denoting item amounts for ItemAmount(u32) for consistency + use crate::ship::items::ClientItemId; use crate::entity::item::{Meseta, ItemNote}; use std::future::Future; @@ -14,7 +16,7 @@ use crate::entity::item::tool::Tool; use crate::entity::item::ItemModifier; use crate::ship::shops::ShopItem; use crate::ship::trade::TradeItem; -use crate::ship::location::{AreaClient, RoomId}; +use crate::ship::location::AreaClient; use crate::ship::drops::{ItemDrop, ItemDropType}; pub enum TriggerCreateItem { @@ -107,7 +109,7 @@ fn take_item_from_inventory(character_id: CharacterEntityId, item_id: ClientItem move |(mut item_state, mut transaction), _| { Box::pin(async move { let mut inventory = item_state.inventory(&character_id)?; - let item = inventory.take_item(&item_id, amount).ok_or_else (|| ItemStateError::NoFloorItem(item_id))?; + let item = inventory.take_item(&item_id, amount).ok_or (ItemStateError::NoFloorItem(item_id))?; transaction.gateway().set_character_inventory(&character_id, &inventory.as_inventory_entity(&character_id)).await?; item_state.set_inventory(inventory); @@ -235,7 +237,7 @@ fn add_meseta_to_shared_floor(character_id: CharacterEntityId, amount: u32, map_ let floor_item = FloorItem { item_id: item_state.new_item_id()?, item: FloorItemDetail::Meseta(Meseta(amount)), - map_area: map_area, + map_area, x: drop_position.0, y: 0.0, z: drop_position.1, @@ -352,7 +354,7 @@ where { entity_gateway.with_transaction(|transaction| async move { let item_state_proxy = ItemStateProxy::new(item_state); - let ((item_state_proxy, transaction), result) = ItemStateAction::default() + let ((item_state_proxy, transaction), _) = ItemStateAction::default() .act(take_meseta_from_inventory(character.id, amount)) .act(add_meseta_to_bank(character.id, amount)) .commit((item_state_proxy, transaction)) @@ -369,7 +371,7 @@ fn take_item_from_bank(character_id: CharacterEntityId, item_id: ClientItemId, a move |(mut item_state, mut transaction), _| { Box::pin(async move { let mut bank = item_state.bank(&character_id)?; - let item = bank.take_item(&item_id, amount).ok_or_else(|| ItemStateError::NoBankItem(item_id))?; + let item = bank.take_item(&item_id, amount).ok_or(ItemStateError::NoBankItem(item_id))?; transaction.gateway().set_character_bank(&character_id, &bank.as_bank_entity(), &bank.name).await?; item_state.set_bank(bank); @@ -440,7 +442,7 @@ where .act(take_item_from_bank(character.id, *item_id, amount)) //.act(bank_item_to_inventory_item) //.act(add_item_to_inventory) - .act(add_bank_item_to_inventory(&character)) + .act(add_bank_item_to_inventory(character)) .commit((item_state_proxy, transaction)) .await?; item_state_proxy.commit(); @@ -664,10 +666,10 @@ fn feed_mag_item(character: CharacterEntity, mag_item_id: ClientItemId) Box::pin(async move { let mut inventory = item_state.inventory(&character.id)?; let mag_entity = inventory.get_by_client_id_mut(&mag_item_id) - .ok_or_else(|| ItemStateError::InvalidItemId(mag_item_id))? + .ok_or(ItemStateError::InvalidItemId(mag_item_id))? .item .as_individual_mut() - .ok_or_else(|| ItemStateError::NotAMag(mag_item_id))?; + .ok_or(ItemStateError::NotAMag(mag_item_id))?; let mag_entity_id = mag_entity.entity_id; let mut transaction = tool.with_entity_id(transaction, |mut transaction, entity_id| { @@ -683,13 +685,13 @@ fn feed_mag_item(character: CharacterEntity, mag_item_id: ClientItemId) let food_tool = tool .item .stacked() - .ok_or_else(|| ItemStateError::NotMagFood(tool.item_id))? + .ok_or(ItemStateError::NotMagFood(tool.item_id))? .tool .tool; let mag_entity = mag_entity .as_mag_mut() - .ok_or_else(|| ItemStateError::NotAMag(mag_item_id))?; + .ok_or(ItemStateError::NotAMag(mag_item_id))?; mag_entity.feed(food_tool); @@ -745,7 +747,7 @@ fn add_bought_item_to_inventory<'a>(character_id: CharacterEntityId, item: ItemDetail::Tool(tool), }).await?; transaction.gateway().add_item_note(&item_entity.id, ItemNote::BoughtAtShop { - character_id: character_id, + character_id, }).await?; item_entities.push(item_entity); } @@ -754,7 +756,7 @@ fn add_bought_item_to_inventory<'a>(character_id: CharacterEntityId, item_id, item: InventoryItemDetail::Stacked(StackedItemDetail { entity_ids: item_entities.into_iter().map(|i| i.id).collect(), - tool: tool, + tool, }) }; inventory.add_item(inventory_item)?.1 @@ -764,7 +766,7 @@ fn add_bought_item_to_inventory<'a>(character_id: CharacterEntityId, item: item_detail.clone(), }).await?; transaction.gateway().add_item_note(&item_entity.id, ItemNote::BoughtAtShop { - character_id: character_id, + character_id, }).await?; let inventory_item = InventoryItem { @@ -816,7 +818,7 @@ fn sell_inventory_item<'a>(character_id: CharacterEntityId) -> impl Fn((ItemStateProxy<'a>, Box), InventoryItem) -> Pin, Box), InventoryItem), ItemStateError>> + Send + 'a>> { - move |(mut item_state, mut transaction), inventory_item| { + move |(mut item_state, transaction), inventory_item| { Box::pin(async move { let mut inventory = item_state.inventory(&character_id)?; let price = inventory_item.item.sell_price()?; @@ -899,7 +901,7 @@ where -> Pin, Box), O), ItemStateError>> + Send + 'a>> + Send + Sync, T: Clone + Send + Sync, { - move |(mut item_state, mut transaction), arg| { + move |(item_state, transaction), arg| { let input = input.clone(); let func = func.clone(); println!("i {:?} {:?}", input, arg); @@ -958,18 +960,6 @@ where } } - -fn clear<'a, T: Send + Clone + 'a>() - -> impl Fn((ItemStateProxy<'a>, Box), T) - -> Pin, Box), ()), ItemStateError>> + Send + 'a>> -{ - move |state, _| { - Box::pin(async move { - Ok((state, ())) - }) - } -} - fn insert<'a, T: Send + Clone + 'a>(element: T) -> impl Fn((ItemStateProxy<'a>, Box), ()) -> Pin, Box), T), ItemStateError>> + Send + 'a>> @@ -986,26 +976,10 @@ fn add_item_to_inventory(character: CharacterEntity) -> impl for<'a> Fn((ItemStateProxy<'a>, Box), InventoryItem) -> Pin, Box), InventoryItem), ItemStateError>> + Send + 'a>> + Clone { - let character = character.clone(); move |(mut item_state, transaction), inventory_item| { let character = character.clone(); Box::pin(async move { - //let bank_name = item_state.bank(&character.id)?.name; let mut inventory = item_state.inventory(&character.id)?; - - let character_id = character.id; - /* - let transaction = bank_item.with_entity_id(transaction, |mut transaction, entity_id| { - let bank_name = bank_name.clone(); - async move { - transaction.gateway().add_item_note(&entity_id, ItemNote::Withdraw { - character_id, - bank: bank_name, - }).await?; - Ok(transaction) - }}).await?; - */ - let mut transaction = inventory_item.with_mag(transaction, |mut transaction, entity_id, _mag| { let character = character.clone(); async move { @@ -1060,7 +1034,6 @@ fn assign_new_item_id() pub async fn trade_items<'a, EG> ( item_state: &'a mut ItemState, entity_gateway: &mut EG, - room_id: RoomId, p1: (&AreaClient, &CharacterEntity, &Vec, Meseta), p2: (&AreaClient, &CharacterEntity, &Vec, Meseta)) -> Result<(Vec, Vec), ItemStateError> @@ -1137,9 +1110,9 @@ pub async fn take_meseta<'a, EG> ( where EG: EntityGateway, { - entity_gateway.with_transaction(|mut transaction| async move { + entity_gateway.with_transaction(|transaction| async move { let item_state_proxy = ItemStateProxy::new(item_state); - let ((item_state_proxy, transaction), p1_removed_items) = ItemStateAction::default() + let ((item_state_proxy, transaction), _) = ItemStateAction::default() .act(take_meseta_from_inventory(*character_id, meseta.0)) .commit((item_state_proxy, transaction)) .await?; @@ -1270,7 +1243,7 @@ pub async fn enemy_drops_item<'a, EG> ( where EG: EntityGateway, { - entity_gateway.with_transaction(|mut transaction| async move { + entity_gateway.with_transaction(|transaction| async move { let item_state_proxy = ItemStateProxy::new(item_state); let ((item_state_proxy, transaction), floor_item) = ItemStateAction::default() .act(convert_item_drop_to_floor_item(character_id, item_drop)) @@ -1283,17 +1256,17 @@ where }).await } -fn apply_modifier_to_inventory_item(character_id: CharacterEntityId, modifier: ItemModifier) +fn apply_modifier_to_inventory_item(modifier: ItemModifier) -> impl for<'a> Fn((ItemStateProxy<'a>, Box), InventoryItem) -> Pin, Box), InventoryItem), ItemStateError>> + Send + 'a>> { move |(item_state, mut transaction), mut inventory_item| { let modifier = modifier.clone(); Box::pin(async move { - match (&inventory_item.item, modifier) { - (InventoryItemDetail::Individual(IndividualItemDetail{entity_id, item: ItemDetail::Weapon(mut weapon), ..}), ItemModifier::WeaponModifier(modifier)) => { + match (&mut inventory_item.item, modifier) { + (InventoryItemDetail::Individual(IndividualItemDetail{entity_id, item: ItemDetail::Weapon(ref mut weapon), ..}), ItemModifier::WeaponModifier(modifier)) => { weapon.apply_modifier(&modifier); - transaction.gateway().add_weapon_modifier(&entity_id, modifier).await?; + transaction.gateway().add_weapon_modifier(entity_id, modifier).await?; }, _ => return Err(ItemStateError::InvalidModifier) } @@ -1334,7 +1307,7 @@ where let item_state_proxy = ItemStateProxy::new(item_state); let ((item_state_proxy, transaction), item) = ItemStateAction::default() .act(take_item_from_inventory(character.id, item_id, 1)) - .act(apply_modifier_to_inventory_item(character.id, modifier)) + .act(apply_modifier_to_inventory_item(modifier)) .act(add_item_to_inventory(character.clone())) .act(as_individual_item()) .commit((item_state_proxy, transaction)) diff --git a/src/ship/items/apply_item.rs b/src/ship/items/apply_item.rs index ce4a701..c88066e 100644 --- a/src/ship/items/apply_item.rs +++ b/src/ship/items/apply_item.rs @@ -109,7 +109,7 @@ where let mut inventory = item_state.inventory(&character.id)?; let (mag_entity_id, mag) = inventory.equipped_mag_mut() - .ok_or_else(|| ApplyItemError::ItemNotEquipped)?; + .ok_or(ApplyItemError::ItemNotEquipped)?; mag.apply_mag_cell(mag_cell_type)?; entity_gateway.use_mag_cell(&mag_entity_id, &cell_entity_id).await?; diff --git a/src/ship/items/state.rs b/src/ship/items/state.rs index dd39b83..4ff10d3 100644 --- a/src/ship/items/state.rs +++ b/src/ship/items/state.rs @@ -492,15 +492,6 @@ pub enum FloorItemDetail { Meseta(Meseta), } -impl FloorItemDetail { - fn stacked(&self) -> Option<&StackedItemDetail> { - match self { - FloorItemDetail::Stacked(sitem) => Some(sitem), - _ => None, - } - } -} - #[derive(Debug, Clone)] pub struct FloorItem { pub item_id: ClientItemId, @@ -767,7 +758,7 @@ impl InventoryState { Some(InventoryItem { item_id: ClientItemId(self.item_id_counter), item: InventoryItemDetail::Stacked(StackedItemDetail { - entity_ids: entity_ids, + entity_ids, tool: stacked_item.tool, })}) } @@ -870,7 +861,7 @@ impl InventoryState { .find(|(entity_id, _)| *entity_id == mag_id) } - pub fn sort(&mut self, item_ids: &Vec) { + pub fn sort(&mut self, item_ids: &[ClientItemId]) { self.inventory.0.sort_by(|a, b| { let a_index = item_ids.iter().position(|item_id| *item_id == a.item_id); let b_index = item_ids.iter().position(|item_id| *item_id == b.item_id); @@ -984,7 +975,7 @@ impl BankState { if self.meseta.0 + amount > 999999 { return Err(ItemStateError::FullOfMeseta) } - self.meseta.0 += self.meseta.0 + amount; + self.meseta.0 += amount; Ok(()) } @@ -1070,7 +1061,7 @@ impl BankState { Some(BankItem { item_id: ClientItemId(self.item_id_counter), item: BankItemDetail::Stacked(StackedItemDetail { - entity_ids: entity_ids, + entity_ids, tool: stacked_item.tool, })}) } @@ -1223,7 +1214,7 @@ impl FloorState { InventoryItemDetail::Individual(individual_item) => FloorItemDetail::Individual(individual_item), InventoryItemDetail::Stacked(stacked_item) => FloorItemDetail::Stacked(stacked_item), }, - map_area: map_area, + map_area, x: position.0, y: position.1, z: position.2, @@ -1271,13 +1262,13 @@ impl Default for ItemState { impl ItemState { pub fn get_character_inventory(&self, character: &CharacterEntity) -> Result<&InventoryState, ItemStateError> { - Ok(self.character_inventory.get(&character.id) - .ok_or(ItemStateError::NoCharacter(character.id))?) + self.character_inventory.get(&character.id) + .ok_or(ItemStateError::NoCharacter(character.id)) } pub fn get_character_bank(&self, character: &CharacterEntity) -> Result<&BankState, ItemStateError> { - Ok(self.character_bank.get(&character.id) - .ok_or(ItemStateError::NoCharacter(character.id))?) + self.character_bank.get(&character.id) + .ok_or(ItemStateError::NoCharacter(character.id)) } } @@ -1327,7 +1318,7 @@ impl ItemState { character_id: character.id, item_id_counter: 0, inventory: Inventory(inventory_items), - equipped: equipped, + equipped, meseta: character_meseta, }; @@ -1408,7 +1399,7 @@ impl ItemState { .find(|item| item.item_id == *item_id) .map(|item| (item, FloorType::Shared)) }) - .ok_or_else(|| ItemStateError::NoFloorItem(*item_id)) + .ok_or(ItemStateError::NoFloorItem(*item_id)) } } diff --git a/src/ship/location.rs b/src/ship/location.rs index efba55f..f4a1433 100644 --- a/src/ship/location.rs +++ b/src/ship/location.rs @@ -12,7 +12,7 @@ pub enum AreaType { } -#[derive(Debug, Copy, Clone, PartialEq)] +#[derive(Debug, Copy, Clone, PartialEq, Eq)] pub struct LobbyId(pub usize); #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, derive_more::Display)] @@ -25,7 +25,7 @@ impl LobbyId { } -#[derive(Error, Debug, PartialEq)] +#[derive(Error, Debug, PartialEq, Eq)] #[error("create room")] pub enum CreateRoomError { NoOpenSlots, @@ -33,7 +33,7 @@ pub enum CreateRoomError { JoinError, } -#[derive(Error, Debug, PartialEq)] +#[derive(Error, Debug, PartialEq, Eq)] #[error("join room")] pub enum JoinRoomError { RoomDoesNotExist, @@ -41,7 +41,7 @@ pub enum JoinRoomError { ClientInAreaAlready, } -#[derive(Error, Debug, PartialEq)] +#[derive(Error, Debug, PartialEq, Eq)] #[error("join lobby")] pub enum JoinLobbyError { LobbyDoesNotExist, @@ -49,7 +49,7 @@ pub enum JoinLobbyError { ClientInAreaAlready, } -#[derive(Error, Debug, PartialEq)] +#[derive(Error, Debug, PartialEq, Eq)] #[error("get area")] pub enum GetAreaError { NotInRoom, @@ -57,28 +57,28 @@ pub enum GetAreaError { InvalidClient, } -#[derive(Error, Debug, PartialEq)] +#[derive(Error, Debug, PartialEq, Eq)] #[error("client removal")] pub enum ClientRemovalError { ClientNotInArea, InvalidArea, } -#[derive(Error, Debug, PartialEq)] +#[derive(Error, Debug, PartialEq, Eq)] #[error("get clients")] pub enum GetClientsError { InvalidClient, InvalidArea, } -#[derive(Error, Debug, PartialEq)] +#[derive(Error, Debug, PartialEq, Eq)] #[error("get neighbor")] pub enum GetNeighborError { InvalidClient, InvalidArea, } -#[derive(Error, Debug, PartialEq)] +#[derive(Error, Debug, PartialEq, Eq)] #[error("get leader")] pub enum GetLeaderError { InvalidClient, @@ -86,7 +86,7 @@ pub enum GetLeaderError { NoClientInArea, } -#[derive(Error, Debug, PartialEq)] +#[derive(Error, Debug, PartialEq, Eq)] #[error("clientlocation")] pub enum ClientLocationError { CreateRoomError(#[from] CreateRoomError), @@ -100,7 +100,7 @@ pub enum ClientLocationError { } -#[derive(Debug, Copy, Clone, PartialEq)] +#[derive(Debug, Copy, Clone, PartialEq, Eq)] pub struct LocalClientId(usize); impl LocalClientId { @@ -115,19 +115,19 @@ impl PartialEq for LocalClientId { } } -#[derive(Debug, Copy, Clone, PartialEq)] +#[derive(Debug, Copy, Clone, PartialEq, Eq)] pub struct AreaClient { pub client: ClientId, pub local_client: LocalClientId, time_join: SystemTime, } -#[derive(Debug, Copy, Clone, PartialEq)] +#[derive(Debug, Copy, Clone, PartialEq, Eq)] struct Lobby([Option; 12]); -#[derive(Debug, Copy, Clone, PartialEq)] +#[derive(Debug, Copy, Clone, PartialEq, Eq)] struct Room([Option; 4]); -#[derive(Debug, Copy, Clone, PartialEq)] +#[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum RoomLobby { Room(RoomId), Lobby(LobbyId), diff --git a/src/ship/map/area.rs b/src/ship/map/area.rs index 3eaa06b..eb6bb68 100644 --- a/src/ship/map/area.rs +++ b/src/ship/map/area.rs @@ -5,7 +5,7 @@ use thiserror::Error; use crate::ship::room::Episode; use std::fmt; -#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)] +#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)] pub enum MapArea { Pioneer2Ep1, Forest1, diff --git a/src/ship/map/variant.rs b/src/ship/map/variant.rs index 819bcdb..0fa7c65 100644 --- a/src/ship/map/variant.rs +++ b/src/ship/map/variant.rs @@ -5,7 +5,7 @@ use rand::Rng; // TODO: don't use * use crate::ship::map::*; -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Eq)] pub enum MapVariantMode { Online, Offline, diff --git a/src/ship/packet/handler/direct_message.rs b/src/ship/packet/handler/direct_message.rs index 79ed06d..9bb04bd 100644 --- a/src/ship/packet/handler/direct_message.rs +++ b/src/ship/packet/handler/direct_message.rs @@ -24,15 +24,11 @@ const SHOP_OPTION_TOOL: u8 = 0; const SHOP_OPTION_WEAPON: u8 = 1; const SHOP_OPTION_ARMOR: u8 = 2; -const INVENTORY_MESETA_CAPACITY: u32 = 999999; -const BANK_MESETA_CAPACITY: u32 = 999999; - -//const BANK_ACTION_: u8 = 1; - #[derive(thiserror::Error, Debug)] -#[error("")] pub enum MessageError { + #[error("invalid tek {0}")] InvalidTek(ClientItemId), + #[error("mismatched tek {0} {1}")] MismatchedTekIds(ClientItemId, ClientItemId), } @@ -158,7 +154,7 @@ where }; //match item_manager.character_picks_up_item(entity_gateway, &mut client.character, ClientItemId(pickup_item.item_id)).await { - match pick_up_item(item_state, entity_gateway, &mut client.character, &ClientItemId(pickup_item.item_id)).await { + match pick_up_item(item_state, entity_gateway, &client.character, &ClientItemId(pickup_item.item_id)).await { Ok(trigger_create_item) => { let remove_packets: Box + Send> = match floor_type { FloorType::Local => { @@ -374,7 +370,7 @@ where } }; - let inventory_item = buy_shop_item(item_state, entity_gateway, &mut client.character, item, ClientItemId(buy_item.item_id), buy_item.amount as u32).await?; + let inventory_item = buy_shop_item(item_state, entity_gateway, &client.character, item, ClientItemId(buy_item.item_id), buy_item.amount as u32).await?; let create = builder::message::create_withdrawn_inventory_item(area_client, &inventory_item)?; if remove { @@ -432,11 +428,10 @@ where let inventory = item_state.get_character_inventory(&client.character)?; let item = inventory.get_by_client_id(&ClientItemId(tek_request.item_id)) .ok_or(ItemStateError::WrongItemType(ClientItemId(tek_request.item_id)))?; - let mut weapon = item.item.as_individual() + let mut weapon = *item.item.as_individual() .ok_or(ItemStateError::WrongItemType(ClientItemId(tek_request.item_id)))? .as_weapon() - .ok_or(ItemStateError::WrongItemType(ClientItemId(tek_request.item_id)))? - .clone(); + .ok_or(ItemStateError::WrongItemType(ClientItemId(tek_request.item_id)))?; weapon.apply_modifier(&item::weapon::WeaponModifier::Tekked { special: special_mod, diff --git a/src/ship/packet/handler/trade.rs b/src/ship/packet/handler/trade.rs index b6b9faa..60b05c3 100644 --- a/src/ship/packet/handler/trade.rs +++ b/src/ship/packet/handler/trade.rs @@ -10,10 +10,8 @@ use crate::ship::trade::{TradeItem, TradeState, TradeStatus}; use crate::entity::gateway::EntityGateway; use crate::ship::packet::builder; use crate::ship::items::actions::trade_items; -use crate::entity::item::ItemDetail; -use crate::entity::item::tool::Tool; -use crate::ship::location::AreaClient; -use crate::entity::item::{Meseta, ItemNote}; +use crate::ship::location::{AreaClient, RoomId}; +use crate::entity::item::Meseta; pub const MESETA_ITEM_ID: ClientItemId = ClientItemId(0xFFFFFF01); pub const OTHER_MESETA_ITEM_ID: ClientItemId = ClientItemId(0xFFFFFFFF); @@ -441,9 +439,9 @@ where { enum TradeReady<'a> { OnePlayer, - BothPlayers(crate::ship::location::RoomId, - (crate::ship::location::AreaClient, &'a crate::ship::ship::ClientState, crate::ship::trade::ClientTradeState), - (crate::ship::location::AreaClient, &'a crate::ship::ship::ClientState, crate::ship::trade::ClientTradeState)), + BothPlayers(RoomId, + (AreaClient, &'a crate::ship::ship::ClientState, crate::ship::trade::ClientTradeState), + (AreaClient, &'a crate::ship::ship::ClientState, crate::ship::trade::ClientTradeState)), } let trade_instructions = trades @@ -476,7 +474,7 @@ where TradeReady::OnePlayer => { Ok(Box::new(None.into_iter()) as Box + Send>) }, - TradeReady::BothPlayers(room_id, (this_local_client, this_client, this), (other_local_client, other_client, other)) => { + TradeReady::BothPlayers(_room_id, (this_local_client, this_client, this), (other_local_client, other_client, other)) => { let remove_item_packets = this.items .clone() .into_iter() @@ -495,7 +493,6 @@ where let (this_new_items, other_new_items) = trade_items(item_state, entity_gateway, - room_id, (&this_local_client, &this_client.character, &this.items, Meseta(this.meseta as u32)), (&other_local_client, &other_client.character, &other.items, Meseta(other.meseta as u32))).await?; -- 2.36.0 From 2771f5692a3f8024530a0818f435d19b35aedf23 Mon Sep 17 00:00:00 2001 From: jake Date: Tue, 19 Jul 2022 14:30:14 -0600 Subject: [PATCH 52/60] missed a thing --- src/bin/main.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/bin/main.rs b/src/bin/main.rs index b2d9dbe..76c63a6 100644 --- a/src/bin/main.rs +++ b/src/bin/main.rs @@ -69,7 +69,6 @@ fn main() { character.name = format!("Test Char {}", i*2); let character = entity_gateway.create_character(character).await.unwrap(); entity_gateway.set_character_meseta(&character.id, item::Meseta(999999)).await.unwrap(); - let mut character = NewCharacterEntity::new(fake_user.id); entity_gateway.set_bank_meseta(&character.id, &item::BankName("".into()), item::Meseta(999999)).await.unwrap(); let mut character = NewCharacterEntity::new(fake_user.id, 1); character.slot = 2; -- 2.36.0 From 8f72c6074bae7f3f5af50f78671be2ace1b62308 Mon Sep 17 00:00:00 2001 From: jake Date: Tue, 19 Jul 2022 19:04:53 -0600 Subject: [PATCH 53/60] move ItemStateAction to its own file --- src/ship/items/actions.rs | 3 +- src/ship/items/itemstateaction.rs | 137 ++++++++++++++++++++++++++++++ src/ship/items/mod.rs | 6 +- src/ship/items/state.rs | 135 ----------------------------- 4 files changed, 141 insertions(+), 140 deletions(-) create mode 100644 src/ship/items/itemstateaction.rs diff --git a/src/ship/items/actions.rs b/src/ship/items/actions.rs index bc0180e..02e774d 100644 --- a/src/ship/items/actions.rs +++ b/src/ship/items/actions.rs @@ -8,8 +8,9 @@ use std::pin::Pin; use crate::ship::map::MapArea; use crate::entity::character::{CharacterEntity, CharacterEntityId}; use crate::entity::gateway::{EntityGateway, EntityGatewayTransaction}; -use crate::ship::items::state::{ItemState, ItemStateProxy, ItemStateAction, ItemAction, ItemStateError, FloorItem, InventoryItem, AddItemResult, FloorItemDetail, +use crate::ship::items::state::{ItemState, ItemStateProxy, ItemStateError, FloorItem, InventoryItem, AddItemResult, FloorItemDetail, StackedItemDetail, BankItem, BankItemDetail, InventoryItemDetail, IndividualItemDetail}; +use crate::ship::items::itemstateaction::{ItemStateAction, ItemAction}; use crate::ship::items::apply_item::apply_item; use crate::entity::item::{ItemDetail, NewItemEntity, TradeId}; use crate::entity::item::tool::Tool; diff --git a/src/ship/items/itemstateaction.rs b/src/ship/items/itemstateaction.rs new file mode 100644 index 0000000..3b60548 --- /dev/null +++ b/src/ship/items/itemstateaction.rs @@ -0,0 +1,137 @@ +use std::future::Future; + +#[async_trait::async_trait] +pub trait ItemAction { + type Input; + type Output; + type Start; + type Error; + + async fn action(&self, s: Self::Start, i: Self::Input) -> Result<(Self::Start, Self::Output), Self::Error>; + async fn commit(&self, v: Self::Start) -> Result<(Self::Start, Self::Output), Self::Error>; +} + + +pub struct ItemStateAction { + _t: std::marker::PhantomData, + _s: std::marker::PhantomData, + _e: std::marker::PhantomData, +} + +impl Default for ItemStateAction { + fn default() -> ItemStateAction { + ItemStateAction { + _t: std::marker::PhantomData, + _s: std::marker::PhantomData, + _e: std::marker::PhantomData, + } + } +} + +impl ItemStateAction +where + T: Send + Sync, + S: Send + Sync, + E: Send + Sync, +{ + pub fn act(self, f: F) -> ItemActionStage, F, Fut, S, E> + where + F: Fn(S, ()) -> Fut + Send + Sync, + Fut: Future> + Send + { + ItemActionStage { + _s: Default::default(), + _e: std::marker::PhantomData, + prev: self, + actionf: f, + } + } +} + +pub struct ItemActionStage +where + P: ItemAction, + F: Fn(S, P::Output) -> Fut + Send + Sync, + Fut: Future> + Send, +{ + _s: std::marker::PhantomData, + _e: std::marker::PhantomData, + prev: P, + actionf: F, +} + +#[async_trait::async_trait] +impl ItemAction for ItemActionStage +where + P: ItemAction + ItemAction + Send + Sync, + F: Fn(S, P::Output) -> Fut + Send + Sync, + Fut: Future> + Send, + S: Send + Sync, + P::Output: Send + Sync, + E: Send + Sync, + O: Send + Sync, + P::Error: Send + Sync, +{ + type Input = P::Output; + type Output = O; + type Start = S; + type Error = P::Error; + + async fn action(&self, s: Self::Start, i: Self::Input) -> Result<(Self::Start, Self::Output), Self::Error> { + (self.actionf)(s, i).await + } + + async fn commit(&self, i: Self::Start) -> Result<(Self::Start, Self::Output), Self::Error> { + let (i, prev) = self.prev.commit(i).await?; + self.action(i, prev).await + } +} + +impl ItemActionStage +where + P: ItemAction + Send + Sync, + F: Fn(S, P::Output) -> Fut + Send + Sync, + Fut: Future> + Send, + S: Send + Sync, + P::Output: Send + Sync, + E: Send + Sync, + O: Send + Sync, + P::Error: Send + Sync, +{ + #[allow(clippy::type_complexity)] + pub fn act(self, g: G) -> ItemActionStage, G, GFut, S, E> + where + S: Send + Sync, + G: Fn(S, as ItemAction>::Output) -> GFut + Send + Sync, + GFut: Future> + Send, + O2: Send + Sync, + { + ItemActionStage { + _s: Default::default(), + _e: Default::default(), + prev: self, + actionf: g, + } + } +} + +#[async_trait::async_trait] +impl ItemAction for ItemStateAction +where + T: Send + Sync, + S: Send + Sync, + E: Send + Sync, +{ + type Input = T; + type Output = (); + type Start = T; + type Error = E; + + async fn action(&self, s: Self::Start, _i: Self::Input) -> Result<(Self::Start, Self::Output), Self::Error> { + Ok((s, ())) + } + + async fn commit(&self, i: Self::Start) -> Result<(Self::Start, Self::Output), Self::Error> { + Ok((i, ())) + } +} diff --git a/src/ship/items/mod.rs b/src/ship/items/mod.rs index f2a8da1..16d1134 100644 --- a/src/ship/items/mod.rs +++ b/src/ship/items/mod.rs @@ -1,9 +1,7 @@ pub mod state; pub mod actions; pub mod apply_item; -use serde::{Serialize, Deserialize}; +pub mod itemstateaction; -#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, Serialize, Deserialize, derive_more::Display)] +#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, serde::Serialize, serde::Deserialize, derive_more::Display)] pub struct ClientItemId(pub u32); - - diff --git a/src/ship/items/state.rs b/src/ship/items/state.rs index 4ff10d3..2d2afc4 100644 --- a/src/ship/items/state.rs +++ b/src/ship/items/state.rs @@ -84,141 +84,6 @@ pub enum FloorType { } -#[async_trait::async_trait] -pub trait ItemAction { - type Input; - type Output; - type Start; - type Error; - - async fn action(&self, s: Self::Start, i: Self::Input) -> Result<(Self::Start, Self::Output), Self::Error>; - async fn commit(&self, v: Self::Start) -> Result<(Self::Start, Self::Output), Self::Error>; -} - - -pub struct ItemStateAction { - _t: std::marker::PhantomData, - _s: std::marker::PhantomData, - _e: std::marker::PhantomData, -} - -impl Default for ItemStateAction { - fn default() -> ItemStateAction { - ItemStateAction { - _t: std::marker::PhantomData, - _s: std::marker::PhantomData, - _e: std::marker::PhantomData, - } - } -} - -impl ItemStateAction -where - T: Send + Sync, - S: Send + Sync, - E: Send + Sync, -{ - pub fn act(self, f: F) -> ItemActionStage, F, Fut, S, E> - where - F: Fn(S, ()) -> Fut + Send + Sync, - Fut: Future> + Send - { - ItemActionStage { - _s: Default::default(), - _e: std::marker::PhantomData, - prev: self, - actionf: f, - } - } -} - -pub struct ItemActionStage -where - P: ItemAction, - F: Fn(S, P::Output) -> Fut + Send + Sync, - Fut: Future> + Send, -{ - _s: std::marker::PhantomData, - _e: std::marker::PhantomData, - prev: P, - actionf: F, -} - -#[async_trait::async_trait] -impl ItemAction for ItemActionStage -where - P: ItemAction + ItemAction + Send + Sync, - F: Fn(S, P::Output) -> Fut + Send + Sync, - Fut: Future> + Send, - S: Send + Sync, - P::Output: Send + Sync, - E: Send + Sync, - O: Send + Sync, - P::Error: Send + Sync, -{ - type Input = P::Output; - type Output = O; - type Start = S; - type Error = P::Error; - - async fn action(&self, s: Self::Start, i: Self::Input) -> Result<(Self::Start, Self::Output), Self::Error> { - (self.actionf)(s, i).await - } - - async fn commit(&self, i: Self::Start) -> Result<(Self::Start, Self::Output), Self::Error> { - let (i, prev) = self.prev.commit(i).await?; - self.action(i, prev).await - } -} - -impl ItemActionStage -where - P: ItemAction + Send + Sync, - F: Fn(S, P::Output) -> Fut + Send + Sync, - Fut: Future> + Send, - S: Send + Sync, - P::Output: Send + Sync, - E: Send + Sync, - O: Send + Sync, - P::Error: Send + Sync, -{ - #[allow(clippy::type_complexity)] - pub fn act(self, g: G) -> ItemActionStage, G, GFut, S, E> - where - S: Send + Sync, - G: Fn(S, as ItemAction>::Output) -> GFut + Send + Sync, - GFut: Future> + Send, - O2: Send + Sync, - { - ItemActionStage { - _s: Default::default(), - _e: Default::default(), - prev: self, - actionf: g, - } - } -} - -#[async_trait::async_trait] -impl ItemAction for ItemStateAction -where - T: Send + Sync, - S: Send + Sync, - E: Send + Sync, -{ - type Input = T; - type Output = (); - type Start = T; - type Error = E; - - async fn action(&self, s: Self::Start, _i: Self::Input) -> Result<(Self::Start, Self::Output), Self::Error> { - Ok((s, ())) - } - - async fn commit(&self, i: Self::Start) -> Result<(Self::Start, Self::Output), Self::Error> { - Ok((i, ())) - } -} #[derive(Clone, Debug)] -- 2.36.0 From 3babc6f4bdcdae7c3030397a55fb50d6e5db1ec6 Mon Sep 17 00:00:00 2001 From: jake Date: Tue, 19 Jul 2022 19:39:58 -0600 Subject: [PATCH 54/60] move inventory code out of state --- src/ship/character.rs | 3 +- src/ship/items/actions.rs | 5 +- src/ship/items/apply_item.rs | 3 +- src/ship/items/inventory.rs | 529 +++++++++++++++++++++++++++++ src/ship/items/mod.rs | 1 + src/ship/items/state.rs | 520 +--------------------------- src/ship/packet/builder/message.rs | 5 +- src/ship/packet/handler/trade.rs | 3 +- tests/test_trade.rs | 3 +- 9 files changed, 550 insertions(+), 522 deletions(-) create mode 100644 src/ship/items/inventory.rs diff --git a/src/ship/character.rs b/src/ship/character.rs index d2144cd..674e17d 100644 --- a/src/ship/character.rs +++ b/src/ship/character.rs @@ -2,7 +2,8 @@ use libpso::character::character; use crate::common::leveltable::CharacterStats; use crate::entity::character::CharacterEntity; //use crate::ship::items::{CharacterInventory, CharacterBank}; -use crate::ship::items::state::{InventoryState, BankState}; +use crate::ship::items::state::{BankState}; +use crate::ship::items::inventory::InventoryState; use crate::entity::item::Meseta; diff --git a/src/ship/items/actions.rs b/src/ship/items/actions.rs index 02e774d..321db6a 100644 --- a/src/ship/items/actions.rs +++ b/src/ship/items/actions.rs @@ -8,9 +8,10 @@ use std::pin::Pin; use crate::ship::map::MapArea; use crate::entity::character::{CharacterEntity, CharacterEntityId}; use crate::entity::gateway::{EntityGateway, EntityGatewayTransaction}; -use crate::ship::items::state::{ItemState, ItemStateProxy, ItemStateError, FloorItem, InventoryItem, AddItemResult, FloorItemDetail, - StackedItemDetail, BankItem, BankItemDetail, InventoryItemDetail, IndividualItemDetail}; +use crate::ship::items::state::{ItemState, ItemStateProxy, ItemStateError, FloorItem, AddItemResult, FloorItemDetail, + StackedItemDetail, BankItem, BankItemDetail, IndividualItemDetail}; use crate::ship::items::itemstateaction::{ItemStateAction, ItemAction}; +use crate::ship::items::inventory::{InventoryItem, InventoryItemDetail}; use crate::ship::items::apply_item::apply_item; use crate::entity::item::{ItemDetail, NewItemEntity, TradeId}; use crate::entity::item::tool::Tool; diff --git a/src/ship/items/apply_item.rs b/src/ship/items/apply_item.rs index c88066e..d43f55d 100644 --- a/src/ship/items/apply_item.rs +++ b/src/ship/items/apply_item.rs @@ -5,7 +5,8 @@ use crate::entity::character::CharacterEntity; use crate::entity::item::mag::{MagCell, MagCellError}; use crate::entity::item::tool::ToolType; use crate::entity::item::{ItemDetail, ItemEntityId}; -use crate::ship::items::state::{ItemStateProxy, InventoryItem, InventoryItemDetail, ItemStateError}; +use crate::ship::items::state::{ItemStateProxy, ItemStateError}; +use crate::ship::items::inventory::{InventoryItem, InventoryItemDetail}; #[derive(Error, Debug)] diff --git a/src/ship/items/inventory.rs b/src/ship/items/inventory.rs new file mode 100644 index 0000000..602b515 --- /dev/null +++ b/src/ship/items/inventory.rs @@ -0,0 +1,529 @@ +use std::cmp::Ordering; +use libpso::character::character; +use crate::ship::items::ClientItemId; +use crate::entity::item::{Meseta, ItemEntityId, ItemDetail, ItemEntity, InventoryEntity, InventoryItemEntity, EquippedEntity}; +use std::future::Future; + +use crate::entity::character::CharacterEntityId; +use crate::entity::item::tool::ToolType; +use crate::entity::item::mag::Mag; +use crate::ship::shops::{ShopItem, ArmorShopItem, ToolShopItem, WeaponShopItem}; +use crate::ship::items::state::ItemStateError; +use crate::ship::items::state::{IndividualItemDetail, StackedItemDetail, AddItemResult}; +use crate::ship::items::state::{FloorItem, FloorItemDetail}; + +#[derive(Clone, Debug)] +pub enum InventoryItemDetail { + Individual(IndividualItemDetail), + Stacked(StackedItemDetail), +} + +impl InventoryItemDetail { + // TODO: rename as_stacked for consistency + pub fn stacked(&self) -> Option<&StackedItemDetail> { + match self { + InventoryItemDetail::Stacked(sitem) => Some(sitem), + _ => None, + } + } + // TODO: rename as_stacked_mut for consistency + pub fn stacked_mut(&mut self) -> Option<&mut StackedItemDetail> { + match self { + InventoryItemDetail::Stacked(sitem) => Some(sitem), + _ => None, + } + } + + pub fn as_individual(&self) -> Option<&IndividualItemDetail> { + match self { + InventoryItemDetail::Individual(iitem) => Some(iitem), + _ => None, + } + } + + pub fn as_individual_mut(&mut self) -> Option<&mut IndividualItemDetail> { + match self { + InventoryItemDetail::Individual(iitem) => Some(iitem), + _ => None, + } + } + + pub fn as_client_bytes(&self) -> [u8; 16] { + match self { + InventoryItemDetail::Individual(item) => { + item.as_client_bytes() + }, + InventoryItemDetail::Stacked(item) => { + item.tool.as_stacked_bytes(item.entity_ids.len()) + }, + } + } + + // TODO: this should probably go somewhere a bit more fundamental like ItemDetail + pub fn sell_price(&self) -> Result { + match self { + InventoryItemDetail::Individual(individual_item) => { + match &individual_item.item { + // TODO: can wrapped items be sold? + ItemDetail::Weapon(w) => { + if !w.tekked { + return Ok(1u32) + } + if w.is_rare_item() { + return Ok(10u32) + } + Ok((WeaponShopItem::from(w).price() / 8) as u32) + }, + ItemDetail::Armor(a) => { + if a.is_rare_item() { + return Ok(10u32) + } + Ok((ArmorShopItem::from(a).price() / 8) as u32) + }, + ItemDetail::Shield(s) => { + if s.is_rare_item() { + return Ok(10u32) + } + Ok((ArmorShopItem::from(s).price() / 8) as u32) + }, + ItemDetail::Unit(u) => { + if u.is_rare_item() { + return Ok(10u32) + } + Ok((ArmorShopItem::from(u).price() / 8) as u32) + }, + ItemDetail::Tool(t) => { + if !matches!(t.tool, ToolType::PhotonDrop | ToolType::PhotonSphere | ToolType::PhotonCrystal) && t.is_rare_item() { + return Ok(10u32) + } + Ok((ToolShopItem::from(t).price() / 8) as u32) + }, + ItemDetail::TechniqueDisk(d) => { + Ok((ToolShopItem::from(d).price() / 8) as u32) + }, + ItemDetail::Mag(_m) => { + Err(ItemStateError::ItemNotSellable) + }, + ItemDetail::ESWeapon(_e) => { + Ok(10u32) + }, + } + }, + // the number of stacked items sold is handled by the caller. this is just the price of 1 + InventoryItemDetail::Stacked(stacked_item) => { + Ok(((ToolShopItem::from(&stacked_item.tool).price() / 8) as u32) * stacked_item.count() as u32) + }, + } + } + +} + + +#[derive(Clone, Debug)] +pub struct InventoryItem { + pub item_id: ClientItemId, + pub item: InventoryItemDetail, +} + +impl InventoryItem { + pub async fn with_entity_id(&self, mut param: T, mut func: F) -> Result + where + F: FnMut(T, ItemEntityId) -> Fut, + Fut: Future>, + { + match &self.item { + InventoryItemDetail::Individual(individual_item) => { + param = func(param, individual_item.entity_id).await?; + }, + InventoryItemDetail::Stacked(stacked_item) => { + for entity_id in &stacked_item.entity_ids { + param = func(param, *entity_id).await?; + } + } + } + + Ok(param) + } + + pub async fn with_mag(&self, mut param: T, mut func: F) -> Result + where + F: FnMut(T, ItemEntityId, Mag) -> Fut, + Fut: Future>, + { + if let InventoryItemDetail::Individual(individual_item) = &self.item { + if let ItemDetail::Mag(mag) = &individual_item.item { + param = func(param, individual_item.entity_id, mag.clone()).await?; + } + } + Ok(param) + } +} + +#[derive(Clone, Debug)] +pub struct Inventory(Vec); + +impl Inventory { + pub fn new(items: Vec) -> Inventory { + Inventory(items) + } +} + + +#[derive(thiserror::Error, Debug)] +pub enum InventoryError { + #[error("inventory full")] + InventoryFull, + #[error("stack full")] + StackFull, + #[error("meseta full")] + MesetaFull, +} + +#[derive(Clone)] +pub struct InventoryState { + pub character_id: CharacterEntityId, + pub item_id_counter: u32, + pub inventory: Inventory, + pub equipped: EquippedEntity, + pub meseta: Meseta, +} + +impl InventoryState { + pub fn initialize_item_ids(&mut self, base_item_id: u32) { + for (i, item) in self.inventory.0.iter_mut().enumerate() { + item.item_id = ClientItemId(base_item_id + i as u32); + } + self.item_id_counter = base_item_id + self.inventory.0.len() as u32 + 1; + } + + pub fn new_item_id(&mut self) -> ClientItemId { + self.item_id_counter += 1; + ClientItemId(self.item_id_counter) + } + + pub fn count(&self) -> usize { + self.inventory.0.len() + } + + pub fn add_floor_item(&mut self, item: FloorItem) -> Result { + match item.item { + FloorItemDetail::Individual(iitem) => { + if self.inventory.0.len() >= 30 { + Err(InventoryError::InventoryFull) + } + else { + self.inventory.0.push(InventoryItem { + item_id: item.item_id, + item: InventoryItemDetail::Individual(iitem) + }); + Ok(AddItemResult::NewItem) + } + }, + FloorItemDetail::Stacked(sitem) => { + let existing_stack = self.inventory.0 + .iter_mut() + .filter_map(|item| item.item.stacked_mut()) + .find(|item| { + item.tool == sitem.tool + }); + match existing_stack { + Some(existing_stack) => { + if existing_stack.entity_ids.len() + sitem.entity_ids.len() > sitem.tool.max_stack() { + Err(InventoryError::StackFull) + } + else { + existing_stack.entity_ids.append(&mut sitem.entity_ids.clone()); + Ok(AddItemResult::AddToStack) + } + }, + None => { + if self.inventory.0.len() >= 30 { + Err(InventoryError::InventoryFull) + } + else { + self.inventory.0.push(InventoryItem { + item_id: item.item_id, + item: InventoryItemDetail::Stacked(sitem) + }); + Ok(AddItemResult::NewItem) + } + } + } + + }, + FloorItemDetail::Meseta(meseta) => { + if self.meseta == Meseta(999999) { + Err(InventoryError::MesetaFull) + } + else { + self.meseta.0 = std::cmp::min(self.meseta.0 + meseta.0, 999999); + Ok(AddItemResult::Meseta) + } + }, + } + } + + pub fn add_item(&mut self, item: InventoryItem) -> Result<(AddItemResult, InventoryItem), InventoryError> { + match &item.item { + InventoryItemDetail::Individual(_) => { + if self.inventory.0.len() >= 30 { + Err(InventoryError::InventoryFull) + } + else { + self.inventory.0.push(item); + Ok(( + AddItemResult::NewItem, + self.inventory.0 + .last() + .unwrap() + .clone() + )) + } + }, + InventoryItemDetail::Stacked(sitem) => { + let existing_stack = self.inventory.0 + .iter_mut() + .filter_map(|item| item.item.stacked_mut()) + .find(|item| { + item.tool == sitem.tool + }); + match existing_stack { + Some(existing_stack) => { + if existing_stack.entity_ids.len() + sitem.entity_ids.len() > sitem.tool.max_stack() { + Err(InventoryError::StackFull) + } + else { + existing_stack.entity_ids.append(&mut sitem.entity_ids.clone()); + Ok(( + AddItemResult::AddToStack, + self.inventory.0[self.inventory.0 + .iter() + .filter_map(|item| item.item.stacked()) + .position(|item| item.tool == sitem.tool) + .unwrap()] + .clone() + )) + } + }, + None => { + if self.inventory.0.len() >= 30 { + Err(InventoryError::InventoryFull) + } + else { + self.inventory.0.push(item); + Ok(( + AddItemResult::NewItem, + self.inventory.0 + .last() + .unwrap() + .clone() + )) + } + } + } + } + } + } + + pub fn take_item(&mut self, item_id: &ClientItemId, amount: u32) -> Option { + let idx = self.inventory.0 + .iter() + .position(|i| i.item_id == *item_id)?; + match &mut self.inventory.0[idx].item { + InventoryItemDetail::Individual(_individual_item) => { + Some(self.inventory.0.remove(idx)) + }, + InventoryItemDetail::Stacked(stacked_item) => { + let remove_all = (amount == 0) || match stacked_item.entity_ids.len().cmp(&(amount as usize)) { + Ordering::Equal => true, + Ordering::Greater => false, + Ordering::Less => return None, + }; + + if remove_all { + Some(self.inventory.0.remove(idx)) + } + else { + let entity_ids = stacked_item.entity_ids.drain(..(amount as usize)).collect(); + self.item_id_counter += 1; + Some(InventoryItem { + item_id: ClientItemId(self.item_id_counter), + item: InventoryItemDetail::Stacked(StackedItemDetail { + entity_ids, + tool: stacked_item.tool, + })}) + } + } + } + } + + // TODO: rename get_item_by_client_id + pub fn get_by_client_id(&self, item_id: &ClientItemId) -> Option<&InventoryItem> { + self.inventory.0 + .iter() + .find(|i| i.item_id == *item_id) + } + + pub fn get_by_client_id_mut(&mut self, item_id: &ClientItemId) -> Option<&mut InventoryItem> { + self.inventory.0 + .iter_mut() + .find(|i| i.item_id == *item_id) + } + + pub fn add_meseta(&mut self, amount: u32) -> Result<(), ItemStateError> { + if self.meseta.0 == 999999 { + return Err(ItemStateError::FullOfMeseta) + } + self.meseta.0 = std::cmp::min(self.meseta.0 + amount, 999999); + Ok(()) + } + + pub fn add_meseta_no_overflow(&mut self, amount: u32) -> Result<(), ItemStateError> { + if self.meseta.0 + amount > 999999 { + return Err(ItemStateError::FullOfMeseta) + } + self.meseta.0 += amount; + Ok(()) + } + + pub fn remove_meseta(&mut self, amount: u32) -> Result<(), ItemStateError> { + if amount > self.meseta.0 { + return Err(ItemStateError::InvalidMesetaRemoval(amount)) + } + self.meseta.0 -= amount; + Ok(()) + } + + pub fn equip(&mut self, item_id: &ClientItemId, equip_slot: u8) { + for item in &self.inventory.0 { + if let InventoryItemDetail::Individual(inventory_item) = &item.item { + if item.item_id == *item_id { + match inventory_item.item { + ItemDetail::Weapon(_) => self.equipped.weapon = Some(inventory_item.entity_id), + ItemDetail::Armor(_) => self.equipped.armor = Some(inventory_item.entity_id), + ItemDetail::Shield(_) => self.equipped.shield = Some(inventory_item.entity_id), + ItemDetail::Unit(_) => { + if let Some(unit) = self.equipped.unit.get_mut(equip_slot as usize) { + *unit = Some(inventory_item.entity_id) + } + } + ItemDetail::Mag(_) => self.equipped.mag = Some(inventory_item.entity_id), + _ => {} + } + } + } + } + } + + pub fn unequip(&mut self, item_id: &ClientItemId) { + for item in &self.inventory.0 { + if let InventoryItemDetail::Individual(inventory_item) = &item.item { + if item.item_id == *item_id { + match inventory_item.item { + ItemDetail::Weapon(_) => self.equipped.weapon = None, + ItemDetail::Armor(_) => { + self.equipped.armor = None; + self.equipped.unit = [None; 4]; + } + ItemDetail::Shield(_) => self.equipped.shield = None, + ItemDetail::Unit(_) => { + for unit in self.equipped.unit.iter_mut() { + if *unit == Some(inventory_item.entity_id) { + *unit = None + } + } + } + ItemDetail::Mag(_) => self.equipped.mag = Some(inventory_item.entity_id), + _ => {} + } + } + } + } + } + + pub fn equipped_mag_mut(&mut self) -> Option<(ItemEntityId, &mut Mag)> { + let mag_id = self.equipped.mag?; + self.inventory.0 + .iter_mut() + .filter_map(|i| { + let individual = i.item.as_individual_mut()?; + let entity_id = individual.entity_id; + Some((entity_id, individual.as_mag_mut()?)) + }) + .find(|(entity_id, _)| *entity_id == mag_id) + } + + pub fn sort(&mut self, item_ids: &[ClientItemId]) { + self.inventory.0.sort_by(|a, b| { + let a_index = item_ids.iter().position(|item_id| *item_id == a.item_id); + let b_index = item_ids.iter().position(|item_id| *item_id == b.item_id); + + match (a_index, b_index) { + (Some(a_index), Some(b_index)) => { + a_index.cmp(&b_index) + }, + _ => Ordering::Equal + } + }); + } + + pub fn as_inventory_entity(&self, _character_id: &CharacterEntityId) -> InventoryEntity { + InventoryEntity { + items: self.inventory.0.iter() + .map(|item| { + match &item.item { + InventoryItemDetail::Individual(item) => { + InventoryItemEntity::Individual(ItemEntity { + id: item.entity_id, + item: item.item.clone(), + }) + }, + InventoryItemDetail::Stacked(items) => { + InventoryItemEntity::Stacked(items.entity_ids.iter() + .map(|id| { + ItemEntity { + id: *id, + item: ItemDetail::Tool(items.tool) + } + }) + .collect()) + }, + } + }) + .collect() + } + } + + pub fn as_equipped_entity(&self) -> EquippedEntity { + self.equipped.clone() + } + + + pub fn as_client_inventory_items(&self) -> [character::InventoryItem; 30] { + self.inventory.0.iter() + .enumerate() + .fold([character::InventoryItem::default(); 30], |mut inventory, (slot, item)| { + let bytes = item.item.as_client_bytes(); + inventory[slot].data1.copy_from_slice(&bytes[0..12]); + inventory[slot].data2.copy_from_slice(&bytes[12..16]); + inventory[slot].item_id = item.item_id.0; + inventory[slot].equipped = 0; + inventory[slot].flags = 0; + + if let InventoryItemDetail::Individual(individual_item) = &item.item { + if self.equipped.is_equipped(&individual_item.entity_id) { + if let ItemDetail::Unit(_) = individual_item.item { + inventory[slot].data1[4] = self.equipped.unit.iter() + .enumerate() + .find(|(_, u_id)| **u_id == Some(individual_item.entity_id)) + .map(|(a, _)| a) + .unwrap_or(0) as u8 + } + inventory[slot].equipped = 1; + inventory[slot].flags |= 8; + } + } + inventory + }) + } +} diff --git a/src/ship/items/mod.rs b/src/ship/items/mod.rs index 16d1134..3e161f4 100644 --- a/src/ship/items/mod.rs +++ b/src/ship/items/mod.rs @@ -2,6 +2,7 @@ pub mod state; pub mod actions; pub mod apply_item; pub mod itemstateaction; +pub mod inventory; #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, serde::Serialize, serde::Deserialize, derive_more::Display)] pub struct ClientItemId(pub u32); diff --git a/src/ship/items/state.rs b/src/ship/items/state.rs index 2d2afc4..724282e 100644 --- a/src/ship/items/state.rs +++ b/src/ship/items/state.rs @@ -2,18 +2,19 @@ use std::cmp::Ordering; use std::collections::HashMap; use libpso::character::character; use crate::ship::items::ClientItemId; -use crate::entity::item::{Meseta, ItemEntityId, ItemDetail, ItemEntity, InventoryEntity, InventoryItemEntity, BankEntity, BankItemEntity, BankName, EquippedEntity}; +use crate::entity::item::{Meseta, ItemEntityId, ItemDetail, ItemEntity, InventoryItemEntity, BankEntity, BankItemEntity, BankName}; use std::future::Future; use crate::ship::map::MapArea; use crate::ship::location::{AreaClient, RoomId}; use crate::entity::character::{CharacterEntity, CharacterEntityId}; use crate::entity::gateway::{EntityGateway, GatewayError}; -use crate::entity::item::tool::{Tool, ToolType}; +use crate::entity::item::tool::Tool; use crate::entity::item::weapon::Weapon; use crate::entity::item::mag::Mag; use crate::ship::drops::ItemDrop; -use crate::ship::shops::{ShopItem, ArmorShopItem, ToolShopItem, WeaponShopItem}; + +use crate::ship::items::inventory::{Inventory, InventoryItem, InventoryItemDetail, InventoryError, InventoryState}; // TODO: Commit trait that ItemStateProxy and EntityTransaction implement that .commit requires and acts on upon everything succeeding (like 3 less lines of code!) @@ -84,8 +85,6 @@ pub enum FloorType { } - - #[derive(Clone, Debug)] pub struct IndividualItemDetail { pub entity_id: ItemEntityId, @@ -141,154 +140,6 @@ impl StackedItemDetail { } } -#[derive(Clone, Debug)] -pub enum InventoryItemDetail { - Individual(IndividualItemDetail), - Stacked(StackedItemDetail), -} - -impl InventoryItemDetail { - // TODO: rename as_stacked for consistency - pub fn stacked(&self) -> Option<&StackedItemDetail> { - match self { - InventoryItemDetail::Stacked(sitem) => Some(sitem), - _ => None, - } - } - // TODO: rename as_stacked_mut for consistency - pub fn stacked_mut(&mut self) -> Option<&mut StackedItemDetail> { - match self { - InventoryItemDetail::Stacked(sitem) => Some(sitem), - _ => None, - } - } - - pub fn as_individual(&self) -> Option<&IndividualItemDetail> { - match self { - InventoryItemDetail::Individual(iitem) => Some(iitem), - _ => None, - } - } - - pub fn as_individual_mut(&mut self) -> Option<&mut IndividualItemDetail> { - match self { - InventoryItemDetail::Individual(iitem) => Some(iitem), - _ => None, - } - } - - pub fn as_client_bytes(&self) -> [u8; 16] { - match self { - InventoryItemDetail::Individual(item) => { - item.as_client_bytes() - }, - InventoryItemDetail::Stacked(item) => { - item.tool.as_stacked_bytes(item.entity_ids.len()) - }, - } - } - - // TODO: this should probably go somewhere a bit more fundamental like ItemDetail - pub fn sell_price(&self) -> Result { - match self { - InventoryItemDetail::Individual(individual_item) => { - match &individual_item.item { - // TODO: can wrapped items be sold? - ItemDetail::Weapon(w) => { - if !w.tekked { - return Ok(1u32) - } - if w.is_rare_item() { - return Ok(10u32) - } - Ok((WeaponShopItem::from(w).price() / 8) as u32) - }, - ItemDetail::Armor(a) => { - if a.is_rare_item() { - return Ok(10u32) - } - Ok((ArmorShopItem::from(a).price() / 8) as u32) - }, - ItemDetail::Shield(s) => { - if s.is_rare_item() { - return Ok(10u32) - } - Ok((ArmorShopItem::from(s).price() / 8) as u32) - }, - ItemDetail::Unit(u) => { - if u.is_rare_item() { - return Ok(10u32) - } - Ok((ArmorShopItem::from(u).price() / 8) as u32) - }, - ItemDetail::Tool(t) => { - if !matches!(t.tool, ToolType::PhotonDrop | ToolType::PhotonSphere | ToolType::PhotonCrystal) && t.is_rare_item() { - return Ok(10u32) - } - Ok((ToolShopItem::from(t).price() / 8) as u32) - }, - ItemDetail::TechniqueDisk(d) => { - Ok((ToolShopItem::from(d).price() / 8) as u32) - }, - ItemDetail::Mag(_m) => { - Err(ItemStateError::ItemNotSellable) - }, - ItemDetail::ESWeapon(_e) => { - Ok(10u32) - }, - } - }, - // the number of stacked items sold is handled by the caller. this is just the price of 1 - InventoryItemDetail::Stacked(stacked_item) => { - Ok(((ToolShopItem::from(&stacked_item.tool).price() / 8) as u32) * stacked_item.count() as u32) - }, - } - } - -} - - -#[derive(Clone, Debug)] -pub struct InventoryItem { - pub item_id: ClientItemId, - pub item: InventoryItemDetail, -} - -impl InventoryItem { - pub async fn with_entity_id(&self, mut param: T, mut func: F) -> Result - where - F: FnMut(T, ItemEntityId) -> Fut, - Fut: Future>, - { - match &self.item { - InventoryItemDetail::Individual(individual_item) => { - param = func(param, individual_item.entity_id).await?; - }, - InventoryItemDetail::Stacked(stacked_item) => { - for entity_id in &stacked_item.entity_ids { - param = func(param, *entity_id).await?; - } - } - } - - Ok(param) - } - - pub async fn with_mag(&self, mut param: T, mut func: F) -> Result - where - F: FnMut(T, ItemEntityId, Mag) -> Fut, - Fut: Future>, - { - if let InventoryItemDetail::Individual(individual_item) = &self.item { - if let ItemDetail::Mag(mag) = &individual_item.item { - param = func(param, individual_item.entity_id, mag.clone()).await?; - } - } - Ok(param) - } -} - - #[derive(Clone, Debug)] pub enum BankItemDetail { Individual(IndividualItemDetail), @@ -417,20 +268,6 @@ impl FloorItem { } -#[derive(Clone, Debug)] -pub struct Inventory(Vec); - - -#[derive(thiserror::Error, Debug)] -pub enum InventoryError { - #[error("inventory full")] - InventoryFull, - #[error("stack full")] - StackFull, - #[error("meseta full")] - MesetaFull, -} - #[derive(thiserror::Error, Debug)] pub enum BankError { #[error("bank full")] @@ -453,353 +290,6 @@ pub struct LocalFloor(Vec); #[derive(Debug, Clone, Default)] pub struct SharedFloor(Vec); -#[derive(Clone)] -pub struct InventoryState { - character_id: CharacterEntityId, - item_id_counter: u32, - pub inventory: Inventory, - equipped: EquippedEntity, - pub meseta: Meseta, -} - -impl InventoryState { - pub fn initialize_item_ids(&mut self, base_item_id: u32) { - for (i, item) in self.inventory.0.iter_mut().enumerate() { - item.item_id = ClientItemId(base_item_id + i as u32); - } - self.item_id_counter = base_item_id + self.inventory.0.len() as u32 + 1; - } - - pub fn new_item_id(&mut self) -> ClientItemId { - self.item_id_counter += 1; - ClientItemId(self.item_id_counter) - } - - pub fn count(&self) -> usize { - self.inventory.0.len() - } - - pub fn add_floor_item(&mut self, item: FloorItem) -> Result { - match item.item { - FloorItemDetail::Individual(iitem) => { - if self.inventory.0.len() >= 30 { - Err(InventoryError::InventoryFull) - } - else { - self.inventory.0.push(InventoryItem { - item_id: item.item_id, - item: InventoryItemDetail::Individual(iitem) - }); - Ok(AddItemResult::NewItem) - } - }, - FloorItemDetail::Stacked(sitem) => { - let existing_stack = self.inventory.0 - .iter_mut() - .filter_map(|item| item.item.stacked_mut()) - .find(|item| { - item.tool == sitem.tool - }); - match existing_stack { - Some(existing_stack) => { - if existing_stack.entity_ids.len() + sitem.entity_ids.len() > sitem.tool.max_stack() { - Err(InventoryError::StackFull) - } - else { - existing_stack.entity_ids.append(&mut sitem.entity_ids.clone()); - Ok(AddItemResult::AddToStack) - } - }, - None => { - if self.inventory.0.len() >= 30 { - Err(InventoryError::InventoryFull) - } - else { - self.inventory.0.push(InventoryItem { - item_id: item.item_id, - item: InventoryItemDetail::Stacked(sitem) - }); - Ok(AddItemResult::NewItem) - } - } - } - - }, - FloorItemDetail::Meseta(meseta) => { - if self.meseta == Meseta(999999) { - Err(InventoryError::MesetaFull) - } - else { - self.meseta.0 = std::cmp::min(self.meseta.0 + meseta.0, 999999); - Ok(AddItemResult::Meseta) - } - }, - } - } - - pub fn add_item(&mut self, item: InventoryItem) -> Result<(AddItemResult, InventoryItem), InventoryError> { - match &item.item { - InventoryItemDetail::Individual(_) => { - if self.inventory.0.len() >= 30 { - Err(InventoryError::InventoryFull) - } - else { - self.inventory.0.push(item); - Ok(( - AddItemResult::NewItem, - self.inventory.0 - .last() - .unwrap() - .clone() - )) - } - }, - InventoryItemDetail::Stacked(sitem) => { - let existing_stack = self.inventory.0 - .iter_mut() - .filter_map(|item| item.item.stacked_mut()) - .find(|item| { - item.tool == sitem.tool - }); - match existing_stack { - Some(existing_stack) => { - if existing_stack.entity_ids.len() + sitem.entity_ids.len() > sitem.tool.max_stack() { - Err(InventoryError::StackFull) - } - else { - existing_stack.entity_ids.append(&mut sitem.entity_ids.clone()); - Ok(( - AddItemResult::AddToStack, - self.inventory.0[self.inventory.0 - .iter() - .filter_map(|item| item.item.stacked()) - .position(|item| item.tool == sitem.tool) - .unwrap()] - .clone() - )) - } - }, - None => { - if self.inventory.0.len() >= 30 { - Err(InventoryError::InventoryFull) - } - else { - self.inventory.0.push(item); - Ok(( - AddItemResult::NewItem, - self.inventory.0 - .last() - .unwrap() - .clone() - )) - } - } - } - } - } - } - - pub fn take_item(&mut self, item_id: &ClientItemId, amount: u32) -> Option { - let idx = self.inventory.0 - .iter() - .position(|i| i.item_id == *item_id)?; - match &mut self.inventory.0[idx].item { - InventoryItemDetail::Individual(_individual_item) => { - Some(self.inventory.0.remove(idx)) - }, - InventoryItemDetail::Stacked(stacked_item) => { - let remove_all = (amount == 0) || match stacked_item.entity_ids.len().cmp(&(amount as usize)) { - Ordering::Equal => true, - Ordering::Greater => false, - Ordering::Less => return None, - }; - - if remove_all { - Some(self.inventory.0.remove(idx)) - } - else { - let entity_ids = stacked_item.entity_ids.drain(..(amount as usize)).collect(); - self.item_id_counter += 1; - Some(InventoryItem { - item_id: ClientItemId(self.item_id_counter), - item: InventoryItemDetail::Stacked(StackedItemDetail { - entity_ids, - tool: stacked_item.tool, - })}) - } - } - } - } - - pub fn get_by_client_id(&self, item_id: &ClientItemId) -> Option<&InventoryItem> { - self.inventory.0 - .iter() - .find(|i| i.item_id == *item_id) - } - - pub fn get_by_client_id_mut(&mut self, item_id: &ClientItemId) -> Option<&mut InventoryItem> { - self.inventory.0 - .iter_mut() - .find(|i| i.item_id == *item_id) - } - - pub fn add_meseta(&mut self, amount: u32) -> Result<(), ItemStateError> { - if self.meseta.0 == 999999 { - return Err(ItemStateError::FullOfMeseta) - } - self.meseta.0 = std::cmp::min(self.meseta.0 + amount, 999999); - Ok(()) - } - - pub fn add_meseta_no_overflow(&mut self, amount: u32) -> Result<(), ItemStateError> { - if self.meseta.0 + amount > 999999 { - return Err(ItemStateError::FullOfMeseta) - } - self.meseta.0 += amount; - Ok(()) - } - - pub fn remove_meseta(&mut self, amount: u32) -> Result<(), ItemStateError> { - if amount > self.meseta.0 { - return Err(ItemStateError::InvalidMesetaRemoval(amount)) - } - self.meseta.0 -= amount; - Ok(()) - } - - pub fn equip(&mut self, item_id: &ClientItemId, equip_slot: u8) { - for item in &self.inventory.0 { - if let InventoryItemDetail::Individual(inventory_item) = &item.item { - if item.item_id == *item_id { - match inventory_item.item { - ItemDetail::Weapon(_) => self.equipped.weapon = Some(inventory_item.entity_id), - ItemDetail::Armor(_) => self.equipped.armor = Some(inventory_item.entity_id), - ItemDetail::Shield(_) => self.equipped.shield = Some(inventory_item.entity_id), - ItemDetail::Unit(_) => { - if let Some(unit) = self.equipped.unit.get_mut(equip_slot as usize) { - *unit = Some(inventory_item.entity_id) - } - } - ItemDetail::Mag(_) => self.equipped.mag = Some(inventory_item.entity_id), - _ => {} - } - } - } - } - } - - pub fn unequip(&mut self, item_id: &ClientItemId) { - for item in &self.inventory.0 { - if let InventoryItemDetail::Individual(inventory_item) = &item.item { - if item.item_id == *item_id { - match inventory_item.item { - ItemDetail::Weapon(_) => self.equipped.weapon = None, - ItemDetail::Armor(_) => { - self.equipped.armor = None; - self.equipped.unit = [None; 4]; - } - ItemDetail::Shield(_) => self.equipped.shield = None, - ItemDetail::Unit(_) => { - for unit in self.equipped.unit.iter_mut() { - if *unit == Some(inventory_item.entity_id) { - *unit = None - } - } - } - ItemDetail::Mag(_) => self.equipped.mag = Some(inventory_item.entity_id), - _ => {} - } - } - } - } - } - - pub fn equipped_mag_mut(&mut self) -> Option<(ItemEntityId, &mut Mag)> { - let mag_id = self.equipped.mag?; - self.inventory.0 - .iter_mut() - .filter_map(|i| { - let individual = i.item.as_individual_mut()?; - let entity_id = individual.entity_id; - Some((entity_id, individual.as_mag_mut()?)) - }) - .find(|(entity_id, _)| *entity_id == mag_id) - } - - pub fn sort(&mut self, item_ids: &[ClientItemId]) { - self.inventory.0.sort_by(|a, b| { - let a_index = item_ids.iter().position(|item_id| *item_id == a.item_id); - let b_index = item_ids.iter().position(|item_id| *item_id == b.item_id); - - match (a_index, b_index) { - (Some(a_index), Some(b_index)) => { - a_index.cmp(&b_index) - }, - _ => Ordering::Equal - } - }); - } - - pub fn as_inventory_entity(&self, _character_id: &CharacterEntityId) -> InventoryEntity { - InventoryEntity { - items: self.inventory.0.iter() - .map(|item| { - match &item.item { - InventoryItemDetail::Individual(item) => { - InventoryItemEntity::Individual(ItemEntity { - id: item.entity_id, - item: item.item.clone(), - }) - }, - InventoryItemDetail::Stacked(items) => { - InventoryItemEntity::Stacked(items.entity_ids.iter() - .map(|id| { - ItemEntity { - id: *id, - item: ItemDetail::Tool(items.tool) - } - }) - .collect()) - }, - } - }) - .collect() - } - } - - pub fn as_equipped_entity(&self) -> EquippedEntity { - self.equipped.clone() - } - - - pub fn as_client_inventory_items(&self) -> [character::InventoryItem; 30] { - self.inventory.0.iter() - .enumerate() - .fold([character::InventoryItem::default(); 30], |mut inventory, (slot, item)| { - let bytes = item.item.as_client_bytes(); - inventory[slot].data1.copy_from_slice(&bytes[0..12]); - inventory[slot].data2.copy_from_slice(&bytes[12..16]); - inventory[slot].item_id = item.item_id.0; - inventory[slot].equipped = 0; - inventory[slot].flags = 0; - - if let InventoryItemDetail::Individual(individual_item) = &item.item { - if self.equipped.is_equipped(&individual_item.entity_id) { - if let ItemDetail::Unit(_) = individual_item.item { - inventory[slot].data1[4] = self.equipped.unit.iter() - .enumerate() - .find(|(_, u_id)| **u_id == Some(individual_item.entity_id)) - .map(|(a, _)| a) - .unwrap_or(0) as u8 - } - inventory[slot].equipped = 1; - inventory[slot].flags |= 8; - } - } - inventory - }) - } -} #[derive(Clone, Debug)] pub struct Bank(Vec); @@ -1182,7 +672,7 @@ impl ItemState { let inventory_state = InventoryState { character_id: character.id, item_id_counter: 0, - inventory: Inventory(inventory_items), + inventory: Inventory::new(inventory_items), equipped, meseta: character_meseta, }; diff --git a/src/ship/packet/builder/message.rs b/src/ship/packet/builder/message.rs index fb135f7..644d2a1 100644 --- a/src/ship/packet/builder/message.rs +++ b/src/ship/packet/builder/message.rs @@ -4,7 +4,10 @@ use crate::entity::item; use crate::common::leveltable::CharacterStats; use crate::ship::ship::{ShipError}; use crate::ship::items::ClientItemId; -use crate::ship::items::state::{InventoryItem, FloorItem, BankState, IndividualItemDetail}; +use crate::ship::items::inventory::InventoryItem; +use crate::ship::items::state::IndividualItemDetail; +use crate::ship::items::state::BankState; +use crate::ship::items::state::FloorItem; use crate::ship::location::AreaClient; use std::convert::TryInto; use crate::ship::shops::ShopItem; diff --git a/src/ship/packet/handler/trade.rs b/src/ship/packet/handler/trade.rs index 60b05c3..4627697 100644 --- a/src/ship/packet/handler/trade.rs +++ b/src/ship/packet/handler/trade.rs @@ -5,7 +5,8 @@ use crate::common::serverstate::ClientId; use crate::ship::ship::{SendShipPacket, ShipError, Clients}; use crate::ship::location::{ClientLocation, ClientLocationError}; use crate::ship::items::ClientItemId; -use crate::ship::items::state::{ItemState, ItemStateError, InventoryItemDetail}; +use crate::ship::items::state::{ItemState, ItemStateError}; +use crate::ship::items::inventory::InventoryItemDetail; use crate::ship::trade::{TradeItem, TradeState, TradeStatus}; use crate::entity::gateway::EntityGateway; use crate::ship::packet::builder; diff --git a/tests/test_trade.rs b/tests/test_trade.rs index 8e35222..fd2c1b7 100644 --- a/tests/test_trade.rs +++ b/tests/test_trade.rs @@ -5,7 +5,8 @@ use elseware::entity::item; use elseware::ship::ship::{ShipServerState, RecvShipPacket, SendShipPacket, ShipError}; use elseware::entity::item::{Meseta, ItemEntity}; use elseware::ship::packet::handler::trade::TradeError; -use elseware::ship::items::state::{ItemStateError, InventoryError}; +use elseware::ship::items::state::ItemStateError; +use elseware::ship::items::inventory::InventoryError; use libpso::packet::ship::*; use libpso::packet::messages::*; -- 2.36.0 From be5c3ff63357b5d552bf9bb3e0379f488c746932 Mon Sep 17 00:00:00 2001 From: jake Date: Tue, 19 Jul 2022 20:06:43 -0600 Subject: [PATCH 55/60] move floor code out of state --- src/ship/items/actions.rs | 4 +- src/ship/items/floor.rs | 139 ++++++++++++++++++++++ src/ship/items/inventory.rs | 2 +- src/ship/items/mod.rs | 1 + src/ship/items/state.rs | 130 +------------------- src/ship/packet/builder/message.rs | 2 +- src/ship/packet/handler/direct_message.rs | 3 +- 7 files changed, 148 insertions(+), 133 deletions(-) create mode 100644 src/ship/items/floor.rs diff --git a/src/ship/items/actions.rs b/src/ship/items/actions.rs index 321db6a..9753043 100644 --- a/src/ship/items/actions.rs +++ b/src/ship/items/actions.rs @@ -8,10 +8,10 @@ use std::pin::Pin; use crate::ship::map::MapArea; use crate::entity::character::{CharacterEntity, CharacterEntityId}; use crate::entity::gateway::{EntityGateway, EntityGatewayTransaction}; -use crate::ship::items::state::{ItemState, ItemStateProxy, ItemStateError, FloorItem, AddItemResult, FloorItemDetail, - StackedItemDetail, BankItem, BankItemDetail, IndividualItemDetail}; +use crate::ship::items::state::{ItemState, ItemStateProxy, ItemStateError, AddItemResult, StackedItemDetail, BankItem, BankItemDetail, IndividualItemDetail}; use crate::ship::items::itemstateaction::{ItemStateAction, ItemAction}; use crate::ship::items::inventory::{InventoryItem, InventoryItemDetail}; +use crate::ship::items::floor::{FloorItem, FloorItemDetail}; use crate::ship::items::apply_item::apply_item; use crate::entity::item::{ItemDetail, NewItemEntity, TradeId}; use crate::entity::item::tool::Tool; diff --git a/src/ship/items/floor.rs b/src/ship/items/floor.rs new file mode 100644 index 0000000..2aab742 --- /dev/null +++ b/src/ship/items/floor.rs @@ -0,0 +1,139 @@ +use crate::ship::items::ClientItemId; +use crate::entity::item::{Meseta, ItemEntityId, ItemDetail}; +use std::future::Future; + +use crate::ship::map::MapArea; +use crate::entity::character::CharacterEntityId; +use crate::entity::item::mag::Mag; + +use crate::ship::items::state::ItemStateError; +use crate::ship::items::state::{IndividualItemDetail, StackedItemDetail}; +use crate::ship::items::inventory::{InventoryItem, InventoryItemDetail}; + +pub enum FloorType { + Local, + Shared, +} + +#[derive(Debug, Clone)] +pub enum FloorItemDetail { + Individual(IndividualItemDetail), + Stacked(StackedItemDetail), + Meseta(Meseta), +} + +#[derive(Debug, Clone)] +pub struct FloorItem { + pub item_id: ClientItemId, + pub item: FloorItemDetail, + pub map_area: MapArea, + pub x: f32, + pub y: f32, + pub z: f32, +} + +impl FloorItem { + pub async fn with_entity_id(&self, mut param: T, mut func: F) -> Result + where + F: FnMut(T, ItemEntityId) -> Fut, + Fut: Future>, + { + match &self.item { + FloorItemDetail::Individual(individual_item) => { + param = func(param, individual_item.entity_id).await?; + }, + FloorItemDetail::Stacked(stacked_item) => { + for entity_id in &stacked_item.entity_ids { + param = func(param, *entity_id).await?; + } + }, + FloorItemDetail::Meseta(_meseta) => {}, + } + + Ok(param) + } + + pub async fn with_mag(&self, mut param: T, mut func: F) -> Result + where + F: FnMut(T, ItemEntityId, Mag) -> Fut, + Fut: Future>, + { + if let FloorItemDetail::Individual(individual_item) = &self.item { + if let ItemDetail::Mag(mag) = &individual_item.item { + param = func(param, individual_item.entity_id, mag.clone()).await?; + } + } + Ok(param) + } + + pub fn as_client_bytes(&self) -> [u8; 16] { + match &self.item { + FloorItemDetail::Individual(individual_floor_item) => { + individual_floor_item.item.as_client_bytes() + }, + FloorItemDetail::Stacked(stacked_floor_item) => { + stacked_floor_item.tool.as_stacked_bytes(stacked_floor_item.entity_ids.len()) + }, + FloorItemDetail::Meseta(meseta_floor_item) => { + meseta_floor_item.as_bytes() + } + } + } +} + +#[derive(Debug, Clone, Default)] +pub struct LocalFloor(pub Vec); +#[derive(Debug, Clone, Default)] +pub struct SharedFloor(pub Vec); + +#[derive(Debug)] +pub struct FloorState { + pub character_id: CharacterEntityId, + pub local: LocalFloor, + pub shared: SharedFloor, +} + +impl FloorState { + pub fn take_item(&mut self, item_id: &ClientItemId) -> Option { + let item = self.local.0 + .drain_filter(|item| { + item.item_id == *item_id + }) + .next(); + item.or_else(|| { + self.shared.0 + .drain_filter(|item| { + item.item_id == *item_id + }) + .next() + }) + } + + pub fn add_inventory_item(&mut self, inventory_item: InventoryItem, map_area: MapArea, position: (f32, f32, f32)) -> &FloorItem { + let floor_item = FloorItem { + item_id: inventory_item.item_id, + item: match inventory_item.item { + InventoryItemDetail::Individual(individual_item) => FloorItemDetail::Individual(individual_item), + InventoryItemDetail::Stacked(stacked_item) => FloorItemDetail::Stacked(stacked_item), + }, + map_area, + x: position.0, + y: position.1, + z: position.2, + }; + + self.shared.0.push(floor_item); + &self.shared.0[self.shared.0.len()-1] + } + + pub fn add_shared_item(&mut self, floor_item: FloorItem) -> &FloorItem { + self.shared.0.push(floor_item); + &self.shared.0[self.shared.0.len()-1] + } + + pub fn add_local_item(&mut self, floor_item: FloorItem) -> &FloorItem { + self.local.0.push(floor_item); + &self.local.0[self.local.0.len()-1] + } +} + diff --git a/src/ship/items/inventory.rs b/src/ship/items/inventory.rs index 602b515..4024fc8 100644 --- a/src/ship/items/inventory.rs +++ b/src/ship/items/inventory.rs @@ -10,7 +10,7 @@ use crate::entity::item::mag::Mag; use crate::ship::shops::{ShopItem, ArmorShopItem, ToolShopItem, WeaponShopItem}; use crate::ship::items::state::ItemStateError; use crate::ship::items::state::{IndividualItemDetail, StackedItemDetail, AddItemResult}; -use crate::ship::items::state::{FloorItem, FloorItemDetail}; +use crate::ship::items::floor::{FloorItem, FloorItemDetail}; #[derive(Clone, Debug)] pub enum InventoryItemDetail { diff --git a/src/ship/items/mod.rs b/src/ship/items/mod.rs index 3e161f4..e318df8 100644 --- a/src/ship/items/mod.rs +++ b/src/ship/items/mod.rs @@ -3,6 +3,7 @@ pub mod actions; pub mod apply_item; pub mod itemstateaction; pub mod inventory; +pub mod floor; #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, serde::Serialize, serde::Deserialize, derive_more::Display)] pub struct ClientItemId(pub u32); diff --git a/src/ship/items/state.rs b/src/ship/items/state.rs index 724282e..84bd883 100644 --- a/src/ship/items/state.rs +++ b/src/ship/items/state.rs @@ -5,7 +5,6 @@ use crate::ship::items::ClientItemId; use crate::entity::item::{Meseta, ItemEntityId, ItemDetail, ItemEntity, InventoryItemEntity, BankEntity, BankItemEntity, BankName}; use std::future::Future; -use crate::ship::map::MapArea; use crate::ship::location::{AreaClient, RoomId}; use crate::entity::character::{CharacterEntity, CharacterEntityId}; use crate::entity::gateway::{EntityGateway, GatewayError}; @@ -16,6 +15,8 @@ use crate::ship::drops::ItemDrop; use crate::ship::items::inventory::{Inventory, InventoryItem, InventoryItemDetail, InventoryError, InventoryState}; +use crate::ship::items::floor::{FloorState, FloorItem, LocalFloor, SharedFloor, FloorType}; + // TODO: Commit trait that ItemStateProxy and EntityTransaction implement that .commit requires and acts on upon everything succeeding (like 3 less lines of code!) #[derive(thiserror::Error, Debug)] @@ -79,11 +80,6 @@ pub enum ItemStateError { WrongItemType(ClientItemId), } -pub enum FloorType { - Local, - Shared, -} - #[derive(Clone, Debug)] pub struct IndividualItemDetail { @@ -201,71 +197,6 @@ impl BankItem { } } -#[derive(Debug, Clone)] -pub enum FloorItemDetail { - Individual(IndividualItemDetail), - Stacked(StackedItemDetail), - Meseta(Meseta), -} - -#[derive(Debug, Clone)] -pub struct FloorItem { - pub item_id: ClientItemId, - pub item: FloorItemDetail, - pub map_area: MapArea, - pub x: f32, - pub y: f32, - pub z: f32, -} - -impl FloorItem { - pub async fn with_entity_id(&self, mut param: T, mut func: F) -> Result - where - F: FnMut(T, ItemEntityId) -> Fut, - Fut: Future>, - { - match &self.item { - FloorItemDetail::Individual(individual_item) => { - param = func(param, individual_item.entity_id).await?; - }, - FloorItemDetail::Stacked(stacked_item) => { - for entity_id in &stacked_item.entity_ids { - param = func(param, *entity_id).await?; - } - }, - FloorItemDetail::Meseta(_meseta) => {}, - } - - Ok(param) - } - - pub async fn with_mag(&self, mut param: T, mut func: F) -> Result - where - F: FnMut(T, ItemEntityId, Mag) -> Fut, - Fut: Future>, - { - if let FloorItemDetail::Individual(individual_item) = &self.item { - if let ItemDetail::Mag(mag) = &individual_item.item { - param = func(param, individual_item.entity_id, mag.clone()).await?; - } - } - Ok(param) - } - - pub fn as_client_bytes(&self) -> [u8; 16] { - match &self.item { - FloorItemDetail::Individual(individual_floor_item) => { - individual_floor_item.item.as_client_bytes() - }, - FloorItemDetail::Stacked(stacked_floor_item) => { - stacked_floor_item.tool.as_stacked_bytes(stacked_floor_item.entity_ids.len()) - }, - FloorItemDetail::Meseta(meseta_floor_item) => { - meseta_floor_item.as_bytes() - } - } - } -} #[derive(thiserror::Error, Debug)] @@ -285,12 +216,6 @@ pub enum AddItemResult { Meseta, } -#[derive(Debug, Clone, Default)] -pub struct LocalFloor(Vec); -#[derive(Debug, Clone, Default)] -pub struct SharedFloor(Vec); - - #[derive(Clone, Debug)] pub struct Bank(Vec); @@ -539,57 +464,6 @@ impl std::cmp::Ord for BankItem { } } -#[derive(Debug)] -pub struct FloorState { - character_id: CharacterEntityId, - local: LocalFloor, - shared: SharedFloor, -} - -impl FloorState { - pub fn take_item(&mut self, item_id: &ClientItemId) -> Option { - let item = self.local.0 - .drain_filter(|item| { - item.item_id == *item_id - }) - .next(); - item.or_else(|| { - self.shared.0 - .drain_filter(|item| { - item.item_id == *item_id - }) - .next() - }) - } - - pub fn add_inventory_item(&mut self, inventory_item: InventoryItem, map_area: MapArea, position: (f32, f32, f32)) -> &FloorItem { - let floor_item = FloorItem { - item_id: inventory_item.item_id, - item: match inventory_item.item { - InventoryItemDetail::Individual(individual_item) => FloorItemDetail::Individual(individual_item), - InventoryItemDetail::Stacked(stacked_item) => FloorItemDetail::Stacked(stacked_item), - }, - map_area, - x: position.0, - y: position.1, - z: position.2, - }; - - self.shared.0.push(floor_item); - &self.shared.0[self.shared.0.len()-1] - } - - pub fn add_shared_item(&mut self, floor_item: FloorItem) -> &FloorItem { - self.shared.0.push(floor_item); - &self.shared.0[self.shared.0.len()-1] - } - - pub fn add_local_item(&mut self, floor_item: FloorItem) -> &FloorItem { - self.local.0.push(floor_item); - &self.local.0[self.local.0.len()-1] - } -} - pub struct ItemState { character_inventory: HashMap, diff --git a/src/ship/packet/builder/message.rs b/src/ship/packet/builder/message.rs index 644d2a1..fa9368c 100644 --- a/src/ship/packet/builder/message.rs +++ b/src/ship/packet/builder/message.rs @@ -7,7 +7,7 @@ use crate::ship::items::ClientItemId; use crate::ship::items::inventory::InventoryItem; use crate::ship::items::state::IndividualItemDetail; use crate::ship::items::state::BankState; -use crate::ship::items::state::FloorItem; +use crate::ship::items::floor::FloorItem; use crate::ship::location::AreaClient; use std::convert::TryInto; use crate::ship::shops::ShopItem; diff --git a/src/ship/packet/handler/direct_message.rs b/src/ship/packet/handler/direct_message.rs index 9bb04bd..519fa4f 100644 --- a/src/ship/packet/handler/direct_message.rs +++ b/src/ship/packet/handler/direct_message.rs @@ -14,7 +14,8 @@ use crate::entity::item; use libpso::utf8_to_utf16_array; use crate::ship::packet::builder; use crate::ship::shops::{ShopItem, ToolShopItem, ArmorShopItem}; -use crate::ship::items::state::{ItemState, ItemStateError, FloorType, FloorItemDetail}; +use crate::ship::items::state::{ItemState, ItemStateError}; +use crate::ship::items::floor::{FloorType, FloorItemDetail}; use crate::ship::items::actions::{pick_up_item, withdraw_meseta, deposit_meseta, withdraw_item, deposit_item, buy_shop_item, enemy_drops_item, take_meseta, apply_modifier, TriggerCreateItem}; const BANK_ACTION_DEPOSIT: u8 = 0; -- 2.36.0 From 919d509c3a0c3d84e0d787c59ab9d21ff0f7d01e Mon Sep 17 00:00:00 2001 From: jake Date: Tue, 19 Jul 2022 20:48:55 -0600 Subject: [PATCH 56/60] move bank code out of state --- src/ship/character.rs | 2 +- src/ship/items/actions.rs | 3 +- src/ship/items/bank.rs | 340 +++++++++++++++++++++++++++++ src/ship/items/mod.rs | 1 + src/ship/items/state.rs | 328 +--------------------------- src/ship/packet/builder/message.rs | 2 +- 6 files changed, 348 insertions(+), 328 deletions(-) create mode 100644 src/ship/items/bank.rs diff --git a/src/ship/character.rs b/src/ship/character.rs index 674e17d..454b2c8 100644 --- a/src/ship/character.rs +++ b/src/ship/character.rs @@ -2,7 +2,7 @@ use libpso::character::character; use crate::common::leveltable::CharacterStats; use crate::entity::character::CharacterEntity; //use crate::ship::items::{CharacterInventory, CharacterBank}; -use crate::ship::items::state::{BankState}; +use crate::ship::items::bank::BankState; use crate::ship::items::inventory::InventoryState; use crate::entity::item::Meseta; diff --git a/src/ship/items/actions.rs b/src/ship/items/actions.rs index 9753043..e6ee1b4 100644 --- a/src/ship/items/actions.rs +++ b/src/ship/items/actions.rs @@ -8,7 +8,8 @@ use std::pin::Pin; use crate::ship::map::MapArea; use crate::entity::character::{CharacterEntity, CharacterEntityId}; use crate::entity::gateway::{EntityGateway, EntityGatewayTransaction}; -use crate::ship::items::state::{ItemState, ItemStateProxy, ItemStateError, AddItemResult, StackedItemDetail, BankItem, BankItemDetail, IndividualItemDetail}; +use crate::ship::items::state::{ItemState, ItemStateProxy, ItemStateError, AddItemResult, StackedItemDetail, IndividualItemDetail}; +use crate::ship::items::bank::{BankItem, BankItemDetail}; use crate::ship::items::itemstateaction::{ItemStateAction, ItemAction}; use crate::ship::items::inventory::{InventoryItem, InventoryItemDetail}; use crate::ship::items::floor::{FloorItem, FloorItemDetail}; diff --git a/src/ship/items/bank.rs b/src/ship/items/bank.rs new file mode 100644 index 0000000..5abef95 --- /dev/null +++ b/src/ship/items/bank.rs @@ -0,0 +1,340 @@ +use std::cmp::Ordering; +use libpso::character::character; +use crate::ship::items::ClientItemId; +use crate::entity::item::{Meseta, ItemEntityId, ItemDetail, ItemEntity, BankEntity, BankItemEntity, BankName}; +use std::future::Future; + +use crate::entity::character::CharacterEntityId; +use crate::ship::items::state::ItemStateError; +use crate::ship::items::state::{IndividualItemDetail, StackedItemDetail, AddItemResult}; +use crate::ship::items::inventory::{InventoryItem, InventoryItemDetail}; + + +#[derive(thiserror::Error, Debug)] +pub enum BankError { + #[error("bank full")] + BankFull, + #[error("stack full")] + StackFull, + #[error("meseta full")] + MesetaFull, +} + + +#[derive(Clone, Debug)] +pub enum BankItemDetail { + Individual(IndividualItemDetail), + Stacked(StackedItemDetail), +} + +impl BankItemDetail { + fn stacked_mut(&mut self) -> Option<&mut StackedItemDetail> { + match self { + BankItemDetail::Stacked(sitem) => Some(sitem), + _ => None, + } + } + + pub fn as_client_bytes(&self) -> [u8; 16] { + match self { + BankItemDetail::Individual(item) => { + match &item.item { + ItemDetail::Weapon(w) => w.as_bytes(), + ItemDetail::Armor(a) => a.as_bytes(), + ItemDetail::Shield(s) => s.as_bytes(), + ItemDetail::Unit(u) => u.as_bytes(), + ItemDetail::Tool(t) => t.as_individual_bytes(), + ItemDetail::TechniqueDisk(d) => d.as_bytes(), + ItemDetail::Mag(m) => m.as_bytes(), + ItemDetail::ESWeapon(e) => e.as_bytes(), + } + }, + BankItemDetail::Stacked(item) => { + item.tool.as_stacked_bytes(item.entity_ids.len()) + }, + } + } +} + + +#[derive(Clone, Debug)] +pub struct BankItem { + pub item_id: ClientItemId, + pub item: BankItemDetail, +} + +impl BankItem { + pub async fn with_entity_id(&self, mut param: T, mut func: F) -> Result + where + F: FnMut(T, ItemEntityId) -> Fut, + Fut: Future>, + { + match &self.item { + BankItemDetail::Individual(individual_item) => { + param = func(param, individual_item.entity_id).await?; + }, + BankItemDetail::Stacked(stacked_item) => { + for entity_id in &stacked_item.entity_ids { + param = func(param, *entity_id).await?; + } + } + } + Ok(param) + } +} + + +#[derive(Clone, Debug)] +pub struct Bank(Vec); + +impl Bank { + pub fn new(items: Vec) -> Bank { + Bank(items) + } +} + +#[derive(Clone, Debug)] +pub struct BankState { + pub character_id: CharacterEntityId, + pub item_id_counter: u32, + pub name: BankName, + pub bank: Bank, + pub meseta: Meseta, +} + +impl BankState { + pub fn new(character_id: CharacterEntityId, name: BankName, mut bank: Bank, meseta: Meseta) -> BankState { + bank.0.sort(); + BankState { + character_id, + item_id_counter: 0, + name, + bank, + meseta, + } + } + + pub fn count(&self) -> usize { + self.bank.0.len() + } + + pub fn initialize_item_ids(&mut self, base_item_id: u32) { + for (i, item) in self.bank.0.iter_mut().enumerate() { + item.item_id = ClientItemId(base_item_id + i as u32); + } + self.item_id_counter = base_item_id + self.bank.0.len() as u32; + } + + pub fn add_meseta(&mut self, amount: u32) -> Result<(), ItemStateError> { + if self.meseta.0 + amount > 999999 { + return Err(ItemStateError::FullOfMeseta) + } + self.meseta.0 += amount; + Ok(()) + } + + pub fn remove_meseta(&mut self, amount: u32) -> Result<(), ItemStateError> { + if amount > self.meseta.0 { + return Err(ItemStateError::InvalidMesetaRemoval(amount)) + } + self.meseta.0 -= amount; + Ok(()) + } + + pub fn add_inventory_item(&mut self, item: InventoryItem) -> Result { + match item.item { + InventoryItemDetail::Individual(iitem) => { + if self.bank.0.len() >= 30 { + Err(BankError::BankFull) + } + else { + self.bank.0.push(BankItem { + item_id: item.item_id, + item: BankItemDetail::Individual(iitem) + }); + self.bank.0.sort(); + Ok(AddItemResult::NewItem) + } + }, + InventoryItemDetail::Stacked(sitem) => { + let existing_stack = self.bank.0 + .iter_mut() + .filter_map(|item| item.item.stacked_mut()) + .find(|item| { + item.tool == sitem.tool + }); + match existing_stack { + Some(existing_stack) => { + if existing_stack.entity_ids.len() + sitem.entity_ids.len() > sitem.tool.max_stack() { + Err(BankError::StackFull) + } + else { + existing_stack.entity_ids.append(&mut sitem.entity_ids.clone()); + Ok(AddItemResult::AddToStack) + } + }, + None => { + if self.bank.0.len() >= 30 { + Err(BankError::BankFull) + } + else { + self.bank.0.push(BankItem { + item_id: item.item_id, + item: BankItemDetail::Stacked(sitem) + }); + self.bank.0.sort(); + Ok(AddItemResult::NewItem) + } + } + } + } + } + } + + pub fn take_item(&mut self, item_id: &ClientItemId, amount: u32) -> Option { + let idx = self.bank.0 + .iter() + .position(|i| i.item_id == *item_id)?; + match &mut self.bank.0[idx].item { + BankItemDetail::Individual(_individual_item) => { + Some(self.bank.0.remove(idx)) + }, + BankItemDetail::Stacked(stacked_item) => { + let remove_all = match stacked_item.entity_ids.len().cmp(&(amount as usize)) { + Ordering::Equal => true, + Ordering::Greater => false, + Ordering::Less => return None, + }; + + if remove_all { + Some(self.bank.0.remove(idx)) + } + else { + let entity_ids = stacked_item.entity_ids.drain(..(amount as usize)).collect(); + self.item_id_counter += 1; + Some(BankItem { + item_id: ClientItemId(self.item_id_counter), + item: BankItemDetail::Stacked(StackedItemDetail { + entity_ids, + tool: stacked_item.tool, + })}) + } + } + } + } + + pub fn as_client_bank_items(&self) -> character::Bank { + self.bank.0.iter() + .enumerate() + .fold(character::Bank::default(), |mut bank, (slot, item)| { + bank.item_count = (slot + 1) as u32; + let bytes = item.item.as_client_bytes(); + bank.items[slot].data1.copy_from_slice(&bytes[0..12]); + bank.items[slot].data2.copy_from_slice(&bytes[12..16]); + bank.items[slot].item_id = item.item_id.0; + + bank + }) + } + + pub fn as_client_bank_request(&self) -> Vec { + self.bank.0.iter() + .map(|item| { + let bytes = item.item.as_client_bytes(); + let mut data1 = [0; 12]; + let mut data2 = [0; 4]; + data1.copy_from_slice(&bytes[0..12]); + data2.copy_from_slice(&bytes[12..16]); + let amount = match &item.item { + BankItemDetail::Individual(_individual_bank_item) => { + 1 + }, + BankItemDetail::Stacked(stacked_bank_item) => { + stacked_bank_item.count() + }, + }; + character::BankItem { + data1, + data2, + item_id: item.item_id.0, + amount: amount as u16, + flags: 1, + } + }) + .collect() + } + + pub fn as_bank_entity(&self) -> BankEntity { + BankEntity { + items: self.bank.0.iter() + .map(|item| { + match &item.item { + BankItemDetail::Individual(item) => { + BankItemEntity::Individual(ItemEntity { + id: item.entity_id, + item: item.item.clone(), + }) + }, + BankItemDetail::Stacked(items) => { + BankItemEntity::Stacked(items.entity_ids.iter() + .map(|id| { + ItemEntity { + id: *id, + item: ItemDetail::Tool(items.tool) + } + }) + .collect()) + }, + } + }) + .collect() + } + } +} + +impl std::cmp::PartialEq for BankItem { + fn eq(&self, other: &BankItem) -> bool { + let mut self_bytes = [0u8; 4]; + let mut other_bytes = [0u8; 4]; + self_bytes.copy_from_slice(&self.item.as_client_bytes()[0..4]); + other_bytes.copy_from_slice(&other.item.as_client_bytes()[0..4]); + + let self_value = u32::from_be_bytes(self_bytes); + let other_value = u32::from_be_bytes(other_bytes); + + self_value.eq(&other_value) + } +} + +impl std::cmp::Eq for BankItem {} + +impl std::cmp::PartialOrd for BankItem { + fn partial_cmp(&self, other: &BankItem) -> Option { + let mut self_bytes = [0u8; 4]; + let mut other_bytes = [0u8; 4]; + self_bytes.copy_from_slice(&self.item.as_client_bytes()[0..4]); + other_bytes.copy_from_slice(&other.item.as_client_bytes()[0..4]); + + + let self_value = u32::from_be_bytes(self_bytes); + let other_value = u32::from_be_bytes(other_bytes); + + self_value.partial_cmp(&other_value) + } +} + +impl std::cmp::Ord for BankItem { + fn cmp(&self, other: &BankItem) -> std::cmp::Ordering { + let mut self_bytes = [0u8; 4]; + let mut other_bytes = [0u8; 4]; + self_bytes.copy_from_slice(&self.item.as_client_bytes()[0..4]); + other_bytes.copy_from_slice(&other.item.as_client_bytes()[0..4]); + + + let self_value = u32::from_le_bytes(self_bytes); + let other_value = u32::from_le_bytes(other_bytes); + + self_value.cmp(&other_value) + } +} + diff --git a/src/ship/items/mod.rs b/src/ship/items/mod.rs index e318df8..4b9913b 100644 --- a/src/ship/items/mod.rs +++ b/src/ship/items/mod.rs @@ -4,6 +4,7 @@ pub mod apply_item; pub mod itemstateaction; pub mod inventory; pub mod floor; +pub mod bank; #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, serde::Serialize, serde::Deserialize, derive_more::Display)] pub struct ClientItemId(pub u32); diff --git a/src/ship/items/state.rs b/src/ship/items/state.rs index 84bd883..70a5ddf 100644 --- a/src/ship/items/state.rs +++ b/src/ship/items/state.rs @@ -1,9 +1,6 @@ -use std::cmp::Ordering; use std::collections::HashMap; -use libpso::character::character; use crate::ship::items::ClientItemId; -use crate::entity::item::{Meseta, ItemEntityId, ItemDetail, ItemEntity, InventoryItemEntity, BankEntity, BankItemEntity, BankName}; -use std::future::Future; +use crate::entity::item::{ItemEntityId, ItemDetail, ItemEntity, InventoryItemEntity, BankItemEntity, BankName}; use crate::ship::location::{AreaClient, RoomId}; use crate::entity::character::{CharacterEntity, CharacterEntityId}; @@ -14,8 +11,8 @@ use crate::entity::item::mag::Mag; use crate::ship::drops::ItemDrop; use crate::ship::items::inventory::{Inventory, InventoryItem, InventoryItemDetail, InventoryError, InventoryState}; - use crate::ship::items::floor::{FloorState, FloorItem, LocalFloor, SharedFloor, FloorType}; +use crate::ship::items::bank::{Bank, BankState, BankItem, BankItemDetail, BankError}; // TODO: Commit trait that ItemStateProxy and EntityTransaction implement that .commit requires and acts on upon everything succeeding (like 3 less lines of code!) @@ -136,79 +133,8 @@ impl StackedItemDetail { } } -#[derive(Clone, Debug)] -pub enum BankItemDetail { - Individual(IndividualItemDetail), - Stacked(StackedItemDetail), -} - -impl BankItemDetail { - fn stacked_mut(&mut self) -> Option<&mut StackedItemDetail> { - match self { - BankItemDetail::Stacked(sitem) => Some(sitem), - _ => None, - } - } - - pub fn as_client_bytes(&self) -> [u8; 16] { - match self { - BankItemDetail::Individual(item) => { - match &item.item { - ItemDetail::Weapon(w) => w.as_bytes(), - ItemDetail::Armor(a) => a.as_bytes(), - ItemDetail::Shield(s) => s.as_bytes(), - ItemDetail::Unit(u) => u.as_bytes(), - ItemDetail::Tool(t) => t.as_individual_bytes(), - ItemDetail::TechniqueDisk(d) => d.as_bytes(), - ItemDetail::Mag(m) => m.as_bytes(), - ItemDetail::ESWeapon(e) => e.as_bytes(), - } - }, - BankItemDetail::Stacked(item) => { - item.tool.as_stacked_bytes(item.entity_ids.len()) - }, - } - } -} - -#[derive(Clone, Debug)] -pub struct BankItem { - pub item_id: ClientItemId, - pub item: BankItemDetail, -} - -impl BankItem { - pub async fn with_entity_id(&self, mut param: T, mut func: F) -> Result - where - F: FnMut(T, ItemEntityId) -> Fut, - Fut: Future>, - { - match &self.item { - BankItemDetail::Individual(individual_item) => { - param = func(param, individual_item.entity_id).await?; - }, - BankItemDetail::Stacked(stacked_item) => { - for entity_id in &stacked_item.entity_ids { - param = func(param, *entity_id).await?; - } - } - } - Ok(param) - } -} - -#[derive(thiserror::Error, Debug)] -pub enum BankError { - #[error("bank full")] - BankFull, - #[error("stack full")] - StackFull, - #[error("meseta full")] - MesetaFull, -} - #[derive(Clone)] pub enum AddItemResult { NewItem, @@ -216,254 +142,6 @@ pub enum AddItemResult { Meseta, } -#[derive(Clone, Debug)] -pub struct Bank(Vec); - -#[derive(Clone, Debug)] -pub struct BankState { - character_id: CharacterEntityId, - item_id_counter: u32, - pub name: BankName, - bank: Bank, - pub meseta: Meseta, -} - -impl BankState { - pub fn new(character_id: CharacterEntityId, name: BankName, mut bank: Bank, meseta: Meseta) -> BankState { - bank.0.sort(); - BankState { - character_id, - item_id_counter: 0, - name, - bank, - meseta, - } - } - - pub fn count(&self) -> usize { - self.bank.0.len() - } - - pub fn initialize_item_ids(&mut self, base_item_id: u32) { - for (i, item) in self.bank.0.iter_mut().enumerate() { - item.item_id = ClientItemId(base_item_id + i as u32); - } - self.item_id_counter = base_item_id + self.bank.0.len() as u32; - } - - pub fn add_meseta(&mut self, amount: u32) -> Result<(), ItemStateError> { - if self.meseta.0 + amount > 999999 { - return Err(ItemStateError::FullOfMeseta) - } - self.meseta.0 += amount; - Ok(()) - } - - pub fn remove_meseta(&mut self, amount: u32) -> Result<(), ItemStateError> { - if amount > self.meseta.0 { - return Err(ItemStateError::InvalidMesetaRemoval(amount)) - } - self.meseta.0 -= amount; - Ok(()) - } - - pub fn add_inventory_item(&mut self, item: InventoryItem) -> Result { - match item.item { - InventoryItemDetail::Individual(iitem) => { - if self.bank.0.len() >= 30 { - Err(BankError::BankFull) - } - else { - self.bank.0.push(BankItem { - item_id: item.item_id, - item: BankItemDetail::Individual(iitem) - }); - self.bank.0.sort(); - Ok(AddItemResult::NewItem) - } - }, - InventoryItemDetail::Stacked(sitem) => { - let existing_stack = self.bank.0 - .iter_mut() - .filter_map(|item| item.item.stacked_mut()) - .find(|item| { - item.tool == sitem.tool - }); - match existing_stack { - Some(existing_stack) => { - if existing_stack.entity_ids.len() + sitem.entity_ids.len() > sitem.tool.max_stack() { - Err(BankError::StackFull) - } - else { - existing_stack.entity_ids.append(&mut sitem.entity_ids.clone()); - Ok(AddItemResult::AddToStack) - } - }, - None => { - if self.bank.0.len() >= 30 { - Err(BankError::BankFull) - } - else { - self.bank.0.push(BankItem { - item_id: item.item_id, - item: BankItemDetail::Stacked(sitem) - }); - self.bank.0.sort(); - Ok(AddItemResult::NewItem) - } - } - } - } - } - } - - pub fn take_item(&mut self, item_id: &ClientItemId, amount: u32) -> Option { - let idx = self.bank.0 - .iter() - .position(|i| i.item_id == *item_id)?; - match &mut self.bank.0[idx].item { - BankItemDetail::Individual(_individual_item) => { - Some(self.bank.0.remove(idx)) - }, - BankItemDetail::Stacked(stacked_item) => { - let remove_all = match stacked_item.entity_ids.len().cmp(&(amount as usize)) { - Ordering::Equal => true, - Ordering::Greater => false, - Ordering::Less => return None, - }; - - if remove_all { - Some(self.bank.0.remove(idx)) - } - else { - let entity_ids = stacked_item.entity_ids.drain(..(amount as usize)).collect(); - self.item_id_counter += 1; - Some(BankItem { - item_id: ClientItemId(self.item_id_counter), - item: BankItemDetail::Stacked(StackedItemDetail { - entity_ids, - tool: stacked_item.tool, - })}) - } - } - } - } - - pub fn as_client_bank_items(&self) -> character::Bank { - self.bank.0.iter() - .enumerate() - .fold(character::Bank::default(), |mut bank, (slot, item)| { - bank.item_count = (slot + 1) as u32; - let bytes = item.item.as_client_bytes(); - bank.items[slot].data1.copy_from_slice(&bytes[0..12]); - bank.items[slot].data2.copy_from_slice(&bytes[12..16]); - bank.items[slot].item_id = item.item_id.0; - - bank - }) - } - - pub fn as_client_bank_request(&self) -> Vec { - self.bank.0.iter() - .map(|item| { - let bytes = item.item.as_client_bytes(); - let mut data1 = [0; 12]; - let mut data2 = [0; 4]; - data1.copy_from_slice(&bytes[0..12]); - data2.copy_from_slice(&bytes[12..16]); - let amount = match &item.item { - BankItemDetail::Individual(_individual_bank_item) => { - 1 - }, - BankItemDetail::Stacked(stacked_bank_item) => { - stacked_bank_item.count() - }, - }; - character::BankItem { - data1, - data2, - item_id: item.item_id.0, - amount: amount as u16, - flags: 1, - } - }) - .collect() - } - - pub fn as_bank_entity(&self) -> BankEntity { - BankEntity { - items: self.bank.0.iter() - .map(|item| { - match &item.item { - BankItemDetail::Individual(item) => { - BankItemEntity::Individual(ItemEntity { - id: item.entity_id, - item: item.item.clone(), - }) - }, - BankItemDetail::Stacked(items) => { - BankItemEntity::Stacked(items.entity_ids.iter() - .map(|id| { - ItemEntity { - id: *id, - item: ItemDetail::Tool(items.tool) - } - }) - .collect()) - }, - } - }) - .collect() - } - } -} - -impl std::cmp::PartialEq for BankItem { - fn eq(&self, other: &BankItem) -> bool { - let mut self_bytes = [0u8; 4]; - let mut other_bytes = [0u8; 4]; - self_bytes.copy_from_slice(&self.item.as_client_bytes()[0..4]); - other_bytes.copy_from_slice(&other.item.as_client_bytes()[0..4]); - - let self_value = u32::from_be_bytes(self_bytes); - let other_value = u32::from_be_bytes(other_bytes); - - self_value.eq(&other_value) - } -} - -impl std::cmp::Eq for BankItem {} - -impl std::cmp::PartialOrd for BankItem { - fn partial_cmp(&self, other: &BankItem) -> Option { - let mut self_bytes = [0u8; 4]; - let mut other_bytes = [0u8; 4]; - self_bytes.copy_from_slice(&self.item.as_client_bytes()[0..4]); - other_bytes.copy_from_slice(&other.item.as_client_bytes()[0..4]); - - - let self_value = u32::from_be_bytes(self_bytes); - let other_value = u32::from_be_bytes(other_bytes); - - self_value.partial_cmp(&other_value) - } -} - -impl std::cmp::Ord for BankItem { - fn cmp(&self, other: &BankItem) -> std::cmp::Ordering { - let mut self_bytes = [0u8; 4]; - let mut other_bytes = [0u8; 4]; - self_bytes.copy_from_slice(&self.item.as_client_bytes()[0..4]); - other_bytes.copy_from_slice(&other.item.as_client_bytes()[0..4]); - - - let self_value = u32::from_le_bytes(self_bytes); - let other_value = u32::from_le_bytes(other_bytes); - - self_value.cmp(&other_value) - } -} - pub struct ItemState { character_inventory: HashMap, @@ -582,7 +260,7 @@ impl ItemState { .collect::, _>>()?; let bank_meseta = entity_gateway.get_bank_meseta(&character.id, &BankName("".into())).await?; - let bank_state = BankState::new(character.id, BankName("".into()), Bank(bank_items), bank_meseta); + let bank_state = BankState::new(character.id, BankName("".into()), Bank::new(bank_items), bank_meseta); self.character_inventory.insert(character.id, inventory_state); self.character_bank.insert(character.id, bank_state); diff --git a/src/ship/packet/builder/message.rs b/src/ship/packet/builder/message.rs index fa9368c..ac60484 100644 --- a/src/ship/packet/builder/message.rs +++ b/src/ship/packet/builder/message.rs @@ -6,7 +6,7 @@ use crate::ship::ship::{ShipError}; use crate::ship::items::ClientItemId; use crate::ship::items::inventory::InventoryItem; use crate::ship::items::state::IndividualItemDetail; -use crate::ship::items::state::BankState; +use crate::ship::items::bank::BankState; use crate::ship::items::floor::FloorItem; use crate::ship::location::AreaClient; use std::convert::TryInto; -- 2.36.0 From 286b7284414aa03fb0563eb5dc9a8e7e2a9f3204 Mon Sep 17 00:00:00 2001 From: jake Date: Tue, 19 Jul 2022 21:26:00 -0600 Subject: [PATCH 57/60] split actions into actions+tasks --- src/ship/items/actions.rs | 534 ++-------------------- src/ship/items/mod.rs | 1 + src/ship/items/state.rs | 2 - src/ship/items/tasks.rs | 500 ++++++++++++++++++++ src/ship/packet/handler/direct_message.rs | 3 +- src/ship/packet/handler/message.rs | 2 +- src/ship/packet/handler/trade.rs | 2 +- 7 files changed, 537 insertions(+), 507 deletions(-) create mode 100644 src/ship/items/tasks.rs diff --git a/src/ship/items/actions.rs b/src/ship/items/actions.rs index e6ee1b4..c2213b5 100644 --- a/src/ship/items/actions.rs +++ b/src/ship/items/actions.rs @@ -1,5 +1,4 @@ // TODO: replace various u32s and usizes denoting item amounts for ItemAmount(u32) for consistency - use crate::ship::items::ClientItemId; use crate::entity::item::{Meseta, ItemNote}; use std::future::Future; @@ -7,10 +6,9 @@ use std::pin::Pin; use crate::ship::map::MapArea; use crate::entity::character::{CharacterEntity, CharacterEntityId}; -use crate::entity::gateway::{EntityGateway, EntityGatewayTransaction}; -use crate::ship::items::state::{ItemState, ItemStateProxy, ItemStateError, AddItemResult, StackedItemDetail, IndividualItemDetail}; +use crate::entity::gateway::EntityGatewayTransaction; +use crate::ship::items::state::{ItemStateProxy, ItemStateError, AddItemResult, StackedItemDetail, IndividualItemDetail}; use crate::ship::items::bank::{BankItem, BankItemDetail}; -use crate::ship::items::itemstateaction::{ItemStateAction, ItemAction}; use crate::ship::items::inventory::{InventoryItem, InventoryItemDetail}; use crate::ship::items::floor::{FloorItem, FloorItemDetail}; use crate::ship::items::apply_item::apply_item; @@ -18,8 +16,6 @@ use crate::entity::item::{ItemDetail, NewItemEntity, TradeId}; use crate::entity::item::tool::Tool; use crate::entity::item::ItemModifier; use crate::ship::shops::ShopItem; -use crate::ship::trade::TradeItem; -use crate::ship::location::AreaClient; use crate::ship::drops::{ItemDrop, ItemDropType}; pub enum TriggerCreateItem { @@ -27,7 +23,7 @@ pub enum TriggerCreateItem { No } -fn take_item_from_floor(character_id: CharacterEntityId, item_id: ClientItemId) +pub(super) fn take_item_from_floor(character_id: CharacterEntityId, item_id: ClientItemId) -> impl for<'a> Fn((ItemStateProxy<'a>, Box), ()) -> Pin, Box), FloorItem), ItemStateError>> + Send + 'a>> { @@ -42,7 +38,7 @@ fn take_item_from_floor(character_id: CharacterEntityId, item_id: ClientItemId) } } -fn add_floor_item_to_inventory(character: &CharacterEntity) +pub(super) fn add_floor_item_to_inventory(character: &CharacterEntity) -> impl for<'a> Fn((ItemStateProxy<'a>, Box), FloorItem) -> Pin, Box), TriggerCreateItem), ItemStateError>> + Send + 'a>> { @@ -84,28 +80,8 @@ fn add_floor_item_to_inventory(character: &CharacterEntity) } -pub async fn pick_up_item( - item_state: &mut ItemState, - entity_gateway: &mut EG, - character: &CharacterEntity, - item_id: &ClientItemId) - -> Result -where - EG: EntityGateway, -{ - entity_gateway.with_transaction(|transaction| async move { - let item_state_proxy = ItemStateProxy::new(item_state); - let ((item_state_proxy, transaction), result) = ItemStateAction::default() - .act(take_item_from_floor(character.id, *item_id)) - .act(add_floor_item_to_inventory(character)) - .commit((item_state_proxy, transaction)) - .await?; - item_state_proxy.commit(); - Ok((transaction, result)) - }).await -} -fn take_item_from_inventory(character_id: CharacterEntityId, item_id: ClientItemId, amount: u32) +pub(super) fn take_item_from_inventory(character_id: CharacterEntityId, item_id: ClientItemId, amount: u32) -> impl for<'a> Fn((ItemStateProxy<'a>, Box), ()) -> Pin, Box), InventoryItem), ItemStateError>> + Send + 'a>> { @@ -123,7 +99,7 @@ fn take_item_from_inventory(character_id: CharacterEntityId, item_id: ClientItem } -fn add_inventory_item_to_shared_floor(character_id: CharacterEntityId, map_area: MapArea, drop_position: (f32, f32, f32)) +pub(super) fn add_inventory_item_to_shared_floor(character_id: CharacterEntityId, map_area: MapArea, drop_position: (f32, f32, f32)) -> impl for<'a> Fn((ItemStateProxy<'a>, Box), InventoryItem) -> Pin, Box), FloorItem), ItemStateError>> + Send + 'a>> { @@ -150,55 +126,8 @@ fn add_inventory_item_to_shared_floor(character_id: CharacterEntityId, map_area: } } -pub async fn drop_item( - item_state: &mut ItemState, - entity_gateway: &mut EG, - character: &CharacterEntity, - item_id: &ClientItemId, - map_area: MapArea, - drop_position: (f32, f32, f32)) - -> Result -where - EG: EntityGateway, -{ - entity_gateway.with_transaction(|transaction| async move { - let item_state_proxy = ItemStateProxy::new(item_state); - let ((item_state_proxy, transaction), result) = ItemStateAction::default() - .act(take_item_from_inventory(character.id, *item_id, 0)) - .act(add_inventory_item_to_shared_floor(character.id, map_area, drop_position)) - .commit((item_state_proxy, transaction)) - .await?; - item_state_proxy.commit(); - Ok((transaction, result)) - }).await -} - -pub async fn drop_partial_item<'a, EG>( - item_state: &'a mut ItemState, - entity_gateway: &mut EG, - character: &CharacterEntity, - item_id: &ClientItemId, - map_area: MapArea, - drop_position: (f32, f32), - amount: u32) - -> Result -where - EG: EntityGateway, -{ - entity_gateway.with_transaction(|transaction| async move { - let item_state_proxy = ItemStateProxy::new(item_state); - let ((item_state_proxy, transaction), result) = ItemStateAction::default() - .act(take_item_from_inventory(character.id, *item_id, amount)) - .act(add_inventory_item_to_shared_floor(character.id, map_area, (drop_position.0, 0.0, drop_position.1))) - .commit((item_state_proxy, transaction)) - .await?; - item_state_proxy.commit(); - Ok((transaction, result)) - }).await -} - -fn take_meseta_from_inventory(character_id: CharacterEntityId, amount: u32) +pub(super) fn take_meseta_from_inventory(character_id: CharacterEntityId, amount: u32) -> impl for<'a> Fn((ItemStateProxy<'a>, Box), ()) -> Pin, Box), ()), ItemStateError>> + Send + 'a>> { @@ -214,7 +143,7 @@ fn take_meseta_from_inventory(character_id: CharacterEntityId, amount: u32) } } -fn add_meseta_to_inventory(character_id: CharacterEntityId, amount: u32) +pub(super) fn add_meseta_to_inventory(character_id: CharacterEntityId, amount: u32) -> impl for<'a> Fn((ItemStateProxy<'a>, Box), ()) -> Pin, Box), ()), ItemStateError>> + Send + 'a>> { @@ -230,7 +159,7 @@ fn add_meseta_to_inventory(character_id: CharacterEntityId, amount: u32) } } -fn add_meseta_to_shared_floor(character_id: CharacterEntityId, amount: u32, map_area: MapArea, drop_position: (f32, f32)) +pub(super) fn add_meseta_to_shared_floor(character_id: CharacterEntityId, amount: u32, map_area: MapArea, drop_position: (f32, f32)) -> impl for<'a> Fn((ItemStateProxy<'a>, Box), ()) -> Pin, Box), FloorItem), ItemStateError>> + Send + 'a>> { @@ -255,31 +184,7 @@ fn add_meseta_to_shared_floor(character_id: CharacterEntityId, amount: u32, map_ } } -pub async fn drop_meseta<'a, EG>( - item_state: &'a mut ItemState, - entity_gateway: &mut EG, - character: &CharacterEntity, - map_area: MapArea, - drop_position: (f32, f32), - amount: u32) - -> Result -where - EG: EntityGateway, -{ - entity_gateway.with_transaction(|transaction| async move { - let item_state_proxy = ItemStateProxy::new(item_state); - let ((item_state_proxy, transaction), result) = ItemStateAction::default() - .act(take_meseta_from_inventory(character.id, amount)) - .act(add_meseta_to_shared_floor(character.id, amount, map_area, drop_position)) - .commit((item_state_proxy, transaction)) - .await?; - item_state_proxy.commit(); - Ok((transaction, result)) - }).await -} - - -fn take_meseta_from_bank(character_id: CharacterEntityId, amount: u32) +pub(super) fn take_meseta_from_bank(character_id: CharacterEntityId, amount: u32) -> impl for<'a> Fn((ItemStateProxy<'a>, Box), ()) -> Pin, Box), ()), ItemStateError>> + Send + 'a>> { @@ -294,7 +199,7 @@ fn take_meseta_from_bank(character_id: CharacterEntityId, amount: u32) } } -fn add_meseta_from_bank_to_inventory(character_id: CharacterEntityId, amount: u32) +pub(super) fn add_meseta_from_bank_to_inventory(character_id: CharacterEntityId, amount: u32) -> impl for<'a> Fn((ItemStateProxy<'a>, Box), ()) -> Pin, Box), ()), ItemStateError>> + Send + 'a>> { @@ -310,28 +215,7 @@ fn add_meseta_from_bank_to_inventory(character_id: CharacterEntityId, amount: u3 } -pub async fn withdraw_meseta<'a, EG>( - item_state: &'a mut ItemState, - entity_gateway: &mut EG, - character: &CharacterEntity, - amount: u32) - -> Result<(), ItemStateError> -where - EG: EntityGateway, -{ - entity_gateway.with_transaction(|transaction| async move { - let item_state_proxy = ItemStateProxy::new(item_state); - let ((item_state_proxy, transaction), result) = ItemStateAction::default() - .act(take_meseta_from_bank(character.id, amount)) - .act(add_meseta_from_bank_to_inventory(character.id, amount)) - .commit((item_state_proxy, transaction)) - .await?; - item_state_proxy.commit(); - Ok((transaction, result)) - }).await -} - -fn add_meseta_to_bank(character_id: CharacterEntityId, amount: u32) +pub(super) fn add_meseta_to_bank(character_id: CharacterEntityId, amount: u32) -> impl for<'a> Fn((ItemStateProxy<'a>, Box), ()) -> Pin, Box), ()), ItemStateError>> + Send + 'a>> { @@ -346,28 +230,8 @@ fn add_meseta_to_bank(character_id: CharacterEntityId, amount: u32) } } -pub async fn deposit_meseta<'a, EG>( - item_state: &'a mut ItemState, - entity_gateway: &mut EG, - character: &CharacterEntity, - amount: u32) - -> Result<(), ItemStateError> -where - EG: EntityGateway, -{ - entity_gateway.with_transaction(|transaction| async move { - let item_state_proxy = ItemStateProxy::new(item_state); - let ((item_state_proxy, transaction), _) = ItemStateAction::default() - .act(take_meseta_from_inventory(character.id, amount)) - .act(add_meseta_to_bank(character.id, amount)) - .commit((item_state_proxy, transaction)) - .await?; - item_state_proxy.commit(); - Ok((transaction, ())) - }).await -} -fn take_item_from_bank(character_id: CharacterEntityId, item_id: ClientItemId, amount: u32) +pub(super) fn take_item_from_bank(character_id: CharacterEntityId, item_id: ClientItemId, amount: u32) -> impl for<'a> Fn((ItemStateProxy<'a>, Box), ()) -> Pin, Box), BankItem), ItemStateError>> + Send + 'a>> { @@ -383,7 +247,7 @@ fn take_item_from_bank(character_id: CharacterEntityId, item_id: ClientItemId, a } } -fn add_bank_item_to_inventory(character: &CharacterEntity) +pub(super) fn add_bank_item_to_inventory(character: &CharacterEntity) -> impl for<'a> Fn((ItemStateProxy<'a>, Box), BankItem) -> Pin, Box), InventoryItem), ItemStateError>> + Send + 'a>> { @@ -429,31 +293,8 @@ fn add_bank_item_to_inventory(character: &CharacterEntity) } } -pub async fn withdraw_item<'a, EG>( - item_state: &'a mut ItemState, - entity_gateway: &mut EG, - character: &CharacterEntity, - item_id: &ClientItemId, - amount: u32) - -> Result -where - EG: EntityGateway, -{ - entity_gateway.with_transaction(|transaction| async move { - let item_state_proxy = ItemStateProxy::new(item_state); - let ((item_state_proxy, transaction), result) = ItemStateAction::default() - .act(take_item_from_bank(character.id, *item_id, amount)) - //.act(bank_item_to_inventory_item) - //.act(add_item_to_inventory) - .act(add_bank_item_to_inventory(character)) - .commit((item_state_proxy, transaction)) - .await?; - item_state_proxy.commit(); - Ok((transaction, result)) - }).await -} -fn add_inventory_item_to_bank(character_id: CharacterEntityId) +pub(super) fn add_inventory_item_to_bank(character_id: CharacterEntityId) -> impl for<'a> Fn((ItemStateProxy<'a>, Box), InventoryItem) -> Pin, Box), ()), ItemStateError>> + Send + 'a>> { @@ -482,29 +323,8 @@ fn add_inventory_item_to_bank(character_id: CharacterEntityId) } } -pub async fn deposit_item<'a, EG> ( - item_state: &'a mut ItemState, - entity_gateway: &mut EG, - character: &CharacterEntity, - item_id: &ClientItemId, - amount: u32) - -> Result<(), ItemStateError> -where - EG: EntityGateway, -{ - entity_gateway.with_transaction(|transaction| async move { - let item_state_proxy = ItemStateProxy::new(item_state); - let ((item_state_proxy, transaction), result) = ItemStateAction::default() - .act(take_item_from_inventory(character.id, *item_id, amount)) - .act(add_inventory_item_to_bank(character.id)) - .commit((item_state_proxy, transaction)) - .await?; - item_state_proxy.commit(); - Ok((transaction, result)) - }).await -} -fn equip_inventory_item(character_id: CharacterEntityId, item_id: ClientItemId, equip_slot: u8) +pub(super) fn equip_inventory_item(character_id: CharacterEntityId, item_id: ClientItemId, equip_slot: u8) -> impl for<'a> Fn((ItemStateProxy<'a>, Box), ()) -> Pin, Box), ()), ItemStateError>> + Send + 'a>> { @@ -520,29 +340,8 @@ fn equip_inventory_item(character_id: CharacterEntityId, item_id: ClientItemId, } } -pub async fn equip_item<'a, EG> ( - item_state: &'a mut ItemState, - entity_gateway: &mut EG, - character: &CharacterEntity, - item_id: &ClientItemId, - equip_slot: u8, -) -> Result<(), ItemStateError> -where - EG: EntityGateway, -{ - entity_gateway.with_transaction(|transaction| async move { - let item_state_proxy = ItemStateProxy::new(item_state); - let ((item_state_proxy, transaction), result) = ItemStateAction::default() - .act(equip_inventory_item(character.id, *item_id, equip_slot)) - .commit((item_state_proxy, transaction)) - .await?; - item_state_proxy.commit(); - Ok((transaction, result)) - }).await -} - -fn unequip_inventory_item(character_id: CharacterEntityId, item_id: ClientItemId) +pub(super) fn unequip_inventory_item(character_id: CharacterEntityId, item_id: ClientItemId) -> impl for<'a> Fn((ItemStateProxy<'a>, Box), ()) -> Pin, Box), ()), ItemStateError>> + Send + 'a>> { @@ -558,28 +357,9 @@ fn unequip_inventory_item(character_id: CharacterEntityId, item_id: ClientItemId } } -pub async fn unequip_item<'a, EG> ( - item_state: &'a mut ItemState, - entity_gateway: &mut EG, - character: &CharacterEntity, - item_id: &ClientItemId, -) -> Result<(), ItemStateError> -where - EG: EntityGateway, -{ - entity_gateway.with_transaction(|transaction| async move { - let item_state_proxy = ItemStateProxy::new(item_state); - let ((item_state_proxy, transaction), result) = ItemStateAction::default() - .act(unequip_inventory_item(character.id, *item_id)) - .commit((item_state_proxy, transaction)) - .await?; - item_state_proxy.commit(); - Ok((transaction, result)) - }).await -} -fn sort_inventory_items(character_id: CharacterEntityId, item_ids: Vec) +pub(super) fn sort_inventory_items(character_id: CharacterEntityId, item_ids: Vec) -> impl for<'a> Fn((ItemStateProxy<'a>, Box), ()) -> Pin, Box), ()), ItemStateError>> + Send + 'a>> { @@ -596,28 +376,8 @@ fn sort_inventory_items(character_id: CharacterEntityId, item_ids: Vec ( - item_state: &'a mut ItemState, - entity_gateway: &mut EG, - character: &CharacterEntity, - item_ids: Vec, -) -> Result<(), ItemStateError> -where - EG: EntityGateway, -{ - entity_gateway.with_transaction(|transaction| async move { - let item_state_proxy = ItemStateProxy::new(item_state); - let ((item_state_proxy, transaction), result) = ItemStateAction::default() - .act(sort_inventory_items(character.id, item_ids)) - .commit((item_state_proxy, transaction)) - .await?; - item_state_proxy.commit(); - Ok((transaction, result)) - }).await -} - -fn use_consumed_item(character: CharacterEntity) +pub(super) fn use_consumed_item(character: CharacterEntity) -> impl for<'a> Fn((ItemStateProxy<'a>, Box), InventoryItem) -> Pin, Box), CharacterEntity), ItemStateError>> + Send + 'a>> { @@ -637,30 +397,8 @@ fn use_consumed_item(character: CharacterEntity) } } -pub async fn use_item<'a, EG> ( - item_state: &'a mut ItemState, - entity_gateway: &mut EG, - character: &mut CharacterEntity, - item_id: &ClientItemId, - amount: u32, -) -> Result<(), ItemStateError> -where - EG: EntityGateway, -{ - entity_gateway.with_transaction(|transaction| async move { - let item_state_proxy = ItemStateProxy::new(item_state); - let ((item_state_proxy, transaction), new_character) = ItemStateAction::default() - .act(take_item_from_inventory(character.id, *item_id, amount)) - .act(use_consumed_item(character.clone())) - .commit((item_state_proxy, transaction)) - .await?; - item_state_proxy.commit(); - *character = new_character; - Ok((transaction, ())) - }).await -} -fn feed_mag_item(character: CharacterEntity, mag_item_id: ClientItemId) +pub(super) fn feed_mag_item(character: CharacterEntity, mag_item_id: ClientItemId) -> impl for<'a> Fn((ItemStateProxy<'a>, Box), InventoryItem) -> Pin, Box), CharacterEntity), ItemStateError>> + Send + 'a>> { @@ -707,30 +445,7 @@ fn feed_mag_item(character: CharacterEntity, mag_item_id: ClientItemId) } -pub async fn feed_mag<'a, EG> ( - item_state: &'a mut ItemState, - entity_gateway: &mut EG, - character: &CharacterEntity, - mag_item_id: &ClientItemId, - tool_item_id: &ClientItemId, -) -> Result<(), ItemStateError> -where - EG: EntityGateway, -{ - entity_gateway.with_transaction(|transaction| async move { - let item_state_proxy = ItemStateProxy::new(item_state); - let ((item_state_proxy, transaction), _) = ItemStateAction::default() - .act(take_item_from_inventory(character.id, *tool_item_id, 1)) - .act(feed_mag_item(character.clone(), *mag_item_id)) - .commit((item_state_proxy, transaction)) - .await?; - item_state_proxy.commit(); - Ok((transaction, ())) - }).await -} - - -fn add_bought_item_to_inventory<'a>(character_id: CharacterEntityId, +pub(super) fn add_bought_item_to_inventory<'a>(character_id: CharacterEntityId, shop_item: &'a (dyn ShopItem + Send + Sync), item_id: ClientItemId, amount: u32) @@ -790,34 +505,8 @@ fn add_bought_item_to_inventory<'a>(character_id: CharacterEntityId, } } -pub async fn buy_shop_item<'a, EG> ( - item_state: &'a mut ItemState, - entity_gateway: &mut EG, - character: &CharacterEntity, - shop_item: &'a (dyn ShopItem + Send + Sync), - item_id: ClientItemId, - amount: u32, -) -> Result -where - EG: EntityGateway, -{ - let item_price = shop_item.price() as u32 * amount; - entity_gateway.with_transaction(|transaction| async move { - let item_state_proxy = ItemStateProxy::new(item_state); - let ((item_state_proxy, transaction), result) = ItemStateAction::default() - .act(take_meseta_from_inventory(character.id, item_price)) - //.act(bought_item_to_inventory_item) - //.act(add_item_to_inventory) - .act(add_bought_item_to_inventory(character.id, shop_item, item_id, amount)) - .commit((item_state_proxy, transaction)) - .await?; - item_state_proxy.commit(); - Ok((transaction, result)) - }).await -} - -fn sell_inventory_item<'a>(character_id: CharacterEntityId) +pub(super) fn sell_inventory_item<'a>(character_id: CharacterEntityId) -> impl Fn((ItemStateProxy<'a>, Box), InventoryItem) -> Pin, Box), InventoryItem), ItemStateError>> + Send + 'a>> { @@ -839,27 +528,6 @@ fn sell_inventory_item<'a>(character_id: CharacterEntityId) } } -pub async fn sell_item<'a, EG> ( - item_state: &'a mut ItemState, - entity_gateway: &mut EG, - character: &CharacterEntity, - item_id: ClientItemId, - amount: u32, -) -> Result -where - EG: EntityGateway, -{ - entity_gateway.with_transaction(|transaction| async move { - let item_state_proxy = ItemStateProxy::new(item_state); - let ((item_state_proxy, transaction), result) = ItemStateAction::default() - .act(take_item_from_inventory(character.id, item_id, amount)) - .act(sell_inventory_item(character.id)) - .commit((item_state_proxy, transaction)) - .await?; - item_state_proxy.commit(); - Ok((transaction, result)) - }).await -} #[async_recursion::async_recursion] async fn iterate_inner<'a, I, O, T, F, FR>(state: (ItemStateProxy<'a>, Box), @@ -890,7 +558,7 @@ where Ok((state, output)) } -pub fn iterate<'k, I, O, T, F, FR>( +pub(super) fn iterate<'k, I, O, T, F, FR>( input: Vec, func: F) -> impl for<'a> Fn((ItemStateProxy<'a>, Box), T) @@ -942,7 +610,7 @@ where Ok((state, output)) } -pub fn foreach<'k, O, T, F>(func: F) +pub(super) fn foreach<'k, O, T, F>(func: F) -> impl for<'a> Fn((ItemStateProxy<'a>, Box), Vec) -> Pin, Box), Vec), ItemStateError>> + Send + 'a>> where @@ -963,7 +631,7 @@ where } } -fn insert<'a, T: Send + Clone + 'a>(element: T) +pub(super) fn insert<'a, T: Send + Clone + 'a>(element: T) -> impl Fn((ItemStateProxy<'a>, Box), ()) -> Pin, Box), T), ItemStateError>> + Send + 'a>> { @@ -975,7 +643,7 @@ fn insert<'a, T: Send + Clone + 'a>(element: T) } } -fn add_item_to_inventory(character: CharacterEntity) +pub(super) fn add_item_to_inventory(character: CharacterEntity) -> impl for<'a> Fn((ItemStateProxy<'a>, Box), InventoryItem) -> Pin, Box), InventoryItem), ItemStateError>> + Send + 'a>> + Clone { @@ -999,7 +667,7 @@ fn add_item_to_inventory(character: CharacterEntity) } } -fn record_trade(trade_id: TradeId, character_to: CharacterEntityId, character_from: CharacterEntityId) +pub(super) fn record_trade(trade_id: TradeId, character_to: CharacterEntityId, character_from: CharacterEntityId) -> impl for<'a> Fn((ItemStateProxy<'a>, Box), Vec) -> Pin, Box), Vec), ItemStateError>> + Send + 'a>> + Clone { @@ -1022,7 +690,7 @@ fn record_trade(trade_id: TradeId, character_to: CharacterEntityId, character_fr } -fn assign_new_item_id() +pub(super) fn assign_new_item_id() -> impl for<'a> Fn((ItemStateProxy<'a>, Box), InventoryItem) -> Pin, Box), InventoryItem), ItemStateError>> + Send + 'a>> + Clone { @@ -1034,98 +702,8 @@ fn assign_new_item_id() } } -pub async fn trade_items<'a, EG> ( - item_state: &'a mut ItemState, - entity_gateway: &mut EG, - p1: (&AreaClient, &CharacterEntity, &Vec, Meseta), - p2: (&AreaClient, &CharacterEntity, &Vec, Meseta)) - -> Result<(Vec, Vec), ItemStateError> -where - EG: EntityGateway, -{ - let p1_trade_items = p1.2 - .iter() - .map(|item| { - match item { - TradeItem::Individual(item_id) => (*item_id, 1), - TradeItem::Stacked(item_id, amount) => (*item_id, *amount as u32), - } - }) - .collect(); - let p2_trade_items = p2.2 - .iter() - .map(|item| { - match item { - TradeItem::Individual(item_id) => (*item_id, 1), - TradeItem::Stacked(item_id, amount) => (*item_id, *amount as u32), - } - }) - .collect(); - entity_gateway.with_transaction(|mut transaction| async move { - let p1_id = p1.1.id; - let p2_id = p2.1.id; - let trade = transaction.gateway().create_trade(&p1_id, &p2_id).await?; - let item_state_proxy = ItemStateProxy::new(item_state); - let ((item_state_proxy, transaction), p1_removed_items) = ItemStateAction::default() - .act(iterate(p1_trade_items, move |p1_trade_item| take_item_from_inventory(p1_id, p1_trade_item.0, p1_trade_item.1) )) - .act(foreach(assign_new_item_id())) - .commit((item_state_proxy, transaction)) - .await?; - let ((item_state_proxy, transaction), p2_removed_items) = ItemStateAction::default() - .act(iterate(p2_trade_items, move |p2_trade_item| take_item_from_inventory(p2_id, p2_trade_item.0, p2_trade_item.1) )) - .act(foreach(assign_new_item_id())) - .commit((item_state_proxy, transaction)) - .await?; - - let ((item_state_proxy, transaction), p2_new_items) = ItemStateAction::default() - .act(insert(p1_removed_items)) - .act(foreach(add_item_to_inventory(p2.1.clone()))) - .act(record_trade(trade.id, p1_id, p2_id)) - .commit((item_state_proxy, transaction)) - .await?; - let ((item_state_proxy, transaction), p1_new_items) = ItemStateAction::default() - .act(insert(p2_removed_items)) - .act(foreach(add_item_to_inventory(p1.1.clone()))) - .act(record_trade(trade.id, p2_id, p1_id)) - .commit((item_state_proxy, transaction)) - .await?; - - let ((item_state_proxy, transaction), _) = ItemStateAction::default() - .act(take_meseta_from_inventory(p1_id, p1.3.0)) - .act(take_meseta_from_inventory(p2_id, p2.3.0)) - .act(add_meseta_to_inventory(p1_id, p2.3.0)) - .act(add_meseta_to_inventory(p2_id, p1.3.0)) - .commit((item_state_proxy, transaction)) - .await?; - - item_state_proxy.commit(); - Ok((transaction, (p1_new_items, p2_new_items))) - }).await -} - - -pub async fn take_meseta<'a, EG> ( - item_state: &'a mut ItemState, - entity_gateway: &mut EG, - character_id: &CharacterEntityId, - meseta: Meseta) - -> Result<(), ItemStateError> -where - EG: EntityGateway, -{ - entity_gateway.with_transaction(|transaction| async move { - let item_state_proxy = ItemStateProxy::new(item_state); - let ((item_state_proxy, transaction), _) = ItemStateAction::default() - .act(take_meseta_from_inventory(*character_id, meseta.0)) - .commit((item_state_proxy, transaction)) - .await?; - - item_state_proxy.commit(); - Ok((transaction, ())) - }).await -} -fn convert_item_drop_to_floor_item(character_id: CharacterEntityId, item_drop: ItemDrop) +pub(super) fn convert_item_drop_to_floor_item(character_id: CharacterEntityId, item_drop: ItemDrop) -> impl for<'a> Fn((ItemStateProxy<'a>, Box), ()) -> Pin, Box), FloorItem), ItemStateError>> + Send + 'a>> + Clone { @@ -1222,7 +800,7 @@ fn convert_item_drop_to_floor_item(character_id: CharacterEntityId, item_drop: I } } -fn add_item_to_local_floor(character_id: CharacterEntityId) +pub(super) fn add_item_to_local_floor(character_id: CharacterEntityId) -> impl for<'a> Fn((ItemStateProxy<'a>, Box), FloorItem) -> Pin, Box), FloorItem), ItemStateError>> + Send + 'a>> { @@ -1237,29 +815,7 @@ fn add_item_to_local_floor(character_id: CharacterEntityId) } } -pub async fn enemy_drops_item<'a, EG> ( - item_state: &'a mut ItemState, - entity_gateway: &mut EG, - character_id: CharacterEntityId, - item_drop: ItemDrop) - -> Result -where - EG: EntityGateway, -{ - entity_gateway.with_transaction(|transaction| async move { - let item_state_proxy = ItemStateProxy::new(item_state); - let ((item_state_proxy, transaction), floor_item) = ItemStateAction::default() - .act(convert_item_drop_to_floor_item(character_id, item_drop)) - .act(add_item_to_local_floor(character_id)) - .commit((item_state_proxy, transaction)) - .await?; - - item_state_proxy.commit(); - Ok((transaction, floor_item)) - }).await -} - -fn apply_modifier_to_inventory_item(modifier: ItemModifier) +pub(super) fn apply_modifier_to_inventory_item(modifier: ItemModifier) -> impl for<'a> Fn((ItemStateProxy<'a>, Box), InventoryItem) -> Pin, Box), InventoryItem), ItemStateError>> + Send + 'a>> { @@ -1279,7 +835,7 @@ fn apply_modifier_to_inventory_item(modifier: ItemModifier) } } -fn as_individual_item() +pub(super) fn as_individual_item() -> impl for<'a> Fn((ItemStateProxy<'a>, Box), InventoryItem) -> Pin, Box), IndividualItemDetail), ItemStateError>> + Send + 'a>> { @@ -1294,29 +850,3 @@ fn as_individual_item() }) } } - - -pub async fn apply_modifier<'a, EG> ( - item_state: &'a mut ItemState, - entity_gateway: &mut EG, - character: &CharacterEntity, - item_id: ClientItemId, - modifier: ItemModifier) - -> Result -where - EG: EntityGateway, -{ - entity_gateway.with_transaction(|transaction| async move { - let item_state_proxy = ItemStateProxy::new(item_state); - let ((item_state_proxy, transaction), item) = ItemStateAction::default() - .act(take_item_from_inventory(character.id, item_id, 1)) - .act(apply_modifier_to_inventory_item(modifier)) - .act(add_item_to_inventory(character.clone())) - .act(as_individual_item()) - .commit((item_state_proxy, transaction)) - .await?; - - item_state_proxy.commit(); - Ok((transaction, item)) - }).await -} diff --git a/src/ship/items/mod.rs b/src/ship/items/mod.rs index 4b9913b..549ee51 100644 --- a/src/ship/items/mod.rs +++ b/src/ship/items/mod.rs @@ -5,6 +5,7 @@ pub mod itemstateaction; pub mod inventory; pub mod floor; pub mod bank; +pub mod tasks; #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, serde::Serialize, serde::Deserialize, derive_more::Display)] pub struct ClientItemId(pub u32); diff --git a/src/ship/items/state.rs b/src/ship/items/state.rs index 70a5ddf..8ce0d0d 100644 --- a/src/ship/items/state.rs +++ b/src/ship/items/state.rs @@ -14,8 +14,6 @@ use crate::ship::items::inventory::{Inventory, InventoryItem, InventoryItemDetai use crate::ship::items::floor::{FloorState, FloorItem, LocalFloor, SharedFloor, FloorType}; use crate::ship::items::bank::{Bank, BankState, BankItem, BankItemDetail, BankError}; -// TODO: Commit trait that ItemStateProxy and EntityTransaction implement that .commit requires and acts on upon everything succeeding (like 3 less lines of code!) - #[derive(thiserror::Error, Debug)] pub enum ItemStateError { #[error("character {0} not found")] diff --git a/src/ship/items/tasks.rs b/src/ship/items/tasks.rs new file mode 100644 index 0000000..4264420 --- /dev/null +++ b/src/ship/items/tasks.rs @@ -0,0 +1,500 @@ +use crate::ship::items::ClientItemId; +use crate::entity::item::Meseta; + +use crate::ship::map::MapArea; +use crate::entity::character::{CharacterEntity, CharacterEntityId}; +use crate::entity::gateway::EntityGateway; +use crate::ship::items::state::{ItemState, ItemStateProxy, ItemStateError, IndividualItemDetail}; +use crate::ship::items::itemstateaction::{ItemStateAction, ItemAction}; +use crate::ship::items::inventory::InventoryItem; +use crate::ship::items::floor::FloorItem; +use crate::entity::item::ItemModifier; +use crate::ship::shops::ShopItem; +use crate::ship::trade::TradeItem; +use crate::ship::location::AreaClient; +use crate::ship::drops::ItemDrop; + +use crate::ship::items::actions; + +pub async fn pick_up_item( + item_state: &mut ItemState, + entity_gateway: &mut EG, + character: &CharacterEntity, + item_id: &ClientItemId) + -> Result +where + EG: EntityGateway, +{ + entity_gateway.with_transaction(|transaction| async move { + let item_state_proxy = ItemStateProxy::new(item_state); + let ((item_state_proxy, transaction), result) = ItemStateAction::default() + .act(actions::take_item_from_floor(character.id, *item_id)) + .act(actions::add_floor_item_to_inventory(character)) + .commit((item_state_proxy, transaction)) + .await?; + item_state_proxy.commit(); + Ok((transaction, result)) + }).await +} + +pub async fn drop_item( + item_state: &mut ItemState, + entity_gateway: &mut EG, + character: &CharacterEntity, + item_id: &ClientItemId, + map_area: MapArea, + drop_position: (f32, f32, f32)) + -> Result +where + EG: EntityGateway, +{ + entity_gateway.with_transaction(|transaction| async move { + let item_state_proxy = ItemStateProxy::new(item_state); + let ((item_state_proxy, transaction), result) = ItemStateAction::default() + .act(actions::take_item_from_inventory(character.id, *item_id, 0)) + .act(actions::add_inventory_item_to_shared_floor(character.id, map_area, drop_position)) + .commit((item_state_proxy, transaction)) + .await?; + item_state_proxy.commit(); + Ok((transaction, result)) + }).await +} + +pub async fn drop_partial_item<'a, EG>( + item_state: &'a mut ItemState, + entity_gateway: &mut EG, + character: &CharacterEntity, + item_id: &ClientItemId, + map_area: MapArea, + drop_position: (f32, f32), + amount: u32) + -> Result +where + EG: EntityGateway, +{ + entity_gateway.with_transaction(|transaction| async move { + let item_state_proxy = ItemStateProxy::new(item_state); + let ((item_state_proxy, transaction), result) = ItemStateAction::default() + .act(actions::take_item_from_inventory(character.id, *item_id, amount)) + .act(actions::add_inventory_item_to_shared_floor(character.id, map_area, (drop_position.0, 0.0, drop_position.1))) + .commit((item_state_proxy, transaction)) + .await?; + item_state_proxy.commit(); + Ok((transaction, result)) + }).await +} + + + +pub async fn drop_meseta<'a, EG>( + item_state: &'a mut ItemState, + entity_gateway: &mut EG, + character: &CharacterEntity, + map_area: MapArea, + drop_position: (f32, f32), + amount: u32) + -> Result +where + EG: EntityGateway, +{ + entity_gateway.with_transaction(|transaction| async move { + let item_state_proxy = ItemStateProxy::new(item_state); + let ((item_state_proxy, transaction), result) = ItemStateAction::default() + .act(actions::take_meseta_from_inventory(character.id, amount)) + .act(actions::add_meseta_to_shared_floor(character.id, amount, map_area, drop_position)) + .commit((item_state_proxy, transaction)) + .await?; + item_state_proxy.commit(); + Ok((transaction, result)) + }).await +} + + +pub async fn withdraw_meseta<'a, EG>( + item_state: &'a mut ItemState, + entity_gateway: &mut EG, + character: &CharacterEntity, + amount: u32) + -> Result<(), ItemStateError> +where + EG: EntityGateway, +{ + entity_gateway.with_transaction(|transaction| async move { + let item_state_proxy = ItemStateProxy::new(item_state); + let ((item_state_proxy, transaction), result) = ItemStateAction::default() + .act(actions::take_meseta_from_bank(character.id, amount)) + .act(actions::add_meseta_from_bank_to_inventory(character.id, amount)) + .commit((item_state_proxy, transaction)) + .await?; + item_state_proxy.commit(); + Ok((transaction, result)) + }).await +} + + +pub async fn deposit_meseta<'a, EG>( + item_state: &'a mut ItemState, + entity_gateway: &mut EG, + character: &CharacterEntity, + amount: u32) + -> Result<(), ItemStateError> +where + EG: EntityGateway, +{ + entity_gateway.with_transaction(|transaction| async move { + let item_state_proxy = ItemStateProxy::new(item_state); + let ((item_state_proxy, transaction), _) = ItemStateAction::default() + .act(actions::take_meseta_from_inventory(character.id, amount)) + .act(actions::add_meseta_to_bank(character.id, amount)) + .commit((item_state_proxy, transaction)) + .await?; + item_state_proxy.commit(); + Ok((transaction, ())) + }).await +} + + +pub async fn withdraw_item<'a, EG>( + item_state: &'a mut ItemState, + entity_gateway: &mut EG, + character: &CharacterEntity, + item_id: &ClientItemId, + amount: u32) + -> Result +where + EG: EntityGateway, +{ + entity_gateway.with_transaction(|transaction| async move { + let item_state_proxy = ItemStateProxy::new(item_state); + let ((item_state_proxy, transaction), result) = ItemStateAction::default() + .act(actions::take_item_from_bank(character.id, *item_id, amount)) + //.act(bank_item_to_inventory_item) + //.act(add_item_to_inventory) + .act(actions::add_bank_item_to_inventory(character)) + .commit((item_state_proxy, transaction)) + .await?; + item_state_proxy.commit(); + Ok((transaction, result)) + }).await +} + + +pub async fn deposit_item<'a, EG> ( + item_state: &'a mut ItemState, + entity_gateway: &mut EG, + character: &CharacterEntity, + item_id: &ClientItemId, + amount: u32) + -> Result<(), ItemStateError> +where + EG: EntityGateway, +{ + entity_gateway.with_transaction(|transaction| async move { + let item_state_proxy = ItemStateProxy::new(item_state); + let ((item_state_proxy, transaction), result) = ItemStateAction::default() + .act(actions::take_item_from_inventory(character.id, *item_id, amount)) + .act(actions::add_inventory_item_to_bank(character.id)) + .commit((item_state_proxy, transaction)) + .await?; + item_state_proxy.commit(); + Ok((transaction, result)) + }).await +} + +pub async fn equip_item<'a, EG> ( + item_state: &'a mut ItemState, + entity_gateway: &mut EG, + character: &CharacterEntity, + item_id: &ClientItemId, + equip_slot: u8, +) -> Result<(), ItemStateError> +where + EG: EntityGateway, +{ + entity_gateway.with_transaction(|transaction| async move { + let item_state_proxy = ItemStateProxy::new(item_state); + let ((item_state_proxy, transaction), result) = ItemStateAction::default() + .act(actions::equip_inventory_item(character.id, *item_id, equip_slot)) + .commit((item_state_proxy, transaction)) + .await?; + item_state_proxy.commit(); + Ok((transaction, result)) + }).await +} + + +pub async fn unequip_item<'a, EG> ( + item_state: &'a mut ItemState, + entity_gateway: &mut EG, + character: &CharacterEntity, + item_id: &ClientItemId, +) -> Result<(), ItemStateError> +where + EG: EntityGateway, +{ + entity_gateway.with_transaction(|transaction| async move { + let item_state_proxy = ItemStateProxy::new(item_state); + let ((item_state_proxy, transaction), result) = ItemStateAction::default() + .act(actions::unequip_inventory_item(character.id, *item_id)) + .commit((item_state_proxy, transaction)) + .await?; + item_state_proxy.commit(); + Ok((transaction, result)) + }).await +} + + +pub async fn sort_inventory<'a, EG> ( + item_state: &'a mut ItemState, + entity_gateway: &mut EG, + character: &CharacterEntity, + item_ids: Vec, +) -> Result<(), ItemStateError> +where + EG: EntityGateway, +{ + entity_gateway.with_transaction(|transaction| async move { + let item_state_proxy = ItemStateProxy::new(item_state); + let ((item_state_proxy, transaction), result) = ItemStateAction::default() + .act(actions::sort_inventory_items(character.id, item_ids)) + .commit((item_state_proxy, transaction)) + .await?; + item_state_proxy.commit(); + Ok((transaction, result)) + }).await +} + + +pub async fn use_item<'a, EG> ( + item_state: &'a mut ItemState, + entity_gateway: &mut EG, + character: &mut CharacterEntity, + item_id: &ClientItemId, + amount: u32, +) -> Result<(), ItemStateError> +where + EG: EntityGateway, +{ + entity_gateway.with_transaction(|transaction| async move { + let item_state_proxy = ItemStateProxy::new(item_state); + let ((item_state_proxy, transaction), new_character) = ItemStateAction::default() + .act(actions::take_item_from_inventory(character.id, *item_id, amount)) + .act(actions::use_consumed_item(character.clone())) + .commit((item_state_proxy, transaction)) + .await?; + item_state_proxy.commit(); + *character = new_character; + Ok((transaction, ())) + }).await +} + + +pub async fn feed_mag<'a, EG> ( + item_state: &'a mut ItemState, + entity_gateway: &mut EG, + character: &CharacterEntity, + mag_item_id: &ClientItemId, + tool_item_id: &ClientItemId, +) -> Result<(), ItemStateError> +where + EG: EntityGateway, +{ + entity_gateway.with_transaction(|transaction| async move { + let item_state_proxy = ItemStateProxy::new(item_state); + let ((item_state_proxy, transaction), _) = ItemStateAction::default() + .act(actions::take_item_from_inventory(character.id, *tool_item_id, 1)) + .act(actions::feed_mag_item(character.clone(), *mag_item_id)) + .commit((item_state_proxy, transaction)) + .await?; + item_state_proxy.commit(); + Ok((transaction, ())) + }).await +} + + +pub async fn buy_shop_item<'a, EG> ( + item_state: &'a mut ItemState, + entity_gateway: &mut EG, + character: &CharacterEntity, + shop_item: &'a (dyn ShopItem + Send + Sync), + item_id: ClientItemId, + amount: u32, +) -> Result +where + EG: EntityGateway, +{ + let item_price = shop_item.price() as u32 * amount; + entity_gateway.with_transaction(|transaction| async move { + let item_state_proxy = ItemStateProxy::new(item_state); + let ((item_state_proxy, transaction), result) = ItemStateAction::default() + .act(actions::take_meseta_from_inventory(character.id, item_price)) + //.act(bought_item_to_inventory_item) + //.act(add_item_to_inventory) + .act(actions::add_bought_item_to_inventory(character.id, shop_item, item_id, amount)) + .commit((item_state_proxy, transaction)) + .await?; + item_state_proxy.commit(); + Ok((transaction, result)) + }).await +} + + +pub async fn sell_item<'a, EG> ( + item_state: &'a mut ItemState, + entity_gateway: &mut EG, + character: &CharacterEntity, + item_id: ClientItemId, + amount: u32, +) -> Result +where + EG: EntityGateway, +{ + entity_gateway.with_transaction(|transaction| async move { + let item_state_proxy = ItemStateProxy::new(item_state); + let ((item_state_proxy, transaction), result) = ItemStateAction::default() + .act(actions::take_item_from_inventory(character.id, item_id, amount)) + .act(actions::sell_inventory_item(character.id)) + .commit((item_state_proxy, transaction)) + .await?; + item_state_proxy.commit(); + Ok((transaction, result)) + }).await +} +pub async fn trade_items<'a, EG> ( + item_state: &'a mut ItemState, + entity_gateway: &mut EG, + p1: (&AreaClient, &CharacterEntity, &Vec, Meseta), + p2: (&AreaClient, &CharacterEntity, &Vec, Meseta)) + -> Result<(Vec, Vec), ItemStateError> +where + EG: EntityGateway, +{ + let p1_trade_items = p1.2 + .iter() + .map(|item| { + match item { + TradeItem::Individual(item_id) => (*item_id, 1), + TradeItem::Stacked(item_id, amount) => (*item_id, *amount as u32), + } + }) + .collect(); + let p2_trade_items = p2.2 + .iter() + .map(|item| { + match item { + TradeItem::Individual(item_id) => (*item_id, 1), + TradeItem::Stacked(item_id, amount) => (*item_id, *amount as u32), + } + }) + .collect(); + entity_gateway.with_transaction(|mut transaction| async move { + let p1_id = p1.1.id; + let p2_id = p2.1.id; + let trade = transaction.gateway().create_trade(&p1_id, &p2_id).await?; + let item_state_proxy = ItemStateProxy::new(item_state); + let ((item_state_proxy, transaction), p1_removed_items) = ItemStateAction::default() + .act(actions::iterate(p1_trade_items, move |p1_trade_item| actions::take_item_from_inventory(p1_id, p1_trade_item.0, p1_trade_item.1) )) + .act(actions::foreach(actions::assign_new_item_id())) + .commit((item_state_proxy, transaction)) + .await?; + let ((item_state_proxy, transaction), p2_removed_items) = ItemStateAction::default() + .act(actions::iterate(p2_trade_items, move |p2_trade_item| actions::take_item_from_inventory(p2_id, p2_trade_item.0, p2_trade_item.1) )) + .act(actions::foreach(actions::assign_new_item_id())) + .commit((item_state_proxy, transaction)) + .await?; + + let ((item_state_proxy, transaction), p2_new_items) = ItemStateAction::default() + .act(actions::insert(p1_removed_items)) + .act(actions::foreach(actions::add_item_to_inventory(p2.1.clone()))) + .act(actions::record_trade(trade.id, p1_id, p2_id)) + .commit((item_state_proxy, transaction)) + .await?; + let ((item_state_proxy, transaction), p1_new_items) = ItemStateAction::default() + .act(actions::insert(p2_removed_items)) + .act(actions::foreach(actions::add_item_to_inventory(p1.1.clone()))) + .act(actions::record_trade(trade.id, p2_id, p1_id)) + .commit((item_state_proxy, transaction)) + .await?; + + let ((item_state_proxy, transaction), _) = ItemStateAction::default() + .act(actions::take_meseta_from_inventory(p1_id, p1.3.0)) + .act(actions::take_meseta_from_inventory(p2_id, p2.3.0)) + .act(actions::add_meseta_to_inventory(p1_id, p2.3.0)) + .act(actions::add_meseta_to_inventory(p2_id, p1.3.0)) + .commit((item_state_proxy, transaction)) + .await?; + + item_state_proxy.commit(); + Ok((transaction, (p1_new_items, p2_new_items))) + }).await +} + + +pub async fn take_meseta<'a, EG> ( + item_state: &'a mut ItemState, + entity_gateway: &mut EG, + character_id: &CharacterEntityId, + meseta: Meseta) + -> Result<(), ItemStateError> +where + EG: EntityGateway, +{ + entity_gateway.with_transaction(|transaction| async move { + let item_state_proxy = ItemStateProxy::new(item_state); + let ((item_state_proxy, transaction), _) = ItemStateAction::default() + .act(actions::take_meseta_from_inventory(*character_id, meseta.0)) + .commit((item_state_proxy, transaction)) + .await?; + + item_state_proxy.commit(); + Ok((transaction, ())) + }).await +} + +pub async fn enemy_drops_item<'a, EG> ( + item_state: &'a mut ItemState, + entity_gateway: &mut EG, + character_id: CharacterEntityId, + item_drop: ItemDrop) + -> Result +where + EG: EntityGateway, +{ + entity_gateway.with_transaction(|transaction| async move { + let item_state_proxy = ItemStateProxy::new(item_state); + let ((item_state_proxy, transaction), floor_item) = ItemStateAction::default() + .act(actions::convert_item_drop_to_floor_item(character_id, item_drop)) + .act(actions::add_item_to_local_floor(character_id)) + .commit((item_state_proxy, transaction)) + .await?; + + item_state_proxy.commit(); + Ok((transaction, floor_item)) + }).await +} + + +pub async fn apply_modifier<'a, EG> ( + item_state: &'a mut ItemState, + entity_gateway: &mut EG, + character: &CharacterEntity, + item_id: ClientItemId, + modifier: ItemModifier) + -> Result +where + EG: EntityGateway, +{ + entity_gateway.with_transaction(|transaction| async move { + let item_state_proxy = ItemStateProxy::new(item_state); + let ((item_state_proxy, transaction), item) = ItemStateAction::default() + .act(actions::take_item_from_inventory(character.id, item_id, 1)) + .act(actions::apply_modifier_to_inventory_item(modifier)) + .act(actions::add_item_to_inventory(character.clone())) + .act(actions::as_individual_item()) + .commit((item_state_proxy, transaction)) + .await?; + + item_state_proxy.commit(); + Ok((transaction, item)) + }).await +} diff --git a/src/ship/packet/handler/direct_message.rs b/src/ship/packet/handler/direct_message.rs index 519fa4f..4861b7b 100644 --- a/src/ship/packet/handler/direct_message.rs +++ b/src/ship/packet/handler/direct_message.rs @@ -16,7 +16,8 @@ use crate::ship::packet::builder; use crate::ship::shops::{ShopItem, ToolShopItem, ArmorShopItem}; use crate::ship::items::state::{ItemState, ItemStateError}; use crate::ship::items::floor::{FloorType, FloorItemDetail}; -use crate::ship::items::actions::{pick_up_item, withdraw_meseta, deposit_meseta, withdraw_item, deposit_item, buy_shop_item, enemy_drops_item, take_meseta, apply_modifier, TriggerCreateItem}; +use crate::ship::items::actions::TriggerCreateItem; +use crate::ship::items::tasks::{pick_up_item, withdraw_meseta, deposit_meseta, withdraw_item, deposit_item, buy_shop_item, enemy_drops_item, take_meseta, apply_modifier}; const BANK_ACTION_DEPOSIT: u8 = 0; const BANK_ACTION_WITHDRAW: u8 = 1; diff --git a/src/ship/packet/handler/message.rs b/src/ship/packet/handler/message.rs index dbb4695..d0d4f44 100644 --- a/src/ship/packet/handler/message.rs +++ b/src/ship/packet/handler/message.rs @@ -9,7 +9,7 @@ use crate::ship::location::{ClientLocation, ClientLocationError}; use crate::ship::items::ClientItemId; use crate::ship::packet::builder; use crate::ship::items::state::ItemState; -use crate::ship::items::actions::{drop_item, drop_partial_item, drop_meseta, equip_item, unequip_item, sort_inventory, use_item, feed_mag, sell_item, take_meseta}; +use crate::ship::items::tasks::{drop_item, drop_partial_item, drop_meseta, equip_item, unequip_item, sort_inventory, use_item, feed_mag, sell_item, take_meseta}; pub async fn request_exp(id: ClientId, request_exp: &RequestExp, diff --git a/src/ship/packet/handler/trade.rs b/src/ship/packet/handler/trade.rs index 4627697..7b01ed6 100644 --- a/src/ship/packet/handler/trade.rs +++ b/src/ship/packet/handler/trade.rs @@ -10,7 +10,7 @@ use crate::ship::items::inventory::InventoryItemDetail; use crate::ship::trade::{TradeItem, TradeState, TradeStatus}; use crate::entity::gateway::EntityGateway; use crate::ship::packet::builder; -use crate::ship::items::actions::trade_items; +use crate::ship::items::tasks::trade_items; use crate::ship::location::{AreaClient, RoomId}; use crate::entity::item::Meseta; -- 2.36.0 From 0cd92fd4ac1544c6b304b4dc232b6e9d9812fc05 Mon Sep 17 00:00:00 2001 From: jake Date: Tue, 19 Jul 2022 23:01:18 -0600 Subject: [PATCH 58/60] put proper error strings on this struct --- src/ship/ship.rs | 35 ++++++++++++++++++++++++++++++----- 1 file changed, 30 insertions(+), 5 deletions(-) diff --git a/src/ship/ship.rs b/src/ship/ship.rs index 23af6a1..773be42 100644 --- a/src/ship/ship.rs +++ b/src/ship/ship.rs @@ -41,41 +41,66 @@ pub type Rooms = [Option; MAX_ROOMS]; pub type Clients = HashMap; #[derive(Error, Debug)] -#[error("shiperror {0:?}")] pub enum ShipError { + #[error("client not found {0}")] ClientNotFound(ClientId), + #[error("no character in slot {0} {1}")] NoCharacterInSlot(ClientId, u32), + #[error("invalid slot {0} {1}")] InvalidSlot(ClientId, u32), - #[error("")] + #[error("too many clients")] TooManyClients, + #[error("client error location {0}")] ClientLocationError(#[from] ClientLocationError), + #[error("get neighbor error {0}")] GetNeighborError(#[from] GetNeighborError), + #[error("get clients error {0}")] GetClientsError(#[from] GetClientsError), + #[error("get area error {0}")] GetAreaError(#[from] GetAreaError), + #[error("maps error {0}")] MapsError(#[from] MapsError), + #[error("map area error {0}")] MapAreaError(#[from] MapAreaError), + #[error("invalid room {0}")] InvalidRoom(u32), + #[error("monster already droppped item {0} {1}")] MonsterAlreadyDroppedItem(ClientId, u16), + #[error("slice error {0}")] SliceError(#[from] std::array::TryFromSliceError), - #[error("")] + #[error("item error")] ItemError, // TODO: refine this + #[error("pick up invalid item id {0}")] PickUpInvalidItemId(u32), + #[error("drop invalid item id {0}")] DropInvalidItemId(u32), + #[error("item state error {0}")] ItemStateError(#[from] items::state::ItemStateError), - #[error("")] + #[error("item drop location not set")] ItemDropLocationNotSet, + #[error("box already dropped item {0} {1}")] BoxAlreadyDroppedItem(ClientId, u16), + #[error("invalid quest category {0}")] InvalidQuestCategory(u32), + #[error("invalid quest {0}")] InvalidQuest(u32), + #[error("invalid quest filename {0}")] InvalidQuestFilename(String), + #[error("io error {0}")] IoError(#[from] std::io::Error), + #[error("not enough meseta {0} {1}")] NotEnoughMeseta(ClientId, u32), - #[error("")] + #[error("shop error")] ShopError, + #[error("gateway error {0}")] GatewayError(#[from] GatewayError), + #[error("unknown monster {0}")] UnknownMonster(crate::ship::monster::MonsterType), + #[error("invalid ship {0}")] InvalidShip(usize), + #[error("invalid block {0}")] InvalidBlock(usize), + #[error("invalid item {0}")] InvalidItem(items::ClientItemId), #[error("trade error {0}")] TradeError(#[from] crate::ship::packet::handler::trade::TradeError), -- 2.36.0 From b80e308cf2914f27f9c5692047850a0c1be2da56 Mon Sep 17 00:00:00 2001 From: andy Date: Wed, 6 Jul 2022 22:41:30 +0000 Subject: [PATCH 59/60] room tab info page 1 --- src/ship/packet/handler/lobby.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ship/packet/handler/lobby.rs b/src/ship/packet/handler/lobby.rs index dd0e6d9..2e1625c 100644 --- a/src/ship/packet/handler/lobby.rs +++ b/src/ship/packet/handler/lobby.rs @@ -155,4 +155,4 @@ pub fn get_room_tab_info(id: ClientId, } else { Ok(vec![(id, SendShipPacket::SmallLeftDialog(SmallLeftDialog::new("Game is no longer active".into())))]) } -} \ No newline at end of file +} -- 2.36.0 From ee8c80f42faa105fee52e8485c7292c3bf45d43e Mon Sep 17 00:00:00 2001 From: andy Date: Mon, 25 Jul 2022 17:18:04 +0000 Subject: [PATCH 60/60] rebasing onto the last refactor ever --- src/ship/packet/handler/room.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ship/packet/handler/room.rs b/src/ship/packet/handler/room.rs index 96688f5..9d5eb57 100644 --- a/src/ship/packet/handler/room.rs +++ b/src/ship/packet/handler/room.rs @@ -120,11 +120,11 @@ pub fn join_room(id: ClientId, 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_manager.add_character_to_room(room_id, &client.character, area_client); + 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_manager, level_table, room_id)?; + 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; -- 2.36.0