-
-
Settings
-
-
-
-
-
-
Grid width
-
-
Grid color
-
-
Grid type
-
+
+
+
Admin
+
+
+
+
+
+
+
+
+
+
diff --git a/assets/web/style.css b/assets/web/style.css
index 87ed95a..dd67b1a 100644
--- a/assets/web/style.css
+++ b/assets/web/style.css
@@ -102,13 +102,21 @@ body {
top: 10px;
left: 5px;
z-index: 5;
- border-color: black;
- border-width: 2px;
+ border-color: transparent;
+ border-width: 0px;
border-style: solid;
- background-color: black;
+ background-color: transparent;
}
#scene-list button {
background-color: wheat;
border-width: 0px;
+}
+
+.exit-button {
+ height: fit-content;
+ align-self: center;
+ background-color: transparent;
+ font-size: 20px;
+ border: 0;
}
\ No newline at end of file
diff --git a/assets/web/tavern.js b/assets/web/tavern.js
index 366434a..84cd3d1 100644
--- a/assets/web/tavern.js
+++ b/assets/web/tavern.js
@@ -1,7 +1,21 @@
class Tavern {
+ // Callback definitions!
+ onlogin = (_loggedIn) => { };
+ onmessage = (_message) => { };
+ onspawntoken = (_spawnToken) => { };
+ onmovetoken = (_moveToken) => { };
+ onshowscene = (_scene) => { };
+ onscenelist = (_sceneList) => { };
+ onconnectedplayers = (_playersList) => { };
+
constructor() {
this.socket = new WebSocket('ws:/' + window.location.host + '/ws');
this.socket.onopen = () => this.connected = true;
+ this.socket.onclose = () => {
+ this.connected = false;
+ this.loggedIn = false;
+ this.call(this.onclose);
+ };
this.msgs = [];
this.connected = false;
this.loggedIn = false;
@@ -36,6 +50,9 @@ class Tavern {
if (m.scene_list) {
this.call(this.onscenelist, m.scene_list);
}
+ if (m.connected_players) {
+ this.call(this.onconnectedplayers, m.connected_players);
+ }
}
}
call(f, ...args) {
@@ -101,7 +118,7 @@ class Tavern {
}
get_scene = (id) => {
if (!this.connected || this.loggedIn) { return; }
- this.socket.send(JSON.stringify({ get_scene: { id: id } }))
+ this.socket.send(JSON.stringify({ get_scene: { scene: id } }))
}
get_scene_list = () => {
if (!this.connected || this.loggedIn) { return; }
@@ -120,9 +137,25 @@ class Tavern {
this.socket.send(JSON.stringify({ create_scene: { title: title } }));
}
shutdown = () => {
- if (!this.connected || this.loggedIn) { return; }
+ if (!this.connected || this.loggedIn || !this.admin) { return; }
this.socket.send(JSON.stringify('shutdown'));
}
+ save = () => {
+ if (!this.connected || this.loggedIn || !this.admin) { return; }
+ this.socket.send(JSON.stringify('save'));
+ }
+ kick = (id) => {
+ if (!this.connected || this.loggedIn || !this.admin) { return; }
+ this.socket.send(JSON.stringify({ kick: id }));
+ }
+ show_scene = (id) => {
+ if (!this.connected || this.loggedIn || !this.admin) { return; }
+ this.socket.send(JSON.stringify({ show_scene: { scene: id } }));
+ }
+ set_scene_visible = (id, visible) => {
+ if (!this.connected || this.loggedIn || !this.admin) { return; }
+ this.socket.send(JSON.stringify({ set_scene_visible: { scene: id, visible: visible } }));
+ }
}
const tavern = new Tavern();
diff --git a/src/api/mod.rs b/src/api/mod.rs
index 426f7b8..92cd4b6 100644
--- a/src/api/mod.rs
+++ b/src/api/mod.rs
@@ -16,6 +16,7 @@ pub enum Request {
Login(login::LoginRequest),
Quit,
Kick(String),
+ Save,
Shutdown,
// Character stuff
CreateCharacter,
@@ -57,12 +58,15 @@ pub enum Request {
grid_cell_size: f32,
grid_offset: [f32; 2],
},
- SceneSetVisible {
+ SetSceneVisible {
scene: usize,
visible: bool,
},
GetScene {
- id: usize,
+ scene: usize,
+ },
+ ShowScene {
+ scene: usize,
},
/// TODO: Perhaps remove it, as the client should auto get it on login and on scene list updates
GetSceneList,
@@ -101,7 +105,7 @@ pub enum Response {
grid_offset: Option<[f32; 2]>,
},
SceneList {
- scenes: Vec<(usize, String)>,
+ scenes: Vec<(usize, String, bool)>,
},
MoveToken {
token_id: usize,
@@ -110,6 +114,7 @@ pub enum Response {
},
SpawnToken(SpawnToken),
CharacterCreated(usize),
+ ConnectedPlayers(Vec
),
Quit {
id: String,
},
diff --git a/src/lib.rs b/src/lib.rs
index ef99e42..8ab90dd 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -69,6 +69,7 @@ impl GameServer {
}
pub async fn save(&self) {
+ println!("Saving game data!");
let _ = tokio::fs::create_dir("data").await;
if let Ok(json) = serde_json::to_string(self) {
let _ = tokio::fs::write(GAME_SAVE_FILE, json.as_bytes()).await;
@@ -108,6 +109,8 @@ impl GameServer {
// Send the list of scenes and chat history and such
_ = broadcast.send((SendTo::User(login.username.clone()), self.get_scene_list(admin)));
_ = broadcast.send((SendTo::User(login.username.clone()), self.get_last_messages(50)));
+ let c = self.users.keys().cloned().collect();
+ _ = broadcast.send((SendTo::All, api::Response::ConnectedPlayers(c)))
} else {
_ = broadcast.send((
SendTo::User(id.clone()),
@@ -163,11 +166,26 @@ impl GameServer {
}
}
}
- api::Request::GetScene { id: scene_id } => {
- if let Some(response) = self.get_scene(&id, scene_id) {
+ api::Request::GetScene { scene } => {
+ if let Some(response) = self.get_scene(&id, scene) {
_ = broadcast.send((SendTo::User(id), response));
}
}
+ api::Request::ShowScene { scene } => {
+ if self.is_admin(&id) {
+ if let Some(s) = self.game.get_scene_mut(scene) {
+ // Force visible to players
+ if !s.visible_to_users {
+ s.visible_to_users = true;
+ _ = broadcast.send((SendTo::AllBut(id.clone()), self.get_scene_list(false)));
+ _ = broadcast.send((SendTo::User(id.clone()), self.get_scene_list(true)));
+ }
+ }
+ if let Some(response) = self.get_scene(&id, scene) {
+ _ = broadcast.send((SendTo::All, response))
+ }
+ }
+ }
api::Request::CreateScene { title } => {
if self.is_admin(&id) {
self.game.create_scene(title);
@@ -197,7 +215,7 @@ impl GameServer {
}
}
}
- api::Request::SceneSetVisible { scene, visible } => {
+ api::Request::SetSceneVisible { scene, visible } => {
if self.is_admin(&id) {
self.game.scene_visible(scene, visible);
_ = broadcast.send((SendTo::AllBut(id.clone()), self.get_scene_list(false)));
@@ -272,19 +290,24 @@ impl GameServer {
if self.users.contains_key(&id) {
self.users.remove(&id);
}
- _ = broadcast.send((SendTo::All, api::Response::Quit { id }));
+ // _ = broadcast.send((SendTo::All, api::Response::Quit { id }));
+ let c = self.users.keys().cloned().collect();
+ _ = broadcast.send((SendTo::All, api::Response::ConnectedPlayers(c)))
}
api::Request::Kick(id) => {
if self.users.contains_key(&id) {
self.users.remove(&id);
}
_ = broadcast.send((SendTo::User(id), api::Response::Shutdown));
+ let c = self.users.keys().cloned().collect();
+ _ = broadcast.send((SendTo::All, api::Response::ConnectedPlayers(c)))
}
api::Request::Shutdown => {
if self.is_admin(&id) {
break;
}
}
+ api::Request::Save => self.save().await,
api::Request::CharacterDisplay { id: _ } => {}
api::Request::CharacterInputs { id: _ } => todo!(),
api::Request::CharacterGetField { id: _, field: _ } => todo!(),
@@ -313,7 +336,7 @@ impl GameServer {
.map(|id| self.game.get_scene(*id).map(|s| (id, s)))
.flatten()
.filter(|(_, scene)| admin || scene.visible_to_users)
- .map(|(id, s)| (*id, s.title.to_string()))
+ .map(|(id, s)| (*id, s.title.to_string(), s.visible_to_users))
.collect::>();
api::Response::SceneList { scenes }
}