dcso3/
lib.rs

1/*
2Copyright 2024 Eric Stokes.
3
4This file is part of dcso3.
5
6dcso3 is free software: you can redistribute it and/or modify it under
7the terms of the MIT License.
8
9dcso3 is distributed in the hope that it will be useful, but WITHOUT
10ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11FITNESS FOR A PARTICULAR PURPOSE.
12*/
13
14extern crate nalgebra as na;
15use anyhow::{anyhow, bail, Result};
16use compact_str::CompactString;
17use fxhash::FxHashMap;
18use log::error;
19use mlua::{prelude::*, Value};
20use serde_derive::{Deserialize, Serialize};
21use std::{
22    backtrace::Backtrace,
23    borrow::Borrow,
24    cell::RefCell,
25    collections::hash_map::Entry,
26    f64,
27    fmt::Debug,
28    marker::PhantomData,
29    ops::{Add, AddAssign, Deref, DerefMut, Sub},
30    panic::{self, AssertUnwindSafe},
31};
32
33pub mod airbase;
34pub mod attribute;
35pub mod coalition;
36pub mod controller;
37pub mod coord;
38pub mod country;
39pub mod dcs;
40pub mod env;
41pub mod event;
42pub mod group;
43pub mod hooks;
44pub mod land;
45pub mod lfs;
46pub mod mission_commands;
47pub mod net;
48pub mod object;
49pub mod perf;
50pub mod spot;
51pub mod static_object;
52pub mod timer;
53pub mod trigger;
54pub mod unit;
55pub mod warehouse;
56pub mod weapon;
57pub mod world;
58
59#[macro_export]
60macro_rules! atomic_id {
61    ($name:ident) => {
62        paste::paste! {
63            #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
64            pub struct $name(i64);
65
66            impl From<i64> for $name {
67                fn from(x: i64) -> Self {
68                    Self(x)
69                }
70            }
71
72            impl std::str::FromStr for $name {
73                type Err = anyhow::Error;
74
75                fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
76                    Ok(Self(s.parse::<i64>()?))
77                }
78            }
79
80            impl serde::Serialize for $name {
81                fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: serde::Serializer {
82                    serializer.serialize_i64(self.0)
83                }
84            }
85
86            pub struct [<$name Visitor>];
87
88            impl<'de> serde::de::Visitor<'de> for [<$name Visitor>] {
89                type Value = $name;
90
91                fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
92                    write!(formatter, "a i64")
93                }
94
95                fn visit_i64<E>(self, v: i64) -> Result<Self::Value, E>
96                where
97                    E: serde::de::Error,
98                {
99                    $name::update_max(v);
100                    Ok($name(v))
101                }
102
103                fn visit_u64<E>(self, v: u64) -> Result<Self::Value, E>
104                where
105                    E: serde::de::Error,
106                {
107                    let v = v as i64;
108                    $name::update_max(v);
109                    Ok($name(v))
110                }
111            }
112
113            impl<'de> serde::Deserialize<'de> for $name {
114                fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: serde::Deserializer<'de> {
115                    deserializer.deserialize_i64([<$name Visitor>])
116                }
117            }
118
119            static [<MAX_ $name:upper _ID>]: std::sync::atomic::AtomicI64 = std::sync::atomic::AtomicI64::new(0);
120
121            impl std::fmt::Display for $name {
122                fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
123                    write!(f, "{}", self.0)
124                }
125            }
126
127            impl std::default::Default for $name {
128                fn default() -> Self {
129                    Self::new()
130                }
131            }
132
133            impl<'lua> mlua::FromLua<'lua> for $name {
134                fn from_lua(value: mlua::Value<'lua>, lua: &'lua mlua::Lua) -> mlua::prelude::LuaResult<Self> {
135                    let i = i64::from_lua(value, lua)?;
136                    Ok($name(i))
137                }
138            }
139
140            impl<'lua> mlua::IntoLua<'lua> for $name {
141                fn into_lua(self, lua: &'lua mlua::Lua) -> mlua::prelude::LuaResult<mlua::Value<'lua>> {
142                    self.0.into_lua(lua)
143                }
144            }
145
146            impl $name {
147                pub fn new() -> Self {
148                    Self([<MAX_ $name:upper _ID>].fetch_add(1, std::sync::atomic::Ordering::Relaxed))
149                }
150
151                fn update_max(n: i64) {
152                    const O: std::sync::atomic::Ordering = std::sync::atomic::Ordering::Relaxed;
153                    let _: Result<_, _> = [<MAX_ $name:upper _ID>].fetch_update(O, O, |cur| {
154                        if n >= cur {
155                            Some(n.wrapping_add(1))
156                        } else {
157                            None
158                        }
159                    });
160                }
161
162                pub fn inner(&self) -> i64 {
163                    self.0
164                }
165
166                pub fn setseq(i: i64) {
167                    [<MAX_ $name:upper _ID>].store(i, std::sync::atomic::Ordering::Relaxed)
168                }
169
170                pub fn seq() -> i64 {
171                    [<MAX_ $name:upper _ID>].load(std::sync::atomic::Ordering::Relaxed)
172                }
173
174                pub fn zero() -> Self {
175                    Self(0)
176                }
177            }
178        }
179    }
180}
181
182#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
183pub struct Quad2 {
184    pub p0: LuaVec2,
185    pub p1: LuaVec2,
186    pub p2: LuaVec2,
187    pub p3: LuaVec2,
188}
189
190impl<'lua> FromLua<'lua> for Quad2 {
191    fn from_lua(value: Value<'lua>, _lua: &'lua Lua) -> LuaResult<Self> {
192        let verts = as_tbl("Quad", None, value).map_err(lua_err)?;
193        Ok(Self {
194            p0: verts.raw_get(1)?,
195            p1: verts.raw_get(2)?,
196            p2: verts.raw_get(3)?,
197            p3: verts.raw_get(4)?,
198        })
199    }
200}
201
202impl Quad2 {
203    pub fn contains(&self, p: LuaVec2) -> bool {
204        fn horizontal_ray_intersects_edge(p: &LuaVec2, v0: &LuaVec2, v1: &LuaVec2) -> bool {
205            if (v0.y > p.y) == (v1.y > p.y) {
206                // we're casting horizontally so we don't need to consider the case where
207                // there couldn't be a horizontal intersection
208                false
209            } else {
210                let int_x = v0.x + (p.y - v0.y) * (v1.x - v0.x) / (v1.y - v0.y);
211                int_x > p.x
212            }
213        }
214        if p == self.p0 || p == self.p1 || p == self.p2 || p == self.p3 {
215            return true;
216        }
217        let mut intersections = 0;
218        macro_rules! check_edge {
219            ($v0:expr, $v1:expr) => {
220                if $v0.y != $v1.y && horizontal_ray_intersects_edge(&p, &$v0, &$v1) {
221                    // if the ray passes through a vertex only count it if
222                    // it passes through the upper vertex (to avoid counting it twice)
223                    if $v0.y == p.y || $v1.y == p.y {
224                        let to_check = if $v0.y == p.y { $v1 } else { $v0 };
225                        if to_check.y < p.y {
226                            intersections += 1;
227                        }
228                    } else {
229                        intersections += 1
230                    }
231                }
232            };
233        }
234        check_edge!(self.p0, self.p1);
235        check_edge!(self.p1, self.p2);
236        check_edge!(self.p2, self.p3);
237        check_edge!(self.p3, self.p0);
238        intersections % 2 == 1
239    }
240
241    pub fn longest_edge(&self) -> (Vector2, Vector2, f64) {
242        [
243            (self.p0.0, self.p1.0),
244            (self.p1.0, self.p2.0),
245            (self.p2.0, self.p3.0),
246            (self.p3.0, self.p0.0),
247        ]
248        .into_iter()
249        .fold(
250            (Vector2::default(), Vector2::default(), 0.),
251            |x @ (_, _, d), (v0, v1)| {
252                let d2 = na::distance_squared(&v0.into(), &v1.into());
253                if d2 > d {
254                    (v0, v1, d2)
255                } else {
256                    x
257                }
258            },
259        )
260    }
261
262    pub fn center(&self) -> Vector2 {
263        centroid2d([self.p0.0, self.p1.0, self.p2.0, self.p3.0])
264    }
265
266    /// Scale this quad by the specified factor. The factor must be
267    /// positive. Numbers less than 1 will make the quad smaller,
268    /// numbers greater than 1 will make it bigger. If a negative
269    /// number is provided then 0 will be used.
270    pub fn scale(&self, factor: f64) -> Self {
271        let factor = factor.clamp(0., f64::INFINITY);
272        let center = self.center();
273        let p0 = (self.p0.0 - center).normalize();
274        let p1 = (self.p1.0 - center).normalize();
275        let p2 = (self.p2.0 - center).normalize();
276        let p3 = (self.p3.0 - center).normalize();
277        let pd0 = na::distance(&center.into(), &self.p0.0.into()) * factor;
278        let pd1 = na::distance(&center.into(), &self.p1.0.into()) * factor;
279        let pd2 = na::distance(&center.into(), &self.p2.0.into()) * factor;
280        let pd3 = na::distance(&center.into(), &self.p3.0.into()) * factor;
281        Self {
282            p0: LuaVec2(center + p0 * pd0),
283            p1: LuaVec2(center + p1 * pd1),
284            p2: LuaVec2(center + p2 * pd2),
285            p3: LuaVec2(center + p3 * pd3),
286        }
287    }
288
289    /// return true if the specified circle is fully contained within the quad.
290    pub fn contains_circle(&self, center: Vector2, radius: f64) -> bool {
291        fn distance_to_segment(p: Vector2, a: Vector2, b: Vector2) -> f64 {
292            let ap = p - a;
293            let ab = b - a;
294            let t = (ap.dot(&ab) / ab.dot(&ab)).clamp(0., 1.);
295            na::distance(&p.into(), &(a + t * ab).into())
296        }
297        self.contains(LuaVec2(center))
298            && distance_to_segment(center, self.p0.0, self.p1.0) >= radius
299            && distance_to_segment(center, self.p1.0, self.p2.0) >= radius
300            && distance_to_segment(center, self.p2.0, self.p3.0) >= radius
301            && distance_to_segment(center, self.p3.0, self.p0.0) >= radius
302    }
303}
304
305#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
306pub struct Color {
307    r: f32,
308    g: f32,
309    b: f32,
310    a: f32,
311}
312
313impl Color {
314    pub fn black(a: f32) -> Color {
315        Color {
316            r: 0.,
317            g: 0.,
318            b: 0.,
319            a,
320        }
321    }
322
323    pub fn red(a: f32) -> Color {
324        Color {
325            r: 1.,
326            g: 0.,
327            b: 0.,
328            a,
329        }
330    }
331
332    pub fn white(a: f32) -> Color {
333        Color {
334            r: 1.,
335            g: 1.,
336            b: 1.,
337            a,
338        }
339    }
340
341    pub fn blue(a: f32) -> Color {
342        Color {
343            r: 0.,
344            g: 0.,
345            b: 1.,
346            a,
347        }
348    }
349
350    pub fn gray(a: f32) -> Color {
351        Color {
352            r: 0.25,
353            g: 0.25,
354            b: 0.25,
355            a,
356        }
357    }
358
359    pub fn green(a: f32) -> Color {
360        Color {
361            r: 0.,
362            g: 1.,
363            b: 0.,
364            a,
365        }
366    }
367
368    pub fn yellow(a: f32) -> Color {
369        Color {
370            r: 0.75,
371            g: 1.,
372            b: 0.,
373            a,
374        }
375    }
376}
377
378impl<'lua> FromLua<'lua> for Color {
379    fn from_lua(value: Value<'lua>, _lua: &'lua Lua) -> LuaResult<Self> {
380        let tbl = as_tbl("Color", None, value).map_err(lua_err)?;
381        Ok(Self {
382            r: tbl.raw_get(1)?,
383            g: tbl.raw_get(2)?,
384            b: tbl.raw_get(3)?,
385            a: tbl.raw_get(4)?,
386        })
387    }
388}
389
390impl<'lua> IntoLua<'lua> for Color {
391    fn into_lua(self, lua: &'lua Lua) -> LuaResult<Value<'lua>> {
392        let tbl = lua.create_table()?;
393        tbl.set(1, self.r)?;
394        tbl.set(2, self.g)?;
395        tbl.set(3, self.b)?;
396        tbl.set(4, self.a)?;
397        Ok(Value::Table(tbl))
398    }
399}
400
401pub fn wrap_f<'lua, L: LuaEnv<'lua>, R: Default, F: FnOnce(L) -> Result<R>>(
402    name: &str,
403    lua: L,
404    f: F,
405) -> LuaResult<R> {
406    match panic::catch_unwind(AssertUnwindSafe(|| wrap(name, f(lua)))) {
407        Ok(r) => r,
408        Err(e) => {
409            match e.downcast::<anyhow::Error>() {
410                Ok(e) => error!("{} panicked {:?} {}", name, e, Backtrace::capture()),
411                Err(_) => error!("{} panicked {}", name, Backtrace::capture()),
412            }
413            Ok(R::default())
414        }
415    }
416}
417
418pub fn wrap<'lua, R: Default>(name: &str, res: Result<R>) -> LuaResult<R> {
419    match res {
420        Ok(r) => Ok(r),
421        Err(e) => {
422            error!("{}: {:?}", name, e);
423            Ok(R::default())
424        }
425    }
426}
427
428pub fn lua_err<E: Debug>(err: E) -> LuaError {
429    LuaError::RuntimeError(format!("{:?}", err))
430}
431
432pub trait LuaEnv<'a> {
433    fn inner(self) -> &'a Lua;
434}
435
436impl<'lua> LuaEnv<'lua> for &'lua Lua {
437    fn inner(self) -> &'lua Lua {
438        self
439    }
440}
441
442#[derive(Debug, Clone, Copy)]
443pub struct HooksLua<'lua>(&'lua Lua);
444
445impl<'lua> LuaEnv<'lua> for HooksLua<'lua> {
446    fn inner(self) -> &'lua Lua {
447        self.0
448    }
449}
450
451#[derive(Debug, Clone, Copy)]
452pub struct MizLua<'lua>(&'lua Lua);
453
454impl<'lua> LuaEnv<'lua> for MizLua<'lua> {
455    fn inner(self) -> &'lua Lua {
456        self.0
457    }
458}
459
460pub fn create_root_module<H, M>(lua: &Lua, init_hooks: H, init_miz: M) -> LuaResult<LuaTable>
461where
462    H: Fn(HooksLua) -> Result<()> + 'static,
463    M: Fn(MizLua) -> Result<()> + 'static,
464{
465    let exports = lua.create_table()?;
466    exports.set(
467        "initHooks",
468        lua.create_function(move |lua, _: ()| wrap_f("init_hooks", HooksLua(lua), &init_hooks))?,
469    )?;
470    exports.set(
471        "initMiz",
472        lua.create_function(move |lua, _: ()| wrap_f("init_miz", MizLua(lua), &init_miz))?,
473    )?;
474    Ok(exports)
475}
476
477#[macro_export]
478macro_rules! wrapped_table {
479    ($name:ident, $class:expr) => {
480        #[derive(Clone, Serialize)]
481        pub struct $name<'lua> {
482            t: mlua::Table<'lua>,
483            #[allow(dead_code)]
484            #[serde(skip)]
485            lua: &'lua Lua,
486        }
487
488        impl<'lua> std::fmt::Debug for $name<'lua> {
489            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
490                match self.t.raw_get::<&str, Value>("id_") {
491                    Ok(Value::Nil) => {
492                        let v = crate::value_to_json(&Value::Table(self.t.clone()));
493                        write!(f, "{v}")
494                    }
495                    Ok(v) => {
496                        let class: String = self
497                            .t
498                            .get_metatable()
499                            .and_then(|mt| mt.raw_get("className_").ok())
500                            .unwrap_or(String::from("unknown"));
501                        write!(f, "{{ class: {}, id: {:?} }}", class, v)
502                    }
503                    Err(_) => write!(f, "{:?}", self.t),
504                }
505            }
506        }
507
508        impl<'lua> Deref for $name<'lua> {
509            type Target = mlua::Table<'lua>;
510
511            fn deref(&self) -> &Self::Target {
512                &self.t
513            }
514        }
515
516        impl<'lua> FromLua<'lua> for $name<'lua> {
517            fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> LuaResult<Self> {
518                Ok(Self {
519                    t: as_tbl(stringify!($name), $class, value).map_err(crate::lua_err)?,
520                    lua,
521                })
522            }
523        }
524
525        impl<'lua> IntoLua<'lua> for $name<'lua> {
526            fn into_lua(self, _lua: &'lua Lua) -> LuaResult<Value<'lua>> {
527                Ok(Value::Table(self.t))
528            }
529        }
530    };
531}
532
533#[macro_export]
534macro_rules! simple_enum {
535    ($name:ident, $repr:ident, [$($case:ident => $num:literal),+]) => {
536        #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
537        #[allow(non_camel_case_types)]
538        #[repr($repr)]
539        pub enum $name {
540            $($case = $num),+
541        }
542
543        impl<'lua> FromLua<'lua> for $name {
544            fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> LuaResult<Self> {
545                Ok(match $repr::from_lua(value, lua)? {
546                    $($num => Self::$case),+,
547                    _ => return Err(cvt_err(stringify!($name)))
548                })
549            }
550        }
551
552        impl<'lua> IntoLua<'lua> for $name {
553            fn into_lua(self, _lua: &'lua Lua) -> LuaResult<Value<'lua>> {
554                Ok(Value::Integer(self as i64))
555            }
556        }
557    };
558}
559
560#[macro_export]
561macro_rules! bitflags_enum {
562    ($name:ident, $repr:ident, [$($case:ident => $num:literal),+]) => {
563        #[bitflags]
564        #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
565        #[allow(non_camel_case_types)]
566        #[repr($repr)]
567        pub enum $name {
568            $($case = $num),+
569        }
570
571        impl<'lua> FromLua<'lua> for $name {
572            fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> LuaResult<Self> {
573                Ok(match $repr::from_lua(value, lua)? {
574                    $($num => Self::$case),+,
575                    _ => return Err(cvt_err(stringify!($name)))
576                })
577            }
578        }
579
580        impl<'lua> IntoLua<'lua> for $name {
581            fn into_lua(self, _lua: &'lua Lua) -> LuaResult<Value<'lua>> {
582                Ok(Value::Integer(self as i64))
583            }
584        }
585    };
586}
587
588#[macro_export]
589macro_rules! string_enum {
590    ($name:ident, $repr:ident, [$($case:ident => $str:literal),+]) => {
591        string_enum!($name, $repr, [$($case => $str),+], []);
592    };
593    ($name:ident,
594     $repr:ident,
595     [$($case:ident => $str:literal),+],
596     [$($altcase:ident => $altstr:literal),*]) => {
597        #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
598        #[allow(non_camel_case_types)]
599        #[repr($repr)]
600        pub enum $name {
601            $($case),+,
602            Custom(String)
603        }
604
605        impl<'lua> FromLua<'lua> for $name {
606            fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> LuaResult<Self> {
607                let s = String::from_lua(value, lua)?;
608                Ok(match s.as_str() {
609                    $($str => Self::$case,)+
610                    $($altstr => Self::$altcase,)*
611                    _ => Self::Custom(s)
612                })
613            }
614        }
615
616        impl<'lua> IntoLua<'lua> for $name {
617            fn into_lua(self, lua: &'lua Lua) -> LuaResult<Value<'lua>> {
618                Ok(Value::String(match self {
619                    $(Self::$case => lua.create_string($str)?),+,
620                    Self::Custom(s) => lua.create_string(s.as_str())?
621                }))
622            }
623        }
624    };
625}
626
627#[macro_export]
628macro_rules! wrapped_prim {
629    ($name:ident, $type:ty) => {
630        wrapped_prim!($name, $type, );
631    };
632    ($name:ident, $type:ty, $($extra_derives:ident),*) => {
633        #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, $($extra_derives),*)]
634        pub struct $name($type);
635
636        impl $name {
637            pub fn inner(self) -> $type {
638                self.0
639            }
640        }
641
642        impl From<$type> for $name {
643            fn from(t: $type) -> Self {
644                Self(t)
645            }
646        }
647
648        impl<'lua> FromLua<'lua> for $name {
649            fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> LuaResult<Self> {
650                Ok(Self(FromLua::from_lua(value, lua)?))
651            }
652        }
653
654        impl<'lua> IntoLua<'lua> for $name {
655            fn into_lua(self, lua: &'lua Lua) -> LuaResult<Value<'lua>> {
656                Ok(self.0.into_lua(lua)?)
657            }
658        }
659    };
660}
661
662pub fn cvt_err(to: &'static str) -> LuaError {
663    LuaError::FromLuaConversionError {
664        from: "value",
665        to,
666        message: None,
667    }
668}
669
670pub fn err(msg: &str) -> LuaError {
671    LuaError::runtime(msg)
672}
673
674pub fn as_tbl_ref<'a: 'lua, 'lua>(
675    to: &'static str,
676    value: &'a Value<'lua>,
677) -> Result<&'a mlua::Table<'lua>> {
678    value
679        .as_table()
680        .ok_or_else(|| anyhow!("can't convert {:?} to {}", value, to))
681}
682
683fn check_implements(tbl: &mlua::Table, class: &str) -> bool {
684    let mut parent = None;
685    loop {
686        let tbl = match parent.as_ref() {
687            None => tbl,
688            Some(tbl) => tbl,
689        };
690        match tbl.raw_get::<_, String>("className_") {
691            Err(_) => break false,
692            Ok(s) if s.as_str() == class => break true,
693            Ok(_) => match tbl.raw_get::<_, mlua::Table>("parentClass_") {
694                Err(_) => break false,
695                Ok(t) => {
696                    parent = Some(t);
697                }
698            },
699        }
700    }
701}
702
703pub fn as_tbl<'lua>(
704    to: &'static str,
705    objtyp: Option<&'static str>,
706    value: Value<'lua>,
707) -> Result<mlua::Table<'lua>> {
708    match value {
709        Value::Table(tbl) => match objtyp {
710            None => Ok(tbl),
711            Some(typ) => match tbl.get_metatable() {
712                None => bail!(
713                    "to: {to}. not an object, expected object of type {} got {:?}",
714                    typ,
715                    tbl
716                ),
717                Some(meta) => {
718                    if check_implements(&meta, typ) {
719                        Ok(tbl)
720                    } else {
721                        bail!("to: {to}. expected object of type {}, got {:?}", typ, tbl)
722                    }
723                }
724            },
725        },
726        _ => bail!("expected a table, got {:?}", value),
727    }
728}
729
730pub trait DeepClone<'lua>: IntoLua<'lua> + FromLua<'lua> + Clone {
731    fn deep_clone(&self, lua: &'lua Lua) -> Result<Self>;
732}
733
734impl<'lua, T> DeepClone<'lua> for T
735where
736    T: IntoLua<'lua> + FromLua<'lua> + Clone,
737{
738    fn deep_clone(&self, lua: &'lua Lua) -> Result<Self> {
739        let v = match self.clone().into_lua(lua)? {
740            Value::Boolean(b) => Value::Boolean(b),
741            Value::Error(e) => Value::Error(e),
742            Value::Function(f) => Value::Function(f),
743            Value::Integer(i) => Value::Integer(i),
744            Value::LightUserData(d) => Value::LightUserData(d),
745            Value::Nil => Value::Nil,
746            Value::Number(n) => Value::Number(n),
747            Value::String(s) => Value::String(lua.create_string(s)?),
748            Value::Table(t) => {
749                let new = lua.create_table()?;
750                new.set_metatable(t.get_metatable());
751                for r in t.pairs::<Value, Value>() {
752                    let (k, v) = r?;
753                    new.set(k.deep_clone(lua)?, v.deep_clone(lua)?)?
754                }
755                Value::Table(new)
756            }
757            Value::Thread(t) => Value::Thread(t),
758            Value::UserData(d) => Value::UserData(d),
759        };
760        Ok(T::from_lua(v, lua)?)
761    }
762}
763
764pub fn is_hooks_env(lua: &Lua) -> bool {
765    lua.globals().contains_key("DCS").unwrap_or(false)
766}
767
768#[derive(Debug, Clone, PartialEq, Serialize)]
769pub enum PathElt {
770    Integer(i64),
771    String(String),
772}
773
774impl From<&str> for PathElt {
775    fn from(value: &str) -> Self {
776        PathElt::String(String::from(value))
777    }
778}
779
780impl From<String> for PathElt {
781    fn from(value: String) -> Self {
782        PathElt::String(value)
783    }
784}
785
786impl From<std::string::String> for PathElt {
787    fn from(value: std::string::String) -> Self {
788        PathElt::String(String::from(value))
789    }
790}
791
792impl From<usize> for PathElt {
793    fn from(value: usize) -> Self {
794        PathElt::Integer(value as i64)
795    }
796}
797
798impl From<u64> for PathElt {
799    fn from(value: u64) -> Self {
800        PathElt::Integer(value as i64)
801    }
802}
803
804impl From<u32> for PathElt {
805    fn from(value: u32) -> Self {
806        PathElt::Integer(value as i64)
807    }
808}
809
810impl From<i64> for PathElt {
811    fn from(value: i64) -> Self {
812        PathElt::Integer(value)
813    }
814}
815
816impl From<i32> for PathElt {
817    fn from(value: i32) -> Self {
818        PathElt::Integer(value as i64)
819    }
820}
821
822impl From<u8> for PathElt {
823    fn from(value: u8) -> Self {
824        PathElt::Integer(value as i64)
825    }
826}
827
828impl<'lua> IntoLua<'lua> for &PathElt {
829    fn into_lua(self, lua: &'lua Lua) -> LuaResult<Value<'lua>> {
830        Ok(match self {
831            PathElt::Integer(i) => Value::Integer(*i),
832            PathElt::String(s) => Value::String(lua.create_string(s.as_bytes())?),
833        })
834    }
835}
836
837impl<'lua> FromLua<'lua> for PathElt {
838    fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> LuaResult<Self> {
839        Ok(match value {
840            Value::Integer(n) => PathElt::Integer(n),
841            Value::String(_) => PathElt::String(String::from_lua(value, lua)?),
842            _ => return Err(cvt_err("String")),
843        })
844    }
845}
846
847#[derive(Debug, Clone, Serialize, Default)]
848pub struct Path(Vec<PathElt>);
849
850impl Deref for Path {
851    type Target = [PathElt];
852
853    fn deref(&self) -> &Self::Target {
854        &self.0[..]
855    }
856}
857
858impl<'a> IntoIterator for &'a Path {
859    type IntoIter = std::slice::Iter<'a, PathElt>;
860    type Item = &'a PathElt;
861
862    fn into_iter(self) -> Self::IntoIter {
863        self.0.iter()
864    }
865}
866
867impl Path {
868    pub fn new() -> Self {
869        Self(vec![])
870    }
871
872    pub fn push<T: Into<PathElt>>(&mut self, t: T) {
873        self.0.push(t.into())
874    }
875
876    pub fn pop(&mut self) -> Option<PathElt> {
877        self.0.pop()
878    }
879
880    pub fn append<T: Into<PathElt>, I: IntoIterator<Item = T>>(&self, elts: I) -> Self {
881        let mut new_t = self.clone();
882        for elt in elts {
883            new_t.push(elt)
884        }
885        new_t
886    }
887
888    pub fn get(&self, i: usize) -> Option<&PathElt> {
889        self.0.get(i)
890    }
891}
892
893#[macro_export]
894macro_rules! path {
895    ($($v:expr),*) => {{
896        let mut path = dcso3::Path::new();
897        $(path.push($v));*;
898        path
899    }}
900}
901
902pub trait DcsTableExt<'lua> {
903    fn raw_get_path<T>(&self, path: &Path) -> Result<T>
904    where
905        T: FromLua<'lua>;
906
907    fn get_path<T>(&self, path: &Path) -> Result<T>
908    where
909        T: FromLua<'lua>;
910}
911
912fn table_raw_get_path<'lua, T>(tbl: &mlua::Table<'lua>, path: &[PathElt]) -> Result<T>
913where
914    T: FromLua<'lua>,
915{
916    match path {
917        [] => bail!("path not found"),
918        [elt] => Ok(tbl.raw_get(elt)?),
919        [elt, path @ ..] => {
920            let tbl: mlua::Table = tbl.raw_get(elt)?;
921            table_raw_get_path(&tbl, path)
922        }
923    }
924}
925
926fn table_get_path<'lua, T>(tbl: &mlua::Table<'lua>, path: &[PathElt]) -> Result<T>
927where
928    T: FromLua<'lua>,
929{
930    match path {
931        [] => bail!("path not found"),
932        [elt] => Ok(tbl.get(elt)?),
933        [elt, path @ ..] => {
934            let tbl: mlua::Table = tbl.get(elt)?;
935            table_get_path(&tbl, path)
936        }
937    }
938}
939
940impl<'lua> DcsTableExt<'lua> for mlua::Table<'lua> {
941    fn raw_get_path<T>(&self, path: &Path) -> Result<T>
942    where
943        T: FromLua<'lua>,
944    {
945        table_raw_get_path(self, &**path)
946    }
947
948    fn get_path<T>(&self, path: &Path) -> Result<T>
949    where
950        T: FromLua<'lua>,
951    {
952        table_get_path(self, &**path)
953    }
954}
955
956pub type Vector2 = na::base::Vector2<f64>;
957
958#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Default, Serialize, Deserialize)]
959pub struct LuaVec2(pub na::base::Vector2<f64>);
960
961impl Deref for LuaVec2 {
962    type Target = na::base::Vector2<f64>;
963
964    fn deref(&self) -> &Self::Target {
965        &self.0
966    }
967}
968
969impl DerefMut for LuaVec2 {
970    fn deref_mut(&mut self) -> &mut Self::Target {
971        &mut self.0
972    }
973}
974
975impl<'lua> IntoLua<'lua> for LuaVec2 {
976    fn into_lua(self, lua: &'lua Lua) -> LuaResult<Value<'lua>> {
977        let tbl = lua.create_table()?;
978        tbl.set("x", self.0.x)?;
979        tbl.set("y", self.0.y)?;
980        Ok(Value::Table(tbl))
981    }
982}
983
984impl<'lua> FromLua<'lua> for LuaVec2 {
985    fn from_lua(value: Value<'lua>, _: &'lua Lua) -> LuaResult<Self> {
986        let tbl = as_tbl("Vec2", None, value).map_err(lua_err)?;
987        Ok(Self(na::base::Vector2::new(
988            tbl.raw_get("x")?,
989            tbl.raw_get("y")?,
990        )))
991    }
992}
993
994impl LuaVec2 {
995    pub fn new(x: f64, y: f64) -> Self {
996        LuaVec2(na::base::Vector2::new(x, y))
997    }
998}
999
1000pub type Vector3 = na::base::Vector3<f64>;
1001
1002#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Default, Serialize, Deserialize)]
1003pub struct LuaVec3(pub na::base::Vector3<f64>);
1004
1005impl Deref for LuaVec3 {
1006    type Target = na::base::Vector3<f64>;
1007
1008    fn deref(&self) -> &Self::Target {
1009        &self.0
1010    }
1011}
1012
1013impl DerefMut for LuaVec3 {
1014    fn deref_mut(&mut self) -> &mut Self::Target {
1015        &mut self.0
1016    }
1017}
1018
1019impl<'lua> FromLua<'lua> for LuaVec3 {
1020    fn from_lua(value: Value<'lua>, _: &'lua Lua) -> LuaResult<Self> {
1021        let tbl = as_tbl("Vec3", None, value).map_err(lua_err)?;
1022        Ok(Self(na::base::Vector3::new(
1023            tbl.raw_get("x")?,
1024            tbl.raw_get("y")?,
1025            tbl.raw_get("z")?,
1026        )))
1027    }
1028}
1029
1030impl<'lua> IntoLua<'lua> for LuaVec3 {
1031    fn into_lua(self, lua: &'lua Lua) -> LuaResult<Value<'lua>> {
1032        let tbl = lua.create_table()?;
1033        tbl.raw_set("x", self.0.x)?;
1034        tbl.raw_set("y", self.0.y)?;
1035        tbl.raw_set("z", self.0.z)?;
1036        Ok(Value::Table(tbl))
1037    }
1038}
1039
1040impl LuaVec3 {
1041    pub fn new(x: f64, y: f64, z: f64) -> Self {
1042        Self(na::base::Vector3::new(x, y, z))
1043    }
1044}
1045
1046#[derive(Debug, Clone, Copy, PartialEq, Default, Serialize, Deserialize)]
1047pub struct Position3 {
1048    pub p: LuaVec3,
1049    pub x: LuaVec3,
1050    pub y: LuaVec3,
1051    pub z: LuaVec3,
1052}
1053
1054impl<'lua> FromLua<'lua> for Position3 {
1055    fn from_lua(value: Value<'lua>, _: &'lua Lua) -> LuaResult<Self> {
1056        let tbl = as_tbl("Position3", None, value).map_err(lua_err)?;
1057        Ok(Self {
1058            p: tbl.raw_get("p")?,
1059            x: tbl.raw_get("x")?,
1060            y: tbl.raw_get("y")?,
1061            z: tbl.raw_get("z")?,
1062        })
1063    }
1064}
1065
1066impl<'lua> IntoLua<'lua> for Position3 {
1067    fn into_lua(self, lua: &'lua Lua) -> LuaResult<Value<'lua>> {
1068        let tbl = lua.create_table()?;
1069        tbl.raw_set("p", self.p)?;
1070        tbl.raw_set("x", self.x)?;
1071        tbl.raw_set("y", self.y)?;
1072        tbl.raw_set("z", self.z)?;
1073        Ok(Value::Table(tbl))
1074    }
1075}
1076
1077#[derive(Debug, Clone, Copy, PartialEq, Serialize)]
1078pub struct Box3 {
1079    pub min: LuaVec3,
1080    pub max: LuaVec3,
1081}
1082
1083impl<'lua> FromLua<'lua> for Box3 {
1084    fn from_lua(value: Value<'lua>, _: &'lua Lua) -> LuaResult<Self> {
1085        let tbl = as_tbl("Box3", None, value).map_err(lua_err)?;
1086        Ok(Self {
1087            min: tbl.raw_get("min")?,
1088            max: tbl.raw_get("max")?,
1089        })
1090    }
1091}
1092
1093#[derive(Debug, Clone, Default, Hash, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
1094pub struct String(CompactString);
1095
1096impl std::fmt::Display for String {
1097    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1098        write!(f, "{}", self.0)
1099    }
1100}
1101
1102impl Deref for String {
1103    type Target = compact_str::CompactString;
1104
1105    fn deref(&self) -> &Self::Target {
1106        &self.0
1107    }
1108}
1109
1110impl DerefMut for String {
1111    fn deref_mut(&mut self) -> &mut Self::Target {
1112        &mut self.0
1113    }
1114}
1115
1116impl AsRef<str> for String {
1117    fn as_ref(&self) -> &str {
1118        self.0.as_ref()
1119    }
1120}
1121
1122impl Borrow<str> for String {
1123    fn borrow(&self) -> &str {
1124        self.0.borrow()
1125    }
1126}
1127
1128impl<'lua> IntoLua<'lua> for String {
1129    fn into_lua(self, lua: &'lua Lua) -> LuaResult<Value<'lua>> {
1130        Ok(Value::String(lua.create_string(self.0)?))
1131    }
1132}
1133
1134impl<'lua> FromLua<'lua> for String {
1135    fn from_lua(value: Value<'lua>, _: &'lua Lua) -> LuaResult<Self> {
1136        use compact_str::format_compact;
1137        match value {
1138            Value::String(s) => Ok(Self(CompactString::from(s.to_str()?))),
1139            Value::Boolean(b) => Ok(Self(format_compact!("{b}"))),
1140            Value::Integer(n) => Ok(Self(format_compact!("{n}"))),
1141            Value::Number(n) => Ok(Self(format_compact!("{n}"))),
1142            v => Ok(Self(CompactString::from(v.to_string()?))),
1143        }
1144    }
1145}
1146
1147impl From<&str> for String {
1148    fn from(value: &str) -> Self {
1149        Self(CompactString::from(value))
1150    }
1151}
1152
1153impl From<std::string::String> for String {
1154    fn from(value: std::string::String) -> Self {
1155        Self(CompactString::from(value))
1156    }
1157}
1158
1159impl From<CompactString> for String {
1160    fn from(value: CompactString) -> Self {
1161        Self(value)
1162    }
1163}
1164
1165#[derive(Debug, Clone, Copy, Default, PartialEq, PartialOrd, Serialize, Deserialize)]
1166pub struct Time(pub f32);
1167
1168impl<'lua> IntoLua<'lua> for Time {
1169    fn into_lua(self, lua: &'lua Lua) -> LuaResult<Value<'lua>> {
1170        self.0.into_lua(lua)
1171    }
1172}
1173
1174impl<'lua> FromLua<'lua> for Time {
1175    fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> LuaResult<Self> {
1176        Ok(Self(f32::from_lua(value, lua)?))
1177    }
1178}
1179
1180impl Add<f32> for Time {
1181    type Output = Self;
1182
1183    fn add(self, rhs: f32) -> Self::Output {
1184        Time(self.0 + rhs)
1185    }
1186}
1187
1188impl AddAssign<f32> for Time {
1189    fn add_assign(&mut self, rhs: f32) {
1190        self.0 += rhs
1191    }
1192}
1193
1194impl Sub for Time {
1195    type Output = f32;
1196
1197    fn sub(self, rhs: Self) -> f32 {
1198        self.0 - rhs.0
1199    }
1200}
1201
1202#[derive(Debug, Clone, Serialize)]
1203pub enum VolumeType {
1204    Segment,
1205    Box,
1206    Sphere,
1207    Pyramid,
1208}
1209
1210#[derive(Debug, Clone, Serialize)]
1211pub struct Sequence<'lua, T> {
1212    t: mlua::Table<'lua>,
1213    #[serde(skip)]
1214    lua: &'lua Lua,
1215    ph: PhantomData<T>,
1216}
1217
1218impl<'lua, T: FromLua<'lua> + 'lua> FromLua<'lua> for Sequence<'lua, T> {
1219    fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> LuaResult<Self> {
1220        match value {
1221            Value::Table(t) => Ok(Self {
1222                t,
1223                lua,
1224                ph: PhantomData,
1225            }),
1226            Value::Nil => Ok(Self {
1227                t: lua.create_table()?,
1228                lua,
1229                ph: PhantomData,
1230            }),
1231            _ => Err(cvt_err("Sequence")),
1232        }
1233    }
1234}
1235
1236impl<'lua, T: IntoLua<'lua> + 'lua> IntoLua<'lua> for Sequence<'lua, T> {
1237    fn into_lua(self, _lua: &'lua Lua) -> LuaResult<Value<'lua>> {
1238        Ok(Value::Table(self.t))
1239    }
1240}
1241
1242impl<'lua, T: FromLua<'lua> + 'lua> IntoIterator for Sequence<'lua, T> {
1243    type IntoIter = mlua::TableSequence<'lua, T>;
1244    type Item = LuaResult<T>;
1245
1246    fn into_iter(self) -> Self::IntoIter {
1247        self.t.sequence_values()
1248    }
1249}
1250
1251impl<'lua, T: FromLua<'lua> + 'lua> Sequence<'lua, T> {
1252    pub fn get(&self, i: i64) -> Result<T> {
1253        Ok(self.t.raw_get(i)?)
1254    }
1255}
1256
1257impl<'lua, T: IntoLua<'lua> + 'lua> Sequence<'lua, T> {
1258    pub fn set(&self, i: i64, t: T) -> Result<()> {
1259        Ok(self.t.raw_set(i, t)?)
1260    }
1261}
1262
1263impl<'lua, T: FromLua<'lua> + 'lua> Sequence<'lua, T> {
1264    pub fn empty(lua: &'lua Lua) -> Result<Self> {
1265        Ok(Self {
1266            t: lua.create_table()?,
1267            lua: lua,
1268            ph: PhantomData,
1269        })
1270    }
1271
1272    pub fn len(&self) -> usize {
1273        self.t.raw_len()
1274    }
1275
1276    pub fn into_inner(self) -> mlua::Table<'lua> {
1277        self.t
1278    }
1279
1280    pub fn remove(&self, i: i64) -> Result<()> {
1281        Ok(self.t.raw_remove(i)?)
1282    }
1283
1284    pub fn first(&self) -> Result<T> {
1285        Ok(self.t.raw_get(1)?)
1286    }
1287
1288    pub fn for_each<F: FnMut(Result<T>) -> Result<()>>(&self, mut f: F) -> Result<()> {
1289        Ok(self.t.for_each(|_: Value, v: Value| {
1290            f(T::from_lua(v, &self.lua).map_err(anyhow::Error::from)).map_err(lua_err)
1291        })?)
1292    }
1293}
1294
1295impl<'lua, T: FromLua<'lua> + IntoLua<'lua> + 'lua> Sequence<'lua, T> {
1296    pub fn push(&self, t: T) -> Result<()> {
1297        Ok(self.t.push(t)?)
1298    }
1299
1300    pub fn pop(&self) -> Result<T> {
1301        Ok(self.t.pop()?)
1302    }
1303}
1304
1305pub fn value_to_json(v: &Value) -> serde_json::Value {
1306    thread_local! {
1307        static CTX: RefCell<FxHashMap<usize, String>> = RefCell::new(FxHashMap::default());
1308    }
1309    fn inner(
1310        ctx: &mut FxHashMap<usize, String>,
1311        key: Option<&str>,
1312        v: &Value,
1313    ) -> serde_json::Value {
1314        use serde_json::{json, Map, Value as JVal};
1315        match v {
1316            Value::Nil => JVal::Null,
1317            Value::Boolean(b) => json!(b),
1318            Value::LightUserData(_) => json!("<LightUserData>"),
1319            Value::Integer(i) => json!(*i),
1320            Value::Number(i) => json!(*i),
1321            Value::UserData(_) => json!("<UserData>"),
1322            Value::String(s) => json!(s),
1323            Value::Function(_) => json!("<Function>"),
1324            Value::Thread(_) => json!("<Thread>"),
1325            Value::Error(e) => json!(format!("{e}")),
1326            Value::Table(tbl) => {
1327                let address = tbl.to_pointer() as usize;
1328                match ctx.entry(address) {
1329                    Entry::Occupied(e) => json!(format!("<Table(0x{:x} {})>", address, e.get())),
1330                    Entry::Vacant(e) => {
1331                        e.insert(String::from(key.unwrap_or("Root")));
1332                        let mut map = Map::new();
1333                        for pair in tbl.clone().pairs::<Value, Value>() {
1334                            let (k, v) = pair.unwrap();
1335                            let k = match inner(ctx, None, &k) {
1336                                JVal::String(s) => s,
1337                                v => v.to_string(),
1338                            };
1339                            let v = inner(ctx, Some(k.as_str()), &v);
1340                            map.insert(k, v);
1341                        }
1342                        JVal::Object(map)
1343                    }
1344                }
1345            }
1346        }
1347    }
1348    CTX.with_borrow_mut(|ctx| {
1349        let r = inner(ctx, None, v);
1350        ctx.clear();
1351        r
1352    })
1353}
1354
1355pub fn centroid2d(points: impl IntoIterator<Item = Vector2>) -> Vector2 {
1356    let (n, sum) = points
1357        .into_iter()
1358        .fold((0, Vector2::new(0., 0.)), |(n, c), p| (n + 1, c + p));
1359    sum / (n as f64)
1360}
1361
1362pub fn centroid3d(points: impl IntoIterator<Item = Vector3>) -> Vector3 {
1363    let (n, sum) = points
1364        .into_iter()
1365        .fold((0, Vector3::new(0., 0., 0.)), |(n, c), p| (n + 1, c + p));
1366    sum / (n as f64)
1367}
1368
1369/// Rotate a collection of points in 2d space around their center
1370/// point keeping the relative orientations of the points
1371/// constant. The angle is in radians. General about the underlying
1372/// point container type
1373pub fn rotate2d_gen<T, F: Fn(&mut T) -> &mut Vector2>(angle: f64, points: &mut [T], f: F) {
1374    let centroid = centroid2d(points.into_iter().map(|t| *f(t)));
1375    let sin = angle.sin();
1376    let cos = angle.cos();
1377    for t in points {
1378        let p = f(t);
1379        *p -= centroid;
1380        let x = p.x;
1381        let y = p.y;
1382        p.x = x * cos - y * sin;
1383        p.y = x * sin + y * cos;
1384        *p += centroid
1385    }
1386}
1387
1388/// Rotate a collection of points in 2d space around their center point
1389/// keeping the relative orientations of the points constant. The angle
1390/// is in radians.
1391pub fn rotate2d(angle: f64, points: &mut [Vector2]) {
1392    rotate2d_gen(angle, points, |x| x)
1393}
1394
1395/// return a unit vector pointing in the specified direction. angle is in radians
1396pub fn pointing_towards2(angle: f64) -> Vector2 {
1397    let sin = angle.sin();
1398    let cos = angle.cos();
1399    // dcs coords are reversed x is north/south y is east/west
1400    Vector2::new(cos, sin).normalize()
1401}
1402
1403pub fn normal2(v: Vector2) -> Vector2 {
1404    Vector2::new(v.y, -v.x)
1405}
1406
1407/// Same as rotate2d, but construct and return a vec containing the rotated points
1408/// in the same order as the they appear in the input slice.
1409pub fn rotate2d_vec(angle: f64, points: &[Vector2]) -> Vec<Vector2> {
1410    let mut points = Vec::from_iter(points.into_iter().map(|p| *p));
1411    rotate2d(angle, &mut points);
1412    points
1413}
1414
1415pub fn radians_to_degrees(radians: f64) -> f64 {
1416    radians * (180. / std::f64::consts::PI)
1417}
1418
1419pub fn degrees_to_radians(degrees: f64) -> f64 {
1420    degrees * (std::f64::consts::PI / 180.)
1421}
1422
1423/// change the heading (in radians) by the specified amount, adjusting if it rotates through 0/2 pi.
1424/// e.g. `change_heading(3/2 pi, pi) -> pi / 2 not 5 / 2 pi`
1425pub fn change_heading(heading: f64, change: f64) -> f64 {
1426    const PI2: f64 = f64::consts::PI * 2.;
1427    let change = if change.abs() > PI2 {
1428        change % PI2
1429    } else {
1430        change
1431    };
1432    let res = heading + change;
1433    if res > PI2 {
1434        res - PI2
1435    } else if res < 0. {
1436        res + PI2
1437    } else {
1438        res
1439    }
1440}
1441
1442pub fn azumith2d(v: Vector2) -> f64 {
1443    let az = v.y.atan2(v.x);
1444    if az < 0. {
1445        az + 2. * std::f64::consts::PI
1446    } else {
1447        az
1448    }
1449}
1450
1451pub fn azumith2d_to(from: Vector2, to: Vector2) -> f64 {
1452    azumith2d(to - from)
1453}
1454
1455pub fn azumith3d(v: Vector3) -> f64 {
1456    let az = v.z.atan2(v.x);
1457    if az < 0. {
1458        az + 2. * std::f64::consts::PI
1459    } else {
1460        az
1461    }
1462}
1463
1464pub fn azumith3d_to(from: Vector3, to: Vector3) -> f64 {
1465    azumith3d(to - from)
1466}