forked from feather-rs/feather
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathimpls.rs
More file actions
254 lines (216 loc) · 7.09 KB
/
impls.rs
File metadata and controls
254 lines (216 loc) · 7.09 KB
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
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
//! The implementations of various commands.
use crate::arguments::Coordinates;
use crate::{
arguments::{EntitySelector, ParsedGamemode, TextArgument},
CommandCtx,
};
use feather_core::text::{Text, TextComponentBuilder, TextValue};
use feather_core::util::{Gamemode, Position};
use feather_server_types::{
ChatEvent, ChatPosition, GamemodeUpdateEvent, MessageReceiver, Name, ShutdownChannels,
Teleported,
};
use fecs::{Entity, ResourcesProvider, World};
use lieutenant::command;
use thiserror::Error;
#[derive(Debug, Error)]
pub enum TpError {
#[error("no entities matched the target selector")]
NoMatchingEntities,
}
#[command(usage = "tp|teleport <destination>")]
pub fn tp_1(ctx: &mut CommandCtx, destination: EntitySelector) -> anyhow::Result<()> {
if let Some(first) = destination.entities.first() {
if let Some(pos) = ctx.world.try_get::<Position>(*first).map(|r| *r) {
teleport_entity_to_pos(&mut ctx.world, ctx.sender, pos);
}
Ok(())
} else {
Err(TpError::NoMatchingEntities.into())
}
}
#[command(usage = "tp|teleport <location>")]
pub fn tp_2(ctx: &mut CommandCtx, location: Coordinates) -> anyhow::Result<()> {
teleport_entity(&mut ctx.world, ctx.sender, location);
Ok(())
}
#[command(usage = "tp|teleport <targets> <location>")]
pub fn tp_3(
ctx: &mut CommandCtx,
targets: EntitySelector,
location: Coordinates,
) -> anyhow::Result<()> {
for entity in &targets.entities {
teleport_entity(&mut ctx.world, *entity, location);
}
Ok(())
}
#[command(usage = "tp|teleport <targets> <destination>")]
pub fn tp_4(
ctx: &mut CommandCtx,
targets: EntitySelector,
destination: EntitySelector,
) -> anyhow::Result<()> {
if let Some(location) = destination
.entities
.first()
.map(|e| ctx.world.try_get::<Position>(*e).map(|r| *r))
.flatten()
{
for entity in targets.entities {
teleport_entity_to_pos(&mut ctx.world, entity, location);
}
Ok(())
} else {
Err(TpError::NoMatchingEntities.into())
}
}
fn teleport_entity(world: &mut World, entity: Entity, location: Coordinates) {
let new_pos = world
.try_get::<Position>(entity)
.map(|r| *r)
.map(|relative_to| location.into_position(relative_to));
if let Some(new_pos) = new_pos {
teleport_entity_to_pos(world, entity, new_pos);
}
}
fn teleport_entity_to_pos(world: &mut World, entity: Entity, pos: Position) {
if let Some(mut old_pos) = world.try_get_mut::<Position>(entity) {
*old_pos = pos;
}
let _ = world.add(entity, Teleported);
}
#[command(usage = "gamemode <gamemode>")]
pub fn gamemode_1(ctx: &mut CommandCtx, gamemode: ParsedGamemode) -> anyhow::Result<()> {
update_gamemode(ctx, gamemode.0, ctx.sender);
Ok(())
}
#[command(usage = "gamemode <gamemode> <target>")]
pub fn gamemode_2(
ctx: &mut CommandCtx,
gamemode: ParsedGamemode,
target: EntitySelector,
) -> anyhow::Result<()> {
for entity in target.entities {
update_gamemode(ctx, gamemode.0, entity)
}
Ok(())
}
fn update_gamemode(ctx: &mut CommandCtx, gamemode: Gamemode, entity: Entity) {
let event = if let Some(mut old) = ctx.world.try_get_mut::<Gamemode>(ctx.sender) {
let old_val = *old;
*old = gamemode;
let event = GamemodeUpdateEvent {
player: entity,
old: old_val,
new: gamemode,
};
Some(event)
} else {
None
};
if let Some(event) = event {
ctx.game.handle(&mut *ctx.world, event);
}
}
#[command(usage = "tell|msg|w <target> <message>")]
pub fn whisper(
ctx: &mut CommandCtx,
target: EntitySelector,
message: TextArgument,
) -> anyhow::Result<()> {
let sender_name = if let Some(sender_name) = ctx.world.try_get::<Name>(ctx.sender) {
sender_name.0.clone()
} else {
// Use a default value if the executor has no Name component
String::from("Server")
};
// The message that is returned to the whisperer
// You whisper to [player] (and [player]): [message]
let mut response_message = String::from("You whisper to");
// Tracks if there needs to be "and" before the next player added to the response message
let mut needs_and = false;
for entity in target.entities {
if let Some(mut message_receiver) = ctx.world.try_get_mut::<MessageReceiver>(ctx.sender) {
message_receiver.send(
Text::from(format!(
"{} whispers to you: {}",
sender_name,
message.0.clone()
))
.gray()
.italic(),
);
} else {
// If the entity doesn't have a message receiver it is not a player and there is no need to continue
continue;
};
if let Some(player_name) = ctx.world.try_get::<Name>(entity) {
if needs_and {
response_message += format!(" and {}", player_name.0).as_str();
} else {
needs_and = true;
response_message += format!(" {}", player_name.0).as_str();
}
}
}
// Send the whisperer a confirmation message
if let Some(mut sender_message_receiver) = ctx.world.try_get_mut::<MessageReceiver>(ctx.sender)
{
response_message += format!(": {}", message.0).as_str();
let return_text = Text::from(response_message).gray().italic();
sender_message_receiver.send(return_text);
}
Ok(())
}
#[command(usage = "say <message>")]
pub fn say(ctx: &mut CommandCtx, message: TextArgument) -> anyhow::Result<()> {
let name = ctx.world.try_get::<Name>(ctx.sender);
let sender_name = if let Some(name) = &name {
&name.0
} else {
"Server"
};
let command_output = Text::from(format!("[{}] {}", sender_name, message.0));
drop(name);
ctx.game.handle(
&mut ctx.world,
ChatEvent {
message: command_output.into(),
position: ChatPosition::Chat,
},
);
Ok(())
}
#[command(usage = "me <action>")]
pub fn me(ctx: &mut CommandCtx, action: TextArgument) -> anyhow::Result<()> {
let command_output = {
let name = ctx.world.try_get::<Name>(ctx.sender);
let sender_name = name.as_deref().map_or("@", |Name(n)| n);
Text::from(format!("* {} {}", sender_name, action.as_ref()))
};
ctx.game.handle(
&mut ctx.world,
ChatEvent {
message: command_output.into(),
position: ChatPosition::Chat,
},
);
Ok(())
}
#[command(usage = "stop")]
pub fn stop(ctx: &mut CommandCtx) -> anyhow::Result<()> {
// Confirmation message
// TODO Server ops should also see the message
if let Some(mut sender_message_receiver) = ctx.world.try_get_mut::<MessageReceiver>(ctx.sender)
{
let text = Text::from(TextValue::translate("commands.stop.stopping"));
sender_message_receiver.send(text);
}
ctx.game
.resources
.get::<ShutdownChannels>()
.tx
.try_send(())?;
Ok(())
}