Skip to main content

yog_api/nanite/
inject.rs

1//! Injections (`#[inject]`, `docs/design/nanites.md`): what a mod hooks,
2//! known before the game's classes load — the runtime asks the mod's
3//! library for them (`yog_mod_nanites`) — and its handlers, subscribed when
4//! the mod registers to the point's keyed event.
5
6use yog_event::nanite::{Inject, Injected, Nanites, point};
7use yog_sync::Mutex;
8
9use super::{Java, Member, Nanite};
10use crate::Registry;
11
12/// A declared nanite: its kind, its member, where in it, its handler.
13pub struct InjectEntry {
14    /// `yog_ids::nanite_kind`.
15    pub kind: &'static str,
16    pub member: &'static Member,
17    /// `yog_ids::mixin_at`.
18    pub at: &'static str,
19    /// What an `INVOKE`/`FIELD`/`NEW` at names, else empty.
20    pub target: &'static str,
21    pub ordinal: i32,
22    /// `yog_ids::mixin_shift`.
23    pub shift: &'static str,
24    pub cancellable: bool,
25    /// Among the point's handlers (higher first).
26    pub priority: i32,
27    /// A `modify_arg`'s argument, a `modify_variable`'s local (-1: by type).
28    pub index: i32,
29    /// A `modify_constant`'s constant (`int:5`).
30    pub constant: &'static str,
31    /// A `modify_variable`'s local's type (a descriptor, Mojang's).
32    pub value_desc: &'static str,
33    /// None for what has no code of the mod's (an added field, an interface).
34    pub handler: Option<fn(&mut Hook)>,
35}
36
37impl InjectEntry {
38    /// What the host makes of it.
39    pub fn spec(&self) -> Inject {
40        let (method, desc) = self.member.signature();
41        let class = self.member.class();
42        let mut spec = Inject {
43            point: String::new(),
44            kind: self.kind.to_owned(),
45            class: class.to_owned(),
46            method: method.to_owned(),
47            desc,
48            at: self.at.to_owned(),
49            target: self.target.to_owned(),
50            ordinal: self.ordinal,
51            shift: self.shift.to_owned(),
52            cancellable: self.cancellable,
53            index: self.index,
54            constant: self.constant.to_owned(),
55            value_desc: self.value_desc.to_owned(),
56        };
57        spec.point = point(&spec);
58        spec
59    }
60}
61
62/// What a redirect's original call is: a `Function` of its arguments.
63const ORIGINAL_CLASS: &str = "java.util.function.Function";
64const ORIGINAL_METHOD: &str = "apply";
65
66fn original_desc() -> String {
67    let object = super::java::class_desc("java.lang.Object");
68    super::method_desc(std::slice::from_ref(&object), &object)
69}
70
71/// This mod's injections (each `#[inject]`'s `ctor` adds its own).
72#[doc(hidden)]
73pub fn __yog_nanite_registry() -> &'static Mutex<Vec<InjectEntry>> {
74    static REG: Mutex<Vec<InjectEntry>> = Mutex::new(Vec::new());
75    &REG
76}
77
78/// A declaration with no handler (an added field, an interface): its kind,
79/// member, target.
80#[doc(hidden)]
81pub fn __declare(kind: &'static str, member: &'static Member, target: &'static str) {
82    __yog_nanite_registry().lock().push(InjectEntry {
83        kind,
84        member,
85        at: "",
86        target,
87        ordinal: -1,
88        shift: "",
89        cancellable: false,
90        priority: 0,
91        index: -1,
92        constant: "",
93        value_desc: "",
94        handler: None,
95    });
96}
97
98/// Every injection of this mod, as the runtime asks before the game loads.
99#[doc(hidden)]
100pub fn __yog_nanites() -> Vec<u8> {
101    let injects = __yog_nanite_registry()
102        .lock()
103        .iter()
104        .map(InjectEntry::spec)
105        .collect();
106    yog_data::to_bytes(&Nanites { injects })
107}
108
109/// A handler to subscribe: its point, priority, function.
110type Subscription = (String, i32, fn(&mut Hook));
111
112/// Each injection's handler on its point (when the mod registers).
113pub fn subscribe(registry: &mut Registry) {
114    let entries: Vec<Subscription> = __yog_nanite_registry()
115        .lock()
116        .iter()
117        .filter_map(|e| Some((e.spec().point, e.priority, e.handler?)))
118        .collect();
119    for (key, priority, handler) in entries {
120        registry.change_key_at::<Injected>(&key, priority, move |e, _| handler(&mut Hook(e)));
121    }
122}
123
124/// An injection point reached, as a handler sees it.
125pub struct Hook<'a>(&'a mut Injected);
126
127impl<'a> Hook<'a> {
128    /// The point reached, to be changed.
129    pub fn new(e: &'a mut Injected) -> Self {
130        Hook(e)
131    }
132
133    /// The object the method runs on (none in a static one).
134    pub fn this<T: Nanite>(&self) -> Option<T> {
135        T::from_value(self.0.this.clone())
136    }
137
138    /// Argument `i`.
139    pub fn arg<T: Java>(&self, i: usize) -> Option<T> {
140        T::from_value(self.0.args.get(i)?.clone())
141    }
142
143    /// How many arguments there are.
144    pub fn args(&self) -> usize {
145        self.0.args.len()
146    }
147
148    /// The value returned, at a `RETURN`.
149    pub fn value<T: Java>(&self) -> Option<T> {
150        T::from_value(self.0.value.clone())
151    }
152
153    /// Stops the method (a cancellable injection's).
154    pub fn cancel(&mut self) {
155        self.0.cancelled = true;
156    }
157
158    /// Stops the method returning `value`.
159    pub fn set_return<T: Java>(&mut self, value: T) {
160        self.0.value = value.to_value();
161        self.0.cancelled = true;
162        self.0.returned = true;
163    }
164
165    /// Makes `value` the answer: a modified value, a redirected call's result.
166    pub fn set_value<T: Java>(&mut self, value: T) {
167        self.0.value = value.to_value();
168        self.0.returned = true;
169    }
170
171    /// Runs a redirected call as it was, with `args` (the receiver first): its result.
172    pub fn call_original(&self, args: Vec<yog_data::Value>) -> Option<yog_data::Value> {
173        static APPLY: Member = Member::new(ORIGINAL_CLASS, ORIGINAL_METHOD, original_desc);
174        APPLY.call(self.0.original.clone(), vec![yog_data::Value::List(args)])
175    }
176
177    /// Whether a handler before this one stopped it.
178    pub fn cancelled(&self) -> bool {
179        self.0.cancelled
180    }
181}