Skip to content

Commit 61cfe88

Browse files
committed
dev server, websockets working
1 parent fbeb9d6 commit 61cfe88

12 files changed

+2141
-85
lines changed

Cargo.lock

Lines changed: 1770 additions & 73 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
[workspace]
22
members = [
3-
"engine"
3+
"engine",
4+
"dev"
45
]
56

67
[profile.release]

README.MD

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# mafia
2+
3+
## Running a development instance
4+
5+
Make sure you have the most recent stable release of rust installed
6+
7+
```bash
8+
rustup update
9+
```
10+
11+
```bash
12+
cargo run -p dev
13+
```

dev/Cargo.toml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
[package]
2+
name = "dev"
3+
version = "0.1.0"
4+
authors = ["ahouts <[email protected]>"]
5+
edition = "2018"
6+
7+
[dependencies]
8+
mafia = { path = "../engine" }
9+
actix = "0.8"
10+
actix-web = "1.0"
11+
actix-web-actors = "1.0"
12+
uuid = { version = "0.7", features = ["v4"] }
13+
serde = "1.0"
14+
serde_json = "1.0"
15+
env_logger = "0.6"

dev/src/main.rs

Lines changed: 288 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,288 @@
1+
use actix::prelude::Future;
2+
use actix::{
3+
Actor, Addr, AsyncContext, Context, Handler, Message, Running, StreamHandler, WeakAddr,
4+
};
5+
use actix_web::error::InternalError;
6+
use actix_web::http::StatusCode;
7+
use actix_web::middleware::Logger;
8+
use actix_web::{web, App, Error, HttpRequest, HttpResponse, HttpServer};
9+
use actix_web_actors::ws;
10+
use mafia::{Error as MError, PlayerConnection, Response, Session};
11+
use serde::Deserialize;
12+
use std::collections::HashMap;
13+
use std::sync::Arc;
14+
use uuid::Uuid;
15+
16+
fn gen_uuid() -> String {
17+
let mut buffer = [0_u8; 36];
18+
let id = Uuid::new_v4().to_hyphenated();
19+
let s = id.encode_upper(&mut buffer);
20+
s.to_string()
21+
}
22+
23+
struct AppState {
24+
cm: Addr<ConnectionManager>,
25+
sm: Addr<SessionManager>,
26+
}
27+
28+
impl Default for AppState {
29+
fn default() -> Self {
30+
AppState {
31+
cm: ConnectionManager::default().start(),
32+
sm: SessionManager::default().start(),
33+
}
34+
}
35+
}
36+
37+
struct ConnectionManager {
38+
connections: HashMap<String, Addr<Connection>>,
39+
}
40+
41+
impl Default for ConnectionManager {
42+
fn default() -> Self {
43+
ConnectionManager {
44+
connections: HashMap::new(),
45+
}
46+
}
47+
}
48+
49+
impl Actor for ConnectionManager {
50+
type Context = Context<Self>;
51+
}
52+
53+
#[derive(Message)]
54+
struct Connect {
55+
id: String,
56+
addr: Addr<Connection>,
57+
}
58+
59+
impl Handler<Connect> for ConnectionManager {
60+
type Result = ();
61+
62+
fn handle(&mut self, msg: Connect, _: &mut Self::Context) -> Self::Result {
63+
self.connections.insert(msg.id, msg.addr);
64+
}
65+
}
66+
67+
#[derive(Message)]
68+
struct Disconnect {
69+
id: String,
70+
}
71+
72+
impl Handler<Disconnect> for ConnectionManager {
73+
type Result = ();
74+
75+
fn handle(&mut self, msg: Disconnect, _: &mut Self::Context) -> Self::Result {
76+
self.connections.remove(&msg.id);
77+
}
78+
}
79+
80+
struct SessionManager {
81+
sessions: HashMap<String, Addr<GameSession>>,
82+
}
83+
84+
impl Default for SessionManager {
85+
fn default() -> Self {
86+
SessionManager {
87+
sessions: HashMap::new(),
88+
}
89+
}
90+
}
91+
92+
impl Actor for SessionManager {
93+
type Context = Context<Self>;
94+
}
95+
96+
#[derive(Message)]
97+
struct RegisterSession {
98+
id: String,
99+
addr: Addr<GameSession>,
100+
}
101+
102+
impl Handler<RegisterSession> for SessionManager {
103+
type Result = ();
104+
105+
fn handle(&mut self, msg: RegisterSession, _: &mut Self::Context) -> Self::Result {
106+
self.sessions.insert(msg.id, msg.addr);
107+
}
108+
}
109+
110+
struct GetSession {
111+
id: String,
112+
}
113+
114+
impl Message for GetSession {
115+
type Result = Option<Addr<GameSession>>;
116+
}
117+
118+
impl Handler<GetSession> for SessionManager {
119+
type Result = Option<Addr<GameSession>>;
120+
121+
fn handle(&mut self, msg: GetSession, _: &mut Self::Context) -> Self::Result {
122+
self.sessions.get(&msg.id).map(|v| v.clone())
123+
}
124+
}
125+
126+
struct GameSession {
127+
game: Session<ConnectionAddr>,
128+
}
129+
130+
impl Actor for GameSession {
131+
type Context = Context<Self>;
132+
}
133+
134+
struct GetPlayername {
135+
secret: String,
136+
}
137+
138+
impl Message for GetPlayername {
139+
type Result = Option<String>;
140+
}
141+
142+
impl Handler<GetPlayername> for GameSession {
143+
type Result = Option<String>;
144+
145+
fn handle(&mut self, msg: GetPlayername, _: &mut Self::Context) -> Self::Result {
146+
self.game.get_playername(&msg.secret)
147+
}
148+
}
149+
150+
#[derive(Clone)]
151+
struct Connection {
152+
id: String,
153+
name: String,
154+
cm: Addr<ConnectionManager>,
155+
sess: Addr<GameSession>,
156+
}
157+
158+
impl Connection {
159+
fn new(name: String, cm: Addr<ConnectionManager>, sess: Addr<GameSession>) -> Self {
160+
Connection {
161+
id: gen_uuid(),
162+
name,
163+
cm,
164+
sess,
165+
}
166+
}
167+
}
168+
169+
impl Actor for Connection {
170+
type Context = ws::WebsocketContext<Self>;
171+
172+
fn started(&mut self, ctx: &mut Self::Context) {
173+
self.cm.do_send(Connect {
174+
id: self.id.clone(),
175+
addr: ctx.address(),
176+
});
177+
}
178+
179+
fn stopping(&mut self, _: &mut Self::Context) -> Running {
180+
self.cm.do_send(Disconnect {
181+
id: self.id.clone(),
182+
});
183+
Running::Stop
184+
}
185+
}
186+
187+
impl StreamHandler<ws::Message, ws::ProtocolError> for Connection {
188+
fn handle(&mut self, msg: ws::Message, ctx: &mut Self::Context) {
189+
match msg {
190+
ws::Message::Ping(msg) => ctx.pong(&msg),
191+
ws::Message::Text(text) => ctx.text(text),
192+
_ => (),
193+
}
194+
}
195+
}
196+
197+
#[derive(Message)]
198+
struct SendMsg(String);
199+
200+
impl Handler<SendMsg> for Connection {
201+
type Result = ();
202+
203+
fn handle(&mut self, msg: SendMsg, ctx: &mut Self::Context) -> Self::Result {
204+
ctx.text(msg.0);
205+
}
206+
}
207+
208+
#[derive(Clone)]
209+
struct ConnectionAddr(Arc<WeakAddr<Connection>>);
210+
211+
impl PlayerConnection for ConnectionAddr {
212+
fn send(&self, r: Response) {
213+
if let Some(addr) = self.0.upgrade() {
214+
addr.do_send(SendMsg(
215+
serde_json::to_string(&r).expect("error serializing response"),
216+
))
217+
}
218+
}
219+
220+
fn is_alive(&self) -> bool {
221+
self.0.upgrade().is_some()
222+
}
223+
}
224+
225+
#[derive(Deserialize)]
226+
pub struct WebsocketAuth {
227+
session: String,
228+
secret: String,
229+
}
230+
231+
fn connect_websocket(
232+
data: web::Data<AppState>,
233+
web::Query(info): web::Query<WebsocketAuth>,
234+
req: HttpRequest,
235+
stream: web::Payload,
236+
) -> impl Future<Item = HttpResponse, Error = Error> {
237+
data.sm
238+
.send(GetSession {
239+
id: info.session.clone(),
240+
})
241+
.map_err(|e| Error::from(e))
242+
.and_then(move |sess| {
243+
let game_sess = match sess {
244+
Some(game_sess) => game_sess,
245+
None => {
246+
return Err(Error::from(InternalError::new(
247+
MError::InvalidSession,
248+
StatusCode::from_u16(500).unwrap(),
249+
)))
250+
}
251+
};
252+
253+
Ok(game_sess
254+
.send(GetPlayername {
255+
secret: info.secret,
256+
})
257+
.map_err(|e| Error::from(e))
258+
.and_then(move |playername_res| match playername_res {
259+
Some(name) => ws::start(
260+
Connection::new(name, data.cm.clone(), game_sess),
261+
&req,
262+
stream,
263+
),
264+
None => Err(Error::from(InternalError::new(
265+
MError::InvalidSecret,
266+
StatusCode::from_u16(500).unwrap(),
267+
))),
268+
}))
269+
})
270+
.flatten()
271+
}
272+
273+
fn main() {
274+
std::env::set_var("RUST_LOG", "actix_web=debug");
275+
std::env::set_var("RUST_BACKTRACE", "1");
276+
env_logger::init();
277+
278+
HttpServer::new(|| {
279+
App::new()
280+
.data(AppState::default())
281+
.route("/ws", web::get().to_async(connect_websocket))
282+
.wrap(Logger::new("ip=%a code=%r req_mili=%D resp_size=%b"))
283+
})
284+
.bind("127.0.0.1:8088")
285+
.unwrap()
286+
.run()
287+
.unwrap();
288+
}

engine/src/error.rs

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,26 @@
11
use crate::player::PlayerName;
2-
use serde::{Deserialize, Serialize};
2+
use serde::Serialize;
3+
use std::error::Error as ErrorT;
4+
use std::fmt;
35

46
pub type Result<T> = std::result::Result<T, Error>;
57

6-
#[derive(Serialize, Deserialize)]
8+
#[derive(Debug, Serialize)]
79
pub enum Error {
810
InvalidPlayerName(PlayerName),
11+
InvalidSession,
12+
InvalidSecret,
913
}
14+
15+
impl fmt::Display for Error {
16+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
17+
use Error::*;
18+
match self {
19+
InvalidPlayerName(p) => write!(f, "invalid player name: {}", p),
20+
InvalidSession => write!(f, "invalid session id"),
21+
InvalidSecret => write!(f, "invalid secret"),
22+
}
23+
}
24+
}
25+
26+
impl ErrorT for Error {}

engine/src/lib.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,8 @@ mod ruleset;
88
mod session;
99
mod state;
1010
mod util;
11+
12+
pub use error::{Error, Result};
13+
pub use player_connection::PlayerConnection;
14+
pub use response::Response;
15+
pub use session::Session;

engine/src/player.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,20 @@ pub struct Player<PC: PlayerConnection> {
1010
pub connection: Option<PC>,
1111
pub state: PlayerState,
1212
pub role: Role,
13-
pub secret_key: String,
13+
pub secret: String,
1414
}
1515

1616
impl<PC: PlayerConnection> Player<PC> {
17+
pub fn new(name: String, secret: String) -> Self {
18+
Player {
19+
name,
20+
connection: None,
21+
state: PlayerState::Alive,
22+
role: Role::Townie,
23+
secret,
24+
}
25+
}
26+
1727
pub fn get_name(&self) -> PlayerNameRef {
1828
self.name.as_ref()
1929
}

engine/src/player_connection.rs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,5 @@ use crate::response::Response;
22

33
pub trait PlayerConnection: Clone {
44
fn send(&self, r: Response);
5-
fn id(&self) -> &str;
6-
fn eq(&self, other: &Self) -> bool {
7-
self.id() == other.id()
8-
}
5+
fn is_alive(&self) -> bool;
96
}

0 commit comments

Comments
 (0)