use std::{
future::Future,
pin::Pin,
sync::{
atomic::{AtomicI32, Ordering},
Arc,
},
task::{Context, Poll},
};
#[cfg(feature = "brs")]
use brickadia::save;
use dashmap::{mapref::entry::Entry, DashMap};
use events::{BrickInteraction, Event};
use resources::{GhostBrick, Player, PlayerPaint, Plugin, TemplateBounds};
use serde_json::{json, Value};
use thiserror::Error;
use tokio::{
io::{stdin, AsyncBufReadExt, BufReader},
sync::{
mpsc::{self, UnboundedReceiver},
oneshot,
},
};
use crate::resources::PlayerPosition;
pub mod events;
pub mod resources;
pub mod rpc;
pub type EventReceiver = UnboundedReceiver<Event>;
pub struct ResponseAwaiter(oneshot::Receiver<rpc::Response>);
impl Future for ResponseAwaiter {
type Output = Result<Option<Value>, ResponseError>;
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
match Pin::new(&mut self.0).poll(cx) {
Poll::Ready(Ok(response)) => Poll::Ready(match response.error {
Some(e) => Err(ResponseError::Rpc(e)),
None => Ok(response.result),
}),
Poll::Ready(Err(error)) => Poll::Ready(Err(ResponseError::Recv(error))),
Poll::Pending => Poll::Pending,
}
}
}
#[derive(Error, Debug)]
pub enum ResponseError {
#[error("rpc error")]
Rpc(rpc::Error),
#[error("receive error")]
Recv(#[from] oneshot::error::RecvError),
}
pub struct Omegga {
pub awaiter_txs: Arc<DashMap<rpc::RequestId, oneshot::Sender<rpc::Response>>>,
request_id: Arc<AtomicI32>,
}
impl Omegga {
pub fn new() -> Self {
Self {
awaiter_txs: Arc::new(DashMap::new()),
request_id: Arc::new(AtomicI32::new(-1)),
}
}
pub fn spawn(&self) -> EventReceiver {
let (tx, rx) = mpsc::unbounded_channel::<Event>();
let awaiter_txs = Arc::clone(&self.awaiter_txs);
tokio::spawn(async move {
let reader = BufReader::new(stdin());
let mut lines = reader.lines();
while let Some(line) = lines.next_line().await.unwrap() {
let message: rpc::Message = match serde_json::from_str(&line) {
Ok(v) => v,
Err(_) => continue,
};
match message {
rpc::Message::Response {
id, result, error, ..
} => {
if let Entry::Occupied(entry) = awaiter_txs.entry(id) {
let (id, sender) = entry.remove_entry();
let _ = sender.send(rpc::Response { id, result, error });
}
}
rpc::Message::Request {
id, method, params, ..
} => match method.as_str() {
"init" => {
let _ = tx.send(Event::Init {
id,
config: params.unwrap_or(Value::Null),
});
}
"stop" => {
let _ = tx.send(Event::Stop { id });
}
"plugin:emit" => match params {
Some(Value::Array(v)) => {
let mut params = v.into_iter();
let event = match params.next().unwrap() {
Value::String(s) => s,
_ => continue,
};
let from = match params.next().unwrap() {
Value::String(s) => s,
_ => continue,
};
let _ = tx.send(Event::PluginEmit {
id,
event,
from,
args: params.collect(),
});
}
_ => (),
},
_ => (),
},
rpc::Message::Notification { method, params, .. } => match method.as_str() {
"bootstrap" => {
let _ = tx.send(Event::Bootstrap {
omegga: params.unwrap_or(Value::Null),
});
}
"plugin:players:raw" => {
let _ = tx.send(Event::PluginPlayersRaw {
players: serde_json::from_value(params.unwrap())
.unwrap_or_default(),
});
}
"line" => {
let _ = tx.send(Event::Line(
serde_json::from_value::<Vec<String>>(params.unwrap())
.unwrap()
.into_iter()
.next()
.unwrap(),
));
}
"start" => {
#[derive(serde::Deserialize)]
struct MapParams {
map: String,
}
let _ = tx.send(Event::Start {
map: serde_json::from_value::<Vec<MapParams>>(params.unwrap())
.unwrap()
.into_iter()
.next()
.unwrap()
.map,
});
}
"host" => {
#[derive(serde::Deserialize)]
struct HostParams {
name: String,
id: String,
}
let host = serde_json::from_value::<Vec<HostParams>>(params.unwrap())
.unwrap()
.into_iter()
.next()
.unwrap();
let _ = tx.send(Event::Host {
name: host.name,
id: host.id,
});
}
"version" => {
let _ = tx.send(Event::Version(params.unwrap()));
}
"unauthorized" => {
let _ = tx.send(Event::Unauthorized);
}
"join" => {
let _ = tx.send(Event::Join(
serde_json::from_value::<Vec<_>>(params.unwrap())
.unwrap()
.into_iter()
.next()
.unwrap(),
));
}
"leave" => {
let _ = tx.send(Event::Leave(
serde_json::from_value::<Vec<_>>(params.unwrap())
.unwrap()
.into_iter()
.next()
.unwrap(),
));
}
e if e.starts_with("cmd:") => {
let c = &e[4..];
let mut params = serde_json::from_value::<Vec<String>>(params.unwrap())
.unwrap()
.into_iter();
let _ = tx.send(Event::Command {
player: params.next().unwrap(),
command: c.to_string(),
args: params.collect(),
});
}
e if e.starts_with("chatcmd:") => {
let c = &e[8..];
let mut params = serde_json::from_value::<Vec<String>>(params.unwrap())
.unwrap()
.into_iter();
let _ = tx.send(Event::ChatCommand {
player: params.next().unwrap(),
command: c.to_string(),
args: params.collect(),
});
}
"chat" => {
let mut params = serde_json::from_value::<Vec<String>>(params.unwrap())
.unwrap()
.into_iter();
let _ = tx.send(Event::Chat {
player: params.next().unwrap(),
message: params.next().unwrap(),
});
}
"mapchange" => {
#[derive(serde::Deserialize)]
struct MapParams {
map: String,
}
let _ = tx.send(Event::MapChange(
serde_json::from_value::<Vec<MapParams>>(params.unwrap())
.unwrap()
.into_iter()
.next()
.unwrap()
.map,
));
}
"interact" => {
let _ = tx.send(Event::Interact(
serde_json::from_value::<BrickInteraction>(params.unwrap())
.unwrap(),
));
}
e if e.starts_with("event:") => {
let e = &e[6..];
match params {
Some(Value::Array(params)) => {
let mut params = params.into_iter();
let player =
serde_json::from_value::<Player>(params.next().unwrap())
.unwrap();
let args = params
.map(|a| String::from(a.as_str().unwrap()))
.collect::<Vec<_>>();
let _ = tx.send(Event::Event {
name: String::from(e),
player,
args,
});
}
_ => continue,
}
}
"autorestart" => {
let _ = tx.send(Event::Autorestart(params.unwrap_or_default()));
}
_ => (),
},
};
}
});
rx
}
pub fn write(&self, message: rpc::Message) {
println!("{}", serde_json::to_string(&message).unwrap());
}
pub fn write_notification(&self, method: impl Into<String>, params: Option<Value>) {
self.write(rpc::Message::notification(method.into(), params));
}
pub fn write_response(
&self,
id: rpc::RequestId,
params: Option<Value>,
error: Option<rpc::Error>,
) {
self.write(rpc::Message::response(id, params, error));
}
pub fn write_request(
&self,
id: rpc::RequestId,
method: impl Into<String>,
params: Option<Value>,
) {
self.write(rpc::Message::request(id, method.into(), params));
}
pub fn request(&self, method: impl Into<String>, params: Option<Value>) -> ResponseAwaiter {
let id = self.request_id.fetch_sub(-1, Ordering::SeqCst);
self.write_request(rpc::RequestId::Int(id), method, params);
let (tx, rx) = oneshot::channel::<rpc::Response>();
self.awaiter_txs.insert(rpc::RequestId::Int(id), tx);
ResponseAwaiter(rx)
}
pub fn register_commands(&self, id: rpc::RequestId, commands: &[&str]) {
self.write_response(id, Some(json!({ "registeredCommands": commands })), None);
}
pub fn log(&self, line: impl Into<String>) {
self.write_notification("log", Some(Value::String(line.into())));
}
pub fn error(&self, line: impl Into<String>) {
self.write_notification("error", Some(Value::String(line.into())));
}
pub fn info(&self, line: impl Into<String>) {
self.write_notification("info", Some(Value::String(line.into())));
}
pub fn warn(&self, line: impl Into<String>) {
self.write_notification("warn", Some(Value::String(line.into())));
}
pub fn trace(&self, line: impl Into<String>) {
self.write_notification("trace", Some(Value::String(line.into())));
}
pub async fn store_get(&self, key: impl Into<String>) -> Result<Option<Value>, ResponseError> {
self.request("store.get", Some(Value::String(key.into())))
.await
}
pub fn store_set(&self, key: impl Into<String>, value: Value) {
self.write_notification("store.set", Some(json!([key.into(), value])))
}
pub async fn store_delete(&self, key: impl Into<String>) {
self.write_notification("store.delete", Some(Value::String(key.into())))
}
pub fn store_wipe(&self) {
self.write_notification("store.wipe", None)
}
pub async fn store_keys(&self) -> Result<Vec<String>, ResponseError> {
self.request("store.keys", None).await.map(|r| match r {
Some(r) => serde_json::from_value::<Vec<String>>(r).unwrap_or_else(|_| vec![]),
None => vec![],
})
}
pub fn writeln(&self, line: impl Into<String>) {
self.write_notification("exec", Some(Value::String(line.into())));
}
pub fn broadcast(&self, line: impl Into<String>) {
self.write_notification("broadcast", Some(Value::String(line.into())));
}
pub fn whisper(&self, username: impl Into<String>, line: impl Into<String>) {
self.write_notification(
"whisper",
Some(json!({"target": username.into(), "line": line.into()})),
);
}
pub fn middle_print(&self, username: impl Into<String>, line: impl Into<String>) {
self.write_notification(
"middlePrint",
Some(json!({"target": username.into(), "line": line.into()})),
);
}
pub async fn get_players(&self) -> Result<Vec<Player>, ResponseError> {
self.request("getPlayers", None).await.map(|r| match r {
Some(r) => serde_json::from_value::<Vec<Player>>(r).unwrap_or_else(|_| vec![]),
None => vec![],
})
}
pub async fn get_all_player_positions(&self) -> Result<Vec<PlayerPosition>, ResponseError> {
self.request("getAllPlayerPositions", None)
.await
.map(|r| match r {
Some(r) => {
serde_json::from_value::<Vec<PlayerPosition>>(r).unwrap_or_else(|_| vec![])
}
None => vec![],
})
}
pub async fn get_role_setup(&self) -> Result<Value, ResponseError> {
self.request("getRoleSetup", None).await.map(Option::unwrap)
}
pub async fn get_ban_list(&self) -> Result<Value, ResponseError> {
self.request("getBanList", None).await.map(Option::unwrap)
}
pub async fn get_saves(&self) -> Result<Vec<String>, ResponseError> {
self.request("getSaves", None).await.map(|r| match r {
Some(r) => serde_json::from_value::<Vec<String>>(r).unwrap_or_else(|_| vec![]),
None => vec![],
})
}
pub async fn get_save_path(
&self,
save: impl Into<String>,
) -> Result<Option<String>, ResponseError> {
self.request("getSavePath", Some(Value::String(save.into())))
.await
.map(|r| match r {
Some(r) => serde_json::from_value::<String>(r).ok(),
None => None,
})
}
#[cfg(not(feature = "brs"))]
pub async fn get_save_data(&self) -> Result<Value, ResponseError> {
self.request("getSaveData", None).await.map(Option::unwrap)
}
#[cfg(feature = "brs")]
pub async fn get_save_data(&self) -> Result<save::SaveData, ResponseError> {
self.request("getSaveData", None)
.await
.map(|r| serde_json::from_value::<save::SaveData>(r.unwrap()).unwrap())
}
pub fn clear_bricks(&self, target: impl Into<String>, quiet: bool) {
self.write_notification(
"clearBricks",
Some(json!({"target": target.into(), "quiet": quiet})),
);
}
pub fn clear_all_bricks(&self, quiet: bool) {
self.write_notification("clearAllBricks", Some(json!({ "quiet": quiet })));
}
pub async fn save_bricks(&self, name: impl Into<String>) -> Result<(), ResponseError> {
self.request("saveBricks", Some(Value::String(name.into())))
.await
.map(|_| ())
}
pub async fn load_bricks(
&self,
name: impl Into<String>,
quiet: bool,
offset: (i32, i32, i32),
) -> Result<(), ResponseError> {
self.request("loadBricks", Some(json!({"name": name.into(), "quiet": quiet, "offX": offset.0, "offY": offset.1, "offZ": offset.2}))).await.map(|_| ())
}
pub async fn load_bricks_on_player(
&self,
name: impl Into<String>,
player: impl Into<String>,
offset: (i32, i32, i32),
) -> Result<(), ResponseError> {
self.request("loadBricksOnPlayer", Some(json!({"name": name.into(), "player": player.into(), "offX": offset.0, "offY": offset.1, "offZ": offset.2}))).await.map(|_| ())
}
#[cfg(not(feature = "brs"))]
pub async fn read_save_data(
&self,
name: impl Into<String>,
) -> Result<Option<Value>, ResponseError> {
self.request("readSaveData", Some(Value::String(name.into())))
.await
}
#[cfg(feature = "brs")]
pub async fn read_save_data(
&self,
name: impl Into<String>,
) -> Result<Option<save::SaveData>, ResponseError> {
self.request("readSaveData", Some(Value::String(name.into())))
.await
.map(|r| match r {
Some(r) => serde_json::from_value::<save::SaveData>(r).ok(),
None => None,
})
}
#[cfg(not(feature = "brs"))]
pub async fn load_save_data(
&self,
data: Value,
quiet: bool,
offset: (i32, i32, i32),
) -> Result<(), ResponseError> {
self.request("loadSaveData", Some(json!({"data": data, "quiet": quiet, "offX": offset.0, "offY": offset.1, "offZ": offset.2}))).await.map(|_| ())
}
#[cfg(feature = "brs")]
pub async fn load_save_data(
&self,
data: save::SaveData,
quiet: bool,
offset: (i32, i32, i32),
) -> Result<(), ResponseError> {
self.request("loadSaveData", Some(json!({"data": data, "quiet": quiet, "offX": offset.0, "offY": offset.1, "offZ": offset.2}))).await.map(|_| ())
}
#[cfg(not(feature = "brs"))]
pub async fn load_save_data_on_player(
&self,
data: Value,
player: impl Into<String>,
offset: (i32, i32, i32),
) -> Result<(), ResponseError> {
self.request("loadSaveDataOnPlayer", Some(json!({"data": data, "player": player.into(), "offX": offset.0, "offY": offset.1, "offZ": offset.2}))).await.map(|_| ())
}
#[cfg(feature = "brs")]
pub async fn load_save_data_on_player(
&self,
data: save::SaveData,
player: impl Into<String>,
offset: (i32, i32, i32),
) -> Result<(), ResponseError> {
self.request("loadSaveDataOnPlayer", Some(json!({"data": data, "player": player.into(), "offX": offset.0, "offY": offset.1, "offZ": offset.2}))).await.map(|_| ())
}
pub async fn change_map(&self, map: impl Into<String>) -> Result<(), ResponseError> {
self.request("changeMap", Some(Value::String(map.into())))
.await
.map(|_| ())
}
pub async fn get_player(
&self,
target: impl Into<String>,
) -> Result<Option<Player>, ResponseError> {
self.request("player.get", Some(Value::String(target.into())))
.await
.map(|r| serde_json::from_value::<Player>(r.unwrap_or(Value::Null)).ok())
}
pub async fn get_player_roles(
&self,
target: impl Into<String>,
) -> Result<Option<Vec<String>>, ResponseError> {
self.request("player.getRoles", Some(Value::String(target.into())))
.await
.map(|r| serde_json::from_value::<Vec<String>>(r.unwrap_or(Value::Null)).ok())
}
pub async fn get_player_permissions(
&self,
target: impl Into<String>,
) -> Result<Value, ResponseError> {
self.request("player.getPermissions", Some(Value::String(target.into())))
.await
.map(|r| r.unwrap_or(Value::Null))
}
pub async fn get_player_name_color(
&self,
target: impl Into<String>,
) -> Result<Option<String>, ResponseError> {
self.request("player.getNameColor", Some(Value::String(target.into())))
.await
.map(|r| r.and_then(|r| serde_json::from_value::<_>(r).ok()))
}
pub async fn get_player_position(
&self,
target: impl Into<String>,
) -> Result<Option<(f64, f64, f64)>, ResponseError> {
self.request("player.getPosition", Some(Value::String(target.into())))
.await
.map(|r| match r {
Some(r) => serde_json::from_value::<(f64, f64, f64)>(r).ok(),
None => None,
})
}
pub async fn get_player_ghost_brick(
&self,
target: impl Into<String>,
) -> Result<Option<GhostBrick>, ResponseError> {
self.request("player.getGhostBrick", Some(Value::String(target.into())))
.await
.map(|r| r.and_then(|r| serde_json::from_value::<_>(r).ok()))
}
pub async fn get_player_paint(
&self,
target: impl Into<String>,
) -> Result<Option<PlayerPaint>, ResponseError> {
self.request("player.getPaint", Some(Value::String(target.into())))
.await
.map(|r| r.and_then(|r| serde_json::from_value::<_>(r).ok()))
}
pub async fn get_player_template_bounds(
&self,
target: impl Into<String>,
) -> Result<Option<TemplateBounds>, ResponseError> {
self.request(
"player.getTemplateBounds",
Some(Value::String(target.into())),
)
.await
.map(|r| r.and_then(|r| serde_json::from_value::<_>(r).ok()))
}
#[cfg(not(feature = "brs"))]
pub async fn get_player_template_bounds_data(
&self,
target: impl Into<String>,
) -> Result<Option<Value>, ResponseError> {
self.request(
"player.getTemplateBoundsData",
Some(Value::String(target.into())),
)
.await
}
#[cfg(feature = "brs")]
pub async fn get_player_template_bounds_data(
&self,
target: impl Into<String>,
) -> Result<Option<save::SaveData>, ResponseError> {
self.request(
"player.getTemplateBoundsData",
Some(Value::String(target.into())),
)
.await
.map(|r| r.and_then(|r| serde_json::from_value::<_>(r).ok()))
}
#[cfg(not(feature = "brs"))]
pub async fn load_data_at_ghost_brick(
&self,
target: impl Into<String>,
data: Value,
offset: (i32, i32, i32),
rotate: bool,
quiet: bool,
) -> Result<(), ResponseError> {
self.request("player.loadDataAtGhostBrick", Some(json!({"target": target.into(), "data": data, "offX": offset.0, "offY": offset.1, "offZ": offset.2, "rotate": rotate, "quiet": quiet})))
.await
.map(|_| ())
}
#[cfg(feature = "brs")]
pub async fn load_data_at_ghost_brick(
&self,
target: impl Into<String>,
data: save::SaveData,
offset: (i32, i32, i32),
rotate: bool,
quiet: bool,
) -> Result<(), ResponseError> {
self.request("player.loadDataAtGhostBrick", Some(json!({"target": target.into(), "data": data, "offX": offset.0, "offY": offset.1, "offZ": offset.2, "rotate": rotate, "quiet": quiet})))
.await
.map(|_| ())
}
pub async fn get_plugin(
&self,
target: impl Into<String>,
) -> Result<Option<Plugin>, ResponseError> {
self.request("plugin.get", Some(Value::String(target.into())))
.await
.map(|r| r.and_then(|r| serde_json::from_value::<_>(r).ok()))
}
pub async fn emit_plugin<T>(
&self,
target: String,
event: String,
args: Vec<Value>,
) -> Result<Option<T>, ResponseError>
where
T: serde::de::DeserializeOwned,
{
let mut query = vec![Value::String(target.into()), Value::String(event.into())];
query.extend(args.into_iter());
self.request("plugin.emit", Some(Value::Array(query)))
.await
.map(|r| {
serde_json::from_value::<_>(r.unwrap_or_default())
.ok()
.unwrap_or_default()
})
}
}
impl Default for Omegga {
fn default() -> Self {
Self::new()
}
}