Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Structure actors and its packet handling #1

Open
wants to merge 14 commits into
base: master
Choose a base branch
from
Open
10 changes: 9 additions & 1 deletion packet-macro/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
이렇게 적으면

```rust
#[packet(namespace = "common")]
#[packet(namespace = "common", handler_target = "Server")]
pub enum CommonPacket {
#[packet(id = "goto")]
Goto {
Expand Down Expand Up @@ -45,4 +45,12 @@ pub enum CommonPacketClient {
body: GotoRequestBody
},
}
impl Handler<CommonPacketClient> for Server
{
fn handle(&mut self, message: CommonPacketClient, ctx: &mut Self::Context) {
match message {
Goto { body } => self.handle(body, ctx),
}
}
}
```
68 changes: 57 additions & 11 deletions packet-macro/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,33 +1,50 @@
use darling::{Error, FromMeta, FromVariant};
use proc_macro::TokenStream;
use quote::{format_ident, quote};
use syn::{parse_macro_input, AttributeArgs, Fields, ItemEnum};
use syn::{
parse_macro_input, parse_str, spanned::Spanned, Attribute, AttributeArgs, Fields, ItemEnum,
Type,
};

#[derive(Debug, FromMeta)]
struct PacketItem {
namespace: String,
handler_target: String,
}

// uses_type_params!(PacketItem, handler_target);

#[derive(Debug, FromVariant)]
#[darling(attributes(packet))]
struct PacketVariant {
id: String,
attrs: Vec<Attribute>,
}

#[proc_macro_attribute]
pub fn packet(args: TokenStream, item: TokenStream) -> TokenStream {
let attr_args = parse_macro_input!(args as AttributeArgs);
let attr_span = attr_args.first().and_then(|first| {
first.span().join(
attr_args
.last()
.expect("If first exists, last exists")
.span(),
)
});

let attr_args = match PacketItem::from_list(&attr_args) {
Ok(v) => v,
Err(e) => {
return TokenStream::from(e.write_errors());
return e.write_errors().into();
}
};

let item = parse_macro_input!(item as ItemEnum);

let mut variant_generated_client = Vec::new();
let mut variant_generated_server = Vec::new();
let mut variant_generated_names = Vec::new();

for variant in &item.variants {
let variant_attrs = match PacketVariant::from_variant(&variant) {
Expand All @@ -36,14 +53,7 @@ pub fn packet(args: TokenStream, item: TokenStream) -> TokenStream {
return e.write_errors().into();
}
};
let attrs: Vec<_> = variant
.attrs
.iter()
.filter(|it| match it.path.get_ident() {
Some(v) if v.to_string() == "packet" => false,
_ => true,
})
.collect();
let attrs: Vec<_> = variant_attrs.attrs;
let name = &variant.ident;
let rename = format!("{}:{}", attr_args.namespace, variant_attrs.id);
match &variant.fields {
Expand Down Expand Up @@ -87,6 +97,7 @@ pub fn packet(args: TokenStream, item: TokenStream) -> TokenStream {
body: #req_type,
}
});
variant_generated_names.push(name.clone());
}
Fields::Unnamed(unnamed) => variant_generated_server.push(quote! {
#(#attrs)*
Expand All @@ -110,18 +121,53 @@ pub fn packet(args: TokenStream, item: TokenStream) -> TokenStream {
let server_name = format_ident!("{}Server", name);
let client_name = format_ident!("{}Client", name);
let generics = item.generics;
let attrs = item.attrs;
let handler_target: Type = if let Ok(handler_target) = parse_str(&attr_args.handler_target) {
handler_target
} else {
let mut error = Error::custom(format_args!(
"Expected type in string literal, but {} received",
attr_args.handler_target
));
if let Some(attr_span) = attr_span {
error = error.with_span(&attr_span);
}
return error.write_errors().into();
};

(quote! {
#[derive(serde::Serialize)]
#[derive(serde::Serialize, actix::MessageResponse)]
#[serde(tag = "kind")]
#(#attrs)*
#vis enum #server_name #generics {
#(#variant_generated_server,)*
}
#[derive(serde::Deserialize)]
#[serde(tag = "kind")]
#(#attrs)*
#vis enum #client_name #generics {
#(#variant_generated_client,)*
}
impl Message for #client_name {
type Result = #server_name;
}

impl actix::Handler<#client_name> for #handler_target {
type Result = #server_name;
fn handle(&mut self, message: #client_name, ctx: &mut Self::Context) -> Self::Result {
match message {
#(
#client_name::#variant_generated_names { body } => {
let result = self.handle(body, ctx);
#server_name::#variant_generated_names {
ok: result.is_ok(),
body: result,
}
}
),*
}
}
}
})
.into()
}
9 changes: 9 additions & 0 deletions src/core/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
mod room;
mod server;
mod session;
mod typealias;

pub use room::*;
pub use server::*;
pub use session::*;
pub use typealias::*;
2 changes: 2 additions & 0 deletions src/core/room.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#[derive(Clone)]
RanolP marked this conversation as resolved.
Show resolved Hide resolved
pub struct Room {}
13 changes: 13 additions & 0 deletions src/core/server.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
use crate::core::{Room, RoomId, Session, UserId};
use actix::prelude::*;
use std::collections::HashMap;

#[derive(Clone, Default)]
pub struct Server {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actor 디자인 상 전체를 관리하는 개념이 아니라 각각의 역할 분담하는 구조라, Server를 만들기보다 User를 가지고 있는 Lobby와 Room 정보를 가지고 있는 RoomManager로 분리하면 좋을 것 같아요.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

611fe11 에서 Lobby와 RoomManager로 분리했습니다. 더 개선이 필요할까요?

sessions: HashMap<UserId, Session>,
rooms: HashMap<RoomId, Room>,
}

impl Actor for Server {
type Context = Context<Self>;
}
11 changes: 11 additions & 0 deletions src/core/session.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
use crate::core::{RoomId, UserId};
use crate::protocol::PacketServer;
use actix::Recipient;

#[derive(Clone)]
pub struct Session {
id: UserId,
pipe: Recipient<PacketServer>,
room: Option<RoomId>,
name: String,
}
RanolP marked this conversation as resolved.
Show resolved Hide resolved
2 changes: 2 additions & 0 deletions src/core/typealias.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
pub type UserId = usize;
pub type RoomId = usize;
RanolP marked this conversation as resolved.
Show resolved Hide resolved
6 changes: 6 additions & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,18 @@ extern crate diesel;
use std::io;
use std::sync::Arc;

use actix::prelude::*;
use actix_rt;
use actix_web::middleware::errhandlers::ErrorHandlers;
use actix_web::{dev, http, middleware, web, App, Error, HttpResponse, HttpServer, Responder};
use juniper::http::{playground::playground_source, GraphQLRequest};
use middleware::errhandlers::ErrorHandlerResponse;

use crate::core::Server;
use crate::gql::{Context, Mutation, Query, Schema};

mod config;
mod core;
mod database;
mod gql;
mod log;
Expand Down Expand Up @@ -64,10 +67,13 @@ async fn main() -> io::Result<()> {
let context = Arc::new(Context::new(pool));
let schema = Arc::new(Schema::new(Query, Mutation));

let server = Server::default().start();

HttpServer::new(move || {
App::new()
.data(schema.clone())
.data(context.clone())
.data(server.clone())
.wrap(middleware::Logger::default())
.wrap(ErrorHandlers::new().handler(http::StatusCode::NOT_FOUND, render_404))
.service(web::resource("/graphql").route(web::post().to(graphql)))
Expand Down
34 changes: 34 additions & 0 deletions src/protocol/common/goto.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
use crate::core::Server;
use crate::protocol::PacketResult;
use actix::prelude::*;
use log::info;
use serde::*;

#[derive(Debug, Serialize, Deserialize, Message)]
#[rtype(GotoPacketResponse)]
pub struct GotoPacketRequest {
// game_category: Option<GameCategory>,
// room: Option<RoomId>,
}

#[derive(Debug, Serialize, Deserialize, MessageResponse)]
#[serde(rename_all = "kebab-case")]
pub enum GotoPacketResponseError {
IsRoomFull,
Unknown,
}

#[derive(Debug, Serialize, Deserialize, MessageResponse)]
pub struct GotoPacketResponseOk {}

pub type GotoPacketResponse = PacketResult<GotoPacketResponseOk, GotoPacketResponseError>;

impl Handler<GotoPacketRequest> for Server {
type Result = GotoPacketResponse;

fn handle(&mut self, message: GotoPacketRequest, context: &mut Self::Context) -> Self::Result {
info!("Test {:?}", message);

PacketResult::Ok(GotoPacketResponseOk {})
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
use crate::core::Server;
use actix::prelude::*;
use packet_macro::packet;

mod goto;

#[packet(namespace = "common")]
pub use goto::*;

#[packet(namespace = "common", handler_target = "Server")]
#[derive(Debug)]
pub enum CommonPacket {
#[packet(id = "goto")]
Goto {
Expand Down
10 changes: 7 additions & 3 deletions src/protocol/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
mod handler;
mod common;
mod packet;
mod structure;
mod result;
mod wordchain;

pub use packet::{PacketClient, PacketResult, PacketServer};
pub use common::*;
pub use packet::{PacketClient, PacketServer};
pub use result::PacketResult;
pub use wordchain::*;
30 changes: 16 additions & 14 deletions src/protocol/packet.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,9 @@
use crate::protocol::structure::*;
use actix::Message;
use crate::core::Server;
use crate::protocol::*;
use actix::prelude::*;
use serde::*;
#[derive(Serialize, Deserialize)]
#[serde(untagged)]
pub enum PacketResult<OkBody: Serialize, ErrorKind: Serialize> {
Ok(OkBody),
Err {
kind: ErrorKind,
description: Option<String>,
},
}

#[derive(Serialize)]
#[derive(Debug, Serialize, MessageResponse)]
#[serde(untagged)]
pub enum PacketServer {
Common(CommonPacketServer),
Expand All @@ -21,12 +13,22 @@ impl Message for PacketServer {
type Result = ();
}

#[derive(Deserialize)]
#[derive(Debug, Deserialize)]
#[serde(untagged)]
pub enum PacketClient {
Common(CommonPacketClient),
}

impl Message for PacketClient {
type Result = ();
type Result = PacketServer;
}

impl Handler<PacketClient> for Server {
type Result = PacketServer;

fn handle(&mut self, msg: PacketClient, ctx: &mut Self::Context) -> Self::Result {
match msg {
PacketClient::Common(common) => PacketServer::Common(self.handle(common, ctx)),
}
}
}
55 changes: 55 additions & 0 deletions src/protocol/result.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
use actix::dev::{MessageResponse, ResponseChannel};
use actix::prelude::*;
use serde::*;
use std::fmt;

#[derive(Serialize, Deserialize)]
#[serde(untagged)]
pub enum PacketResult<OkBody: Serialize, ErrorKind: Serialize> {
Ok(OkBody),
Err {
kind: ErrorKind,
description: Option<String>,
},
}

impl<OkBody, ErrorKind> PacketResult<OkBody, ErrorKind>
where
OkBody: Serialize,
ErrorKind: Serialize,
{
pub fn is_ok(&self) -> bool {
matches!(self, &PacketResult::Ok(_))
}
}

impl<OkBody, ErrorKind> fmt::Debug for PacketResult<OkBody, ErrorKind>
where
OkBody: Serialize + fmt::Debug,
ErrorKind: Serialize + fmt::Debug,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
PacketResult::Ok(body) => f.debug_tuple("PacketResult::Ok").field(body).finish(),
PacketResult::Err { kind, description } => f
.debug_struct("PacketResult::Err")
.field("kind", kind)
.field("description", description)
.finish(),
}
}
}

impl<A, M, OkBody, ErrorKind> MessageResponse<A, M> for PacketResult<OkBody, ErrorKind>
where
A: Actor,
M: Message<Result = PacketResult<OkBody, ErrorKind>>,
OkBody: Serialize + fmt::Debug + 'static,
ErrorKind: Serialize + fmt::Debug + 'static,
{
fn handle<R: ResponseChannel<M>>(self, _: &mut A::Context, tx: Option<R>) {
if let Some(tx) = tx {
tx.send(self);
}
}
}
Loading