You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

561 lines
20 KiB

2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
  1. use std::collections::{HashMap, BinaryHeap};
  2. use std::cmp::Reverse;
  3. use async_std::sync::{Arc, RwLock, Mutex};
  4. use futures::stream::{FuturesOrdered, StreamExt};
  5. use anyhow::Context;
  6. use entity::gateway::{EntityGateway, GatewayError};
  7. use entity::character::{CharacterEntity, CharacterEntityId};
  8. use entity::item::{ItemEntityId, ItemDetail, ItemEntity, InventoryItemEntity, BankItemEntity, BankIdentifier};
  9. use entity::item::tool::Tool;
  10. use entity::item::weapon::Weapon;
  11. use entity::item::mag::Mag;
  12. use crate::ship::drops::ItemDrop;
  13. use crate::ship::items::ClientItemId;
  14. use crate::ship::items::inventory::{Inventory, InventoryItem, InventoryItemDetail, InventoryError, InventoryState};
  15. use crate::ship::items::floor::{FloorState, FloorItem, LocalFloor, SharedFloor, FloorType};
  16. use crate::ship::items::bank::{Bank, BankState, BankItem, BankItemDetail, BankError};
  17. use crate::ship::location::{AreaClient, RoomId};
  18. #[derive(thiserror::Error, Debug)]
  19. pub enum ItemStateError {
  20. #[error("character {0} not found")]
  21. NoCharacter(CharacterEntityId),
  22. #[error("room {0} not found")]
  23. NoRoom(RoomId),
  24. #[error("inventory item {0} not found")]
  25. NoInventoryItem(ClientItemId),
  26. #[error("floor item {0} not found")]
  27. NoFloorItem(ClientItemId),
  28. #[error("expected {0} to be a tool")]
  29. NotATool(ClientItemId),
  30. #[error("bank item {0} not found")]
  31. NoBankItem(ClientItemId),
  32. #[error("inventory error {0}")]
  33. InventoryError(#[from] InventoryError),
  34. #[error("bank error {0}")]
  35. BankError(#[from] BankError),
  36. #[error("invalid item id {0}")]
  37. InvalidItemId(ClientItemId),
  38. #[error("invalid drop? {0:?} (this shouldn't occur)")]
  39. BadItemDrop(ItemDrop),
  40. #[error("idk")]
  41. Dummy,
  42. #[error("gateway")]
  43. GatewayError(#[from] GatewayError),
  44. #[error("tried to remove more meseta than exists: {0}")]
  45. InvalidMesetaRemoval(u32),
  46. #[error("tried to add meseta when there is no more room")]
  47. FullOfMeseta,
  48. #[error("stacked item")]
  49. StackedItemError(Vec<ItemEntity>),
  50. #[error("apply item {0}")]
  51. ApplyItemError(#[from] crate::ship::items::apply_item::ApplyItemError),
  52. #[error("item is not a mag {0}")]
  53. NotAMag(ClientItemId),
  54. #[error("item is not mag food {0}")]
  55. NotMagFood(ClientItemId),
  56. #[error("item is not sellable")]
  57. ItemNotSellable,
  58. #[error("could not modify item")]
  59. InvalidModifier,
  60. #[error("wrong item type {0}")]
  61. WrongItemType(ClientItemId),
  62. }
  63. #[derive(Clone, Debug)]
  64. pub struct IndividualItemDetail {
  65. pub entity_id: ItemEntityId,
  66. pub item: ItemDetail,
  67. }
  68. impl IndividualItemDetail {
  69. pub fn as_weapon(&self) -> Option<&Weapon> {
  70. match &self.item {
  71. ItemDetail::Weapon(weapon) => Some(weapon),
  72. _ => None
  73. }
  74. }
  75. pub fn as_mag(&self) -> Option<&Mag> {
  76. match &self.item {
  77. ItemDetail::Mag(mag) => Some(mag),
  78. _ => None
  79. }
  80. }
  81. pub fn as_mag_mut(&mut self) -> Option<&mut Mag> {
  82. match &mut self.item {
  83. ItemDetail::Mag(mag) => Some(mag),
  84. _ => None
  85. }
  86. }
  87. pub fn as_weapon_mut(&mut self) -> Option<&mut Weapon> {
  88. match &mut self.item {
  89. ItemDetail::Weapon(weapon) => Some(weapon),
  90. _ => None
  91. }
  92. }
  93. pub fn as_client_bytes(&self) -> [u8; 16] {
  94. match &self.item {
  95. ItemDetail::Weapon(w) => w.as_bytes(),
  96. ItemDetail::Armor(a) => a.as_bytes(),
  97. ItemDetail::Shield(s) => s.as_bytes(),
  98. ItemDetail::Unit(u) => u.as_bytes(),
  99. ItemDetail::Tool(t) => t.as_individual_bytes(),
  100. ItemDetail::TechniqueDisk(d) => d.as_bytes(),
  101. ItemDetail::Mag(m) => m.as_bytes(),
  102. ItemDetail::ESWeapon(e) => e.as_bytes(),
  103. }
  104. }
  105. }
  106. #[derive(Clone, Debug)]
  107. pub struct StackedItemDetail {
  108. pub entity_ids: Vec<ItemEntityId>,
  109. pub tool: Tool,
  110. }
  111. impl StackedItemDetail {
  112. pub fn count(&self) -> usize {
  113. self.entity_ids.len()
  114. }
  115. }
  116. #[derive(Clone)]
  117. pub enum AddItemResult {
  118. NewItem,
  119. AddToStack,
  120. Meseta,
  121. }
  122. #[derive(Clone, Debug)]
  123. struct RoomGemItemIdCounter {
  124. inventory: [Arc<Mutex<u32>>; 4],
  125. bank: [Arc<Mutex<u32>>; 4],
  126. }
  127. impl Default for RoomGemItemIdCounter {
  128. fn default() -> RoomGemItemIdCounter {
  129. RoomGemItemIdCounter {
  130. inventory: core::array::from_fn(|gem| Arc::new(Mutex::new(((gem as u32) << 21) | 0x10000))),
  131. bank: core::array::from_fn(|gem| Arc::new(Mutex::new(((gem as u32) << 21) | 0x20000))),
  132. }
  133. }
  134. }
  135. impl RoomGemItemIdCounter {
  136. fn inventory(&self, area_client: &AreaClient) -> Arc<Mutex<u32>> {
  137. self.inventory[area_client.local_client.id() as usize].clone()
  138. }
  139. fn bank(&self, area_client: &AreaClient) -> Arc<Mutex<u32>> {
  140. self.bank[area_client.local_client.id() as usize].clone()
  141. }
  142. }
  143. #[derive(Clone, Debug)]
  144. pub struct ItemState {
  145. character_inventory: Arc<RwLock<HashMap<CharacterEntityId, RwLock<InventoryState>>>>,
  146. character_bank: Arc<RwLock<HashMap<CharacterEntityId, RwLock<BankState>>>>,
  147. character_room: Arc<RwLock<HashMap<CharacterEntityId, RoomId>>>,
  148. character_floor: Arc<RwLock<HashMap<CharacterEntityId, RwLock<LocalFloor>>>>,
  149. room_floor: Arc<RwLock<HashMap<RoomId, RwLock<SharedFloor>>>>,
  150. room_gem_item_ids: Arc<RwLock<HashMap<RoomId, RoomGemItemIdCounter>>>,
  151. room_item_id_counter: Arc<RwLock<u32>>,
  152. }
  153. impl Default for ItemState {
  154. fn default() -> ItemState {
  155. ItemState {
  156. character_inventory: Arc::new(RwLock::new(HashMap::new())),
  157. character_bank: Arc::new(RwLock::new(HashMap::new())),
  158. character_room: Arc::new(RwLock::new(HashMap::new())),
  159. character_floor: Arc::new(RwLock::new(HashMap::new())),
  160. room_floor: Arc::new(RwLock::new(HashMap::new())),
  161. room_gem_item_ids: Arc::new(RwLock::new(HashMap::new())),
  162. room_item_id_counter: Arc::new(RwLock::new(0x00810000)),
  163. }
  164. }
  165. }
  166. impl ItemState {
  167. pub async fn get_character_inventory(&self, character: &CharacterEntity) -> Result<InventoryState, anyhow::Error> {
  168. Ok(self.character_inventory
  169. .read()
  170. .await
  171. .get(&character.id)
  172. .ok_or_else(|| ItemStateError::NoCharacter(character.id))?
  173. .read()
  174. .await
  175. .clone())
  176. }
  177. pub async fn get_character_bank(&self, character: &CharacterEntity) -> Result<BankState, anyhow::Error> {
  178. Ok(self.character_bank
  179. .read()
  180. .await
  181. .get(&character.id)
  182. .ok_or_else(|| ItemStateError::NoCharacter(character.id))?
  183. .read()
  184. .await
  185. .clone())
  186. }
  187. }
  188. impl ItemState {
  189. async fn new_item_id(&mut self) -> Result<ClientItemId, anyhow::Error> {
  190. *self.room_item_id_counter
  191. .write()
  192. .await += 1;
  193. Ok(ClientItemId(*self.room_item_id_counter.read().await))
  194. }
  195. pub async fn load_character_inventory<EG: EntityGateway>(&mut self, entity_gateway: &mut EG, character: &CharacterEntity) -> Result<(), anyhow::Error> {
  196. let inventory = entity_gateway.get_character_inventory(&character.id).await?;
  197. let equipped = entity_gateway.get_character_equips(&character.id).await?;
  198. let inventory_items = inventory.items.into_iter()
  199. .map(|item| -> Result<InventoryItem, anyhow::Error> {
  200. Ok(match item {
  201. InventoryItemEntity::Individual(item) => {
  202. InventoryItem {
  203. item_id: ClientItemId(0),
  204. item: InventoryItemDetail::Individual(IndividualItemDetail {
  205. entity_id: item.id,
  206. item: item.item,
  207. }),
  208. }
  209. },
  210. InventoryItemEntity::Stacked(items) => {
  211. InventoryItem {
  212. item_id: ClientItemId(0),
  213. item: InventoryItemDetail::Stacked(StackedItemDetail {
  214. entity_ids: items.iter().map(|i| i.id).collect(),
  215. tool: items.get(0)
  216. .ok_or_else(|| ItemStateError::StackedItemError(items.clone()))?
  217. .item
  218. .clone()
  219. .as_tool()
  220. .ok_or_else(|| ItemStateError::StackedItemError(items.clone()))?
  221. })
  222. }
  223. },
  224. })
  225. })
  226. .collect::<Result<Vec<_>, anyhow::Error>>()?;
  227. let character_meseta = entity_gateway.get_character_meseta(&character.id).await?;
  228. let inventory_state = InventoryState {
  229. character_id: character.id,
  230. item_id_counter: Arc::new(Mutex::new(0)),
  231. inventory: Inventory::new(inventory_items),
  232. equipped,
  233. meseta: character_meseta,
  234. };
  235. self.character_inventory
  236. .write()
  237. .await
  238. .insert(character.id, RwLock::new(inventory_state));
  239. Ok(())
  240. }
  241. pub async fn load_character_bank<EG: EntityGateway>(&mut self, entity_gateway: &mut EG, character: &CharacterEntity, bank_identifier: BankIdentifier) -> Result<(), anyhow::Error> {
  242. let bank = entity_gateway.get_character_bank(&character.id, &bank_identifier).await?;
  243. let bank_items = bank.items
  244. .into_iter()
  245. .map(|item| {
  246. Ok(Reverse(match item {
  247. BankItemEntity::Individual(item) => {
  248. BankItemDetail::Individual(IndividualItemDetail {
  249. entity_id: item.id,
  250. item: item.item,
  251. })
  252. },
  253. BankItemEntity::Stacked(items) => {
  254. BankItemDetail::Stacked(StackedItemDetail {
  255. entity_ids: items.iter().map(|i| i.id).collect(),
  256. tool: items.get(0)
  257. .ok_or_else(|| ItemStateError::StackedItemError(items.clone()))?
  258. .item
  259. .clone()
  260. .as_tool()
  261. .ok_or_else(|| ItemStateError::StackedItemError(items.clone()))?
  262. })
  263. }
  264. }))
  265. })
  266. .collect::<Result<BinaryHeap<_>, anyhow::Error>>()?
  267. .into_iter()
  268. .map(|item| {
  269. let mut citem_state = self.clone();
  270. async move {
  271. Ok(BankItem {
  272. item_id: citem_state.new_item_id().await?,
  273. item: item.0,
  274. })
  275. }
  276. })
  277. .collect::<FuturesOrdered<_>>()
  278. .collect::<Vec<_>>()
  279. .await
  280. .into_iter()
  281. .collect::<Result<Vec<_>, anyhow::Error>>()?;
  282. let bank_meseta = entity_gateway.get_bank_meseta(&character.id, &bank_identifier).await?;
  283. let bank_state = BankState::new(character.id, bank_identifier, Bank::new(bank_items), bank_meseta);
  284. self.character_bank
  285. .write()
  286. .await
  287. .insert(character.id, RwLock::new(bank_state));
  288. Ok(())
  289. }
  290. pub async fn load_character<EG: EntityGateway>(&mut self, entity_gateway: &mut EG, character: &CharacterEntity) -> Result<(), anyhow::Error> {
  291. self.load_character_inventory(entity_gateway, character).await?;
  292. self.load_character_bank(entity_gateway, character, BankIdentifier::Character).await?;
  293. Ok(())
  294. }
  295. pub async fn add_character_to_room(&mut self, room_id: RoomId, character: &CharacterEntity, area_client: AreaClient) {
  296. let mut base_item_ids = self.room_gem_item_ids
  297. .write()
  298. .await;
  299. let base_item_ids = base_item_ids
  300. .entry(room_id)
  301. .or_insert_with(RoomGemItemIdCounter::default);
  302. self.character_inventory
  303. .read()
  304. .await
  305. .get(&character.id)
  306. .unwrap()
  307. .write()
  308. .await
  309. .initialize_item_ids(base_item_ids.inventory(&area_client).clone())
  310. .await;
  311. self.character_bank
  312. .read()
  313. .await
  314. .get(&character.id)
  315. .unwrap()
  316. .write()
  317. .await
  318. .initialize_item_ids(base_item_ids.bank(&area_client))
  319. .await;
  320. self.character_room
  321. .write()
  322. .await
  323. .insert(character.id, room_id);
  324. self.character_floor
  325. .write()
  326. .await
  327. .insert(character.id, RwLock::new(LocalFloor::default()));
  328. self.room_floor
  329. .write()
  330. .await
  331. .entry(room_id)
  332. .or_insert_with(Default::default);
  333. }
  334. pub async fn remove_character_from_room(&mut self, character: &CharacterEntity) {
  335. self.character_inventory
  336. .write()
  337. .await
  338. .remove(&character.id);
  339. self.character_floor
  340. .write()
  341. .await
  342. .remove(&character.id);
  343. let removed = {
  344. self.character_room.write().await.remove(&character.id)
  345. };
  346. if let Some(room) = removed.as_ref() {
  347. // TODO: this looks wrong, .all(r != room) maybe?
  348. if self.character_room.read().await.iter().any(|(_, r)| r == room) {
  349. self.room_floor
  350. .write()
  351. .await
  352. .remove(room);
  353. }
  354. }
  355. }
  356. pub async fn get_floor_item(&self, character_id: &CharacterEntityId, item_id: &ClientItemId) -> Result<(FloorItem, FloorType), anyhow::Error> {
  357. let local_floors = self.character_floor
  358. .read()
  359. .await;
  360. let local_floor = local_floors
  361. .get(character_id)
  362. .ok_or_else(|| ItemStateError::NoCharacter(*character_id))?
  363. .read()
  364. .await;
  365. let rooms = self.character_room
  366. .read()
  367. .await;
  368. let room = rooms
  369. .get(character_id)
  370. .ok_or_else(||ItemStateError::NoCharacter(*character_id))?;
  371. let shared_floors = self.room_floor
  372. .read()
  373. .await;
  374. let shared_floor = shared_floors
  375. .get(room)
  376. .ok_or_else(||ItemStateError::NoCharacter(*character_id))?
  377. .read()
  378. .await;
  379. local_floor.0
  380. .iter()
  381. .find(|item| item.item_id == *item_id)
  382. .map(|item| (item.clone(), FloorType::Local))
  383. .or_else(|| {
  384. shared_floor.0
  385. .iter()
  386. .find(|item| item.item_id == *item_id)
  387. .map(|item| (item.clone(), FloorType::Shared))
  388. })
  389. .ok_or_else(|| ItemStateError::NoFloorItem(*item_id))
  390. .with_context(|| format!("character {character_id}\nlocal floors: {local_floors:#?}\nshared floors: {shared_floors:#?}"))
  391. }
  392. }
  393. #[derive(Default, Clone)]
  394. struct ProxiedItemState {
  395. character_inventory: Arc<Mutex<HashMap<CharacterEntityId, InventoryState>>>,
  396. character_bank: Arc<Mutex<HashMap<CharacterEntityId, BankState>>>,
  397. //character_room: HashMap<CharacterEntityId, RoomId>,
  398. character_floor: Arc<Mutex<HashMap<CharacterEntityId, LocalFloor>>>,
  399. room_floor: Arc<Mutex<HashMap<RoomId, SharedFloor>>>,
  400. }
  401. #[derive(Clone)]
  402. pub struct ItemStateProxy {
  403. item_state: ItemState,
  404. proxied_state: ProxiedItemState,
  405. }
  406. impl ItemStateProxy {
  407. pub async fn commit(self) {
  408. async fn copy_back<K, V>(master: &Arc<RwLock<HashMap<K, RwLock<V>>>>,
  409. proxy: Arc<Mutex<HashMap<K, V>>>)
  410. where
  411. K: Eq + std::hash::Hash,
  412. V: Clone,
  413. {
  414. for (key, value) in proxy.lock().await.iter() {
  415. if let Some(element) = master
  416. .read()
  417. .await
  418. .get(key) {
  419. *element
  420. .write()
  421. .await = value.clone();
  422. }
  423. }
  424. }
  425. copy_back(&self.item_state.character_inventory, self.proxied_state.character_inventory).await;
  426. copy_back(&self.item_state.character_bank, self.proxied_state.character_bank).await;
  427. //copy_back(self.item_state.character_room, self.proxied_state.character_room).await;
  428. copy_back(&self.item_state.character_floor, self.proxied_state.character_floor).await;
  429. copy_back(&self.item_state.room_floor, self.proxied_state.room_floor).await;
  430. }
  431. }
  432. async fn get_or_clone<K, V>(master: &Arc<RwLock<HashMap<K, RwLock<V>>>>,
  433. proxy: &Arc<Mutex<HashMap<K, V>>>,
  434. key: K,
  435. err: fn(K) -> ItemStateError) -> Result<V, anyhow::Error>
  436. where
  437. K: Eq + std::hash::Hash + Copy,
  438. V: Clone
  439. {
  440. let existing_element = master
  441. .read()
  442. .await
  443. .get(&key)
  444. .ok_or_else(|| err(key))?
  445. .read()
  446. .await
  447. .clone();
  448. Ok(proxy
  449. .lock()
  450. .await
  451. .entry(key)
  452. .or_insert_with(|| existing_element)
  453. .clone())
  454. }
  455. impl ItemStateProxy {
  456. pub fn new(item_state: ItemState) -> Self {
  457. ItemStateProxy {
  458. item_state,
  459. proxied_state: Default::default(),
  460. }
  461. }
  462. pub async fn inventory(&mut self, character_id: &CharacterEntityId) -> Result<InventoryState, anyhow::Error> {
  463. get_or_clone(&self.item_state.character_inventory,
  464. &self.proxied_state.character_inventory,
  465. *character_id,
  466. ItemStateError::NoCharacter).await
  467. }
  468. pub async fn set_inventory(&mut self, inventory: InventoryState) {
  469. self.proxied_state.character_inventory.lock().await.insert(inventory.character_id, inventory);
  470. }
  471. pub async fn bank(&mut self, character_id: &CharacterEntityId) -> Result<BankState, anyhow::Error> {
  472. get_or_clone(&self.item_state.character_bank,
  473. &self.proxied_state.character_bank,
  474. *character_id,
  475. ItemStateError::NoCharacter).await
  476. }
  477. pub async fn set_bank(&mut self, bank: BankState) {
  478. self.proxied_state.character_bank.lock().await.insert(bank.character_id, bank);
  479. }
  480. pub async fn floor(&mut self, character_id: &CharacterEntityId) -> Result<FloorState, anyhow::Error> {
  481. let room_id = *self.item_state.character_room.read().await.get(character_id)
  482. .ok_or_else(|| anyhow::Error::from(ItemStateError::NoCharacter(*character_id)))
  483. .with_context(|| format!("character {character_id}\nrooms: {:#?}", self.item_state.character_room))?;
  484. Ok(FloorState {
  485. character_id: *character_id,
  486. local: get_or_clone(&self.item_state.character_floor, &self.proxied_state.character_floor, *character_id, ItemStateError::NoCharacter).await
  487. .with_context(|| format!("no local_floor state: {character_id:?} {:#?}\nproxy: {:#?}", self.item_state.character_floor, self.proxied_state.character_floor))?,
  488. shared: get_or_clone(&self.item_state.room_floor, &self.proxied_state.room_floor, room_id, ItemStateError::NoRoom).await
  489. .with_context(|| format!("no share_floor state: {character_id:?} {:#?}\nproxy: {:#?}", self.item_state.room_floor, self.proxied_state.room_floor))?,
  490. })
  491. }
  492. pub async fn set_floor(&mut self, floor: FloorState) {
  493. let room_id = *self.item_state.character_room.read().await.get(&floor.character_id)
  494. .ok_or_else(|| anyhow::Error::from(ItemStateError::NoCharacter(floor.character_id)))
  495. .with_context(|| format!("character {}\nrooms: {:#?}", floor.character_id, self.item_state.character_room)).unwrap();
  496. self.proxied_state.character_floor.lock().await.insert(floor.character_id, floor.local);
  497. self.proxied_state.room_floor.lock().await.insert(room_id, floor.shared);
  498. }
  499. pub async fn new_item_id(&mut self) -> Result<ClientItemId, anyhow::Error> {
  500. self.item_state.new_item_id().await
  501. }
  502. }