1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
//! Key values the engine or game made (`KeyValues`), such as the commands
//! clients send as key values, read by name, along with the key values they
//! hold and their string values.
use crate::NotThreadSafe;
use std::ffi::CStr;
use std::iter::FusedIterator;
use std::marker::PhantomData;
use std::ptr::NonNull;
/// Key values another module owns, readable for `'k` (`KeyValues`).
///
/// Hooks on `IServerGameClients::ClientCommandKeyValues` get one, for the
/// command a client sent. Like other handles, it frees nothing.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct KeyValues<'k> {
raw: NonNull<sys::KeyValues>,
_scope: PhantomData<&'k ()>,
_not_thread_safe: NotThreadSafe,
}
impl<'k> KeyValues<'k> {
/// Wraps key values another module owns.
///
/// # Safety
///
/// `raw` must point to live key values that stay allocated, unchanged, for
/// `'k`, along with the key values they hold, laid out as TF2's engine and
/// game lay them out (see [`sdk_raw::key_values`]), used only on the
/// server's main thread.
pub const unsafe fn from_raw(raw: NonNull<sys::KeyValues>) -> Self {
Self {
raw,
_scope: PhantomData,
_not_thread_safe: PhantomData,
}
}
/// Returns the key values pointer, for calls this crate does not wrap.
pub const fn as_ptr(self) -> *mut sys::KeyValues {
self.raw.as_ptr()
}
/// The key values' name, such as the command a client sent, or `None` if it
/// cannot be found: vstdlib, whose symbol table holds most names, is not
/// loaded, or the key values keep their name in a table of their own that is
/// not laid out as TF2's. Symbol tables keep the first spelling of each name
/// they were given, and match names ignoring case, so compare names ignoring
/// ASCII case.
#[doc(alias("GetName"))]
pub fn name(self) -> Option<&'k CStr> {
let system = sdk_raw::key_values::key_values_system();
// SAFETY: The key values are live, laid out as TF2's, and keep their name
// for `'k`. The process's system keeps its names for as long as it runs.
let name = unsafe { sdk_raw::key_values::key_name(system, self.raw) };
// SAFETY: A name that is found is NUL-terminated, and lives for `'k`.
unsafe { sdk_raw::util::cstr::borrow_cstr(name) }
}
/// The first of their own key values named `name`, ignoring ASCII case as
/// tier1 does, or `None` if they hold none of that name. Key values whose
/// name cannot be found (see [`Self::name`]) are skipped.
#[doc(alias("FindKey"))]
pub fn find_key(self, name: &CStr) -> Option<KeyValues<'k>> {
self.sub_keys().find(|key| {
key.name()
.is_some_and(|found| found.to_bytes().eq_ignore_ascii_case(name.to_bytes()))
})
}
/// The first of their own key values, or `None` if they hold none.
#[doc(alias("GetFirstSubKey"))]
pub fn first_sub_key(self) -> Option<KeyValues<'k>> {
// SAFETY: The key values are live, laid out as TF2's, and keep their own
// key values for `'k`.
let key = unsafe { sdk_raw::key_values::first_sub_key(self.raw) };
// SAFETY: Key values of the tree live as long as their parent.
NonNull::new(key).map(|key| unsafe { Self::from_raw(key) })
}
/// The key values after these in their parent's list, or `None` after the
/// last.
#[doc(alias("GetNextKey"))]
pub fn next_key(self) -> Option<KeyValues<'k>> {
// SAFETY: As for `first_sub_key`.
let key = unsafe { sdk_raw::key_values::next_key(self.raw) };
// SAFETY: As for `first_sub_key`.
NonNull::new(key).map(|key| unsafe { Self::from_raw(key) })
}
/// Their value, if it is a string. tier1's `GetString` would also convert a
/// number or a pointer, and keep the string in its place; this only reads.
#[doc(alias("GetString"))]
pub fn string(self) -> Option<&'k CStr> {
// SAFETY: As for `first_sub_key`, with their value kept for `'k`.
let string = unsafe { sdk_raw::key_values::string_value(self.raw) };
// SAFETY: A string value is NUL-terminated, and lives for `'k`.
unsafe { sdk_raw::util::cstr::borrow_cstr(string) }
}
/// Their own key values, in order.
pub fn sub_keys(self) -> SubKeys<'k> {
SubKeys {
next: self.first_sub_key(),
}
}
}
/// The key values that key values hold, in order, from
/// [`KeyValues::sub_keys`].
#[derive(Debug, Clone)]
pub struct SubKeys<'k> {
next: Option<KeyValues<'k>>,
}
impl FusedIterator for SubKeys<'_> {}
impl<'k> Iterator for SubKeys<'k> {
type Item = KeyValues<'k>;
fn next(&mut self) -> Option<Self::Item> {
let key = self.next?;
self.next = key.next_key();
Some(key)
}
}