dcso3/
hooks.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 crate::{
16    coalition::Side,
17    net::{PlayerId, SlotId, Ucid},
18    wrap_f, HooksLua, LuaEnv, String,
19};
20use anyhow::Result;
21use mlua::prelude::*;
22
23#[derive(Debug)]
24pub struct UserHooks<'lua> {
25    on_mission_load_begin: Option<mlua::Function<'lua>>,
26    on_mission_load_progress: Option<mlua::Function<'lua>>,
27    on_mission_load_end: Option<mlua::Function<'lua>>,
28    on_simulation_start: Option<mlua::Function<'lua>>,
29    on_simulation_stop: Option<mlua::Function<'lua>>,
30    on_simulation_frame: Option<mlua::Function<'lua>>,
31    on_simulation_pause: Option<mlua::Function<'lua>>,
32    on_simulation_resume: Option<mlua::Function<'lua>>,
33    on_player_connect: Option<mlua::Function<'lua>>,
34    on_player_disconnect: Option<mlua::Function<'lua>>,
35    on_player_start: Option<mlua::Function<'lua>>,
36    on_player_stop: Option<mlua::Function<'lua>>,
37    on_player_change_slot: Option<mlua::Function<'lua>>,
38    on_player_try_connect: Option<mlua::Function<'lua>>,
39    on_player_try_send_chat: Option<mlua::Function<'lua>>,
40    on_player_try_change_slot: Option<mlua::Function<'lua>>,
41    lua: &'lua Lua,
42}
43
44impl<'lua> UserHooks<'lua> {
45    pub fn new(lua: HooksLua<'lua>) -> Self {
46        Self {
47            on_mission_load_begin: None,
48            on_mission_load_progress: None,
49            on_mission_load_end: None,
50            on_simulation_start: None,
51            on_simulation_stop: None,
52            on_simulation_frame: None,
53            on_simulation_pause: None,
54            on_simulation_resume: None,
55            on_player_connect: None,
56            on_player_disconnect: None,
57            on_player_start: None,
58            on_player_stop: None,
59            on_player_change_slot: None,
60            on_player_try_change_slot: None,
61            on_player_try_connect: None,
62            on_player_try_send_chat: None,
63            lua: lua.inner(),
64        }
65    }
66
67    pub fn register(&mut self) -> Result<()> {
68        let Self {
69            on_mission_load_begin,
70            on_mission_load_progress,
71            on_mission_load_end,
72            on_simulation_start,
73            on_simulation_stop,
74            on_simulation_frame,
75            on_simulation_pause,
76            on_simulation_resume,
77            on_player_connect,
78            on_player_disconnect,
79            on_player_start,
80            on_player_stop,
81            on_player_change_slot,
82            on_player_try_connect,
83            on_player_try_send_chat,
84            on_player_try_change_slot,
85            lua: _,
86        } = self;
87        let tbl = self.lua.create_table()?;
88        if let Some(f) = on_mission_load_begin.take() {
89            tbl.set("onMissionLoadBegin", f)?;
90        }
91        if let Some(f) = on_mission_load_progress.take() {
92            tbl.set("onMissionLoadProgress", f)?;
93        }
94        if let Some(f) = on_mission_load_end.take() {
95            tbl.set("onMissionLoadEnd", f)?;
96        }
97        if let Some(f) = on_simulation_start.take() {
98            tbl.set("onSimulationStart", f)?;
99        }
100        if let Some(f) = on_simulation_stop.take() {
101            tbl.set("onSimulationStop", f)?;
102        }
103        if let Some(f) = on_simulation_frame.take() {
104            tbl.set("onSimulationFrame", f)?;
105        }
106        if let Some(f) = on_simulation_pause.take() {
107            tbl.set("onSimulationPause", f)?;
108        }
109        if let Some(f) = on_simulation_resume.take() {
110            tbl.set("onSimulationResume", f)?;
111        }
112        if let Some(f) = on_player_connect.take() {
113            tbl.set("onPlayerConnect", f)?;
114        }
115        if let Some(f) = on_player_disconnect.take() {
116            tbl.set("onPlayerDisconnect", f)?;
117        }
118        if let Some(f) = on_player_start.take() {
119            tbl.set("onPlayerStart", f)?;
120        }
121        if let Some(f) = on_player_stop.take() {
122            tbl.set("onPlayerStop", f)?;
123        }
124        if let Some(f) = on_player_change_slot.take() {
125            tbl.set("onPlayerChangeSlot", f)?;
126        }
127        if let Some(f) = on_player_try_connect.take() {
128            tbl.set("onPlayerTryConnect", f)?;
129        }
130        if let Some(f) = on_player_try_send_chat.take() {
131            tbl.set("onPlayerTrySendChat", f)?;
132        }
133        if let Some(f) = on_player_try_change_slot.take() {
134            tbl.set("onPlayerTryChangeSlot", f)?;
135        }
136        let dcs: mlua::Table = self.lua.globals().get("DCS")?;
137        Ok(dcs.call_function("setUserCallbacks", tbl)?)
138    }
139
140    pub fn on_mission_load_begin<F>(&mut self, f: F) -> Result<&mut Self>
141    where
142        F: Fn(HooksLua) -> Result<()> + 'static,
143    {
144        self.on_mission_load_begin =
145            Some(self.lua.create_function(move |lua, ()| {
146                wrap_f("on_mission_load_begin", HooksLua(lua), &f)
147            })?);
148        Ok(self)
149    }
150
151    /// f(progress, message)
152    pub fn on_mission_load_progress<F>(&mut self, f: F) -> Result<&mut Self>
153    where
154        F: Fn(HooksLua, String, String) -> Result<()> + 'static,
155    {
156        self.on_mission_load_progress = Some(self.lua.create_function(
157            move |lua, (progress, message): (String, String)| {
158                wrap_f("on_mission_load_progress", HooksLua(lua), |lua| {
159                    f(lua, progress, message)
160                })
161            },
162        )?);
163        Ok(self)
164    }
165
166    pub fn on_mission_load_end<F>(&mut self, f: F) -> Result<&mut Self>
167    where
168        F: Fn(HooksLua) -> Result<()> + 'static,
169    {
170        self.on_mission_load_end =
171            Some(self.lua.create_function(move |lua, ()| {
172                wrap_f("on_mission_load_end", HooksLua(lua), &f)
173            })?);
174        Ok(self)
175    }
176
177    pub fn on_simulation_start<F>(&mut self, f: F) -> Result<&mut Self>
178    where
179        F: Fn(HooksLua) -> Result<()> + 'static,
180    {
181        self.on_simulation_start =
182            Some(self.lua.create_function(move |lua, ()| {
183                wrap_f("on_simulation_start", HooksLua(lua), &f)
184            })?);
185        Ok(self)
186    }
187
188    pub fn on_simulation_stop<F>(&mut self, f: F) -> Result<&mut Self>
189    where
190        F: Fn(HooksLua) -> Result<()> + 'static,
191    {
192        self.on_simulation_stop = Some(
193            self.lua
194                .create_function(move |lua, ()| wrap_f("on_simulation_stop", HooksLua(lua), &f))?,
195        );
196        Ok(self)
197    }
198
199    pub fn on_simulation_frame<F>(&mut self, f: F) -> Result<&mut Self>
200    where
201        F: Fn(HooksLua) -> Result<()> + 'static,
202    {
203        self.on_simulation_frame =
204            Some(self.lua.create_function(move |lua, ()| {
205                wrap_f("on_simulation_frame", HooksLua(lua), &f)
206            })?);
207        Ok(self)
208    }
209
210    pub fn on_simulation_pause<F>(&mut self, f: F) -> Result<&mut Self>
211    where
212        F: Fn(HooksLua) -> Result<()> + 'static,
213    {
214        self.on_simulation_pause =
215            Some(self.lua.create_function(move |lua, ()| {
216                wrap_f("on_simulation_pause", HooksLua(lua), &f)
217            })?);
218        Ok(self)
219    }
220
221    pub fn on_simulation_resume<F>(&mut self, f: F) -> Result<&mut Self>
222    where
223        F: Fn(HooksLua) -> Result<()> + 'static,
224    {
225        self.on_simulation_resume =
226            Some(self.lua.create_function(move |lua, ()| {
227                wrap_f("on_simulation_resume", HooksLua(lua), &f)
228            })?);
229        Ok(self)
230    }
231
232    pub fn on_player_connect<F>(&mut self, f: F) -> Result<&mut Self>
233    where
234        F: Fn(HooksLua, PlayerId) -> Result<()> + 'static,
235    {
236        self.on_player_connect = Some(self.lua.create_function(move |lua, id| {
237            wrap_f("on_player_connect", HooksLua(lua), |lua| f(lua, id))
238        })?);
239        Ok(self)
240    }
241
242    pub fn on_player_disconnect<F>(&mut self, f: F) -> Result<&mut Self>
243    where
244        F: Fn(HooksLua, PlayerId) -> Result<()> + 'static,
245    {
246        self.on_player_disconnect = Some(self.lua.create_function(move |lua, id| {
247            wrap_f("on_player_disconnect", HooksLua(lua), |lua| f(lua, id))
248        })?);
249        Ok(self)
250    }
251
252    pub fn on_player_start<F>(&mut self, f: F) -> Result<&mut Self>
253    where
254        F: Fn(HooksLua, PlayerId) -> Result<()> + 'static,
255    {
256        self.on_player_start = Some(self.lua.create_function(move |lua, id| {
257            wrap_f("on_player_start", HooksLua(lua), |lua| f(lua, id))
258        })?);
259        Ok(self)
260    }
261
262    pub fn on_player_stop<F>(&mut self, f: F) -> Result<&mut Self>
263    where
264        F: Fn(HooksLua, PlayerId) -> Result<()> + 'static,
265    {
266        self.on_player_stop = Some(self.lua.create_function(move |lua, id| {
267            wrap_f("on_player_stop", HooksLua(lua), |lua| f(lua, id))
268        })?);
269        Ok(self)
270    }
271
272    pub fn on_player_change_slot<F>(&mut self, f: F) -> Result<&mut Self>
273    where
274        F: Fn(HooksLua, PlayerId) -> Result<()> + 'static,
275    {
276        self.on_player_change_slot = Some(self.lua.create_function(move |lua, id| {
277            wrap_f("on_player_change_slot", HooksLua(lua), |lua| f(lua, id))
278        })?);
279        Ok(self)
280    }
281
282    /// f(addr, ucid, name, id), return `None` to accept the player,
283    /// return `Some("reason for rejection")` to reject the player.
284    pub fn on_player_try_connect<F>(&mut self, f: F) -> Result<&mut Self>
285    where
286        F: Fn(HooksLua, String, String, Ucid, PlayerId) -> Result<Option<String>> + 'static,
287    {
288        self.on_player_try_connect = Some(self.lua.create_function(
289            move |lua, (addr, name, ucid, id): (String, String, Ucid, PlayerId)| {
290                wrap_f("on_player_try_connect", HooksLua(lua), |lua| {
291                    let mut rval = LuaMultiValue::new();
292                    match f(lua, addr, name, ucid, id) {
293                        Err(e) => return Err(e),
294                        Ok(None) => rval.push_front(LuaValue::Boolean(true)),
295                        Ok(Some(reason)) => {
296                            rval.push_front(reason.into_lua(lua.inner())?);
297                            rval.push_front(LuaValue::Boolean(false));
298                        }
299                    }
300                    Ok(rval)
301                })
302            },
303        )?);
304        Ok(self)
305    }
306
307    /// f(id, message, all)
308    pub fn on_player_try_send_chat<F>(&mut self, f: F) -> Result<&mut Self>
309    where
310        F: Fn(HooksLua, PlayerId, String, bool) -> Result<String> + 'static,
311    {
312        self.on_player_try_send_chat = Some(self.lua.create_function(
313            move |lua, (id, msg, all): (PlayerId, String, bool)| {
314                wrap_f("on_player_try_send_chat", HooksLua(lua), |lua| {
315                    f(lua, id, msg, all)
316                })
317            },
318        )?);
319        Ok(self)
320    }
321
322    /// f(id, message, all)
323    pub fn on_player_try_change_slot<F>(&mut self, f: F) -> Result<&mut Self>
324    where
325        F: Fn(HooksLua, PlayerId, Side, SlotId) -> Result<Option<bool>> + 'static,
326    {
327        self.on_player_try_change_slot = Some(self.lua.create_function(
328            move |lua, (id, side, slot): (PlayerId, Side, SlotId)| {
329                wrap_f("on_player_try_change_slot", HooksLua(lua), |lua| {
330                    f(lua, id, side, slot)
331                })
332            },
333        )?);
334        Ok(self)
335    }
336}