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(#[from] GatewayError), } #[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, { 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 { 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, //F: FnOnce(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: 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, 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, { 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, { 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, })) }) } #[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> { 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) } */