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
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
//! Data a mod attaches to any entity, such as a placed chest's owner or a
//! survivor's nickname: typed values in kinds named `<mod>:<name>`, kept
//! per world and, for kinds that ask for it, sent to players.
//!
//! A load numbers the world's entities anew, so the server keeps each value
//! under its entity's place: the template and the position as the save
//! keeps it. After the first step of a loaded world every value finds its
//! entity again; a value whose place no entity, or more than one, holds
//! goes aside in the world's file and is never given to another entity.
//! Only an entity the save keeps in place carries a value: a player's
//! character does not, so [`EntityData::set`] refuses it with
//! [`SetError::NotKept`]. Once the entity is gone or holds another
//! template, the value reads as missing on the server's game thread, and the
//! server drops it when the world saves. Values live in
//! `mod-data/enshwick/worlds/<key>/`, never in the game's save, beside
//! [item data](crate::item_data), which is the same store for items.
//!
//! A kind opened with [`Replication::Clients`] goes to the clients that run
//! Enshwick, keyed by the server's entity ids, which are not the ids the
//! client's own world gives the same things: a client reads a value by an
//! id the server named, such as one in a mod message. A value is encoded
//! with `postcard`, at most [`MAX_VALUE`] bytes.
//!
//! # Examples
//!
//! ```
//! use enshwick::Entity;
//! use enshwick::entity_data::Replication;
//!
//! #[derive(serde::Serialize, serde::Deserialize)]
//! struct Owner {
//! player: u64,
//! }
//!
//! fn init(load: &mut enshwick::Load<'_>) -> Result<(), enshwick::Off> {
//! let owners = load.entity_data::<Owner>("owner", Replication::Server)?;
//! let chest = Entity(4_200);
//! if owners.get(chest).is_none() {
//! let _set = owners.set(chest, &Owner { player: 7 });
//! }
//! Ok(())
//! }
//! ```
use core::marker::PhantomData;
use crate::abi::{EntityDataFlags, Status, Str};
pub use crate::item_data::{MAX_VALUE, SetError};
use crate::{Entity, ItemId};
use crate::{Host, Load, Off};
/// Who gets a kind's values.
///
/// # Examples
///
/// See the [module](self).
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum Replication {
/// The server alone.
#[default]
Server,
/// The server, and the clients that run Enshwick, which get every value
/// on joining and every change after.
Clients,
}
/// A kind of entity data with values of type `T`; get it from
/// [`Load::entity_data`]. Copy it anywhere.
///
/// # Examples
///
/// See the [module](self).
pub struct EntityData<T> {
host: Host,
kind: u32,
value: PhantomData<fn() -> T>,
}
impl<T> Clone for EntityData<T> {
fn clone(&self) -> Self {
*self
}
}
impl<T> Copy for EntityData<T> {}
impl<T> core::fmt::Debug for EntityData<T> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("EntityData")
.field("kind", &self.kind)
.finish_non_exhaustive()
}
}
impl<T> EntityData<T>
where
T: serde::Serialize + serde::de::DeserializeOwned,
{
/// The entity's value; `None` when it has none, when the value does not
/// decode as `T`, or, read on the server's game thread, when the entity
/// is gone or another thing now. Any thread: on the server its own
/// values, on a client what the server sent.
///
/// # Examples
///
/// See the [module](self).
#[must_use]
pub fn get(&self, entity: Entity) -> Option<T> {
let mut buffer = [0u8; MAX_VALUE];
let mut len = 0u32;
let status = (self.host.api().item_data_get)(
self.host.handle(),
self.kind,
entity,
ItemId::new(0),
buffer.as_mut_ptr(),
u32::try_from(buffer.len()).unwrap_or(u32::MAX),
&raw mut len,
);
if !status.is_ok() {
return None;
}
postcard::from_bytes(buffer.get(..usize::try_from(len).ok()?)?).ok()
}
/// Sets the entity's value, kept with the entity's template; the clients
/// get it when the kind is sent. On the server's game thread, the
/// dedicated server's or a hosting client's, once the world has loaded.
///
/// # Errors
///
/// [`SetError`] when the value does not encode or is too long, off the
/// server or its game thread, before the world has loaded, for an
/// entity that does not exist, or for one no load finds again
/// ([`SetError::NotKept`]).
///
/// # Examples
///
/// See the [module](self).
pub fn set(&self, entity: Entity, value: &T) -> Result<(), SetError> {
let bytes = postcard::to_allocvec(value).map_err(|e| SetError::Encode(e.to_string()))?;
if bytes.is_empty() || bytes.len() > MAX_VALUE {
return Err(SetError::TooLarge(bytes.len()));
}
self.write(entity, &bytes)
}
/// Removes the entity's value.
///
/// # Errors
///
/// As for [`EntityData::set`].
///
/// # Examples
///
/// ```
/// use enshwick::Entity;
///
/// fn forget(owners: &enshwick::entity_data::EntityData<u64>, chest: Entity) {
/// let _removed = owners.remove(chest);
/// }
/// ```
pub fn remove(&self, entity: Entity) -> Result<(), SetError> {
self.write(entity, &[])
}
fn write(&self, entity: Entity, bytes: &[u8]) -> Result<(), SetError> {
let status = (self.host.api().item_data_set)(
self.host.handle(),
self.kind,
entity,
ItemId::new(0),
bytes.as_ptr(),
u32::try_from(bytes.len()).unwrap_or(u32::MAX),
);
match status {
Status::OK => Ok(()),
Status::REFUSED | Status::WRONG_THREAD => Err(SetError::NotServer),
Status::MISSING => Err(SetError::NoWorld),
Status::BAD_ARGS => Err(SetError::NoEntity),
Status::NOT_KEPT => Err(SetError::NotKept),
other => Err(SetError::Refused(other.0)),
}
}
}
impl Load<'_> {
/// Opens the entity data kind `id`: `name` for the mod's own,
/// `<mod>:<name>`, or `mod:name` for another mod's, each side 1 to 64 of
/// `[a-z0-9_-]`. Every mod that opens one id shares its values, so the
/// value type must match; the kind is sent to clients once any mod
/// opens it with [`Replication::Clients`]. Call it during `init`, on
/// both sides.
///
/// # Errors
///
/// [`Off`] for a malformed id, an id opened as an item kind, or after
/// init.
///
/// # Examples
///
/// See the [module](self).
pub fn entity_data<T>(
&mut self,
id: &str,
replication: Replication,
) -> Result<EntityData<T>, Off>
where
T: serde::Serialize + serde::de::DeserializeOwned,
{
let host = self.host();
let flags = match replication {
Replication::Server => EntityDataFlags::NONE,
Replication::Clients => EntityDataFlags::SENT,
};
let mut kind = 0;
let status =
(host.api().entity_data_open)(host.handle(), Str::new(id), flags, &raw mut kind);
crate::load::ok(
status,
EntityData {
host,
kind,
value: PhantomData,
},
|| format!("entity data {id:?}"),
)
}
}