Skip to content

Commit

Permalink
finally figured out bevy
Browse files Browse the repository at this point in the history
  • Loading branch information
LasmGratel committed Dec 15, 2021
1 parent 15ae270 commit db8419f
Show file tree
Hide file tree
Showing 21 changed files with 1,104 additions and 2,341 deletions.
2,027 changes: 111 additions & 1,916 deletions Cargo.lock

Large diffs are not rendered by default.

5 changes: 3 additions & 2 deletions cardgame-bevy-client/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@ edition = "2018"
[dependencies]
cardgame = {path = "../cardgame"}
cardgame-common = {path = "../cardgame-common"}
bevy = "0.5"
bevy = { version = "0.5", default-features = false, features = ["bevy_dylib", "bevy_dynamic_plugin"] }
bevy_spicy_networking = "0.6"
uuid = {version = "0.8", features = ["serde", "v4"]}
humantime = "2.1.0"
timeago = "0.3.0"
anyhow = "1.0"
anyhow = "1.0"
crossbeam = "0.8"
51 changes: 51 additions & 0 deletions cardgame-bevy-client/src/console_plugin.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
use bevy::{
ecs::{archetype::Archetypes, component::Components, entity::Entities},
prelude::*,
reflect::TypeRegistry,
tasks::AsyncComputeTaskPool,
};
use crossbeam::channel::{bounded, Receiver};
use std::io::{self, BufRead, Write};
use bevy::ecs::schedule::ShouldRun;

pub struct InputEvent(pub String);

fn parse_input(
time: Res<Time>,
line_channel: Res<Receiver<String>>,
mut events: EventWriter<InputEvent>,
) {
if let Ok(line) = line_channel.try_recv() {
events.send(InputEvent(line));
io::stdout().flush().unwrap();
}
}

fn spawn_io_thread(mut commands: Commands, thread_pool: Res<AsyncComputeTaskPool>) {
println!("Bevy Console Debugger. Type 'help' for list of commands.");
print!(">>> ");
io::stdout().flush().unwrap();

let (tx, rx) = bounded(2);
let task = thread_pool.spawn(async move {
let stdin = io::stdin();
loop {
let line = stdin.lock().lines().next().unwrap().unwrap();
tx.send(line)
.expect("error sending user input to other thread");
}
});
task.detach();
commands.insert_resource(rx);
}

pub struct ConsoleDebugPlugin;
impl Plugin for ConsoleDebugPlugin {
fn build(&self, app: &mut AppBuilder) {
app
.add_event::<InputEvent>()
.add_startup_system(spawn_io_thread.system())
.add_system(parse_input.system())
;
}
}
264 changes: 232 additions & 32 deletions cardgame-bevy-client/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,14 @@ use bevy::log::LogPlugin;
use std::sync::mpsc;
use std::{thread, time, io};
use std::sync::mpsc::{TryRecvError, Receiver};
use crate::ui::{UIPlugin, ConnectButton};
use std::time::{Duration, SystemTime};
use bevy::app::{ScheduleRunnerPlugin, ScheduleRunnerSettings};
use cardgame::Card;
use cardgame::error::{GameError, RoomError};
use crate::chat::{ChatMessages, ChatMessage};
use crate::chat::ChatMessage::SystemMessage;
use crate::ClientStatus::{Gaming, Idle, WaitingForLandlord};
use crate::console_plugin::{ConsoleDebugPlugin, InputEvent};

#[derive(Clone, PartialEq)]
struct ConsoleInput(String);
Expand All @@ -22,12 +27,18 @@ fn main() {

// You need to add the `ClientPlugin` first before you can register
// `ClientMessage`s
app.add_plugin(bevy_spicy_networking::ClientPlugin);
app.add_plugin(UIPlugin);
// app.add_startup_system(connect_to_server.system());
app.add_plugin(bevy_spicy_networking::ClientPlugin::default());
app.add_plugin(ConsoleDebugPlugin);
app.add_startup_system(connect_to_server.system());
app.add_system(handle_incoming_messages.system());
app.add_system(handle_network_events.system());
app.add_system(handle_input.system());
app.insert_resource(ClientState::default());
register_messages(&mut app);
app.add_plugin(ScheduleRunnerPlugin::default());
app.insert_resource(ScheduleRunnerSettings::run_loop(Duration::from_millis(
50 // 20 tps
)));
app.run();
}

Expand All @@ -54,60 +65,249 @@ fn connect_to_server(mut net: ResMut<NetworkClient>) {
);
}

/// 客户端状态
#[derive(Eq, PartialEq, Clone)]
enum ClientStatus {
/// 未登入
NotLoggedIn,

/// 空闲
Idle,

/// 在房间中等待玩家
WaitingForPlayers(String),

/// 等待重新比赛
WaitingForRematch,

/// 等待叫地主
WaitingForLandlord,

/// 游戏中
Gaming,
}

struct ClientState {
pub cards: Vec<Card>,
pub user_name: String,
pub landlord_name: String,
pub status: ClientStatus,
pub last_packet_time: SystemTime,
}

impl Default for ClientState {
fn default() -> Self {
ClientState {
status: Idle,
last_packet_time: SystemTime::now(),
landlord_name: String::default(),
cards: vec![],
user_name: String::default(),
}
}
}

fn handle_input(
net: Res<NetworkClient>,
mut state: ResMut<ClientState>,
mut events: EventReader<InputEvent>
) {
for event in events.iter() {
let line: &str = &event.0;
match line {
"list_rooms" => {
net.send_message(C2SMessage::QueryRoomList);
}
"ping" => {
state.last_packet_time = SystemTime::now();
net.send_message(C2SMessage::Ping);
}
_ => {
if line.starts_with("join ") {

} else {
println!("Unknown command {}", line);
}
}
}
}
}

fn handle_incoming_messages(
mut messages: Query<&mut ChatMessages>,
mut state: ResMut<ClientState>,
mut new_messages: EventReader<NetworkData<S2CMessage>>,
) {
let mut messages = messages.single_mut().unwrap();
for message in new_messages.iter() {
if **message == S2CMessage::Pong {
messages.add(SystemMessage("Pong!".to_string()));
} else if **message == S2CMessage::Pong2 {
messages.add(SystemMessage("Pong2!".to_string()));
} else {
messages.add(SystemMessage("Other message".to_string()));
match &**message {
S2CMessage::Chat(msg) => {
println!("Chat: {}", msg);
}
S2CMessage::Pong => {
let recv_time = SystemTime::now();
let send_time = state.last_packet_time.clone();

let duration = recv_time.duration_since(send_time).expect("Time went backwards");
println!("砰!延迟为 {}", humantime::format_duration(duration));
}
S2CMessage::RoomJoined(room) => {
println!("加入房间:{}", room);
state.status = ClientStatus::WaitingForPlayers(room.to_string());
}
S2CMessage::LandlordMove(landlord) => {
let user_name = &state.user_name;
if user_name == landlord {
println!("{} 你是否叫地主?", landlord);
} else {
println!("等待 {} 叫地主", landlord);
}

state.landlord_name = landlord.to_string();
}
S2CMessage::CardsSubmitted(player, cards) => {
println!("{} 出牌:{}", player, cards_to_string(&cards));
if &state.user_name == player {
let mut cards_mut = &mut state.cards;
for card in cards.iter() {
let pos = cards_mut.iter().position(|x| x == card).unwrap();
cards_mut.remove(pos);
}
print!("你的手牌:");
print_cards(&cards_mut);
}
}
S2CMessage::Move(player) => {
println!("{} 请出牌", player);
}
S2CMessage::LordCards(landlord, cards) => {
println!("{} 叫地主,地主牌为 {}", landlord, cards_to_string(&cards));
if &state.user_name == landlord {
let mut player_cards = &mut state.cards;
for card in cards.iter() {
player_cards.push(*card);
player_cards.sort();
}
print!("你的手牌:");
print_cards(&player_cards);
}
state.status = Gaming;
}
S2CMessage::RoomErr(err) => {
match err {
RoomError::NotReady => {
println!("房间未准备好!");
}
RoomError::NotStarted => {
println!("游戏还未开始!");
}
RoomError::NotLandlordPlayer => {
println!("不是你叫地主!");
}
RoomError::RoomFull => {
println!("Room is full");
}
}
}
S2CMessage::GameErr(err) => {
match err {
GameError::NotYourTurn => {
println!("你还不能出牌!");
}
GameError::NoSuchCards => {
println!("你没有这些牌");
}
GameError::WrongRule => {
println!("你出的牌不满足当前规则");
}
GameError::NoRule => {
println!("你出的牌不匹配任何规则")
}
GameError::Win(player, player_type, score) => {
println!("{} 赢了。", player);
state.status = Idle;
state.landlord_name = String::default();
state.cards.clear();

println!("现在你可以输入 再来一局|摸了 来进行重新比赛投票,也可以安全地离开房间。");
}
_ => {}
}
}
S2CMessage::RematchVote(player, rematch, count) => {
if *rematch {
println!("{} 同意再来一局。({}/3)", player, count);
} else {
println!("{} 不同意再来一局,房间销毁。", player);
state.status = Idle;
state.landlord_name = String::new();
state.cards.clear();
}
}
S2CMessage::GameStarted(cards, landlord) => {
let user_name = &state.user_name;
print!("你的手牌: ");
print_cards(&cards);

print!("游戏开始,");
if user_name == landlord {
println!("{} 你是否叫地主?", landlord);
} else {
println!("等待 {} 叫地主", landlord);
}

state.cards = cards.clone();
state.landlord_name = landlord.to_string();
state.status = WaitingForLandlord;
}
S2CMessage::MatchmakeStatus(count, expected_time, remaining_time) => {
if remaining_time.is_zero() {
println!("无法匹配到玩家,请重试。");
state.status = Idle;
} else {
println!("当前匹配队列共有 {} 位玩家,剩余匹配时间:{}s", count, remaining_time.as_secs());
}
}
_ => {
println!("Other message");
}
}
}
}

fn print_cards(cards: &[Card]) {
println!("{}", cards_to_string(cards));
}

fn cards_to_string(cards: &[Card]) -> String {
let mut s = String::new();
for c in cards.iter() {
s += "[";
s += c.to_string();
s += "]";
}
s
}

fn handle_network_events(
connect_button_query: Query<&Children, With<ConnectButton>>,
mut text_query: Query<&mut Text>,
mut new_network_events: EventReader<ClientNetworkEvent>,
net: Res<NetworkClient>,
mut messages: Query<&mut ChatMessages>,
) {
let connect_children = connect_button_query.single().unwrap();
let mut text = text_query.get_mut(connect_children[0]).unwrap();
let mut messages = messages.single_mut().unwrap();
for event in new_network_events.iter() {
match event {
ClientNetworkEvent::Connected => {
println!("Connected to server");
messages.add(ChatMessage::SystemMessage(
"Successfully connected to server!".to_string(),
));
text.sections[0].value = String::from("Disconnect");
net.send_message(C2SMessage::Ping);
}

ClientNetworkEvent::Disconnected => {
messages.add(ChatMessage::SystemMessage(
"Disconnected".to_string(),
));
text.sections[0].value = String::from("Connect to server");
println!("Server disconnected");
}
ClientNetworkEvent::Error(err) => {
messages.add(ChatMessage::SystemMessage(
format!("Error: {:?}", err)
));
error!("{:?}", err);
error!("Network error: {:?}", err);
}
}
}
}

pub mod ui;
pub mod chat;
pub mod game_render;
pub mod game_render;
pub mod console_plugin;
Loading

0 comments on commit db8419f

Please sign in to comment.