1extern 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 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 $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 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(¢er.into(), &self.p0.0.into()) * factor;
278 let pd1 = na::distance(¢er.into(), &self.p1.0.into()) * factor;
279 let pd2 = na::distance(¢er.into(), &self.p2.0.into()) * factor;
280 let pd3 = na::distance(¢er.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 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
1369pub 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
1388pub fn rotate2d(angle: f64, points: &mut [Vector2]) {
1392 rotate2d_gen(angle, points, |x| x)
1393}
1394
1395pub fn pointing_towards2(angle: f64) -> Vector2 {
1397 let sin = angle.sin();
1398 let cos = angle.cos();
1399 Vector2::new(cos, sin).normalize()
1401}
1402
1403pub fn normal2(v: Vector2) -> Vector2 {
1404 Vector2::new(v.y, -v.x)
1405}
1406
1407pub 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
1423pub 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}