summaryrefslogtreecommitdiff
path: root/server
diff options
context:
space:
mode:
Diffstat (limited to 'server')
-rw-r--r--server/protocol/src/lib.rs5
-rw-r--r--server/protocol/src/movement.rs13
-rw-r--r--server/protocol/src/registry.rs4
-rw-r--r--server/registry/Cargo.toml3
-rw-r--r--server/registry/src/lobby.rs164
-rw-r--r--server/registry/src/main.rs25
6 files changed, 204 insertions, 10 deletions
diff --git a/server/protocol/src/lib.rs b/server/protocol/src/lib.rs
index 56ddc7a8..b826edae 100644
--- a/server/protocol/src/lib.rs
+++ b/server/protocol/src/lib.rs
@@ -28,7 +28,7 @@ pub use glam;
pub mod movement;
pub mod registry;
-pub const VERSION: (u32, u32) = (7, 0);
+pub const VERSION: (u32, u32) = (7, 1);
pub const BINCODE_CONFIG: Configuration<LittleEndian, Varint, Limit<4096>> =
standard().with_limit();
@@ -247,6 +247,9 @@ pub enum PacketC {
item: ItemIndex,
success: bool,
},
+ Redirect {
+ uri: Vec<String>,
+ },
/// For use in replay sessions only
ReplayStart,
diff --git a/server/protocol/src/movement.rs b/server/protocol/src/movement.rs
index 85accb31..a02ee8ee 100644
--- a/server/protocol/src/movement.rs
+++ b/server/protocol/src/movement.rs
@@ -17,7 +17,7 @@
*/
use crate::{
glam::{IVec2, Vec2},
- PacketS, PlayerID,
+ PacketC, PacketS, PlayerID,
};
use std::collections::HashSet;
@@ -81,7 +81,7 @@ impl MovementBase {
collide_player_tiles(self, map);
}
- pub fn movement_packet(&self, player: PlayerID) -> PacketS {
+ pub fn movement_packet_s(&self, player: PlayerID) -> PacketS {
PacketS::Movement {
pos: Some(self.position),
boost: self.input_boost,
@@ -89,6 +89,15 @@ impl MovementBase {
player,
}
}
+ pub fn movement_packet_c(&self, player: PlayerID) -> PacketC {
+ PacketC::Movement {
+ rot: self.rotation,
+ pos: self.position,
+ boost: self.input_boost,
+ dir: self.input_direction,
+ player,
+ }
+ }
pub fn collide(&mut self, other: &mut Self, dt: f32) {
let diff = self.position - other.position;
diff --git a/server/protocol/src/registry.rs b/server/protocol/src/registry.rs
index 92f2c454..b5b83bd1 100644
--- a/server/protocol/src/registry.rs
+++ b/server/protocol/src/registry.rs
@@ -1,6 +1,6 @@
use serde::{Deserialize, Serialize};
-#[derive(Debug, Serialize)]
+#[derive(Debug, Clone, Serialize)]
pub struct Entry {
pub name: String,
pub address: Vec<String>,
@@ -9,7 +9,7 @@ pub struct Entry {
pub version: (u32, u32),
}
-#[derive(Debug, Deserialize, Serialize)]
+#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct Submission {
pub secret: u128,
pub name: String,
diff --git a/server/registry/Cargo.toml b/server/registry/Cargo.toml
index 8acd3c4c..2eeebb30 100644
--- a/server/registry/Cargo.toml
+++ b/server/registry/Cargo.toml
@@ -12,6 +12,9 @@ tokio = { version = "1.39.2", features = ["full"] }
serde_json = "1.0.128"
markup = "0.15.0"
serde = { version = "1.0.210", features = ["derive"] }
+tokio-tungstenite = { version = "0.23.1", features = [
+ "rustls-tls-native-roots",
+] }
hurrycurry-protocol = { path = "../protocol" }
hurrycurry-client-lib = { path = "../client-lib" }
diff --git a/server/registry/src/lobby.rs b/server/registry/src/lobby.rs
new file mode 100644
index 00000000..f74dd48e
--- /dev/null
+++ b/server/registry/src/lobby.rs
@@ -0,0 +1,164 @@
+use crate::Registry;
+use anyhow::Result;
+use hurrycurry_protocol::{
+ glam::{ivec2, IVec2, Vec2},
+ movement::MovementBase,
+ registry::Entry,
+ Gamedata, PacketC, PacketS, PlayerID, TileIndex, VERSION,
+};
+use log::{error, info, warn};
+use rocket::futures::{SinkExt, StreamExt};
+use std::{
+ collections::{HashMap, HashSet},
+ net::SocketAddr,
+ sync::Arc,
+ time::Instant,
+};
+use tokio::{
+ net::{TcpListener, TcpStream},
+ spawn,
+ sync::RwLock,
+};
+use tokio_tungstenite::tungstenite::Message;
+
+pub(crate) async fn lobby_wrapper(registry: Arc<RwLock<Registry>>, saddr: SocketAddr) {
+ let Err(e) = lobby(registry, saddr).await;
+ error!("lobby crashed: {e}");
+}
+
+const TILES: &[(&str, bool)] = &[("grass", false), ("black-hole", false)];
+
+async fn lobby(registry: Arc<RwLock<Registry>>, saddr: SocketAddr) -> Result<!> {
+ let ws_listener = TcpListener::bind(saddr).await?;
+
+ loop {
+ let (sock, addr) = ws_listener.accept().await?;
+ let r = registry.clone();
+ spawn(async move {
+ let entries = r.read().await.entries.clone();
+ if let Err(e) = handle_conn(sock, addr, &entries).await {
+ warn!("client error: {e}");
+ }
+ });
+ }
+}
+
+#[allow(unused_assignments)]
+async fn handle_conn(sock: TcpStream, addr: SocketAddr, entries: &[Entry]) -> Result<()> {
+ let sock = tokio_tungstenite::accept_async(sock).await?;
+ info!("{addr} connected via websocket");
+
+ let mut tiles = HashMap::<IVec2, TileIndex>::new();
+ for x in -5..5 + 5 * entries.len() as i32 {
+ for y in -5..5 {
+ tiles.insert(ivec2(x, y), TileIndex(0));
+ }
+ }
+ let portal_location = |i: usize| ivec2(i as i32 * 5 + 5, 0);
+ for (i, _) in entries.iter().enumerate() {
+ tiles.insert(portal_location(i), TileIndex(1));
+ }
+
+ let mut out = Vec::new();
+ out.push(PacketC::Version {
+ major: VERSION.0,
+ minor: VERSION.1,
+ supports_bincode: false,
+ });
+ out.push(PacketC::Data {
+ data: Gamedata {
+ tile_collide: TILES.iter().map(|(_, c)| *c).collect(),
+ tile_interact: TILES.iter().map(|_| false).collect(),
+ tile_names: TILES.iter().map(|(s, _)| s.to_string()).collect(),
+ current_map: "registry".to_owned(),
+ ..Default::default()
+ },
+ });
+ let walkable = HashSet::from_iter(tiles.iter().filter(|(_, v)| !TILES[v.0].1).map(|(k, _)| *k));
+ for (&tile, &kind) in &tiles {
+ out.push(PacketC::UpdateMap {
+ tile,
+ kind: Some(kind),
+ neighbors: [None, None, None, None],
+ });
+ }
+ out.push(PacketC::SetIngame {
+ state: true,
+ lobby: false, // very ironic
+ });
+
+ let (mut write, mut read) = sock.split();
+
+ loop {
+ for p in out.drain(..) {
+ write
+ .send(tokio_tungstenite::tungstenite::Message::Text(
+ serde_json::to_string(&p).unwrap(),
+ ))
+ .await?;
+ }
+
+ let Some(message) = read.next().await.transpose()? else {
+ break;
+ };
+ let packet = match message {
+ Message::Text(line) => match serde_json::from_str::<PacketS>(&line) {
+ Ok(p) => p,
+ Err(e) => {
+ warn!("Invalid json packet: {e}");
+ break;
+ }
+ },
+ Message::Close(_) => break,
+ _ => continue,
+ };
+
+ let mut joined = false;
+ let mut redirected = false;
+ let mut movement = MovementBase::new(Vec2::ZERO);
+ let mut last_movement = Instant::now();
+ match packet {
+ PacketS::Join {
+ character, name, ..
+ } if !joined => {
+ out.push(PacketC::Joined { id: PlayerID(0) });
+ out.push(PacketC::AddPlayer {
+ id: PlayerID(0),
+ position: movement.position,
+ character,
+ name,
+ });
+ joined = true;
+ }
+ PacketS::Leave { .. } if joined => {
+ out.push(PacketC::RemovePlayer { id: PlayerID(0) });
+ joined = false;
+ }
+ PacketS::Movement {
+ player,
+ dir,
+ boost,
+ pos,
+ } => {
+ let dt = last_movement.elapsed();
+ last_movement += dt;
+ movement.position = pos.unwrap_or(movement.position);
+ movement.input(dir, boost);
+ movement.update(&walkable, dt.as_secs_f32());
+ out.push(movement.movement_packet_c(player));
+ if !redirected {
+ for (i, e) in entries.iter().enumerate() {
+ if movement.position.distance(portal_location(i).as_vec2()) < 0.5 {
+ redirected = true;
+ out.push(PacketC::Redirect {
+ uri: e.address.clone(),
+ });
+ }
+ }
+ }
+ }
+ _ => (),
+ }
+ }
+ Ok(())
+}
diff --git a/server/registry/src/main.rs b/server/registry/src/main.rs
index 670ac3a6..2e0b7656 100644
--- a/server/registry/src/main.rs
+++ b/server/registry/src/main.rs
@@ -15,12 +15,15 @@
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
+#![feature(never_type)]
pub mod conn_test;
pub mod list;
+pub mod lobby;
pub mod register;
use hurrycurry_protocol::registry::Entry;
use list::{generate_html_list, generate_json_list, r_list};
+use lobby::lobby_wrapper;
use log::{error, info};
use register::r_register;
use rocket::{get, routes, Config};
@@ -28,7 +31,7 @@ use std::{
cmp::Reverse,
collections::HashMap,
env::var,
- net::{IpAddr, Ipv4Addr},
+ net::{IpAddr, Ipv4Addr, SocketAddr},
str::FromStr,
sync::Arc,
time::{Duration, Instant},
@@ -40,19 +43,29 @@ const MAX_SERVERS: usize = 128;
fn main() {
env_logger::init_from_env("LOG");
+
+ let address = var("BIND_ADDR")
+ .map(|a| IpAddr::from_str(&a).unwrap())
+ .unwrap_or(IpAddr::V4(Ipv4Addr::LOCALHOST));
+ let http_port = var("PORT").map(|p| p.parse().unwrap()).unwrap_or(27033);
+ let lobby_port = var("LOBBY_PORT")
+ .map(|p| p.parse().unwrap())
+ .unwrap_or(27034);
+ let lobby_addr = SocketAddr::new(address, lobby_port);
+
let registry = Arc::new(RwLock::new(Registry::default()));
+
tokio::runtime::Builder::new_multi_thread()
.enable_all()
.build()
.unwrap()
.block_on(async move {
tokio::task::spawn(Registry::update_loop(registry.clone()));
+ tokio::task::spawn(lobby_wrapper(registry.clone(), lobby_addr));
rocket::build()
.configure(Config {
- address: var("BIND_ADDR")
- .map(|a| IpAddr::from_str(&a).unwrap())
- .unwrap_or(IpAddr::V4(Ipv4Addr::LOCALHOST)),
- port: var("PORT").map(|p| p.parse().unwrap()).unwrap_or(27033),
+ address,
+ port: http_port,
..Default::default()
})
.manage(registry)
@@ -70,6 +83,7 @@ fn main() {
struct Registry {
json_response: Arc<str>,
html_response: Arc<str>,
+ entries: Vec<Entry>,
servers: HashMap<u128, InternalEntry>,
}
@@ -107,6 +121,7 @@ impl Registry {
self.json_response = generate_json_list(&list)?;
self.html_response = generate_html_list(&list)?;
+ self.entries = list;
info!("done. {} servers registered", self.servers.len());
Ok(())