mirror of
https://github.com/rojo-rbx/rojo.git
synced 2026-10-25 04:54:07 +00:00
Implement long-polling message passing
This commit is contained in:
parent
a9d24f1493
commit
92fa52c56f
8 changed files with 93 additions and 39 deletions
|
|
@ -22,6 +22,10 @@
|
||||||
"path": "modules/testez/lib",
|
"path": "modules/testez/lib",
|
||||||
"target": "ReplicatedStorage.TestEZ"
|
"target": "ReplicatedStorage.TestEZ"
|
||||||
},
|
},
|
||||||
|
"modules/promise": {
|
||||||
|
"path": "modules/promise/lib",
|
||||||
|
"target": "ReplicatedStorage.Rojo.modules.Promise"
|
||||||
|
},
|
||||||
"tests": {
|
"tests": {
|
||||||
"path": "tests",
|
"path": "tests",
|
||||||
"target": "TestService"
|
"target": "TestService"
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
|
local Promise = require(script.Parent.Parent.modules.Promise)
|
||||||
|
|
||||||
local Config = require(script.Parent.Config)
|
local Config = require(script.Parent.Config)
|
||||||
local Promise = require(script.Parent.Promise)
|
|
||||||
local Version = require(script.Parent.Version)
|
local Version = require(script.Parent.Version)
|
||||||
local Http = require(script.Parent.Http)
|
local Http = require(script.Parent.Http)
|
||||||
|
|
||||||
|
|
@ -24,7 +25,8 @@ function ApiContext.new(url, onMessage)
|
||||||
url = url,
|
url = url,
|
||||||
onMessage = onMessage,
|
onMessage = onMessage,
|
||||||
serverId = nil,
|
serverId = nil,
|
||||||
currentTime = 0,
|
connected = false,
|
||||||
|
eventCursor = -1,
|
||||||
}
|
}
|
||||||
|
|
||||||
setmetatable(context, ApiContext)
|
setmetatable(context, ApiContext)
|
||||||
|
|
@ -35,9 +37,9 @@ end
|
||||||
function ApiContext:connect()
|
function ApiContext:connect()
|
||||||
return Http.get(self.url)
|
return Http.get(self.url)
|
||||||
:andThen(function(response)
|
:andThen(function(response)
|
||||||
response = response:json()
|
local body = response:json()
|
||||||
|
|
||||||
if response.protocolVersion ~= Config.protocolVersion then
|
if body.protocolVersion ~= Config.protocolVersion then
|
||||||
local message = (
|
local message = (
|
||||||
"Found a Rojo dev server, but it's using a different protocol version, and is incompatible." ..
|
"Found a Rojo dev server, but it's using a different protocol version, and is incompatible." ..
|
||||||
"\nMake sure you have matching versions of both the Rojo plugin and server!" ..
|
"\nMake sure you have matching versions of both the Rojo plugin and server!" ..
|
||||||
|
|
@ -47,14 +49,43 @@ function ApiContext:connect()
|
||||||
):format(
|
):format(
|
||||||
Version.display(Config.version), Config.protocolVersion,
|
Version.display(Config.version), Config.protocolVersion,
|
||||||
Config.expectedApiContextVersionString,
|
Config.expectedApiContextVersionString,
|
||||||
response.serverVersion, response.protocolVersion
|
body.serverVersion, body.protocolVersion
|
||||||
)
|
)
|
||||||
|
|
||||||
return Promise.reject(message)
|
return Promise.reject(message)
|
||||||
end
|
end
|
||||||
|
|
||||||
self.serverId = response.serverId
|
self.serverId = body.serverId
|
||||||
self.currentTime = response.currentTime
|
self.connected = true
|
||||||
|
|
||||||
|
return self:_retrieveMessages()
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
function ApiContext:_retrieveMessages()
|
||||||
|
if not self.connected then
|
||||||
|
return Promise.resolve()
|
||||||
|
end
|
||||||
|
|
||||||
|
return Http.get(self.url .. "/subscribe/" .. self.eventCursor)
|
||||||
|
:andThen(function(response)
|
||||||
|
local body = response:json()
|
||||||
|
|
||||||
|
if body.serverId ~= self.serverId then
|
||||||
|
return Promise.reject("server changed ID")
|
||||||
|
end
|
||||||
|
|
||||||
|
for _, event in ipairs(body.events) do
|
||||||
|
self.onMessage(event)
|
||||||
|
end
|
||||||
|
|
||||||
|
self.eventCursor = body.eventCursor
|
||||||
|
|
||||||
|
return self:_retrieveMessages()
|
||||||
|
end, function(err)
|
||||||
|
self.connected = false
|
||||||
|
|
||||||
|
return Promise.reject(err)
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,8 @@ local HttpService = game:GetService("HttpService")
|
||||||
|
|
||||||
local HTTP_DEBUG = false
|
local HTTP_DEBUG = false
|
||||||
|
|
||||||
local Promise = require(script.Parent.Promise)
|
local Promise = require(script.Parent.Parent.modules.Promise)
|
||||||
|
|
||||||
local HttpError = require(script.Parent.HttpError)
|
local HttpError = require(script.Parent.HttpError)
|
||||||
local HttpResponse = require(script.Parent.HttpResponse)
|
local HttpResponse = require(script.Parent.HttpResponse)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -43,12 +43,19 @@ local function main()
|
||||||
|
|
||||||
local toolbar = plugin:CreateToolbar("Rojo " .. displayedVersion)
|
local toolbar = plugin:CreateToolbar("Rojo " .. displayedVersion)
|
||||||
|
|
||||||
|
local currentSession
|
||||||
|
|
||||||
toolbar:CreateButton("Connect", "Connect to Rojo Session", "")
|
toolbar:CreateButton("Connect", "Connect to Rojo Session", "")
|
||||||
.Click:Connect(function()
|
.Click:Connect(function()
|
||||||
checkUpgrade()
|
checkUpgrade()
|
||||||
Session.new()
|
|
||||||
|
|
||||||
error("NYI")
|
if currentSession ~= nil then
|
||||||
|
warn("Rojo: A session is already running!")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
print("Rojo: Started session.")
|
||||||
|
currentSession = Session.new()
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,8 @@
|
||||||
|
local Promise = require(script.Parent.Parent.modules.Promise)
|
||||||
|
|
||||||
local Config = require(script.Parent.Config)
|
local Config = require(script.Parent.Config)
|
||||||
local Http = require(script.Parent.Http)
|
local Http = require(script.Parent.Http)
|
||||||
local ApiContext = require(script.Parent.ApiContext)
|
local ApiContext = require(script.Parent.ApiContext)
|
||||||
local Promise = require(script.Parent.Promise)
|
|
||||||
|
|
||||||
local REMOTE_URL = ("http://localhost:%d"):format(Config.port)
|
local REMOTE_URL = ("http://localhost:%d"):format(Config.port)
|
||||||
|
|
||||||
|
|
@ -13,7 +14,9 @@ function Session.new()
|
||||||
|
|
||||||
setmetatable(self, Session)
|
setmetatable(self, Session)
|
||||||
|
|
||||||
local api = ApiContext.new(REMOTE_URL)
|
local api = ApiContext.new(REMOTE_URL, function(message)
|
||||||
|
print("Got message:", message)
|
||||||
|
end)
|
||||||
api:connect()
|
api:connect()
|
||||||
|
|
||||||
return self
|
return self
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::process;
|
use std::process;
|
||||||
use std::time::Instant;
|
|
||||||
use std::fs;
|
use std::fs;
|
||||||
|
|
||||||
use rand;
|
use rand;
|
||||||
|
|
@ -50,7 +49,6 @@ pub fn serve(project_path: &PathBuf, port: Option<u64>) {
|
||||||
let web_config = web::WebConfig {
|
let web_config = web::WebConfig {
|
||||||
port: port.unwrap_or(project.serve_port),
|
port: port.unwrap_or(project.serve_port),
|
||||||
server_id,
|
server_id,
|
||||||
start_time: Instant::now(),
|
|
||||||
rbx_session: session.get_rbx_session(),
|
rbx_session: session.get_rbx_session(),
|
||||||
events: session.get_events(),
|
events: session.get_events(),
|
||||||
event_listeners: session.get_event_listeners(),
|
event_listeners: session.get_event_listeners(),
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ pub struct Session {
|
||||||
config: SessionConfig,
|
config: SessionConfig,
|
||||||
vfs_session: Arc<RwLock<VfsSession>>,
|
vfs_session: Arc<RwLock<VfsSession>>,
|
||||||
rbx_session: Arc<RwLock<RbxSession>>,
|
rbx_session: Arc<RwLock<RbxSession>>,
|
||||||
middlewares: Vec<Box<Middleware>>,
|
// middlewares: Vec<Box<Middleware>>,
|
||||||
watchers: Vec<PartitionWatcher>,
|
watchers: Vec<PartitionWatcher>,
|
||||||
events: Arc<RwLock<Vec<SessionEvent>>>,
|
events: Arc<RwLock<Vec<SessionEvent>>>,
|
||||||
event_listeners: Arc<Mutex<HashMap<Id, mpsc::Sender<()>>>>,
|
event_listeners: Arc<Mutex<HashMap<Id, mpsc::Sender<()>>>>,
|
||||||
|
|
@ -40,7 +40,7 @@ impl Session {
|
||||||
Session {
|
Session {
|
||||||
vfs_session,
|
vfs_session,
|
||||||
rbx_session,
|
rbx_session,
|
||||||
middlewares: Vec::new(),
|
// middlewares: Vec::new(),
|
||||||
watchers: Vec::new(),
|
watchers: Vec::new(),
|
||||||
events: Arc::new(RwLock::new(Vec::new())),
|
events: Arc::new(RwLock::new(Vec::new())),
|
||||||
event_listeners: Arc::new(Mutex::new(HashMap::new())),
|
event_listeners: Arc::new(Mutex::new(HashMap::new())),
|
||||||
|
|
@ -69,12 +69,32 @@ impl Session {
|
||||||
|
|
||||||
{
|
{
|
||||||
let vfs_session = self.vfs_session.clone();
|
let vfs_session = self.vfs_session.clone();
|
||||||
|
let event_listeners = self.event_listeners.clone();
|
||||||
|
let events = self.events.clone();
|
||||||
|
|
||||||
thread::spawn(move || {
|
thread::spawn(move || {
|
||||||
loop {
|
loop {
|
||||||
match rx.recv() {
|
match rx.recv() {
|
||||||
Ok(change) => {
|
Ok(change) => {
|
||||||
|
{
|
||||||
let mut vfs_session = vfs_session.write().unwrap();
|
let mut vfs_session = vfs_session.write().unwrap();
|
||||||
vfs_session.handle_change(change);
|
vfs_session.handle_change(change);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Handle change in RbxSession
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut events = events.write().unwrap();
|
||||||
|
events.push(SessionEvent::Something);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
let listeners = event_listeners.lock().unwrap();
|
||||||
|
|
||||||
|
for listener in listeners.values() {
|
||||||
|
listener.send(()).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
Err(_) => break,
|
Err(_) => break,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::time::Instant;
|
|
||||||
use std::sync::{mpsc, RwLock, Mutex, Arc};
|
use std::sync::{mpsc, RwLock, Mutex, Arc};
|
||||||
|
|
||||||
use rouille;
|
use rouille;
|
||||||
|
|
@ -14,7 +13,6 @@ use session::SessionEvent;
|
||||||
pub struct WebConfig {
|
pub struct WebConfig {
|
||||||
pub port: u64,
|
pub port: u64,
|
||||||
pub server_id: u64,
|
pub server_id: u64,
|
||||||
pub start_time: Instant,
|
|
||||||
pub rbx_session: Arc<RwLock<RbxSession>>,
|
pub rbx_session: Arc<RwLock<RbxSession>>,
|
||||||
pub events: Arc<RwLock<Vec<SessionEvent>>>,
|
pub events: Arc<RwLock<Vec<SessionEvent>>>,
|
||||||
pub event_listeners: Arc<Mutex<HashMap<Id, mpsc::Sender<()>>>>,
|
pub event_listeners: Arc<Mutex<HashMap<Id, mpsc::Sender<()>>>>,
|
||||||
|
|
@ -26,22 +24,22 @@ struct ServerInfoResponse<'a> {
|
||||||
server_version: &'static str,
|
server_version: &'static str,
|
||||||
protocol_version: u64,
|
protocol_version: u64,
|
||||||
server_id: &'a str,
|
server_id: &'a str,
|
||||||
current_time: f64,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize)]
|
#[derive(Debug, Serialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
struct ReadAllResponse<'a> {
|
struct ReadAllResponse<'a> {
|
||||||
server_id: &'a str,
|
server_id: &'a str,
|
||||||
current_time: f64,
|
|
||||||
instances: &'a HashMap<Id, RbxInstance>,
|
instances: &'a HashMap<Id, RbxInstance>,
|
||||||
|
event_cursor: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize)]
|
#[derive(Debug, Serialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
struct SubscribeResponse<'a> {
|
struct SubscribeResponse<'a> {
|
||||||
server_id: &'a str,
|
server_id: &'a str,
|
||||||
events: &'a [SessionEvent],
|
events: &'a [SessionEvent],
|
||||||
cursor: i32,
|
event_cursor: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Start the Rojo web server and park our current thread.
|
/// Start the Rojo web server and park our current thread.
|
||||||
|
|
@ -57,17 +55,10 @@ pub fn start(config: WebConfig) {
|
||||||
(GET) (/) => {
|
(GET) (/) => {
|
||||||
// Get a summary of information about the server.
|
// Get a summary of information about the server.
|
||||||
|
|
||||||
let current_time = {
|
|
||||||
let elapsed = config.start_time.elapsed();
|
|
||||||
|
|
||||||
elapsed.as_secs() as f64 + elapsed.subsec_nanos() as f64 / 1_000_000_000.0
|
|
||||||
};
|
|
||||||
|
|
||||||
json_response(ServerInfoResponse {
|
json_response(ServerInfoResponse {
|
||||||
server_version,
|
server_version,
|
||||||
protocol_version: 2,
|
protocol_version: 2,
|
||||||
server_id: &server_id,
|
server_id: &server_id,
|
||||||
current_time,
|
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
@ -79,13 +70,13 @@ pub fn start(config: WebConfig) {
|
||||||
{
|
{
|
||||||
let events = config.events.read().unwrap();
|
let events = config.events.read().unwrap();
|
||||||
if cursor < events.len() as i32 - 1 {
|
if cursor < events.len() as i32 - 1 {
|
||||||
let new_events = &events[cursor as usize + 1..];
|
let new_events = &events[(cursor + 1) as usize..];
|
||||||
let new_cursor = cursor + new_events.len() as i32;
|
let new_cursor = cursor + new_events.len() as i32;
|
||||||
|
|
||||||
return json_response(SubscribeResponse {
|
return json_response(SubscribeResponse {
|
||||||
server_id: &server_id,
|
server_id: &server_id,
|
||||||
events: new_events,
|
events: new_events,
|
||||||
cursor: new_cursor,
|
event_cursor: new_cursor,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -110,13 +101,13 @@ pub fn start(config: WebConfig) {
|
||||||
|
|
||||||
{
|
{
|
||||||
let events = config.events.read().unwrap();
|
let events = config.events.read().unwrap();
|
||||||
let new_events = &events[cursor as usize + 1..];
|
let new_events = &events[(cursor + 1) as usize..];
|
||||||
let new_cursor = cursor + new_events.len() as i32;
|
let new_cursor = cursor + new_events.len() as i32;
|
||||||
|
|
||||||
return json_response(SubscribeResponse {
|
return json_response(SubscribeResponse {
|
||||||
server_id: &server_id,
|
server_id: &server_id,
|
||||||
events: new_events,
|
events: new_events,
|
||||||
cursor: new_cursor,
|
event_cursor: new_cursor,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -124,15 +115,14 @@ pub fn start(config: WebConfig) {
|
||||||
(GET) (/read_all) => {
|
(GET) (/read_all) => {
|
||||||
let rbx_session = config.rbx_session.read().unwrap();
|
let rbx_session = config.rbx_session.read().unwrap();
|
||||||
|
|
||||||
let current_time = {
|
let event_cursor = {
|
||||||
let elapsed = config.start_time.elapsed();
|
let events = config.events.read().unwrap();
|
||||||
|
events.len() as i32 - 1
|
||||||
elapsed.as_secs() as f64 + elapsed.subsec_nanos() as f64 / 1_000_000_000.0
|
|
||||||
};
|
};
|
||||||
|
|
||||||
json_response(ReadAllResponse {
|
json_response(ReadAllResponse {
|
||||||
server_id: &server_id,
|
server_id: &server_id,
|
||||||
current_time,
|
event_cursor,
|
||||||
instances: &rbx_session.instances,
|
instances: &rbx_session.instances,
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue