1use alloc::collections::{BTreeSet, VecDeque};
4use alloc::rc::{Rc, Weak};
5use alloc::borrow::ToOwned;
6use alloc::vec::Vec;
7
8use core::marker::PhantomData;
9use core::{iter, fmt, mem};
10use core::ops::Deref;
11use core::cell::Ref;
12
13use rand::distr::uniform::{SampleUniform, SampleRange};
14use checked_float::{FloatChecker, CheckedFloat};
15
16#[cfg(feature = "serde")]
17use serde::{Serialize, Deserialize};
18
19use crate::*;
20use crate::gc::*;
21use crate::json::*;
22use crate::real_time::*;
23use crate::bytecode::*;
24use crate::process::*;
25use crate::compact_str::*;
26use crate::vecmap::*;
27
28#[derive(Debug)]
30pub enum NumberError {
31 Nan,
32 Infinity,
33}
34
35pub struct NumberChecker;
37impl FloatChecker<f64> for NumberChecker {
38 type Error = NumberError;
39 fn check(value: f64) -> Result<f64, Self::Error> {
40 if value.is_nan() { return Err(NumberError::Nan); }
41 if value.is_infinite() { return Err(NumberError::Infinity); }
42 Ok(if value.to_bits() == 0x8000000000000000 { 0.0 } else { value }) }
44}
45
46pub type Number = CheckedFloat<f64, NumberChecker>;
48
49const INLINE_SIZE: usize = {
50 #[cfg(target_pointer_width = "64")] { 14 }
51 #[cfg(not(target_pointer_width = "64"))] { 10 }
52};
53
54pub type Text = our_string::OurString<our_string::comrades::RcBytes, INLINE_SIZE>;
58
59#[derive(Debug)]
60pub enum FromAstError<'a> {
61 BadNumber { error: NumberError },
62 BadKeycode { key: CompactString },
63 UnsupportedEvent { kind: &'a ast::HatKind },
64 CompileError { error: CompileError<'a> },
65}
66impl From<NumberError> for FromAstError<'_> { fn from(error: NumberError) -> Self { Self::BadNumber { error } } }
67impl<'a> From<CompileError<'a>> for FromAstError<'a> { fn from(error: CompileError<'a>) -> Self { Self::CompileError { error } } }
68
69#[derive(Educe)]
71#[educe(Debug, Clone, Copy, PartialEq, Eq)]
72pub enum Type<C: CustomTypes<S>, S: System<C>> {
73 Bool, Number, Text, Image, Audio, List, Closure, Entity, Native(<C::NativeValue as GetType>::Output),
74}
75
76#[derive(Educe)]
78#[educe(Debug)]
79pub struct ConversionError<C: CustomTypes<S>, S: System<C>> {
80 pub got: Type<C, S>,
81 pub expected: Type<C, S>,
82}
83
84#[derive(Educe)]
86#[educe(Debug)]
87pub enum ErrorCause<C: CustomTypes<S>, S: System<C>> {
88 UndefinedVariable { name: CompactString },
90 UndefinedCostume { name: CompactString },
92 UndefinedSound { name: CompactString },
94 UndefinedEntity { name: CompactString },
96 UpvarAtRoot,
98 ConversionError { got: Type<C, S>, expected: Type<C, S> },
100 VariadicConversionError { got: Type<C, S>, expected: Type<C, S> },
102 Incomparable { left: Type<C, S>, right: Type<C, S> },
104 EmptyList,
106 InvalidListLength { expected: usize, got: usize },
108 IndexOutOfBounds { index: i64, len: usize },
110 IndexNotInteger { index: f64 },
112 NoteNotInteger { note: f64 },
114 NoteNotMidi { note: CompactString },
116 InvalidSize { value: f64 },
118 InvalidUnicode { value: f64 },
120 CallDepthLimit { limit: usize },
122 ClosureArgCount { expected: usize, got: usize },
124 CyclicValue,
126 NotCsv { value: CompactString },
128 NotJson { value: CompactString },
130 ToSimpleError { error: ToSimpleError<C, S> },
132 IntoJsonError { error: IntoJsonError<C, S> },
134 FromJsonError { error: FromJsonError },
136 FromNetsBloxJsonError { error: FromNetsBloxJsonError },
138 NumberError { error: NumberError },
140 NotSupported { feature: Feature },
142 Promoted { error: CompactString },
144 Custom { msg: CompactString },
146}
147impl<C: CustomTypes<S>, S: System<C>> From<ConversionError<C, S>> for ErrorCause<C, S> { fn from(e: ConversionError<C, S>) -> Self { Self::ConversionError { got: e.got, expected: e.expected } } }
148impl<C: CustomTypes<S>, S: System<C>> From<ToSimpleError<C, S>> for ErrorCause<C, S> { fn from(error: ToSimpleError<C, S>) -> Self { Self::ToSimpleError { error } } }
149impl<C: CustomTypes<S>, S: System<C>> From<IntoJsonError<C, S>> for ErrorCause<C, S> { fn from(error: IntoJsonError<C, S>) -> Self { Self::IntoJsonError { error } } }
150impl<C: CustomTypes<S>, S: System<C>> From<FromJsonError> for ErrorCause<C, S> { fn from(error: FromJsonError) -> Self { Self::FromJsonError { error } } }
151impl<C: CustomTypes<S>, S: System<C>> From<FromNetsBloxJsonError> for ErrorCause<C, S> { fn from(error: FromNetsBloxJsonError) -> Self { Self::FromNetsBloxJsonError { error } } }
152impl<C: CustomTypes<S>, S: System<C>> From<NumberError> for ErrorCause<C, S> { fn from(error: NumberError) -> Self { Self::NumberError { error } } }
153
154#[derive(Clone, Copy, PartialEq, Eq, Debug)]
156pub struct Color { pub r: u8, pub g: u8, pub b: u8, pub a: u8 }
157impl Color {
158 pub fn from_hsva(mut h: f32, mut s: f32, mut v: f32, mut a: f32) -> Self {
159 h = util::modulus(h, 360.0);
160 s = s.clamp(0.0, 1.0);
161 v = v.clamp(0.0, 1.0);
162 a = a.clamp(0.0, 1.0);
163
164 let c = v * s;
165 let hp = h / 60.0;
166 let x = c * (1.0 - libm::fabsf(hp % 2.0 - 1.0));
167 let m = v - c;
168
169 let (r, g, b) = match hp as usize {
170 0 | 6 => (c, x, 0.0), 1 => (x, c, 0.0),
172 2 => (0.0, c, x),
173 3 => (0.0, x, c),
174 4 => (x, 0.0, c),
175 5 => (c, 0.0, x),
176 _ => unreachable!(),
177 };
178
179 fn f(x: f32) -> u8 { libm::roundf(x * 255.0) as u8 }
180
181 Self { r: f(r + m), g: f(g + m), b: f(b + m), a: f(a) }
182 }
183 pub fn to_hsva(self) -> (f32, f32, f32, f32) {
184 fn f(x: u8) -> f32 { x as f32 / 255.0 }
185
186 let vals = [self.r, self.g, self.b];
187 let (c_max_i, c_max) = vals.iter().copied().enumerate().max_by_key(|x| x.1).map(|(i, v)| (i, f(v))).unwrap();
188 let c_min = vals.iter().copied().min().map(f).unwrap();
189 let delta = c_max - c_min;
190
191 let h = if delta == 0.0 { 0.0 } else {
192 match c_max_i {
193 0 => 60.0 * util::modulus((f(self.g) - f(self.b)) / delta, 6.0),
194 1 => 60.0 * ((f(self.b) - f(self.r)) / delta + 2.0),
195 2 => 60.0 * ((f(self.r) - f(self.g)) / delta + 4.0),
196 _ => unreachable!(),
197 }
198 };
199 let s = if c_max == 0.0 { 0.0 } else { delta / c_max };
200 let v = c_max;
201 let a = f(self.a);
202
203 (h, s, v, a)
204 }
205}
206
207#[test]
208fn test_color_hsv_to_rgb() {
209 assert_eq!(Color::from_hsva(0.0, 0.0, 0.0, 1.0), Color { r: 0x00, g: 0x00, b: 0x00, a: 0xFF });
210 assert_eq!(Color::from_hsva(0.0, -0.5, 0.0, 1.0), Color { r: 0x00, g: 0x00, b: 0x00, a: 0xFF });
211 assert_eq!(Color::from_hsva(0.0, 0.07, 0.36, 1.0), Color { r: 0x5C, g: 0x55, b: 0x55, a: 0xFF });
212 assert_eq!(Color::from_hsva(0.0, 1.0, 0.36, 1.0), Color { r: 92, g: 0, b: 0, a: 0xFF });
213 assert_eq!(Color::from_hsva(0.0, 1.5, 0.36, 1.0), Color { r: 92, g: 0, b: 0, a: 0xFF });
214 assert_eq!(Color::from_hsva(0.0, 1.3, 0.36, 1.0), Color { r: 92, g: 0, b: 0, a: 0xFF });
215 assert_eq!(Color::from_hsva(0.0, 14.5, 0.36, 1.0), Color { r: 92, g: 0, b: 0, a: 0xFF });
216 assert_eq!(Color::from_hsva(0.0, 0.0, 0.36, 1.0), Color { r: 92, g: 92, b: 92, a: 0xFF });
217 assert_eq!(Color::from_hsva(0.0, -2.4, 0.36, 1.0), Color { r: 92, g: 92, b: 92, a: 0xFF });
218 assert_eq!(Color::from_hsva(0.0, -0.4, 0.36, 1.0), Color { r: 92, g: 92, b: 92, a: 0xFF });
219 assert_eq!(Color::from_hsva(360.0, 0.07, 0.36, 1.0), Color { r: 0x5C, g: 0x55, b: 0x55, a: 0xFF });
220 assert_eq!(Color::from_hsva(-360.0, 0.07, 0.36, 1.0), Color { r: 0x5C, g: 0x55, b: 0x55, a: 0xFF });
221 assert_eq!(Color::from_hsva(25.0, 0.5, 0.25, 1.0), Color { r: 0x40, g: 0x2D, b: 0x20, a: 0xFF });
222 assert_eq!(Color::from_hsva(25.0 + 360.0, 0.5, 0.25, 1.0), Color { r: 0x40, g: 0x2D, b: 0x20, a: 0xFF });
223 assert_eq!(Color::from_hsva(25.0 - 360.0, 0.5, 0.25, 1.0), Color { r: 0x40, g: 0x2D, b: 0x20, a: 0xFF });
224 assert_eq!(Color::from_hsva(49.0, 0.75, 0.12, 1.0), Color { r: 0x1F, g: 0x1A, b: 0x08, a: 0xFF });
225 assert_eq!(Color::from_hsva(65.0, 0.12, 0.87, 1.0), Color { r: 0xDC, g: 0xDE, b: 0xC3, a: 0xFF });
226 assert_eq!(Color::from_hsva(65.0, 0.12, 1.0, 1.0), Color { r: 252, g: 255, b: 224, a: 0xFF });
227 assert_eq!(Color::from_hsva(65.0, 0.12, 1.4, 1.0), Color { r: 252, g: 255, b: 224, a: 0xFF });
228 assert_eq!(Color::from_hsva(90.0, 0.22, 0.55, 1.0), Color { r: 0x7D, g: 0x8C, b: 0x6D, a: 0xFF });
229 assert_eq!(Color::from_hsva(90.0 + 360.0, 0.22, 0.55, 1.0), Color { r: 0x7D, g: 0x8C, b: 0x6D, a: 0xFF });
230 assert_eq!(Color::from_hsva(90.0, 0.22, 0.55, 1.0), Color { r: 0x7D, g: 0x8C, b: 0x6D, a: 0xFF });
231 assert_eq!(Color::from_hsva(120.0, 0.26, 0.91, 1.0), Color { r: 0xAC, g: 0xE8, b: 0xAC, a: 0xFF });
232 assert_eq!(Color::from_hsva(175.0, 0.97, 0.04, 1.0), Color { r: 0x00, g: 0x0A, b: 0x09, a: 0xFF });
233 assert_eq!(Color::from_hsva(175.0 + 360.0, 0.97, 0.04, 1.0), Color { r: 0x00, g: 0x0A, b: 0x09, a: 0xFF });
234 assert_eq!(Color::from_hsva(175.0 - 360.0, 0.97, 0.04, 1.0), Color { r: 0x00, g: 0x0A, b: 0x09, a: 0xFF });
235 assert_eq!(Color::from_hsva(180.0, 1.0, 1.0, 1.0), Color { r: 0x00, g: 0xFF, b: 0xFF, a: 0xFF });
236 assert_eq!(Color::from_hsva(211.0, 0.11, 0.59, 1.0), Color { r: 0x86, g: 0x8E, b: 0x96, a: 0xFF });
237 assert_eq!(Color::from_hsva(299.0, 0.58, 0.91, 1.0), Color { r: 0xE6, g: 0x61, b: 0xE8, a: 0xFF });
238 assert_eq!(Color::from_hsva(299.0 + 360.0, 0.58, 0.91, 1.0), Color { r: 0xE6, g: 0x61, b: 0xE8, a: 0xFF });
239 assert_eq!(Color::from_hsva(299.0 - 360.0, 0.58, 0.91, 1.0), Color { r: 0xE6, g: 0x61, b: 0xE8, a: 0xFF });
240 assert_eq!(Color::from_hsva(310.0, 0.33, 0.77, 1.0), Color { r: 0xC4, g: 0x84, b: 0xBA, a: 0xFF });
241 assert_eq!(Color::from_hsva(310.0, 0.33, 0.77, 1.5), Color { r: 0xC4, g: 0x84, b: 0xBA, a: 0xFF });
242 assert_eq!(Color::from_hsva(310.0, 0.33, 0.77, 0.5), Color { r: 0xC4, g: 0x84, b: 0xBA, a: 0x80 });
243 assert_eq!(Color::from_hsva(310.0, 0.33, 0.77, 0.0), Color { r: 0xC4, g: 0x84, b: 0xBA, a: 0x00 });
244 assert_eq!(Color::from_hsva(310.0, 0.33, 0.77, -0.2), Color { r: 0xC4, g: 0x84, b: 0xBA, a: 0x00 });
245}
246#[test]
247fn test_color_rgb_to_hsv() {
248 macro_rules! assert_close {
249 ($c1:expr, $c2:expr) => {{
250 let (h1, s1, v1, a1) = $c1;
251 let (h2, s2, v2, a2) = $c2;
252 let thresh = 1.0 / 255.0;
253 assert!((h1 - h2).abs() < thresh, "{h1} vs {h2}");
254 assert!((s1 - s2).abs() < thresh, "{s1} vs {s2}");
255 assert!((v1 - v2).abs() < thresh, "{v1} vs {v2}");
256 assert!((a1 - a2).abs() < thresh, "{a1} vs {a2}");
257 }}
258 }
259 assert_close!(Color { r: 0x00, g: 0x00, b: 0x00, a: 0xFF }.to_hsva(), (0.0, 0.0, 0.0, 1.0));
260 assert_close!(Color { r: 0x5C, g: 0x55, b: 0x55, a: 0xFF }.to_hsva(), (0.0, 0.076, 0.361, 1.0));
261 assert_close!(Color { r: 92, g: 0, b: 0, a: 0xFF }.to_hsva(), (0.0, 1.0, 0.361, 1.0));
262 assert_close!(Color { r: 92, g: 92, b: 92, a: 0xFF }.to_hsva(), (0.0, 0.0, 0.361, 1.0));
263 assert_close!(Color { r: 0x40, g: 0x2D, b: 0x20, a: 0xFF }.to_hsva(), (24.375, 0.5, 0.251, 1.0));
264 assert_close!(Color { r: 0x1F, g: 0x1A, b: 0x08, a: 0xFF }.to_hsva(), (46.956, 0.742, 0.122, 1.0));
265 assert_close!(Color { r: 0xDC, g: 0xDE, b: 0xC3, a: 0xFF }.to_hsva(), (64.444, 0.122, 0.871, 1.0));
266 assert_close!(Color { r: 252, g: 255, b: 224, a: 0xFF }.to_hsva(), (65.806, 0.122, 1.0, 1.0));
267 assert_close!(Color { r: 0x7D, g: 0x8C, b: 0x6D, a: 0xFF }.to_hsva(), (89.032, 0.221, 0.549, 1.0));
268 assert_close!(Color { r: 0xAC, g: 0xE8, b: 0xAC, a: 0xFF }.to_hsva(), (120.0, 0.259, 0.91, 1.0));
269 assert_close!(Color { r: 0x00, g: 0x0A, b: 0x09, a: 0xFF }.to_hsva(), (174.0, 1.0, 0.039, 1.0));
270 assert_close!(Color { r: 0x00, g: 0xFF, b: 0xFF, a: 0xFF }.to_hsva(), (180.0, 1.0, 1.0, 1.0));
271 assert_close!(Color { r: 0x86, g: 0x8E, b: 0x96, a: 0xFF }.to_hsva(), (210.0, 0.107, 0.588, 1.0));
272 assert_close!(Color { r: 0xE6, g: 0x61, b: 0xE8, a: 0xFF }.to_hsva(), (299.111, 0.582, 0.91, 1.0));
273 assert_close!(Color { r: 0xC4, g: 0x84, b: 0xBA, a: 0xFF }.to_hsva(), (309.375, 0.327, 0.769, 1.0));
274 assert_close!(Color { r: 0xC4, g: 0x84, b: 0xBA, a: 0x80 }.to_hsva(), (309.375, 0.327, 0.769, 0.5));
275 assert_close!(Color { r: 0xC4, g: 0x84, b: 0xBA, a: 0x00 }.to_hsva(), (309.375, 0.327, 0.769, 0.0));
276 assert_close!(Color { r: 255, g: 67, b: 14, a: 255 }.to_hsva(), (13.195, 0.945, 1.0, 1.0));
277 assert_close!(Color { r: 255, g: 14, b: 67, a: 255 }.to_hsva(), (346.805, 0.945, 1.0, 1.0));
278 assert_close!(Color { r: 87, g: 255, b: 33, a: 255 }.to_hsva(), (105.4054, 0.871, 1.0, 1.0));
279 assert_close!(Color { r: 33, g: 255, b: 87, a: 255 }.to_hsva(), (134.594, 0.871, 1.0, 1.0));
280 assert_close!(Color { r: 12, g: 54, b: 255, a: 255 }.to_hsva(), (229.629, 0.953, 1.0, 1.0));
281 assert_close!(Color { r: 54, g: 12, b: 255, a: 255 }.to_hsva(), (250.37, 0.953, 1.0, 1.0));
282
283 macro_rules! assert_round_trip {
284 ($v:expr) => {{
285 let rgba = $v;
286 let hsva = rgba.to_hsva();
287 let back = Color::from_hsva(hsva.0, hsva.1, hsva.2, hsva.3);
288 assert_eq!(rgba, back);
289 }}
290 }
291 assert_round_trip!(Color { r: 12, g: 65, b: 23, a: 87 });
292 assert_round_trip!(Color { r: 128, g: 0, b: 23, a: 186 });
293 assert_round_trip!(Color { r: 0, g: 0, b: 0, a: 0 });
294 assert_round_trip!(Color { r: 0, g: 0, b: 0, a: 255 });
295 assert_round_trip!(Color { r: 255, g: 0, b: 0, a: 255 });
296 assert_round_trip!(Color { r: 0, g: 255, b: 0, a: 255 });
297 assert_round_trip!(Color { r: 0, g: 0, b: 255, a: 255 });
298 assert_round_trip!(Color { r: 255, g: 0, b: 0, a: 0 });
299 assert_round_trip!(Color { r: 0, g: 255, b: 0, a: 0 });
300 assert_round_trip!(Color { r: 0, g: 0, b: 255, a: 0 });
301 assert_round_trip!(Color { r: 57, g: 0, b: 0, a: 0 });
302 assert_round_trip!(Color { r: 0, g: 198, b: 0, a: 0 });
303 assert_round_trip!(Color { r: 0, g: 0, b: 10, a: 0 });
304}
305
306#[derive(Debug, Clone, Copy, FromPrimitive)]
307#[repr(u8)]
308pub enum ImageProperty {
309 Name,
310}
311#[derive(Debug, Clone, Copy, FromPrimitive)]
312#[repr(u8)]
313pub enum AudioProperty {
314 Name,
315}
316
317#[derive(Debug, Clone, Copy, FromPrimitive)]
318#[repr(u8)]
319pub enum Property {
320 XPos, YPos, Heading,
321
322 Visible, Size,
323
324 PenDown, PenSize, PenColor,
325 PenColorH, PenColorS, PenColorV, PenColorT,
326
327 Tempo, Volume, Balance,
328
329 ColorH, ColorS, ColorV, ColorT,
330 Fisheye, Whirl, Pixelate, Mosaic, Negative,
331}
332impl Property {
333 pub(crate) fn from_effect(kind: &ast::EffectKind) -> Self {
334 match kind {
335 ast::EffectKind::Color => Property::ColorH,
336 ast::EffectKind::Saturation => Property::ColorS,
337 ast::EffectKind::Brightness => Property::ColorV,
338 ast::EffectKind::Ghost => Property::ColorT,
339 ast::EffectKind::Fisheye => Property::Fisheye,
340 ast::EffectKind::Whirl => Property::Whirl,
341 ast::EffectKind::Pixelate => Property::Pixelate,
342 ast::EffectKind::Mosaic => Property::Mosaic,
343 ast::EffectKind::Negative => Property::Negative,
344 }
345 }
346 pub(crate) fn from_pen_attr(attr: &ast::PenAttribute) -> Self {
347 match attr {
348 ast::PenAttribute::Size => Property::PenSize,
349 ast::PenAttribute::Hue => Property::PenColorH,
350 ast::PenAttribute::Saturation => Property::PenColorS,
351 ast::PenAttribute::Brightness => Property::PenColorV,
352 ast::PenAttribute::Transparency => Property::PenColorT,
353 }
354 }
355}
356
357#[derive(Clone, Copy)]
359pub struct Effects {
360 pub color_h: Number,
361 pub color_s: Number,
362 pub color_v: Number,
363 pub color_t: Number,
364
365 pub fisheye: Number,
366 pub whirl: Number,
367 pub pixelate: Number,
368 pub mosaic: Number,
369 pub negative: Number,
370}
371impl Default for Effects {
372 fn default() -> Self {
373 let zero = Number::new(0.0).unwrap();
374 Self {
375 color_h: zero,
376 color_s: zero,
377 color_v: zero,
378 color_t: zero,
379
380 fisheye: zero,
381 whirl: zero,
382 pixelate: zero,
383 mosaic: zero,
384 negative: zero,
385 }
386 }
387}
388
389#[derive(Clone, Copy)]
391pub struct Properties {
392 pub pos: (Number, Number),
393 pub heading: Number,
394
395 pub visible: bool,
396 pub size: Number,
397
398 pub pen_down: bool,
399 pub pen_size: Number,
400
401 pub pen_color_h: Number,
402 pub pen_color_s: Number,
403 pub pen_color_v: Number,
404 pub pen_color_t: Number,
405
406 pub tempo: Number,
407 pub volume: Number,
408 pub balance: Number,
409
410 pub effects: Effects,
411}
412impl Default for Properties {
413 fn default() -> Self {
414 let zero = Number::new(0.0).unwrap();
415 let hundred = Number::new(100.0).unwrap();
416
417 Self {
418 pos: (zero, zero),
419 heading: zero,
420
421 visible: true,
422 size: hundred,
423
424 pen_down: false,
425 pen_size: Number::new(1.0).unwrap(),
426
427 pen_color_h: zero,
428 pen_color_s: zero,
429 pen_color_v: zero,
430 pen_color_t: zero,
431
432 tempo: Number::new(60.0).unwrap(),
433 volume: hundred,
434 balance: zero,
435
436 effects: Default::default(),
437 }
438 }
439}
440impl Properties {
441 fn with_value<C: CustomTypes<S>, S: System<C>, T>(&mut self, key: S::CommandKey, value: Result<T, ErrorCause<C, S>>, f: fn(&mut Self, T)) {
442 match value {
443 Ok(x) => {
444 f(self, x);
445 key.complete(Ok(()));
446 }
447 Err(e) => key.complete(Err(format_compact!("{e:?}"))),
448 }
449 }
450
451 pub fn perform_get_property<'gc, C: CustomTypes<S>, S: System<C>>(&self, key: S::RequestKey, prop: Property) -> RequestStatus<'gc, C, S> {
452 let value: SimpleValue = match prop {
453 Property::XPos => self.pos.0.into(),
454 Property::YPos => self.pos.1.into(),
455 Property::Heading => self.heading.into(),
456
457 Property::Visible => self.visible.into(),
458 Property::Size => self.size.into(),
459
460 Property::PenDown => self.pen_down.into(),
461 Property::PenSize => self.pen_size.into(),
462
463 Property::PenColor => {
464 let Color { a, r, g, b } = Color::from_hsva(
465 self.pen_color_h.get() as f32,
466 self.pen_color_s.get() as f32 / 100.0,
467 self.pen_color_v.get() as f32 / 100.0,
468 1.0 - self.pen_color_t.get() as f32 / 100.0
469 );
470 Number::new(u32::from_be_bytes([a, r, g, b]) as f64).unwrap().into()
471 }
472
473 Property::PenColorH => self.pen_color_h.into(),
474 Property::PenColorS => self.pen_color_s.into(),
475 Property::PenColorV => self.pen_color_v.into(),
476 Property::PenColorT => self.pen_color_t.into(),
477
478 Property::Tempo => self.tempo.into(),
479 Property::Volume => self.volume.into(),
480 Property::Balance => self.balance.into(),
481
482 Property::ColorH => self.effects.color_h.into(),
483 Property::ColorS => self.effects.color_s.into(),
484 Property::ColorV => self.effects.color_v.into(),
485 Property::ColorT => self.effects.color_t.into(),
486
487 Property::Fisheye => self.effects.fisheye.into(),
488 Property::Whirl => self.effects.whirl.into(),
489 Property::Pixelate => self.effects.pixelate.into(),
490 Property::Mosaic => self.effects.mosaic.into(),
491 Property::Negative => self.effects.negative.into(),
492 };
493 key.complete(Ok(value.into()));
494 RequestStatus::Handled
495 }
496 pub fn perform_set_property<'gc, 'a, C: CustomTypes<S>, S: System<C>>(&mut self, key: S::CommandKey, prop: Property, value: Value<'gc, C, S>) -> CommandStatus<'gc, 'a, C, S> {
497 match prop {
498 Property::XPos => self.with_value(key, value.as_number().map_err(Into::into), |props, value| props.pos.0 = value),
499 Property::YPos => self.with_value(key, value.as_number().map_err(Into::into), |props, value| props.pos.1 = value),
500 Property::Heading => self.with_value(key, value.as_number().map_err(Into::into).and_then(|x| Number::new(util::modulus(x.get(), 360.0)).map_err(Into::into)), |props, value| props.heading = value),
501
502 Property::Visible => self.with_value(key, value.as_bool().map_err(Into::into), |props, value| props.visible = value),
503 Property::Size => self.with_value(key, value.as_number().map_err(Into::into).and_then(|x| Number::new(x.get().max(0.0)).map_err(Into::into)), |props, value| props.size = value),
504
505 Property::PenDown => self.with_value(key, value.as_bool().map_err(Into::into), |props, value| props.pen_down = value),
506 Property::PenSize => self.with_value(key, value.as_number().map_err(Into::into).and_then(|x| Number::new(x.get().max(0.0)).map_err(Into::into)), |props, value| props.pen_size = value),
507
508 Property::PenColor => self.with_value(key, value.as_number().map_err(Into::into), |props, value| {
509 let [a, r, g, b] = (value.get() as u32).to_be_bytes();
510 let (h, s, v, a) = Color { a, r, g, b }.to_hsva();
511 props.pen_color_h = Number::new(h as f64).unwrap();
512 props.pen_color_s = Number::new(s as f64 * 100.0).unwrap();
513 props.pen_color_v = Number::new(v as f64 * 100.0).unwrap();
514 props.pen_color_t = Number::new((1.0 - a as f64) * 100.0).unwrap();
515 }),
516
517 Property::PenColorH => self.with_value(key, value.as_number().map_err(Into::into).and_then(|x| Number::new(util::modulus(x.get(), 360.0)).map_err(Into::into)), |props, value| props.pen_color_h = value),
518 Property::PenColorS => self.with_value(key, value.as_number().map_err(Into::into).and_then(|x| Number::new(x.get().clamp(0.0, 100.0)).map_err(Into::into)), |props, value| props.pen_color_s = value),
519 Property::PenColorV => self.with_value(key, value.as_number().map_err(Into::into).and_then(|x| Number::new(x.get().clamp(0.0, 100.0)).map_err(Into::into)), |props, value| props.pen_color_v = value),
520 Property::PenColorT => self.with_value(key, value.as_number().map_err(Into::into).and_then(|x| Number::new(x.get().clamp(0.0, 100.0)).map_err(Into::into)), |props, value| props.pen_color_t = value),
521
522 Property::Tempo => self.with_value(key, value.as_number().map_err(Into::into), |props, value| props.tempo = value),
523 Property::Volume => self.with_value(key, value.as_number().map_err(Into::into), |props, value| props.volume = value),
524 Property::Balance => self.with_value(key, value.as_number().map_err(Into::into), |props, value| props.balance = value),
525
526 Property::ColorH => self.with_value(key, value.as_number().map_err(Into::into).and_then(|x| Number::new(util::modulus(x.get(), 360.0)).map_err(Into::into)), |props, value| props.effects.color_h = value),
527 Property::ColorS => self.with_value(key, value.as_number().map_err(Into::into).and_then(|x| Number::new(x.get().clamp(0.0, 100.0)).map_err(Into::into)), |props, value| props.effects.color_s = value),
528 Property::ColorV => self.with_value(key, value.as_number().map_err(Into::into).and_then(|x| Number::new(x.get().clamp(0.0, 100.0)).map_err(Into::into)), |props, value| props.effects.color_v = value),
529 Property::ColorT => self.with_value(key, value.as_number().map_err(Into::into).and_then(|x| Number::new(x.get().clamp(0.0, 100.0)).map_err(Into::into)), |props, value| props.effects.color_t = value),
530
531 Property::Fisheye => self.with_value(key, value.as_number().map_err(Into::into), |props, value| props.effects.fisheye = value),
532 Property::Whirl => self.with_value(key, value.as_number().map_err(Into::into), |props, value| props.effects.whirl = value),
533 Property::Pixelate => self.with_value(key, value.as_number().map_err(Into::into), |props, value| props.effects.pixelate = value),
534 Property::Mosaic => self.with_value(key, value.as_number().map_err(Into::into), |props, value| props.effects.mosaic = value),
535 Property::Negative => self.with_value(key, value.as_number().map_err(Into::into), |props, value| props.effects.negative = value),
536 }
537 CommandStatus::Handled
538 }
539 pub fn perform_change_property<'gc, 'a, C: CustomTypes<S>, S: System<C>>(&mut self, key: S::CommandKey, prop: Property, delta: Value<'gc, C, S>) -> CommandStatus<'gc, 'a, C, S> {
540 match prop {
541 Property::XPos => self.with_value(key, delta.as_number().map_err(Into::into).and_then(|x| self.pos.0.add(x).map_err(Into::into)), |props, value| props.pos.0 = value),
542 Property::YPos => self.with_value(key, delta.as_number().map_err(Into::into).and_then(|x| self.pos.1.add(x).map_err(Into::into)), |props, value| props.pos.1 = value),
543 Property::Heading => self.with_value(key, delta.as_number().map_err(Into::into).and_then(|x| Number::new(util::modulus(self.heading.get() + x.get(), 360.0)).map_err(Into::into)), |props, value| props.heading = value),
544
545 Property::Visible => self.with_value(key, delta.as_bool().map_err(Into::into), |props, value| props.visible ^= value),
546 Property::Size => self.with_value(key, delta.as_number().map_err(Into::into).and_then(|x| Number::new((self.size.get() + x.get()).max(0.0)).map_err(Into::into)), |props, value| props.size = value),
547
548 Property::PenDown => self.with_value(key, delta.as_bool().map_err(Into::into), |props, value| props.pen_down ^= value),
549 Property::PenSize => self.with_value(key, delta.as_number().map_err(Into::into).and_then(|x| Number::new((self.pen_size.get() + x.get()).max(0.0)).map_err(Into::into)), |props, value| props.pen_size = value),
550
551 Property::PenColor => key.complete(Err("attempt to apply relative change to a color".into())),
552
553 Property::PenColorH => self.with_value(key, delta.as_number().map_err(Into::into).and_then(|x| Number::new(util::modulus(self.pen_color_h.get() + x.get(), 360.0)).map_err(Into::into)), |props, value| props.pen_color_h = value),
554 Property::PenColorS => self.with_value(key, delta.as_number().map_err(Into::into).and_then(|x| Number::new((self.pen_color_s.get() + x.get()).clamp(0.0, 100.0)).map_err(Into::into)), |props, value| props.pen_color_s = value),
555 Property::PenColorV => self.with_value(key, delta.as_number().map_err(Into::into).and_then(|x| Number::new((self.pen_color_v.get() + x.get()).clamp(0.0, 100.0)).map_err(Into::into)), |props, value| props.pen_color_v = value),
556 Property::PenColorT => self.with_value(key, delta.as_number().map_err(Into::into).and_then(|x| Number::new((self.pen_color_t.get() + x.get()).clamp(0.0, 100.0)).map_err(Into::into)), |props, value| props.pen_color_t = value),
557
558 Property::Tempo => self.with_value(key, delta.as_number().map_err(Into::into).and_then(|x| self.tempo.add(x).map_err(Into::into)), |props, value| props.tempo = value),
559 Property::Volume => self.with_value(key, delta.as_number().map_err(Into::into).and_then(|x| self.volume.add(x).map_err(Into::into)), |props, value| props.volume = value),
560 Property::Balance => self.with_value(key, delta.as_number().map_err(Into::into).and_then(|x| self.balance.add(x).map_err(Into::into)), |props, value| props.balance = value),
561
562 Property::ColorH => self.with_value(key, delta.as_number().map_err(Into::into).and_then(|x| Number::new(util::modulus(self.effects.color_h.get() + x.get(), 360.0)).map_err(Into::into)), |props, value| props.effects.color_h = value),
563 Property::ColorS => self.with_value(key, delta.as_number().map_err(Into::into).and_then(|x| Number::new((self.effects.color_s.get() + x.get()).clamp(0.0, 100.0)).map_err(Into::into)), |props, value| props.effects.color_s = value),
564 Property::ColorV => self.with_value(key, delta.as_number().map_err(Into::into).and_then(|x| Number::new((self.effects.color_v.get() + x.get()).clamp(0.0, 100.0)).map_err(Into::into)), |props, value| props.effects.color_v = value),
565 Property::ColorT => self.with_value(key, delta.as_number().map_err(Into::into).and_then(|x| Number::new((self.effects.color_t.get() + x.get()).clamp(0.0, 100.0)).map_err(Into::into)), |props, value| props.effects.color_t = value),
566
567 Property::Fisheye => self.with_value(key, delta.as_number().map_err(Into::into).and_then(|x| self.effects.fisheye.add(x).map_err(Into::into)), |props, value| props.effects.fisheye = value),
568 Property::Whirl => self.with_value(key, delta.as_number().map_err(Into::into).and_then(|x| self.effects.whirl.add(x).map_err(Into::into)), |props, value| props.effects.whirl = value),
569 Property::Pixelate => self.with_value(key, delta.as_number().map_err(Into::into).and_then(|x| self.effects.pixelate.add(x).map_err(Into::into)), |props, value| props.effects.pixelate = value),
570 Property::Mosaic => self.with_value(key, delta.as_number().map_err(Into::into).and_then(|x| self.effects.mosaic.add(x).map_err(Into::into)), |props, value| props.effects.mosaic = value),
571 Property::Negative => self.with_value(key, delta.as_number().map_err(Into::into).and_then(|x| self.effects.negative.add(x).map_err(Into::into)), |props, value| props.effects.negative = value),
572 }
573 CommandStatus::Handled
574 }
575
576 pub fn perform_clear_effects<'gc, 'a, C: CustomTypes<S>, S: System<C>>(&mut self, key: S::CommandKey) -> CommandStatus<'gc, 'a, C, S> {
577 self.effects = Default::default();
578 key.complete(Ok(()));
579 CommandStatus::Handled
580 }
581
582 pub fn perform_goto_xy<'gc, 'a, C: CustomTypes<S>, S: System<C>>(&mut self, key: S::CommandKey, x: Number, y: Number) -> CommandStatus<'gc, 'a, C, S> {
583 self.pos = (x, y);
584 key.complete(Ok(()));
585 CommandStatus::Handled
586 }
587
588 pub fn perform_point_towards_xy<'gc, 'a, C: CustomTypes<S>, S: System<C>>(&mut self, key: S::CommandKey, x: Number, y: Number) -> CommandStatus<'gc, 'a, C, S> {
589 let (dx, dy) = (x.get() - self.pos.0.get(), y.get() - self.pos.1.get());
590 let heading = util::modulus(libm::atan2(dx, dy).to_degrees(), 360.0);
591 self.with_value::<C, S, _>(key, Number::new(heading).map_err(Into::into), |props, value| props.heading = value);
592 CommandStatus::Handled
593 }
594
595 pub fn perform_forward<'gc, 'a, C: CustomTypes<S>, S: System<C>>(&mut self, key: S::CommandKey, dist: Number) -> CommandStatus<'gc, 'a, C, S> {
596 let (sin, cos) = libm::sincos(self.heading.get().to_radians());
597 let (x, y) = (self.pos.0.get() + sin * dist.get(), self.pos.1.get() + cos * dist.get());
598 self.with_value::<C, S, _>(key, Number::new(x).map_err(Into::into).and_then(|x| Number::new(y).map(|y| (x, y)).map_err(Into::into)), |props, pos| props.pos = pos);
599 CommandStatus::Handled
600 }
601}
602
603#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
605#[derive(Debug, Clone, Copy, PartialEq, Eq)]
606pub enum KeyCode {
607 Char(char),
609 Up,
611 Down,
613 Left,
615 Right,
617 Enter,
619}
620
621#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
623#[derive(Clone)]
624pub enum Event {
625 OnFlag,
627 OnClone,
629 LocalMessage { msg_type: Option<CompactString> },
631 NetworkMessage { msg_type: CompactString, fields: Vec<CompactString> },
633 OnKey { key_filter: Option<KeyCode> },
635 Custom { name: CompactString, fields: Vec<CompactString> },
637}
638
639#[derive(Debug, Clone, Copy, FromPrimitive)]
640#[repr(u8)]
641pub enum PrintStyle {
642 Say, Think,
643}
644
645#[derive(Educe)]
647#[educe(Debug, Clone, Copy, PartialOrd, Ord, PartialEq, Eq)]
648pub struct Identity<'gc, C: CustomTypes<S>, S: System<C>>(*const (), PhantomData<&'gc Value<'gc, C, S>>);
649
650pub trait GetType {
652 type Output: Clone + Copy + PartialEq + Eq + fmt::Debug;
653 fn get_type(&self) -> Self::Output;
655}
656
657pub trait Key<T> {
658 fn complete(self, value: T);
659}
660
661#[derive(Debug, Clone, PartialEq, Eq)]
663pub struct Image {
664 pub content: Vec<u8>,
666 pub center: Option<(Number, Number)>,
669 pub name: CompactString,
671}
672
673#[derive(Debug, Clone, PartialEq, Eq)]
675pub struct Audio {
676 pub content: Vec<u8>,
678 pub name: CompactString,
680}
681
682#[derive(Educe)]
684#[educe(Debug)]
685pub enum ToSimpleError<C: CustomTypes<S>, S: System<C>> {
686 Cyclic,
687 ComplexType(Type<C, S>),
688}
689#[derive(Educe)]
691#[educe(Debug)]
692pub enum IntoJsonError<C: CustomTypes<S>, S: System<C>> {
693 ComplexType(Type<C, S>),
694}
695
696#[derive(Debug)]
698pub enum FromJsonError {
699 Null,
700}
701#[derive(Debug)]
703pub enum FromNetsBloxJsonError {
704 Null,
705 BadImage,
706 BadAudio,
707}
708
709#[derive(Debug, Clone, PartialEq, Eq)]
711pub enum SimpleValue {
712 Bool(bool),
713 Number(Number),
714 Text(CompactString),
715 Image(Image),
716 Audio(Audio),
717 List(Vec<SimpleValue>),
718}
719
720impl From<bool> for SimpleValue { fn from(x: bool) -> Self { Self::Bool(x) } }
721impl From<Number> for SimpleValue { fn from(x: Number) -> Self { Self::Number(x) } }
722impl From<CompactString> for SimpleValue { fn from(x: CompactString) -> Self { Self::Text(x) } }
723impl From<alloc::string::String> for SimpleValue { fn from(x: alloc::string::String) -> Self { Self::Text(x.into()) } }
724impl From<Image> for SimpleValue { fn from(x: Image) -> Self { Self::Image(x) } }
725impl From<Audio> for SimpleValue { fn from(x: Audio) -> Self { Self::Audio(x) } }
726impl From<Vec<SimpleValue>> for SimpleValue { fn from(x: Vec<SimpleValue>) -> Self { Self::List(x) } }
727
728impl SimpleValue {
729 pub fn into_json<C: CustomTypes<S>, S: System<C>>(self) -> Result<Json, IntoJsonError<C, S>> {
733 Ok(match self {
734 SimpleValue::Bool(x) => Json::Bool(x),
735 SimpleValue::Number(x) => Json::Number(JsonNumber::from_f64(x.get()).unwrap()), SimpleValue::Text(x) => Json::String(x.as_str().to_owned()),
737 SimpleValue::List(x) => Json::Array(x.into_iter().map(SimpleValue::into_json).collect::<Result<_,_>>()?),
738 SimpleValue::Image(_) => return Err(IntoJsonError::ComplexType(Type::Image)),
739 SimpleValue::Audio(_) => return Err(IntoJsonError::ComplexType(Type::Audio)),
740 })
741 }
742 pub fn from_json(value: Json) -> Result<Self, FromJsonError> {
746 Ok(match value {
747 Json::Null => return Err(FromJsonError::Null),
748 Json::Bool(x) => SimpleValue::Bool(x),
749 Json::Number(x) => SimpleValue::Number(Number::new(x.as_f64().unwrap()).unwrap()), Json::String(x) => SimpleValue::Text(x.into()),
751 Json::Array(x) => SimpleValue::List(x.into_iter().map(SimpleValue::from_json).collect::<Result<_,_>>()?),
752 Json::Object(x) => SimpleValue::List(x.into_iter().map(|(k, v)| {
753 Ok(SimpleValue::List(vec![SimpleValue::Text(k.into()), SimpleValue::from_json(v)?]))
754 }).collect::<Result<_,_>>()?),
755 })
756 }
757
758 pub fn into_netsblox_json(self) -> Json {
760 match self {
761 SimpleValue::Bool(x) => Json::Bool(x),
762 SimpleValue::Number(x) => Json::Number(JsonNumber::from_f64(x.get()).unwrap()), SimpleValue::Text(x) => Json::String(x.into()),
764 SimpleValue::List(x) => Json::Array(x.into_iter().map(SimpleValue::into_netsblox_json).collect()),
765 SimpleValue::Image(img) => {
766 let center_attrs = img.center.map(|(x, y)| format!(" center-x=\"{x}\" center-y=\"{y}\"")).unwrap_or_default();
767 Json::String(format!("<costume name=\"{}\"{center_attrs} image=\"data:image/png;base64,{}\" />", ast::util::xml_escape(&img.name), crate::util::base64_encode(&img.content)))
768 },
769 SimpleValue::Audio(audio) => Json::String(format!("<sound name=\"{}\" sound=\"data:audio/mpeg;base64,{}\" />", ast::util::xml_escape(&audio.name), crate::util::base64_encode(&audio.content))),
770 }
771 }
772 pub fn from_netsblox_json(value: Json) -> Result<Self, FromNetsBloxJsonError> {
774 Ok(match value {
775 Json::Null => return Err(FromNetsBloxJsonError::Null),
776 Json::Bool(x) => SimpleValue::Bool(x),
777 Json::Number(x) => SimpleValue::Number(Number::new(x.as_f64().unwrap()).unwrap()), Json::Array(x) => SimpleValue::List(x.into_iter().map(SimpleValue::from_netsblox_json).collect::<Result<_,_>>()?),
779 Json::Object(x) => SimpleValue::List(x.into_iter().map(|(k, v)| {
780 Ok(SimpleValue::List(vec![SimpleValue::Text(k.into()), SimpleValue::from_netsblox_json(v)?]))
781 }).collect::<Result<_,_>>()?),
782 Json::String(x) => {
783 let mut tokenizer = xmlparser::Tokenizer::from(x.as_str());
784 match tokenizer.next() {
785 Some(Ok(xmlparser::Token::ElementStart { local, .. })) => match local.as_str() {
786 "costume" => {
787 let mut center_x = None;
788 let mut center_y = None;
789 let mut content = None;
790 let mut name = "untitled".into();
791 loop {
792 match tokenizer.next() {
793 Some(Ok(xmlparser::Token::Attribute { local, value, .. })) => match local.as_str() {
794 "center-x" => center_x = Some(value.as_str().parse().ok().and_then(|x| Number::new(x).ok()).ok_or(FromNetsBloxJsonError::BadImage)?),
795 "center-y" => center_y = Some(value.as_str().parse().ok().and_then(|y| Number::new(y).ok()).ok_or(FromNetsBloxJsonError::BadImage)?),
796 "name" => name = value.as_str().into(),
797 "image" => match value.as_str().split(";base64,").nth(1) {
798 Some(raw) if value.as_str().starts_with("data:image/") => content = Some(crate::util::base64_decode(raw).map_err(|_| FromNetsBloxJsonError::BadImage)?),
799 _ => return Err(FromNetsBloxJsonError::BadImage),
800 }
801 _ => (),
802 }
803 Some(Ok(xmlparser::Token::ElementEnd { .. })) => match content {
804 Some(content) => return Ok(SimpleValue::Image(Image { content, center: center_x.zip(center_y), name })),
805 None => return Ok(SimpleValue::Text(x.into())),
806 }
807 Some(Ok(_)) => (),
808 None | Some(Err(_)) => return Ok(SimpleValue::Text(x.into())),
809 }
810 }
811 }
812 "sound" => {
813 let mut content = None;
814 let mut name = "untitled".into();
815 loop {
816 match tokenizer.next() {
817 Some(Ok(xmlparser::Token::Attribute { local, value, .. })) => match local.as_str() {
818 "name" => name = value.as_str().into(),
819 "sound" => match value.as_str().split(";base64,").nth(1) {
820 Some(raw) if value.as_str().starts_with("data:audio/") => content = Some(crate::util::base64_decode(raw).map_err(|_| FromNetsBloxJsonError::BadAudio)?),
821 _ => return Err(FromNetsBloxJsonError::BadAudio),
822 }
823 _ => (),
824 }
825 Some(Ok(xmlparser::Token::ElementEnd { .. })) => match content {
826 Some(content) => return Ok(SimpleValue::Audio(Audio { content, name })),
827 None => return Ok(SimpleValue::Text(x.into())),
828 }
829 Some(Ok(_)) => (),
830 None | Some(Err(_)) => return Ok(SimpleValue::Text(x.into())),
831 }
832 }
833 }
834 _ => SimpleValue::Text(x.into()),
835 }
836 _ => SimpleValue::Text(x.into()),
837 }
838 }
839 })
840 }
841
842 pub fn parse_number(s: &str) -> Option<Number> {
844 let s = s.trim();
845 let parsed = match s.get(..2) {
846 Some("0x" | "0X") => i64::from_str_radix(&s[2..], 16).ok().map(|x| x as f64),
847 Some("0o" | "0O") => i64::from_str_radix(&s[2..], 8).ok().map(|x| x as f64),
848 Some("0b" | "0B") => i64::from_str_radix(&s[2..], 2).ok().map(|x| x as f64),
849 _ => s.parse::<f64>().ok(),
850 };
851 parsed.and_then(|x| Number::new(x).ok())
852 }
853 pub fn stringify_number(v: Number) -> Text {
855 debug_assert!(v.get().is_finite());
856 let mut buf = ryu::Buffer::new();
857 let res = buf.format_finite(v.get());
858 Text::from(res.strip_suffix(".0").unwrap_or(res))
859 }
860}
861
862#[test]
863fn test_number_to_string() {
864 assert_eq!(SimpleValue::stringify_number(Number::new(0.0).unwrap()), "0");
865 assert_eq!(SimpleValue::stringify_number(Number::new(-0.0).unwrap()), "0");
866 assert_eq!(SimpleValue::stringify_number(Number::new(1.0).unwrap()), "1");
867 assert_eq!(SimpleValue::stringify_number(Number::new(7.0).unwrap()), "7");
868 assert_eq!(SimpleValue::stringify_number(Number::new(-1.0).unwrap()), "-1");
869 assert_eq!(SimpleValue::stringify_number(Number::new(-13.0).unwrap()), "-13");
870 assert_eq!(SimpleValue::stringify_number(Number::new(123456789.0).unwrap()), "123456789");
871 assert_eq!(SimpleValue::stringify_number(Number::new(5.67e50).unwrap()), "5.67e50");
872 assert_eq!(SimpleValue::stringify_number(Number::new(-8.35e30).unwrap()), "-8.35e30");
873 assert_eq!(SimpleValue::stringify_number(Number::new(6e-24).unwrap()), "6e-24");
874 assert_eq!(SimpleValue::stringify_number(Number::new(1e24).unwrap()), "1e24");
875}
876
877#[test]
878fn test_netsblox_json() {
879 let val = SimpleValue::List(vec![
880 SimpleValue::Bool(false),
881 SimpleValue::Bool(true),
882 SimpleValue::Number(Number::new(0.0).unwrap()),
883 SimpleValue::Number(Number::new(12.5).unwrap()),
884 SimpleValue::Number(Number::new(-6.0).unwrap()),
885 SimpleValue::Text("".into()),
886 SimpleValue::Text("hello world".into()),
887 SimpleValue::Text("<sound>".into()),
888 SimpleValue::Text("<sound/>".into()),
889 SimpleValue::Text("<sound />".into()),
890 SimpleValue::Text("<costume>".into()),
891 SimpleValue::Text("<costume/>".into()),
892 SimpleValue::Text("<costume />".into()),
893 SimpleValue::Image(Image { content: vec![], center: None, name: "test".into() }),
894 SimpleValue::Image(Image { content: vec![], center: Some((Number::new(0.0).unwrap(), Number::new(4.5).unwrap())), name: "another one".into() }),
895 SimpleValue::Image(Image { content: vec![0, 1, 2, 255, 254, 253, 127, 128], center: None, name: "untitled".into() }),
896 SimpleValue::Image(Image { content: vec![0, 1, 2, 255, 254, 253, 127, 128, 6, 9], center: Some((Number::new(12.5).unwrap(), Number::new(-54.0).unwrap())), name: "last one i swear".into() }),
897 SimpleValue::Audio(Audio { content: vec![], name: "something".into() }),
898 SimpleValue::Audio(Audio { content: vec![1, 2, 3], name: "killer move".into() }),
899 SimpleValue::Audio(Audio { content: vec![1, 2, 255, 43, 23, 254], name: "finish".into() }),
900 ]);
901 let js = val.clone().into_netsblox_json();
902 let back = SimpleValue::from_netsblox_json(js).unwrap();
903 assert_eq!(val, back);
904}
905
906#[derive(Educe, Collect)]
908#[educe(Clone)]
909#[collect(no_drop, bound = "")]
910pub enum Value<'gc, C: CustomTypes<S>, S: System<C>> {
911 Bool(#[collect(require_static)] bool),
913 Number(#[collect(require_static)] Number),
915 Text(#[collect(require_static)] Text),
917 Image(#[collect(require_static)] Rc<Image>),
919 Audio(#[collect(require_static)] Rc<Audio>),
921 Native(#[collect(require_static)] Rc<C::NativeValue>),
923 List(Gc<'gc, RefLock<VecDeque<Value<'gc, C, S>>>>),
925 Closure(Gc<'gc, RefLock<Closure<'gc, C, S>>>),
927 Entity(Gc<'gc, RefLock<Entity<'gc, C, S>>>),
929}
930
931impl<'gc, C: CustomTypes<S>, S: System<C>> GetType for Value<'gc, C, S> {
932 type Output = Type<C, S>;
933 fn get_type(&self) -> Self::Output {
934 match self {
935 Value::Bool(_) => Type::Bool,
936 Value::Number(_) => Type::Number,
937 Value::Text(_) => Type::Text,
938 Value::Image(_) => Type::Image,
939 Value::Audio(_) => Type::Audio,
940 Value::List(_) => Type::List,
941 Value::Closure(_) => Type::Closure,
942 Value::Entity(_) => Type::Entity,
943 Value::Native(x) => Type::Native(x.get_type()),
944 }
945 }
946}
947
948#[derive(Clone, Copy)]
949enum FormatStyle {
950 Debug, Display,
951}
952impl<C: CustomTypes<S>, S: System<C>> fmt::Debug for Value<'_, C, S> {
953 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
954 format_value(self, f, FormatStyle::Debug)
955 }
956}
957impl<C: CustomTypes<S>, S: System<C>> fmt::Display for Value<'_, C, S> {
958 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
959 format_value(self, f, FormatStyle::Display)
960 }
961}
962fn format_value<C: CustomTypes<S>, S: System<C>>(value: &Value<'_, C, S>, f: &mut fmt::Formatter<'_>, style: FormatStyle) -> fmt::Result {
963 fn print<'gc, C: CustomTypes<S>, S: System<C>>(value: &Value<'gc, C, S>, f: &mut fmt::Formatter<'_>, style: FormatStyle, cache: &mut BTreeSet<Identity<'gc, C, S>>) -> fmt::Result {
964 match value {
965 Value::Bool(x) => write!(f, "{x}"),
966 Value::Number(x) => write!(f, "{x}"),
967 Value::Text(x) => match style {
968 FormatStyle::Debug => write!(f, "{:?}", x.as_str()),
969 FormatStyle::Display => write!(f, "{}", x.as_str()),
970 }
971 Value::Closure(x) => write!(f, "{:?}", &*x.borrow()),
972 Value::Entity(x) => write!(f, "{:?}", &*x.borrow()),
973 Value::Native(x) => write!(f, "{:?}", &**x),
974 Value::Image(x) => write!(f, "[Image {:?}]", Rc::as_ptr(x)),
975 Value::Audio(x) => write!(f, "[Audio {:?}]", Rc::as_ptr(x)),
976 Value::List(x) => {
977 let identity = value.identity();
978 if !cache.insert(identity) { return write!(f, "[...]") }
979
980 let x = x.borrow();
981 write!(f, "[")?;
982 for (i, val) in x.iter().enumerate() {
983 print(val, f, style, cache)?;
984 if i != x.len() - 1 { write!(f, ",")? }
985 }
986 write!(f, "]")?;
987
988 debug_assert!(cache.contains(&identity));
989 cache.remove(&identity);
990 Ok(())
991 }
992 }
993 }
994 let mut cache = Default::default();
995 let res = print(value, f, style, &mut cache);
996 if res.is_ok() { debug_assert_eq!(cache.len(), 0); }
997 res
998}
999
1000impl<'gc, C: CustomTypes<S>, S: System<C>> From<bool> for Value<'gc, C, S> { fn from(v: bool) -> Self { Value::Bool(v) } }
1001impl<'gc, C: CustomTypes<S>, S: System<C>> From<Number> for Value<'gc, C, S> { fn from(v: Number) -> Self { Value::Number(v) } }
1002impl<'gc, C: CustomTypes<S>, S: System<C>> From<Text> for Value<'gc, C, S> { fn from(v: Text) -> Self { Value::Text(v) } }
1003impl<'gc, C: CustomTypes<S>, S: System<C>> From<Gc<'gc, RefLock<VecDeque<Value<'gc, C, S>>>>> for Value<'gc, C, S> { fn from(v: Gc<'gc, RefLock<VecDeque<Value<'gc, C, S>>>>) -> Self { Value::List(v) } }
1004impl<'gc, C: CustomTypes<S>, S: System<C>> From<Gc<'gc, RefLock<Closure<'gc, C, S>>>> for Value<'gc, C, S> { fn from(v: Gc<'gc, RefLock<Closure<'gc, C, S>>>) -> Self { Value::Closure(v) } }
1005impl<'gc, C: CustomTypes<S>, S: System<C>> From<Gc<'gc, RefLock<Entity<'gc, C, S>>>> for Value<'gc, C, S> { fn from(v: Gc<'gc, RefLock<Entity<'gc, C, S>>>) -> Self { Value::Entity(v) } }
1006impl<'gc, C: CustomTypes<S>, S: System<C>> Value<'gc, C, S> {
1007 pub fn to_simple(&self) -> Result<SimpleValue, ToSimpleError<C, S>> {
1009 fn simplify<'gc, C: CustomTypes<S>, S: System<C>>(value: &Value<'gc, C, S>, cache: &mut BTreeSet<Identity<'gc, C, S>>) -> Result<SimpleValue, ToSimpleError<C, S>> {
1010 Ok(match value {
1011 Value::Bool(x) => SimpleValue::Bool(*x),
1012 Value::Number(x) => SimpleValue::Number(*x),
1013 Value::Text(x) => SimpleValue::Text((**x).into()),
1014 Value::Image(x) => SimpleValue::Image((**x).clone()),
1015 Value::Audio(x) => SimpleValue::Audio((**x).clone()),
1016 Value::List(x) => {
1017 let identity = value.identity();
1018 if !cache.insert(identity) { return Err(ToSimpleError::Cyclic) }
1019 let res = SimpleValue::List(x.borrow().iter().map(|x| simplify(x, cache)).collect::<Result<_,_>>()?);
1020 debug_assert!(cache.contains(&identity));
1021 cache.remove(&identity);
1022 res
1023 }
1024 Value::Closure(_) | Value::Entity(_) | Value::Native(_) => return Err(ToSimpleError::ComplexType(value.get_type())),
1025 })
1026 }
1027 let mut cache = Default::default();
1028 let res = simplify(self, &mut cache);
1029 if res.is_ok() { debug_assert_eq!(cache.len(), 0); }
1030 res
1031 }
1032 pub fn from_simple(mc: &Mutation<'gc>, value: SimpleValue) -> Self {
1034 match value {
1035 SimpleValue::Bool(x) => Value::Bool(x),
1036 SimpleValue::Number(x) => Value::Number(x),
1037 SimpleValue::Text(x) => Value::Text(x.as_str().into()),
1038 SimpleValue::Audio(x) => Value::Audio(Rc::new(x)),
1039 SimpleValue::Image(x) => Value::Image(Rc::new(x)),
1040 SimpleValue::List(x) => Value::List(Gc::new(mc, RefLock::new(x.into_iter().map(|x| Value::from_simple(mc, x)).collect()))),
1041 }
1042 }
1043
1044 pub fn identity(&self) -> Identity<'gc, C, S> {
1048 match self {
1049 Value::Bool(x) => Identity(x as *const bool as *const (), PhantomData),
1050 Value::Number(x) => Identity(x as *const Number as *const (), PhantomData),
1051 Value::Text(x) => Identity(x.as_ptr() as *const (), PhantomData),
1052 Value::Image(x) => Identity(Rc::as_ptr(x) as *const (), PhantomData),
1053 Value::Audio(x) => Identity(Rc::as_ptr(x) as *const (), PhantomData),
1054 Value::List(x) => Identity(Gc::as_ptr(*x) as *const (), PhantomData),
1055 Value::Closure(x) => Identity(Gc::as_ptr(*x) as *const (), PhantomData),
1056 Value::Entity(x) => Identity(Gc::as_ptr(*x) as *const (), PhantomData),
1057 Value::Native(x) => Identity(Rc::as_ptr(x) as *const (), PhantomData),
1058 }
1059 }
1060
1061 pub fn as_bool(&self) -> Result<bool, ConversionError<C, S>> {
1063 Ok(match self {
1064 Value::Bool(x) => *x,
1065 x => return Err(ConversionError { got: x.get_type(), expected: Type::Bool }),
1066 })
1067 }
1068 pub fn as_number(&self) -> Result<Number, ConversionError<C, S>> {
1070 match self {
1071 Value::Number(x) => Ok(*x),
1072 Value::Text(x) => SimpleValue::parse_number(x).ok_or(ConversionError { got: Type::Text, expected: Type::Number }),
1073 x => Err(ConversionError { got: x.get_type(), expected: Type::Number }),
1074 }
1075 }
1076 pub fn as_text(&self) -> Result<Text, ConversionError<C, S>> {
1078 Ok(match self {
1079 Value::Text(x) => x.clone(),
1080 Value::Number(x) => SimpleValue::stringify_number(*x),
1081 x => return Err(ConversionError { got: x.get_type(), expected: Type::Text }),
1082 })
1083 }
1084 pub fn as_image(&self) -> Result<&Rc<Image>, ConversionError<C, S>> {
1086 match self {
1087 Value::Image(x) => Ok(x),
1088 x => Err(ConversionError { got: x.get_type(), expected: Type::Image }),
1089 }
1090 }
1091 pub fn as_audio(&self) -> Result<&Rc<Audio>, ConversionError<C, S>> {
1093 match self {
1094 Value::Audio(x) => Ok(x),
1095 x => Err(ConversionError { got: x.get_type(), expected: Type::Audio }),
1096 }
1097 }
1098 pub fn as_list(&self) -> Result<Gc<'gc, RefLock<VecDeque<Value<'gc, C, S>>>>, ConversionError<C, S>> {
1100 match self {
1101 Value::List(x) => Ok(*x),
1102 x => Err(ConversionError { got: x.get_type(), expected: Type::List }),
1103 }
1104 }
1105 pub fn as_closure(&self) -> Result<Gc<'gc, RefLock<Closure<'gc, C, S>>>, ConversionError<C, S>> {
1107 match self {
1108 Value::Closure(x) => Ok(*x),
1109 x => Err(ConversionError { got: x.get_type(), expected: Type::Closure }),
1110 }
1111 }
1112 pub fn as_entity(&self) -> Result<Gc<'gc, RefLock<Entity<'gc, C, S>>>, ConversionError<C, S>> {
1114 match self {
1115 Value::Entity(x) => Ok(*x),
1116 x => Err(ConversionError { got: x.get_type(), expected: Type::Entity }),
1117 }
1118 }
1119}
1120
1121#[derive(Collect)]
1123#[collect(no_drop, bound = "")]
1124pub struct Closure<'gc, C: CustomTypes<S>, S: System<C>> {
1125 #[collect(require_static)] pub(crate) pos: usize,
1126 #[collect(require_static)] pub(crate) params: Vec<CompactString>,
1127 pub(crate) captures: SymbolTable<'gc, C, S>,
1128}
1129impl<C: CustomTypes<S>, S: System<C>> fmt::Debug for Closure<'_, C, S> {
1130 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1131 write!(f, "Closure {:#08x}", self.pos)
1132 }
1133}
1134
1135pub enum EntityKind<'gc, 'a, C: CustomTypes<S>, S: System<C>> {
1137 Stage { props: Properties },
1138 Sprite { props: Properties },
1139 Clone { parent: &'a Entity<'gc, C, S> },
1140}
1141pub struct ProcessKind<'gc, 'a, C: CustomTypes<S>, S: System<C>> {
1143 pub entity: Gc<'gc, RefLock<Entity<'gc, C, S>>>,
1145 pub dispatcher: Option<&'a Process<'gc, C, S>>,
1147}
1148
1149#[derive(Collect)]
1151#[collect(no_drop, bound = "")]
1152pub struct Entity<'gc, C: CustomTypes<S>, S: System<C>> {
1153 #[collect(require_static)] pub name: Text,
1154 #[collect(require_static)] pub sound_list: Rc<VecMap<CompactString, Rc<Audio>, false>>,
1155 #[collect(require_static)] pub costume_list: Rc<VecMap<CompactString, Rc<Image>, false>>,
1156 #[collect(require_static)] pub costume: Option<Rc<Image>>,
1157 #[collect(require_static)] pub state: C::EntityState,
1158 pub original: Option<Gc<'gc, RefLock<Entity<'gc, C, S>>>>,
1159 pub fields: SymbolTable<'gc, C, S>,
1160}
1161impl<C: CustomTypes<S>, S: System<C>> fmt::Debug for Entity<'_, C, S> {
1162 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1163 write!(f, "Entity {:?}", self.name)
1164 }
1165}
1166
1167#[derive(Collect)]
1172#[collect(no_drop)]
1173pub struct Watcher<'gc, C: CustomTypes<S>, S: System<C>> {
1174 pub entity: GcWeak<'gc, RefLock<Entity<'gc, C, S>>>,
1176 #[collect(require_static)]
1178 pub name: CompactString,
1179 pub value: GcWeak<'gc, RefLock<Value<'gc, C, S>>>,
1181}
1182impl<'gc, C: CustomTypes<S>, S: System<C>> fmt::Debug for Watcher<'gc, C, S> {
1183 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1184 f.debug_struct("Watcher").field("name", &self.name).finish_non_exhaustive()
1185 }
1186}
1187
1188#[derive(Collect, Debug)]
1193#[collect(no_drop)]
1194pub enum Shared<'gc, T: 'gc + Collect> {
1195 Unique(T),
1197 Aliased(Gc<'gc, RefLock<T>>),
1199}
1200impl<'gc, T: 'gc + Collect> Shared<'gc, T> {
1201 pub fn set(&mut self, mc: &Mutation<'gc>, value: T) {
1203 match self {
1204 Shared::Unique(x) => *x = value,
1205 Shared::Aliased(x) => *x.borrow_mut(mc) = value,
1206 }
1207 }
1208 pub fn get(&self) -> SharedRef<T> {
1210 match self {
1211 Shared::Unique(x) => SharedRef::Unique(x),
1212 Shared::Aliased(x) => SharedRef::Aliased(x.borrow()),
1213 }
1214 }
1215 pub fn alias_inner(&mut self, mc: &Mutation<'gc>) -> Gc<'gc, RefLock<T>> {
1218 replace_with::replace_with(self, || unreachable!(), |myself| match myself {
1219 Shared::Unique(x) => Shared::Aliased(Gc::new(mc, RefLock::new(x))),
1220 Shared::Aliased(_) => myself,
1221 });
1222
1223 match self {
1224 Shared::Unique(_) => unreachable!(),
1225 Shared::Aliased(x) => *x,
1226 }
1227 }
1228 pub fn alias(&mut self, mc: &Mutation<'gc>) -> Self {
1231 Shared::Aliased(self.alias_inner(mc))
1232 }
1233}
1234impl<'gc, T: Collect> From<T> for Shared<'gc, T> { fn from(value: T) -> Self { Shared::Unique(value) } }
1235
1236pub enum SharedRef<'a, T> {
1237 Unique(&'a T),
1238 Aliased(Ref<'a, T>)
1239}
1240impl<'a, T> Deref for SharedRef<'a, T> {
1241 type Target = T;
1242 fn deref(&self) -> &Self::Target {
1243 match self {
1244 SharedRef::Unique(x) => x,
1245 SharedRef::Aliased(x) => x,
1246 }
1247 }
1248}
1249
1250#[derive(Collect, Educe)]
1255#[collect(no_drop, bound = "")]
1256#[educe(Default, Debug)]
1257pub struct SymbolTable<'gc, C: CustomTypes<S>, S: System<C>>(VecMap<CompactString, Shared<'gc, Value<'gc, C, S>>, true>);
1258impl<'gc, C: CustomTypes<S>, S: System<C>> Clone for SymbolTable<'gc, C, S> {
1259 fn clone(&self) -> Self {
1261 let mut res = SymbolTable::default();
1262 for (k, v) in self.iter() {
1263 res.define_or_redefine(k, Shared::Unique(v.get().clone()));
1264 }
1265 res
1266 }
1267}
1268impl<'gc, C: CustomTypes<S>, S: System<C>> SymbolTable<'gc, C, S> {
1269 pub fn define_or_redefine(&mut self, var: &str, value: Shared<'gc, Value<'gc, C, S>>) {
1272 self.0.insert(CompactString::new(var), value);
1273 }
1274 pub fn lookup<'a>(&'a self, var: &str) -> Option<&'a Shared<'gc, Value<'gc, C, S>>> {
1277 self.0.get(var)
1278 }
1279 pub fn lookup_mut<'a>(&'a mut self, var: &str) -> Option<&'a mut Shared<'gc, Value<'gc, C, S>>> {
1281 self.0.get_mut(var)
1282 }
1283 pub fn len(&self) -> usize {
1285 self.0.len()
1286 }
1287 pub fn is_empty(&self) -> bool {
1289 self.0.is_empty()
1290 }
1291 pub fn iter(&self) -> crate::vecmap::Iter<CompactString, Shared<'gc, Value<'gc, C, S>>> {
1293 self.0.iter()
1294 }
1295 pub fn iter_mut(&mut self) -> crate::vecmap::IterMut<CompactString, Shared<'gc, Value<'gc, C, S>>> {
1297 self.0.iter_mut()
1298 }
1299}
1300
1301pub(crate) struct LookupGroup<'gc, 'a, 'b, C: CustomTypes<S>, S: System<C>>(&'a mut [&'b mut SymbolTable<'gc, C, S>]);
1303impl<'gc, 'a: 'b, 'b, C: CustomTypes<S>, S: System<C>> LookupGroup<'gc, 'a, 'b, C, S> {
1304 pub fn new(tables: &'a mut [&'b mut SymbolTable<'gc, C, S>]) -> Self {
1307 debug_assert!(!tables.is_empty());
1308 Self(tables)
1309 }
1310 pub fn lookup<'s: 'a>(&'s self, var: &str) -> Option<&'b Shared<'gc, Value<'gc, C, S>>> {
1314 for src in self.0.iter().rev() {
1315 if let Some(val) = src.lookup(var) {
1316 return Some(val);
1317 }
1318 }
1319 None
1320 }
1321 pub fn lookup_mut<'s: 'a>(&'s mut self, var: &str) -> Option<&'b mut Shared<'gc, Value<'gc, C, S>>> {
1323 for src in self.0.iter_mut().rev() {
1324 if let Some(val) = src.lookup_mut(var) {
1325 return Some(val);
1326 }
1327 }
1328 None
1329 }
1330}
1331
1332#[derive(Clone, Copy)]
1334pub enum ErrorScheme {
1335 Soft,
1338 Hard,
1341}
1342
1343#[derive(Clone, Copy)]
1345pub struct Settings {
1346 pub max_call_depth: usize,
1348 pub rpc_error_scheme: ErrorScheme,
1350 pub syscall_error_scheme: ErrorScheme,
1352}
1353impl Default for Settings {
1354 fn default() -> Self {
1355 Self {
1356 max_call_depth: 1024,
1357 rpc_error_scheme: ErrorScheme::Hard,
1358 syscall_error_scheme: ErrorScheme::Hard,
1359 }
1360 }
1361}
1362
1363#[derive(Collect)]
1365#[collect(no_drop, bound = "")]
1366pub struct GlobalContext<'gc, C: CustomTypes<S>, S: System<C>> {
1367 #[collect(require_static)] pub bytecode: Rc<ByteCode>,
1368 #[collect(require_static)] pub settings: Settings,
1369 #[collect(require_static)] pub system: Rc<S>,
1370 #[collect(require_static)] pub timer_start: u64,
1371 #[collect(require_static)] pub proj_name: Text,
1372 pub globals: SymbolTable<'gc, C, S>,
1373 pub entities: VecMap<Text, Gc<'gc, RefLock<Entity<'gc, C, S>>>, false>,
1374}
1375impl<'gc, C: CustomTypes<S>, S: System<C>> GlobalContext<'gc, C, S> {
1376 pub fn from_init(mc: &Mutation<'gc>, init_info: &InitInfo, bytecode: Rc<ByteCode>, settings: Settings, system: Rc<S>) -> Self {
1377 let allocated_refs = init_info.ref_values.iter().map(|ref_value| match ref_value {
1378 RefValue::Text(value) => Value::Text(value.as_str().into()),
1379 RefValue::Image(content, center, name) => Value::Image(Rc::new(Image {content: content.clone(), center: *center, name: name.clone() })),
1380 RefValue::Audio(content, name) => Value::Audio(Rc::new(Audio { content: content.clone(), name: name.clone() })),
1381 RefValue::List(_) => Value::List(Gc::new(mc, Default::default())),
1382 }).collect::<Vec<_>>();
1383
1384 fn get_value<'gc, C: CustomTypes<S>, S: System<C>>(value: &InitValue, allocated_refs: &[Value<'gc, C, S>]) -> Value<'gc, C, S> {
1385 match value {
1386 InitValue::Bool(x) => Value::Bool(*x),
1387 InitValue::Number(x) => Value::Number(*x),
1388 InitValue::Ref(x) => allocated_refs[*x].clone(),
1389 }
1390 }
1391
1392 for (allocated_ref, ref_value) in iter::zip(&allocated_refs, &init_info.ref_values) {
1393 match ref_value {
1394 RefValue::Text(_) | RefValue::Image(_, _, _) | RefValue::Audio(_, _) => continue, RefValue::List(values) => {
1396 let allocated_ref = match allocated_ref {
1397 Value::List(x) => x,
1398 _ => unreachable!(),
1399 };
1400 let mut allocated_ref = allocated_ref.borrow_mut(mc);
1401 for value in values {
1402 allocated_ref.push_back(get_value(value, &allocated_refs));
1403 }
1404 }
1405 }
1406 }
1407
1408 let mut globals = SymbolTable::default();
1409 for (global, value) in init_info.globals.iter() {
1410 globals.define_or_redefine(global, Shared::Unique(get_value(value, &allocated_refs)));
1411 }
1412
1413 let mut entities = VecMap::with_capacity(init_info.entities.len());
1414 for (i, entity_info) in init_info.entities.iter().enumerate() {
1415 let mut fields = SymbolTable::default();
1416 for (field, value) in entity_info.fields.iter() {
1417 fields.define_or_redefine(field, Shared::Unique(get_value(value, &allocated_refs)));
1418 }
1419
1420 let sound_list = {
1421 let mut res = VecMap::with_capacity(entity_info.sounds.len());
1422 for (name, value) in entity_info.sounds.iter() {
1423 let sound = match get_value(value, &allocated_refs) {
1424 Value::Audio(x) => x.clone(),
1425 _ => unreachable!(),
1426 };
1427 res.insert(name.clone(), sound);
1428 }
1429 Rc::new(res)
1430 };
1431
1432 let costume_list = {
1433 let mut res = VecMap::with_capacity(entity_info.costumes.len());
1434 for (name, value) in entity_info.costumes.iter() {
1435 let image = match get_value(value, &allocated_refs) {
1436 Value::Image(x) => x.clone(),
1437 _ => unreachable!(),
1438 };
1439 res.insert(name.clone(), image);
1440 }
1441 Rc::new(res)
1442 };
1443
1444 let costume = entity_info.active_costume.and_then(|x| costume_list.as_slice().get(x)).map(|x| x.value.clone());
1445
1446 let mut props = Properties::default();
1447 props.visible = entity_info.visible;
1448 props.size = entity_info.size;
1449 props.pos = entity_info.pos;
1450 props.heading = entity_info.heading;
1451
1452 let (r, g, b, a) = entity_info.color;
1453 let (h, s, v, a) = Color { r, g, b, a }.to_hsva();
1454 props.pen_color_h = Number::new(h as f64).unwrap();
1455 props.pen_color_s = Number::new(s as f64 * 100.0).unwrap();
1456 props.pen_color_v = Number::new(v as f64 * 100.0).unwrap();
1457 props.pen_color_t = Number::new((1.0 - a as f64) * 100.0).unwrap();
1458
1459 let state = C::EntityState::from(if i == 0 { EntityKind::Stage { props } } else { EntityKind::Sprite { props } });
1460 let name = Text::from(entity_info.name.as_str());
1461
1462 entities.insert(name.clone(), Gc::new(mc, RefLock::new(Entity { original: None, name, fields, sound_list, costume_list, costume, state })));
1463 }
1464
1465 let proj_name = init_info.proj_name.as_str().into();
1466 let timer_start = system.time(Precision::Medium).to_arbitrary_ms::<C, S>().unwrap_or(0);
1467
1468 Self { proj_name, globals, entities, timer_start, system, settings, bytecode }
1469 }
1470}
1471
1472pub enum OutgoingMessage {
1473 Normal {
1474 msg_type: CompactString,
1475 values: VecMap<CompactString, Json, false>,
1476 targets: Vec<CompactString>,
1477 },
1478 Blocking {
1479 msg_type: CompactString,
1480 values: VecMap<CompactString, Json, false>,
1481 targets: Vec<CompactString>,
1482 reply_key: ExternReplyKey,
1483 },
1484 Reply {
1485 value: Json,
1486 reply_key: InternReplyKey,
1487 },
1488}
1489pub struct IncomingMessage {
1490 pub msg_type: CompactString,
1491 pub values: VecMap<CompactString, SimpleValue, false>,
1492 pub reply_key: Option<InternReplyKey>,
1493}
1494
1495#[derive(Debug, Default, Clone)]
1497pub struct Barrier(Rc<()>);
1498#[derive(Debug, Clone)]
1500pub struct BarrierCondition(Weak<()>);
1501impl Barrier {
1502 pub fn new() -> Self {
1505 Barrier(Rc::new(()))
1506 }
1507 pub fn get_condition(&self) -> BarrierCondition {
1510 BarrierCondition(Rc::downgrade(&self.0))
1511 }
1512}
1513impl BarrierCondition {
1514 pub fn is_completed(&self) -> bool {
1516 self.0.strong_count() == 0
1517 }
1518}
1519
1520pub enum AsyncResult<T> {
1522 Pending,
1524 Completed(T),
1526 Consumed,
1528}
1529impl<T> AsyncResult<T> {
1530 pub fn new() -> Self {
1532 Self::Pending
1533 }
1534 pub fn complete(&mut self, value: T) -> Result<(), T> {
1537 match self {
1538 AsyncResult::Pending => {
1539 *self = AsyncResult::Completed(value);
1540 Ok(())
1541 }
1542 AsyncResult::Completed(_) | AsyncResult::Consumed => Err(value),
1543 }
1544 }
1545 pub fn poll(&mut self) -> Self {
1548 match self {
1549 AsyncResult::Pending => AsyncResult::Pending,
1550 AsyncResult::Completed(_) | AsyncResult::Consumed => mem::replace(self, AsyncResult::Consumed),
1551 }
1552 }
1553}
1554
1555#[derive(Debug)]
1557pub enum Feature {
1558 ArbitraryTime,
1560 RealTime,
1562
1563 Input,
1565 Print,
1567
1568 Syscall { name: CompactString },
1570 Rpc { service: CompactString, rpc: CompactString },
1572
1573 GetProperty { prop: Property },
1575 SetProperty { prop: Property },
1577 ChangeProperty { prop: Property },
1579
1580 SetCostume,
1582
1583 PlaySound { blocking: bool },
1585 PlayNotes { blocking: bool },
1587 StopSounds,
1589
1590 ClearEffects,
1592 ClearDrawings,
1594
1595 GotoXY,
1597 GotoEntity,
1599
1600 PointTowardsXY,
1602 PointTowardsEntity,
1604
1605 Forward,
1607
1608 UnknownBlock { name: CompactString },
1610}
1611
1612pub enum Request<'gc, C: CustomTypes<S>, S: System<C>> {
1614 Input { prompt: Option<Value<'gc, C, S>> },
1616 Syscall { name: CompactString, args: Vec<Value<'gc, C, S>> },
1618 Rpc { host: Option<CompactString>, service: CompactString, rpc: CompactString, args: VecMap<CompactString, Value<'gc, C, S>, false> },
1620 Property { prop: Property },
1622 UnknownBlock { name: CompactString, args: Vec<Value<'gc, C, S>> },
1625}
1626impl<'gc, C: CustomTypes<S>, S: System<C>> Request<'gc, C, S> {
1627 pub fn feature(&self) -> Feature {
1629 match self {
1630 Request::Input { .. } => Feature::Input,
1631 Request::Syscall { name, .. } => Feature::Syscall { name: name.clone() },
1632 Request::Rpc { service, rpc, .. } => Feature::Rpc { service: service.clone(), rpc: rpc.clone() },
1633 Request::Property { prop } => Feature::GetProperty { prop: *prop },
1634 Request::UnknownBlock { name, .. } => Feature::UnknownBlock { name: name.clone() },
1635 }
1636 }
1637}
1638
1639pub enum Command<'gc, 'a, C: CustomTypes<S>, S: System<C>> {
1641 Print { style: PrintStyle, value: Option<Value<'gc, C, S>> },
1643
1644 SetProperty { prop: Property, value: Value<'gc, C, S> },
1646 ChangeProperty { prop: Property, delta: Value<'gc, C, S> },
1648
1649 ClearEffects,
1651 ClearDrawings,
1653
1654 SetCostume,
1658
1659 PlaySound { sound: Rc<Audio>, blocking: bool },
1662 PlayNotes { notes: Vec<Note>, beats: Number, blocking: bool },
1665 StopSounds,
1667
1668 GotoXY { x: Number, y: Number },
1670 GotoEntity { target: &'a Entity<'gc, C, S> },
1672
1673 PointTowardsXY { x: Number, y: Number },
1675 PointTowardsEntity { target: &'a Entity<'gc, C, S> },
1677
1678 Forward { distance: Number },
1680}
1681impl<'gc, C: CustomTypes<S>, S: System<C>> Command<'gc, '_, C, S> {
1682 pub fn feature(&self) -> Feature {
1684 match self {
1685 Command::Print { .. } => Feature::Print,
1686 Command::SetProperty { prop, .. } => Feature::SetProperty { prop: *prop },
1687 Command::ChangeProperty { prop, .. } => Feature::ChangeProperty { prop: *prop },
1688 Command::SetCostume => Feature::SetCostume,
1689 Command::PlaySound { blocking, .. } => Feature::PlaySound { blocking: *blocking },
1690 Command::PlayNotes { blocking, .. } => Feature::PlayNotes { blocking: *blocking },
1691 Command::StopSounds => Feature::StopSounds,
1692 Command::ClearEffects => Feature::ClearEffects,
1693 Command::ClearDrawings => Feature::ClearDrawings,
1694 Command::GotoXY { .. } => Feature::GotoXY,
1695 Command::GotoEntity { .. } => Feature::GotoEntity,
1696 Command::PointTowardsXY { .. } => Feature::PointTowardsXY,
1697 Command::PointTowardsEntity { .. } => Feature::PointTowardsEntity,
1698 Command::Forward { .. } => Feature::Forward,
1699 }
1700 }
1701}
1702
1703pub enum RequestStatus<'gc, C: CustomTypes<S>, S: System<C>> {
1705 Handled,
1707 UseDefault { key: S::RequestKey, request: Request<'gc, C, S> },
1710}
1711pub enum CommandStatus<'gc, 'a, C: CustomTypes<S>, S: System<C>> {
1713 Handled,
1715 UseDefault { key: S::CommandKey, command: Command<'gc, 'a, C, S> },
1718}
1719
1720#[derive(Educe)]
1722#[educe(Clone)]
1723pub struct Config<C: CustomTypes<S>, S: System<C>> {
1724 pub request: Option<Rc<dyn for<'gc> Fn(&Mutation<'gc>, S::RequestKey, Request<'gc, C, S>, &mut Process<'gc, C, S>) -> RequestStatus<'gc, C, S>>>,
1726 pub command: Option<Rc<dyn for<'gc, 'a> Fn(&Mutation<'gc>, S::CommandKey, Command<'gc, 'a, C, S>, &mut Process<'gc, C, S>) -> CommandStatus<'gc, 'a, C, S>>>,
1728}
1729impl<C: CustomTypes<S>, S: System<C>> Default for Config<C, S> {
1730 fn default() -> Self {
1731 Self {
1732 request: None,
1733 command: None,
1734 }
1735 }
1736}
1737impl<C: CustomTypes<S>, S: System<C>> Config<C, S> {
1738 pub fn fallback(&self, other: &Self) -> Self {
1740 Self {
1741 request: match (self.request.clone(), other.request.clone()) {
1742 (Some(a), Some(b)) => Some(Rc::new(move |mc, key, request, proc| {
1743 match a(mc, key, request, proc) {
1744 RequestStatus::Handled => RequestStatus::Handled,
1745 RequestStatus::UseDefault { key, request } => b(mc, key, request, proc),
1746 }
1747 })),
1748 (Some(a), None) | (None, Some(a)) => Some(a),
1749 (None, None) => None,
1750 },
1751 command: match (self.command.clone(), other.command.clone()) {
1752 (Some(a), Some(b)) => Some(Rc::new(move |mc, key, command, proc| {
1753 match a(mc, key, command, proc) {
1754 CommandStatus::Handled => CommandStatus::Handled,
1755 CommandStatus::UseDefault { key, command } => b(mc, key, command, proc),
1756 }
1757 })),
1758 (Some(a), None) | (None, Some(a)) => Some(a),
1759 (None, None) => None,
1760 },
1761 }
1762 }
1763}
1764
1765pub trait Unwindable {
1767 type UnwindPoint: 'static;
1769
1770 fn get_unwind_point(&self) -> Self::UnwindPoint;
1772 fn unwind_to(&mut self, unwind_point: &Self::UnwindPoint);
1774}
1775
1776pub trait CustomTypes<S: System<Self>>: 'static + Sized {
1778 type NativeValue: 'static + GetType + fmt::Debug;
1783
1784 type Intermediate: 'static + Send + From<SimpleValue>;
1789
1790 type EntityState: 'static + for<'gc, 'a> From<EntityKind<'gc, 'a, Self, S>>;
1794
1795 type ProcessState: 'static + Unwindable + for<'gc, 'a> From<ProcessKind<'gc, 'a, Self, S>>;
1799
1800 fn from_intermediate<'gc>(mc: &Mutation<'gc>, value: Self::Intermediate) -> Value<'gc, Self, S>;
1802}
1803
1804pub enum SysTime {
1806 Timeless,
1808 Arbitrary { ms: u64 },
1811 Real { local: OffsetDateTime },
1815}
1816impl SysTime {
1817 pub fn to_arbitrary_ms<C: CustomTypes<S>, S: System<C>>(&self) -> Result<u64, ErrorCause<C, S>> {
1819 match self {
1820 Self::Timeless => Err(ErrorCause::NotSupported { feature: Feature::ArbitraryTime }),
1821 Self::Arbitrary { ms } => Ok(*ms),
1822 Self::Real { local } => Ok((local.unix_timestamp_nanos() / 1000000) as u64),
1823 }
1824 }
1825 pub fn to_real_local<C: CustomTypes<S>, S: System<C>>(&self) -> Result<time::OffsetDateTime, ErrorCause<C, S>> {
1827 match self {
1828 Self::Timeless | Self::Arbitrary { .. } => Err(ErrorCause::NotSupported { feature: Feature::RealTime }),
1829 Self::Real { local } => Ok(*local),
1830 }
1831 }
1832}
1833
1834#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
1836pub enum Precision {
1837 Low,
1838 Medium,
1839 High,
1840}
1841
1842const SHARP_NOTES: &'static str = "A A# B C C# D D# E F F# G G#";
1843const FLAT_NOTES: &'static str = "A Bb B C Db D Eb E F Gb G Ab";
1844
1845#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
1847#[repr(transparent)]
1848pub struct Note(u8);
1849impl Note {
1850 pub fn from_midi(val: u8, natural: bool) -> Option<Self> {
1852 if val < 0x80 { Some(Self(if natural { 0x80 } else { 0 } | val)) } else { None }
1853 }
1854 pub fn from_name(name: &str) -> Option<Self> {
1856 let mut c = name.trim().chars().peekable();
1857
1858 let note = c.next().unwrap_or('\0').to_ascii_uppercase();
1859 let note = SHARP_NOTES.split(' ').enumerate().find(|x| x.1.len() == 1 && x.1.starts_with(note))?.0 as i32;
1860
1861 let mut octave = None;
1862 let mut delta = 0;
1863 let mut natural = false;
1864 loop {
1865 match c.next() {
1866 Some(ch) => match ch {
1867 '+' | '-' | '0'..='9' => {
1868 if octave.is_some() { return None }
1869
1870 let mut v = CompactString::default();
1871 v.push(ch);
1872 loop {
1873 match c.peek() {
1874 Some('0'..='9') => v.push(c.next().unwrap()),
1875 _ => break,
1876 }
1877 }
1878 octave = Some(v.parse::<i32>().ok()?);
1879 }
1880 's' | '#' | '♯' => { delta += 1; natural = true; }
1881 'b' | '♭' => { delta -= 1; natural = true; }
1882 'n' | '♮' => natural = true,
1883 _ => return None,
1884 }
1885 None => break,
1886 }
1887 }
1888 let mut octave = octave?;
1889 if note >= 3 {
1890 octave -= 1;
1891 }
1892
1893 let value = 21 + note + 12 * octave + delta;
1894 if value >= 0 { Self::from_midi(value as u8, natural) } else { None }
1895 }
1896 pub fn get_midi(self) -> (u8, bool) {
1898 (self.0 & 0x7f, self.0 & 0x80 != 0)
1899 }
1900 pub fn get_frequency(self) -> Number {
1902 Number::new(440.0 * libm::exp2((self.0 as f64 - 69.0) / 12.0)).unwrap()
1903 }
1904 pub fn get_name(self, prefer_sharps: bool) -> CompactString {
1906 let octave = self.0 as i32 / 12 - 1;
1907 let note = if prefer_sharps { SHARP_NOTES } else { FLAT_NOTES }.split(' ').nth((self.0 as usize + 3) % 12).unwrap();
1908 format_compact!("{note}{octave}")
1909 }
1910}
1911
1912#[derive(Debug, Clone, PartialOrd, Ord, PartialEq, Eq)]
1914pub struct ExternReplyKey {
1915 pub request_id: CompactString,
1916}
1917#[derive(Debug, Clone)]
1919pub struct InternReplyKey {
1920 pub src_id: CompactString,
1921 pub request_id: CompactString,
1922}
1923
1924pub trait System<C: CustomTypes<Self>>: 'static + Sized {
1931 type RequestKey: 'static + Key<Result<C::Intermediate, CompactString>>;
1933 type CommandKey: 'static + Key<Result<(), CompactString>>;
1935
1936 fn rand<T: SampleUniform, R: SampleRange<T>>(&self, range: R) -> T;
1940
1941 fn time(&self, precision: Precision) -> SysTime;
1943
1944 fn perform_request<'gc>(&self, mc: &Mutation<'gc>, request: Request<'gc, C, Self>, proc: &mut Process<'gc, C, Self>) -> Result<Self::RequestKey, ErrorCause<C, Self>>;
1948 fn poll_request<'gc>(&self, mc: &Mutation<'gc>, key: &Self::RequestKey, proc: &mut Process<'gc, C, Self>) -> Result<AsyncResult<Result<Value<'gc, C, Self>, CompactString>>, ErrorCause<C, Self>>;
1951
1952 fn perform_command<'gc>(&self, mc: &Mutation<'gc>, command: Command<'gc, '_, C, Self>, proc: &mut Process<'gc, C, Self>) -> Result<Self::CommandKey, ErrorCause<C, Self>>;
1956 fn poll_command<'gc>(&self, mc: &Mutation<'gc>, key: &Self::CommandKey, proc: &mut Process<'gc, C, Self>) -> Result<AsyncResult<Result<(), CompactString>>, ErrorCause<C, Self>>;
1959
1960 fn send_message(&self, msg_type: CompactString, values: VecMap<CompactString, Json, false>, targets: Vec<CompactString>, expect_reply: bool) -> Result<Option<ExternReplyKey>, ErrorCause<C, Self>>;
1964 fn poll_reply(&self, key: &ExternReplyKey) -> AsyncResult<Option<Json>>;
1968 fn send_reply(&self, key: InternReplyKey, value: Json) -> Result<(), ErrorCause<C, Self>>;
1970 fn receive_message(&self) -> Option<IncomingMessage>;
1974}