summaryrefslogtreecommitdiff
path: root/server/registry
diff options
context:
space:
mode:
authormetamuffin <metamuffin@disroot.org>2024-09-21 18:07:18 +0200
committermetamuffin <metamuffin@disroot.org>2024-09-21 18:07:18 +0200
commit362363b3fd1f1fbffe2a3f81d73e71939addd34b (patch)
tree2c406b674a23a27a9bb6c61026243c5929bdca28 /server/registry
parent34190a70b1efa0972ef58b88d356f985c46b89ae (diff)
downloadhurrycurry-362363b3fd1f1fbffe2a3f81d73e71939addd34b.tar
hurrycurry-362363b3fd1f1fbffe2a3f81d73e71939addd34b.tar.bz2
hurrycurry-362363b3fd1f1fbffe2a3f81d73e71939addd34b.tar.zst
redirect packet
Diffstat (limited to 'server/registry')
-rw-r--r--server/registry/Cargo.toml3
-rw-r--r--server/registry/src/lobby.rs164
-rw-r--r--server/registry/src/main.rs25
3 files changed, 187 insertions, 5 deletions
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(())