Skip to content

Commit

Permalink
WIP refactor API
Browse files Browse the repository at this point in the history
  • Loading branch information
jder committed Feb 8, 2020
1 parent 2a39ad0 commit e975c4b
Show file tree
Hide file tree
Showing 11 changed files with 263 additions and 156 deletions.
3 changes: 1 addition & 2 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
.*
~*
world.json
world.bak.json
/server/state
1 change: 0 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ Note that the config and dockerfiles are aimed at production use, not developmen

# TODO

* sessions so websocket reconnect re-connects to user
* chat history
* passwords
* ping/pong
Expand Down
7 changes: 7 additions & 0 deletions server/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion server/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,5 @@ uuid = { version = "0.8.1", features = ["v4"] }
multimap = "0.8.0"
regex = "1.3.4"
ctrlc = { version = "3.1.3", features = ["termination"] }
lazy_static = "1.4.0"
lazy_static = "1.4.0"
scoped-tls = "1.0.0"
2 changes: 1 addition & 1 deletion server/src/chat.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::lua::SerializableValue;
use crate::object_actor::ObjectMessage;
use crate::object::actor::ObjectMessage;
use crate::world::{Id, WorldRef};
use actix::{Actor, AsyncContext, Handler, Message, StreamHandler};
use actix_web::web;
Expand Down
5 changes: 4 additions & 1 deletion server/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
mod chat;
mod lua;
mod object_actor;
mod object;
mod util;
mod world;

Expand All @@ -18,6 +18,9 @@ use std::env;
use std::fs::{copy, rename, File};
use std::path::Path;

#[macro_use]
extern crate scoped_tls;

async fn index() -> impl Responder {
HttpResponse::Ok().body("Hello world!")
}
Expand Down
100 changes: 100 additions & 0 deletions server/src/object/actor.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
use crate::lua::LuaHost;
use crate::lua::SerializableValue;
use crate::object;
use crate::world::*;

use actix::{Actor, Context, Handler, Message};
use rlua;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;

pub struct ObjectActor {
pub(super) id: Id,
pub(super) world: WorldRef,
pub(super) lua_state: rlua::Lua,
pub(super) state: ObjectActorState,
}

#[derive(Serialize, Deserialize, Clone)]
pub struct ObjectActorState {
persistent_state: HashMap<String, SerializableValue>,
}

impl ObjectActor {
pub fn new(
id: Id,
world_ref: WorldRef,
state: Option<ObjectActorState>,
lua_host: &LuaHost,
) -> ObjectActor {
ObjectActor {
id: id,
world: world_ref,
state: state.unwrap_or(ObjectActorState {
persistent_state: HashMap::new(),
}),
lua_state: lua_host.fresh_state().unwrap(),
}
}

fn run_main(&mut self, msg: ObjectMessage) -> rlua::Result<()> {
// we hold a read lock on the world as a simple form of "transaction isolation" for now
// this is not useful right now but prevents us from accidentally writing to the world
// which could produce globally-visible effects while other objects are running.
let wf = self.world.clone();
let id = self.id;
wf.read(|_w| {
object::api::with_api(self, |lua_ctx| {
let globals = lua_ctx.globals();
let orisa: rlua::Table = globals.get("orisa")?;
orisa.set("self", id)?;
let main: rlua::Function = globals.get("main")?;

main.call::<_, ()>((msg.immediate_sender, msg.name, msg.payload))
})
})
}
}

impl Actor for ObjectActor {
type Context = Context<Self>;

fn started(&mut self, _ctx: &mut Self::Context) {
self
.lua_state
.context(|ctx| object::api::register_api(ctx))
.unwrap();
}
}

impl Handler<ObjectMessage> for ObjectActor {
type Result = ();

fn handle(&mut self, msg: ObjectMessage, _ctx: &mut Self::Context) {
let _ = self
.run_main(msg)
.map_err(|err: rlua::Error| log::error!("Failed running payload: {:?}", err));
}
}

impl Handler<FreezeMessage> for ObjectActor {
type Result = Option<FreezeResponse>;

fn handle(&mut self, _msg: FreezeMessage, _ctx: &mut Self::Context) -> Option<FreezeResponse> {
Some(FreezeResponse {
id: self.id,
state: self.state.clone(),
})
}
}

#[derive(Debug)]
pub struct ObjectMessage {
pub immediate_sender: Id,
pub name: String,
pub payload: SerializableValue,
}

impl Message for ObjectMessage {
type Result = ();
}
145 changes: 145 additions & 0 deletions server/src/object/api.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
use crate::object::actor::ObjectActor;
use crate::world::{Id, World};
use rlua;
use std::cell::RefCell;

pub struct ObjectApiExecutionState<'a> {
actor: RefCell<&'a mut ObjectActor>,
}

impl<'a> ObjectApiExecutionState<'a> {
fn new(actor: &mut ObjectActor) -> ObjectApiExecutionState {
ObjectApiExecutionState {
actor: RefCell::new(actor),
}
}

fn with_state<T, F>(body: F) -> T
where
F: FnOnce(&ObjectApiExecutionState) -> T,
{
EXECUTION_STATE.with(|s| body(s))
}

fn with_actor<T, F>(body: F) -> T
where
F: FnOnce(&ObjectActor) -> T,
{
Self::with_state(|s| body(&s.actor.borrow()))
}

fn with_world<T, F>(body: F) -> T
where
F: FnOnce(&World) -> T,
{
Self::with_state(|s| s.actor.borrow().world.read(|w| body(w)))
}

fn get_id() -> Id {
Self::with_actor(|a| a.id)
}
}

scoped_thread_local! {static EXECUTION_STATE: ObjectApiExecutionState}

// API

mod api {
use crate::chat::{ChatRowContent, ToClientMessage};
use crate::lua::*;
use crate::object::actor::ObjectMessage;
use crate::object::api::ObjectApiExecutionState as S;
use crate::world::Id;
pub fn get_children(_lua_ctx: rlua::Context, object_id: Id) -> rlua::Result<Vec<Id>> {
Ok(S::with_world(|w| {
w.children(object_id).collect::<Vec<Id>>()
}))
}

pub fn send(
_lua_ctx: rlua::Context,
(object_id, name, payload): (Id, String, SerializableValue),
) -> rlua::Result<()> {
Ok(S::with_world(|w| {
w.send_message(
object_id,
ObjectMessage {
immediate_sender: S::get_id(),
name: name,
payload: payload,
},
)
}))
}

pub fn tell(_lua_ctx: rlua::Context, message: String) -> rlua::Result<()> {
Ok(S::with_world(|w| {
w.send_client_message(
S::get_id(),
ToClientMessage::Tell {
content: ChatRowContent::new(&message),
},
)
}))
}

pub fn get_name(_lua_ctx: rlua::Context, id: Id) -> rlua::Result<String> {
Ok(S::with_world(|w| w.username(id)))
}

pub fn get_kind(_lua_ctx: rlua::Context, id: Id) -> rlua::Result<String> {
Ok(S::with_world(|w| w.kind(id).0))
}

// orisa.set(
// "set_state",
// scope
// .create_function_mut(
// |_lua_ctx, (object_id, key, value): (Id, String, SerializableValue)| {
// if object_id != self.id {
// // Someday we might relax this given capabilities and probably containment (for concurrency)
// // Err("Can only set your own properties.")
// Ok(())
// } else {
// self.state.persistent_state.insert(key, value);
// Ok(())
// }
// },
// )
// .unwrap(),
// );
}

pub fn register_api(lua_ctx: rlua::Context) -> rlua::Result<()> {
let globals = lua_ctx.globals();
let orisa = lua_ctx.create_table()?;

orisa.set("get_children", lua_ctx.create_function(api::get_children)?)?;
orisa.set("send", lua_ctx.create_function(api::send)?)?;
orisa.set("tell", lua_ctx.create_function(api::tell)?)?;
orisa.set("get_name", lua_ctx.create_function(api::get_name)?)?;
orisa.set("get_kind", lua_ctx.create_function(api::get_kind)?)?;

globals.set("orisa", orisa)?;
Ok(())
}

pub fn with_api<'a, F, T>(actor: &mut ObjectActor, body: F) -> T
where
F: FnOnce(rlua::Context) -> T,
{
let state = ObjectApiExecutionState::new(actor);

// This is a gross hack but is safe since the scoped thread local ensures
// this value only exists as long as this block.
EXECUTION_STATE.set(unsafe { make_static(&state) }, || {
ObjectApiExecutionState::with_actor(|actor| actor.lua_state.context(|lua_ctx| body(lua_ctx)))
})
}

unsafe fn make_static<'a>(
p: &'a ObjectApiExecutionState<'a>,
) -> &'static ObjectApiExecutionState<'static> {
use std::mem;
mem::transmute(p)
}
2 changes: 2 additions & 0 deletions server/src/object/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
pub mod actor;
mod api;
Loading

0 comments on commit e975c4b

Please sign in to comment.