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.

176 lines
7.5 KiB

4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
  1. use std::io::{Cursor, Read, Seek, SeekFrom};
  2. use libpso::packet::ship::*;
  3. use crate::common::serverstate::ClientId;
  4. use crate::ship::ship::{SendShipPacket, ShipError, Clients, Rooms};
  5. use crate::ship::quests::QuestList;
  6. use crate::ship::location::{ClientLocation, ClientLocationError};
  7. use crate::ship::packet::builder::quest;
  8. use libpso::util::array_to_utf8;
  9. // TOOD: enum
  10. enum QuestFileType {
  11. Bin,
  12. Dat
  13. }
  14. fn parse_filename(filename_bytes: &[u8; 16]) -> Result<(u16, u16, QuestFileType), ShipError> {
  15. let filename = array_to_utf8(*filename_bytes).map_err(|_| ShipError::InvalidQuestFilename("NOT UTF8".to_string()))?;
  16. let (filename, suffix) = {
  17. let mut s = filename.splitn(2, '.');
  18. (s.next().ok_or_else(|| ShipError::InvalidQuestFilename(filename.to_owned()))?,
  19. s.next().ok_or_else(|| ShipError::InvalidQuestFilename(filename.to_owned()))?)
  20. };
  21. let datatype = match suffix {
  22. "bin" => QuestFileType::Bin,
  23. "dat" => QuestFileType::Dat,
  24. _ => return Err(ShipError::InvalidQuestFilename(filename.to_owned()))
  25. };
  26. let (category, quest) = {
  27. let mut s = filename.splitn(2, '-');
  28. (s.next().and_then(|k| k.parse().ok()).ok_or_else(|| ShipError::InvalidQuestFilename(filename.to_owned()))?,
  29. s.next().and_then(|k| k.parse().ok()).ok_or_else(|| ShipError::InvalidQuestFilename(filename.to_owned()))?)
  30. };
  31. Ok((category, quest, datatype))
  32. }
  33. pub fn send_quest_category_list(id: ClientId, quests: &QuestList) -> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, ShipError> {
  34. let qcl = quest::quest_category_list(quests);
  35. Ok(Box::new(vec![(id, SendShipPacket::QuestCategoryList(qcl))].into_iter()))
  36. }
  37. pub fn select_quest_category(id: ClientId, menuselect: &MenuSelect, quests: &QuestList) -> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, ShipError> {
  38. let (_, category_quests) = quests.iter()
  39. .nth(menuselect.item as usize)
  40. .ok_or(ShipError::InvalidQuestCategory(menuselect.item))?;
  41. let ql = quest::quest_list(menuselect.item, category_quests);
  42. Ok(Box::new(vec![(id, SendShipPacket::QuestOptionList(ql))].into_iter()))
  43. }
  44. pub fn quest_detail(id: ClientId, questdetailrequest: &QuestDetailRequest, quests: &QuestList) -> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, ShipError> {
  45. let (_, category_quests) = quests.iter()
  46. .nth(questdetailrequest.category as usize)
  47. .ok_or(ShipError::InvalidQuestCategory(questdetailrequest.category as u32))?;
  48. let quest = category_quests.iter()
  49. .find(|q| {
  50. q.id == questdetailrequest.quest as u16
  51. }).ok_or(ShipError::InvalidQuest(questdetailrequest.quest as u32))?;
  52. let qd = quest::quest_detail(quest);
  53. Ok(Box::new(vec![(id, SendShipPacket::QuestDetail(qd))].into_iter()))
  54. }
  55. pub fn player_chose_quest(id: ClientId, questmenuselect: &QuestMenuSelect, quests: &QuestList, clients: &mut Clients, client_location: &ClientLocation, rooms: &mut Rooms)
  56. -> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, ShipError> {
  57. let (_, category_quests) = quests.iter()
  58. .nth(questmenuselect.category as usize)
  59. .ok_or(ShipError::InvalidQuestCategory(questmenuselect.category as u32))?;
  60. let quest = category_quests.iter()
  61. .find(|q| {
  62. q.id == questmenuselect.quest as u16
  63. }).ok_or(ShipError::InvalidQuest(questmenuselect.quest as u32))?;
  64. let room_id = client_location.get_room(id).map_err(|err| -> ClientLocationError { err.into() })?;
  65. let room = rooms.get_mut(room_id.0)
  66. .ok_or_else(|| ShipError::InvalidRoom(room_id.0 as u32))?.as_mut()
  67. .ok_or_else(|| ShipError::InvalidRoom(room_id.0 as u32))?;
  68. room.maps.set_quest_data(quest.enemies.clone(), quest.objects.clone(), &room.rare_monster_table);
  69. room.map_areas = quest.map_areas.clone();
  70. let bin = quest::quest_header(questmenuselect, &quest.bin_blob, "bin");
  71. let dat = quest::quest_header(questmenuselect, &quest.dat_blob, "dat");
  72. let area_clients = client_location.get_all_clients_by_client(id).map_err(|err| -> ClientLocationError { err.into() })?;
  73. area_clients.iter().for_each(|c| {
  74. if let Some(client) = clients.get_mut(&c.client) {
  75. client.done_loading_quest = false;
  76. }
  77. });
  78. Ok(Box::new(area_clients.into_iter().map(move |c| {
  79. vec![(c.client, SendShipPacket::QuestHeader(bin.clone())), (c.client, SendShipPacket::QuestHeader(dat.clone()))]
  80. }).flatten()))
  81. }
  82. pub fn quest_file_request(id: ClientId, quest_file_request: &QuestFileRequest, quests: &QuestList) -> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, ShipError> {
  83. let (category_id, quest_id, datatype) = parse_filename(&quest_file_request.filename)?;
  84. let (_, category_quests) = quests.iter()
  85. .nth(category_id as usize)
  86. .ok_or(ShipError::InvalidQuestCategory(category_id as u32))?;
  87. let quest = category_quests.iter()
  88. .find(|q| {
  89. q.id == quest_id as u16
  90. }).ok_or(ShipError::InvalidQuest(quest_id as u32))?;
  91. let blob = match datatype {
  92. QuestFileType::Bin => &quest.bin_blob,
  93. QuestFileType::Dat => &quest.dat_blob,
  94. };
  95. let mut blob_cursor = Cursor::new(blob);
  96. let mut subblob = [0u8; 0x400];
  97. let blob_length = blob_cursor.read(&mut subblob)?;
  98. let qc = quest::quest_chunk(0, quest_file_request.filename, subblob, blob_length);
  99. Ok(Box::new(vec![(id, SendShipPacket::QuestChunk(qc))].into_iter()))
  100. }
  101. pub fn quest_chunk_ack(id: ClientId, quest_chunk_ack: &QuestChunkAck, quests: &QuestList) -> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, ShipError> {
  102. let (category_id, quest_id, datatype) = parse_filename(&quest_chunk_ack.filename)?;
  103. let (_, category_quests) = quests.iter()
  104. .nth(category_id as usize)
  105. .ok_or(ShipError::InvalidQuestCategory(category_id as u32))?;
  106. let quest = category_quests.iter()
  107. .find(|q| {
  108. q.id == quest_id
  109. }).ok_or(ShipError::InvalidQuest(quest_id as u32))?;
  110. let blob = match datatype {
  111. QuestFileType::Bin => &quest.bin_blob,
  112. QuestFileType::Dat => &quest.dat_blob,
  113. };
  114. let mut blob_cursor = Cursor::new(blob);
  115. blob_cursor.seek(SeekFrom::Start((quest_chunk_ack.chunk_num as u64 + 1) * 0x400))?;
  116. let mut subblob = [0u8; 0x400];
  117. let blob_length = blob_cursor.read(&mut subblob)?;
  118. if blob_length == 0 {
  119. return Ok(Box::new(None.into_iter()));
  120. }
  121. let qc = quest::quest_chunk(quest_chunk_ack.chunk_num + 1, quest_chunk_ack.filename, subblob, blob_length);
  122. Ok(Box::new(vec![(id, SendShipPacket::QuestChunk(qc))].into_iter()))
  123. }
  124. pub fn done_loading_quest(id: ClientId, clients: &mut Clients, client_location: &ClientLocation)
  125. -> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, ShipError> {
  126. let client = clients.get_mut(&id).ok_or(ShipError::ClientNotFound(id))?;
  127. client.done_loading_quest = true;
  128. let area_clients = client_location.get_all_clients_by_client(id).map_err(|err| -> ClientLocationError { err.into() })?;
  129. let all_loaded = area_clients.iter().all(|c| {
  130. clients.get(&c.client)
  131. .map(|client| {
  132. client.done_loading_quest
  133. })
  134. .unwrap_or(false)
  135. });
  136. if all_loaded {
  137. Ok(Box::new(area_clients.into_iter().map(|c| {
  138. (c.client, SendShipPacket::DoneLoadingQuest(DoneLoadingQuest {}))
  139. })))
  140. }
  141. else {
  142. Ok(Box::new(None.into_iter()))
  143. }
  144. }