Set up a tabletop view for both the GM and the player #260
|
@ -8,7 +8,7 @@ use std::{
|
||||||
use tokio::sync::mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender};
|
use tokio::sync::mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
use crate::types::Message;
|
use crate::types::{AppError, Message, PlayArea};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct WebsocketClient {
|
struct WebsocketClient {
|
||||||
|
@ -19,6 +19,8 @@ struct WebsocketClient {
|
||||||
pub struct AppState {
|
pub struct AppState {
|
||||||
pub image_base: PathBuf,
|
pub image_base: PathBuf,
|
||||||
|
|
||||||
|
pub playfield_background: String,
|
||||||
|
|
||||||
pub clients: HashMap<String, WebsocketClient>,
|
pub clients: HashMap<String, WebsocketClient>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -29,6 +31,7 @@ impl Core {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self(Arc::new(RwLock::new(AppState {
|
Self(Arc::new(RwLock::new(AppState {
|
||||||
image_base: PathBuf::from("/home/savanni/Pictures"),
|
image_base: PathBuf::from("/home/savanni/Pictures"),
|
||||||
|
playfield_background: "moon.jpg".to_owned(),
|
||||||
clients: HashMap::new(),
|
clients: HashMap::new(),
|
||||||
})))
|
})))
|
||||||
}
|
}
|
||||||
|
@ -95,6 +98,15 @@ impl Core {
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_playfield_background(&self, path: String) -> Result<(), AppError> {
|
||||||
|
{
|
||||||
|
let mut state = self.0.write().unwrap();
|
||||||
|
state.playfield_background = path.clone();
|
||||||
|
}
|
||||||
|
self.publish(Message::PlayArea(PlayArea{ background_image: path }));
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
pub fn publish(&self, message: Message) {
|
pub fn publish(&self, message: Message) {
|
||||||
let state = self.0.read().unwrap();
|
let state = self.0.read().unwrap();
|
||||||
|
|
||||||
|
|
|
@ -98,13 +98,14 @@ pub async fn handle_connect_websocket(
|
||||||
ws.on_upgrade(move |socket| {
|
ws.on_upgrade(move |socket| {
|
||||||
let core = core.clone();
|
let core = core.clone();
|
||||||
async move {
|
async move {
|
||||||
let (mut ws_sender, mut ws_recv) = socket.split();
|
let (mut ws_sender, _) = socket.split();
|
||||||
let mut receiver = core.connect_client(client_id);
|
let mut receiver = core.connect_client(client_id);
|
||||||
|
|
||||||
tokio::task::spawn(async move {
|
tokio::task::spawn(async move {
|
||||||
|
let background_image = core.0.read().unwrap().playfield_background.clone();
|
||||||
let _ = ws_sender
|
let _ = ws_sender
|
||||||
.send(Message::text(
|
.send(Message::text(
|
||||||
serde_json::to_string(&crate::types::Message::Count(0)).unwrap(),
|
serde_json::to_string(&crate::types::Message::PlayArea(PlayArea{ background_image })).unwrap(),
|
||||||
))
|
))
|
||||||
.await;
|
.await;
|
||||||
while let Some(msg) = receiver.recv().await {
|
while let Some(msg) = receiver.recv().await {
|
||||||
|
|
|
@ -134,14 +134,13 @@ pub async fn main() {
|
||||||
move |ws, client_id| handle_connect_websocket(core.clone(), ws, client_id)
|
move |ws, client_id| handle_connect_websocket(core.clone(), ws, client_id)
|
||||||
});
|
});
|
||||||
|
|
||||||
let route_publish = warp::path!("api" / "v1" / "message")
|
let route_set_playfield_bg = warp::path!("api" / "v1" / "playfield" / "bg")
|
||||||
.and(warp::post())
|
.and(warp::put())
|
||||||
.and(warp::body::json())
|
.and(warp::body::json())
|
||||||
.map({
|
.map({
|
||||||
let core = core.clone();
|
let core = core.clone();
|
||||||
move |body| {
|
move |body| {
|
||||||
println!("route_publish: {:?}", body);
|
core.set_playfield_background(body);
|
||||||
core.publish(body);
|
|
||||||
warp::reply()
|
warp::reply()
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -152,7 +151,7 @@ pub async fn main() {
|
||||||
.or(route_playing_field)
|
.or(route_playing_field)
|
||||||
.or(route_image)
|
.or(route_image)
|
||||||
.or(route_available_images)
|
.or(route_available_images)
|
||||||
.or(route_publish)
|
.or(route_set_playfield_bg)
|
||||||
.recover(handle_rejection);
|
.recover(handle_rejection);
|
||||||
|
|
||||||
let server = warp::serve(filter);
|
let server = warp::serve(filter);
|
||||||
|
|
|
@ -12,9 +12,9 @@ pub enum AppError {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||||
|
#[serde(tag = "type")]
|
||||||
pub enum Message {
|
pub enum Message {
|
||||||
Count(u32),
|
PlayArea(PlayArea),
|
||||||
// PlayArea(PlayArea),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,7 @@ const App = ({ client }: AppProps) => {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/",
|
path: "/",
|
||||||
element: websocketUrl ? <WebsocketPlayingFieldComponent websocketUrl={websocketUrl} /> : <div> </div>
|
element: websocketUrl ? <WebsocketPlayingFieldComponent client={client} websocketUrl={websocketUrl} /> : <div> </div>
|
||||||
}
|
}
|
||||||
]);
|
]);
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -26,15 +26,26 @@ export const PlayingFieldComponent = ({ client }: PlayingFieldProps) => {
|
||||||
*/
|
*/
|
||||||
|
|
||||||
interface WebsocketPlayingFieldProps {
|
interface WebsocketPlayingFieldProps {
|
||||||
|
client: Client;
|
||||||
websocketUrl: string;
|
websocketUrl: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const WebsocketPlayingFieldComponent = ({ websocketUrl }: WebsocketPlayingFieldProps) => {
|
type Message = |
|
||||||
|
{
|
||||||
|
type: "PlayArea";
|
||||||
|
background_image: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const WebsocketPlayingFieldComponent = ({ client, websocketUrl }: WebsocketPlayingFieldProps) => {
|
||||||
const { lastMessage, readyState } = useWebSocket(websocketUrl);
|
const { lastMessage, readyState } = useWebSocket(websocketUrl);
|
||||||
|
const [backgroundUrl, setBackgroundUrl] = useState<URL | undefined>(undefined);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (lastMessage !== null) {
|
if (lastMessage !== null) {
|
||||||
console.log("last message: ", lastMessage);
|
const message: Message = JSON.parse(lastMessage.data);
|
||||||
|
console.log("playing area: ", message);
|
||||||
|
console.log("playing area: ", message.background_image);
|
||||||
|
setBackgroundUrl(client.imageUrl(message.background_image));
|
||||||
}
|
}
|
||||||
}, [lastMessage]);
|
}, [lastMessage]);
|
||||||
|
|
||||||
|
@ -46,11 +57,11 @@ export const WebsocketPlayingFieldComponent = ({ websocketUrl }: WebsocketPlayin
|
||||||
[ReadyState.UNINSTANTIATED]: 'Uninstantiated',
|
[ReadyState.UNINSTANTIATED]: 'Uninstantiated',
|
||||||
}[readyState];
|
}[readyState];
|
||||||
|
|
||||||
return <PlayingFieldComponent backgroundUrl={undefined} connectionStatus={connectionStatus} />;
|
return <PlayingFieldComponent backgroundUrl={backgroundUrl} connectionStatus={connectionStatus} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface PlayingFieldProps {
|
interface PlayingFieldProps {
|
||||||
backgroundUrl: string | undefined;
|
backgroundUrl: URL | undefined;
|
||||||
connectionStatus: string;
|
connectionStatus: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue