1use crate::prelude::*;
12
13visible_slotmap_key!{ FastSplitId(b'F') }
14
15impl FastSplitId {
16 pub fn new_placeholder() -> Option<FastSplitId> { Some(default()) }
17}
18
19#[derive(Default,Debug,Serialize,Deserialize)]
20pub struct IFastSplits {
21 table: DenseSlotMap<FastSplitId, Record>
22}
23
24use crate::*; #[derive(Serialize,Deserialize,Debug)]
27struct Piece {
28 #[serde(skip)]
42 ipc: Option<Arc<IPiece>>,
43}
44
45#[derive(Debug,Serialize,Deserialize)]
46pub struct Record {
47 ipc: Arc<IPiece>,
48}
49
50impl Piece {
51 fn inner(&self) -> &dyn PieceTrait {
52 self.ipc
53 .as_ref()
54 .expect("attempted to invoke unresolved fastsplit::Piece")
55 .p.direct_trait_access()
56 }
57}
58
59impl_via_ambassador!{
60 #[dyn_upcast]
61 impl OutlineTrait for Piece { inner() }
62
63 #[dyn_upcast]
64 impl PieceBaseTrait for Piece { inner() }
65
66 #[typetag::serde(name="FastSplit")]
67 impl PieceTrait for Piece { inner() }
68}
69
70impl Record {
71 fn dummy() -> Record {
72 let p = Piece { ipc: None };
73 let p = IPieceTraitObj::new(Box::new(p) as _);
74 let ipc = IPiece { p, occilk: None, special: default() };
75 Record { ipc: Arc::new(ipc) }
76 }
77}
78
79impl InstanceGuard<'_> {
80 #[throws(ApiPieceOpError)]
82 pub fn fastsplit_split<I>(
83 &mut self, player: PlayerId,
84 tpiece: PieceId, _: ShowUnocculted, tpc_new_z: ShouldSetZLevel,
85 implementation: I
86 ) -> UpdateFromOpComplex
87 where I: FnOnce(&IOccults, &GOccults, &GPlayer,
88 &mut GPiece, &IPiece,
89 &mut GPiece)
90 -> Result<UpdateFromOpComplex, ApiPieceOpError>
91 {
92 let modperm = self.modify_pieces_not_necessarily_saving_aux();
99
100 let ig = &mut **self;
101 let gpl = ig.gs.players.byid(player)?;
102 let tgpc = ig.gs.pieces.byid_mut(tpiece)?;
103 let tipc = ig.ipieces.get(tpiece).ok_or(Ia::PieceGone)?;
104
105 let fs_record = (||{
106 let fsid = tgpc.fastsplit?;
107 ig.ifastsplits.table.get(fsid)
108 })().ok_or_else(|| internal_logic_error("fastsplit on non-fastsplit"))?;
109
110 let npc_z = ZLevel { z: tgpc.zlevel.z.clone(), zg: ig.gs.gen };
115 if ! (tpc_new_z.inspect() > &npc_z) {
116 throw!(Ia::BadPieceStateForOperation);
117 }
118
119 let mut ngpc = GPiece {
120 pos: tgpc.pos,
121 face: tgpc.face,
122 held: None,
123 zlevel: npc_z,
124 pinned: tgpc.pinned,
125 occult: default(),
126 angle: tgpc.angle,
127 gen: ig.gs.gen,
128 lastclient: default(),
129 last_released: default(),
130 gen_before_lastclient: Generation(0),
131 xdata: default(),
132 moveable: tgpc.moveable,
133 rotateable: tgpc.rotateable,
134 fastsplit: tgpc.fastsplit,
135 };
136
137 let (t_pu, t_unprepared) = implementation(
138 &ig.ioccults, &ig.gs.occults, gpl,
139 tgpc, tipc,
140 &mut ngpc
141 )?;
142
143 let ipc_from_record = fs_record.ipc.clone();
144
145 tpc_new_z.implement(tgpc);
150 (||{
151 let nipc = IFastSplits::make_ipc(&mut ig.ioccults.ilks, ipc_from_record);
152 let npiece = ig.gs.pieces.as_mut(modperm).insert(ngpc);
153 ig.ipieces.as_mut(modperm).insert(npiece, nipc);
154
155 let ((), r_unprepared) = ToRecalculate::with(|mut to_permute| {
157 let () = recalculate_occultation_general(
158 &mut ig.gs.gen.unique_gen(),
159 &mut ig.gs.players, &mut ig.gs.pieces,
160 &mut ig.gs.occults, &ig.ipieces, &ig.ioccults,
161 &mut to_permute, npiece,
162 || (),
167 (),
168 |_old,_new,_desc| (),
169 |_puopp, ()| (),
170 ).unwrap_or_else(|e| {
171 error!("failed to recalculate after piece split! {:?}", &e);
173 });
174 ((), to_permute.implement(ig))
175 });
176
177 let n_unprepared = vec![(
178 npiece,
179 PUOs::Simple(PUO::Insert(())),
180 )].into_unprepared(None);
181
182 let unprepared = chain!(
183 n_unprepared,
184 r_unprepared,
185 t_unprepared,
186 ).collect();
187
188 (t_pu, unprepared)
189 })()}
191
192 #[throws(IE)]
193 pub fn fastsplit_delete(&mut self,
194 piece: PieceId,
195 #[allow(clippy::ptr_arg)]
196 _proof_that_caller_handles_logging: &Vec<LogEntry>)
197 -> (PieceUpdateOp<(),()>, UnpreparedUpdates)
198 {
199 let missing_e = || internal_error_bydebug(&piece);
200
201 let modperm = self.modify_pieces_not_necessarily_saving_aux();
203
204 let gpc = self.gs.pieces.get(piece).ok_or_else(missing_e)?;
205 let ipc = self.ipieces.get(piece).ok_or_else(missing_e)?;
206
207 let _p: &Piece = ipc.p.direct_trait_access().downcast_piece()?;
212
213 let _fsid: &FastSplitId = gpc.fastsplit.as_ref()
214 .ok_or_else(|| internal_error_bydebug(gpc))?;
215 match ToRecalculate::with(|mut to_permute| {
222 let r = self.delete_piece(modperm,&mut to_permute, piece,|_,_,_,_|());
223 (r, to_permute.implement(self))
224 }) {
225 (Err(e), uu_p) => {
226 PrepareUpdatesBuffer::only_unprepared(self, uu_p);
227 throw!(e);
228 },
229 (Ok(((), puo, uu_d)), uu_p) => {
230 (puo, chain!(uu_d, uu_p).collect())
231 },
232 }
233 }
234}
235
236impl IFastSplits {
237 pub fn new_original(&mut self, ilks: &mut OccultIlks, ipc: IPiece)
241 -> (IPiece, FastSplitId) {
242 let ipc = Arc::new(ipc);
243 let record = Record { ipc: ipc.clone() };
244 let fsid = self.table.insert(record);
245 (Self::make_ipc(ilks, ipc), fsid)
246 }
247
248 #[throws(as Option)]
253 pub fn recover_ipc(&self, ilks: &mut OccultIlks, fsid: FastSplitId)
254 -> IPiece {
255 let record = self.table.get(fsid)?;
256 Self::make_ipc(ilks, record.ipc.clone())
257 }
258
259 fn make_ipc(ilks: &mut OccultIlks, ipc: Arc<IPiece>) -> IPiece {
261 let occilk = ipc.occilk.as_ref().map(|i| ilks.clone_iilk(i));
262 let special = ipc.special.clone();
263 let piece = Box::new(Piece { ipc: Some(ipc) });
264 IPiece {
265 p: IPieceTraitObj::new(piece as _),
266 occilk, special,
267 }
268 }
269
270 pub fn cleanup(&mut self, ilks: &mut OccultIlks) {
272 self.table.retain(|_fsid, record| {
273 if Arc::strong_count(&record.ipc) != 1 { return true; }
274 let removed = mem::replace(record, Record::dummy());
275 if_let!{
276 Ok(ipc) = Arc::try_unwrap(removed.ipc);
277 Err(busy) => {
278 *record = Record { ipc: busy };
281 return true;
282 }
283 };
284 if let Some(i) = ipc.occilk { ilks.dispose_iilk(i); }
285 false
286 })
287 }
288}
289
290#[ext(pub)]
291impl<'r> &'r dyn PieceTrait {
292 #[throws(PieceTraitDowncastFailed<'r>)]
293 fn downcast_piece_fastsplit<P: PieceTrait>(self) -> &'r P {
294 self.downcast_piece::<Piece>()?
295 .ipc.as_ref().ok_or_else(
296 || PieceTraitDowncastFailed { p: self, why: "unresolved fastsplit"})?
297 .p.direct_trait_access() .downcast_piece::<P>()?
299 }
300}