-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
11 changed files
with
263 additions
and
156 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,3 @@ | ||
.* | ||
~* | ||
world.json | ||
world.bak.json | ||
/server/state |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 = (); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
pub mod actor; | ||
mod api; |
Oops, something went wrong.