[workshop] Add Rust workshop files
Test: lol
Change-Id: I7ab6693f8a823311c55396bdb72281f697c6cd60
diff --git a/bin/tennis/BUILD.gn b/bin/tennis/BUILD.gn
new file mode 100644
index 0000000..63e3c16
--- /dev/null
+++ b/bin/tennis/BUILD.gn
@@ -0,0 +1,117 @@
+# Copyright 2018 The Fuchsia Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//build/rust/rustc_binary.gni")
+import("//build/package.gni")
+
+package("tennis_service") {
+ deps = [
+ ":tennis_service_bin",
+ ]
+
+ binary = "rust_crates/tennis_service"
+
+ meta = [
+ {
+ path = rebase_path("meta/tennis_service.cmx")
+ dest = "tennis_service.cmx"
+ },
+ ]
+}
+
+rustc_binary("tennis_service_bin") {
+ name = "tennis_service"
+ with_unit_tests = true
+ edition = "2018"
+
+ deps = [
+ "//garnet/public/fidl/fuchsia.game.tennis:fuchsia.game.tennis-rustc",
+ "//garnet/public/lib/fidl/rust/fidl",
+ "//garnet/public/rust/crates/fuchsia-app",
+ "//garnet/public/rust/crates/fuchsia-async",
+ "//garnet/public/rust/crates/fuchsia-syslog",
+ "//garnet/public/rust/crates/fuchsia-zircon",
+ "//third_party/rust-crates/rustc_deps:failure",
+ "//third_party/rust-crates/rustc_deps:futures-preview",
+ "//third_party/rust-crates/rustc_deps:parking_lot",
+ ]
+}
+
+package("tennis_viewer") {
+ deps = [
+ ":tennis_viewer_bin",
+ ]
+
+ binary = "rust_crates/tennis_viewer"
+
+ meta = [
+ {
+ path = rebase_path("meta/tennis_viewer.cmx")
+ dest = "tennis_viewer.cmx"
+ },
+ ]
+}
+
+rustc_binary("tennis_viewer_bin") {
+ name = "tennis_viewer"
+ edition = "2018"
+
+ source_root = "viewer/main.rs"
+
+ deps = [
+ "//garnet/public/fidl/fuchsia.game.tennis:fuchsia.game.tennis-rustc",
+ "//garnet/public/lib/fidl/rust/fidl",
+ "//garnet/public/rust/crates/fuchsia-app",
+ "//garnet/public/rust/crates/fuchsia-async",
+ "//garnet/public/rust/crates/fuchsia-syslog",
+ "//garnet/public/rust/crates/fuchsia-zircon",
+ "//third_party/rust-crates/rustc_deps:failure",
+ "//third_party/rust-crates/rustc_deps:futures-preview",
+ "//third_party/rust-crates/rustc_deps:parking_lot",
+ ]
+}
+
+package("tennis_example_ai") {
+ deps = [
+ ":tennis_example_ai_bin",
+ ]
+
+ binary = "rust_crates/tennis_example_ai"
+
+ meta = [
+ {
+ path = rebase_path("meta/tennis_example_ai.cmx")
+ dest = "tennis_example_ai.cmx"
+ },
+ ]
+}
+
+rustc_binary("tennis_example_ai_bin") {
+ name = "tennis_example_ai"
+ edition = "2018"
+
+ source_root = "example_ai/main.rs"
+
+ deps = [
+ "//garnet/public/fidl/fuchsia.game.tennis:fuchsia.game.tennis-rustc",
+ "//garnet/public/lib/fidl/rust/fidl",
+ "//garnet/public/rust/crates/fuchsia-app",
+ "//garnet/public/rust/crates/fuchsia-async",
+ "//garnet/public/rust/crates/fuchsia-syslog",
+ "//garnet/public/rust/crates/fuchsia-zircon",
+ "//third_party/rust-crates/rustc_deps:failure",
+ "//third_party/rust-crates/rustc_deps:futures-preview",
+ "//third_party/rust-crates/rustc_deps:parking_lot",
+ ]
+}
+
+package("tennis_sysmgr_config") {
+ deprecated_system_image = true
+ resources = [
+ {
+ dest = "sysmgr/tennis.config"
+ path = rebase_path("tennis_sysmgr.config")
+ }
+ ]
+}
diff --git a/bin/tennis/example_ai/main.rs b/bin/tennis/example_ai/main.rs
new file mode 100644
index 0000000..cacd0ba
--- /dev/null
+++ b/bin/tennis/example_ai/main.rs
@@ -0,0 +1,74 @@
+// Copyright 2018 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#![deny(warnings)]
+
+#![feature(try_from,async_await,await_macro)]
+
+use failure::{Error, ResultExt};
+use fuchsia_async as fasync;
+use fidl::endpoints::create_endpoints;
+use fuchsia_app::client::connect_to_service;
+use fidl_fuchsia_game_tennis::{TennisServiceMarker, PaddleRequest};
+use fuchsia_zircon::DurationNum;
+use parking_lot::Mutex;
+use std::sync::Arc;
+use futures::TryStreamExt;
+
+fn main() -> Result<(), Error> {
+ let mut executor = fasync::Executor::new().context("Error creating executor")?;
+ let tennis_service = connect_to_service::<TennisServiceMarker>()?;
+
+ let (client_end, paddle_controller) = create_endpoints()?;
+
+ let (mut prs, paddle_control_handle) = paddle_controller.into_stream_and_control_handle()?;
+
+ tennis_service.register_paddle("example_ai", client_end)?;
+
+ let i_am_player_2 = Arc::new(Mutex::new(false));
+ let i_am_player_2_clone = i_am_player_2.clone();
+
+ println!("registering with game service");
+ fasync::spawn(
+ async move {
+ while let Some(PaddleRequest::NewGame{is_player_2, ..}) = await!(prs.try_next()).unwrap() { // TODO: remove unwrap
+ if is_player_2 {
+ println!("I am player 2");
+ } else {
+ println!("I am player 1");
+ }
+ *i_am_player_2_clone.lock() = is_player_2
+ }
+ }
+ );
+
+ let resp: Result<(), Error> = executor.run_singlethreaded(async move {
+ loop {
+ let time_step: i64 = 1000 / 5;
+ await!(fuchsia_async::Timer::new(time_step.millis().after_now()));
+
+ let state = await!(tennis_service.get_state())?;
+ if state.game_num == 0 {
+ continue;
+ }
+ let my_y;
+ if *i_am_player_2.lock() {
+ my_y = state.player_2_y;
+ } else {
+ my_y = state.player_1_y;
+ }
+ if state.ball_y > my_y {
+ println!("moving down");
+ paddle_control_handle.send_down()?;
+ } else if state.ball_y < my_y{
+ println!("moving up");
+ paddle_control_handle.send_up()?;
+ } else {
+ println!("staying still");
+ paddle_control_handle.send_stop()?;
+ }
+ }
+ });
+ resp
+}
diff --git a/bin/tennis/meta/tennis_example_ai.cmx b/bin/tennis/meta/tennis_example_ai.cmx
new file mode 100644
index 0000000..67591d3
--- /dev/null
+++ b/bin/tennis/meta/tennis_example_ai.cmx
@@ -0,0 +1,11 @@
+{
+ "program": {
+ "binary": "bin/app"
+ },
+ "sandbox": {
+ "services": [
+ "fuchsia.logger.LogSink",
+ "fuchsia.game.tennis.TennisService"
+ ]
+ }
+}
diff --git a/bin/tennis/meta/tennis_service.cmx b/bin/tennis/meta/tennis_service.cmx
new file mode 100644
index 0000000..2994d32
--- /dev/null
+++ b/bin/tennis/meta/tennis_service.cmx
@@ -0,0 +1,8 @@
+{
+ "program": {
+ "binary": "bin/app"
+ },
+ "sandbox": {
+ "services": [ "fuchsia.logger.LogSink" ]
+ }
+}
diff --git a/bin/tennis/meta/tennis_viewer.cmx b/bin/tennis/meta/tennis_viewer.cmx
new file mode 100644
index 0000000..67591d3
--- /dev/null
+++ b/bin/tennis/meta/tennis_viewer.cmx
@@ -0,0 +1,11 @@
+{
+ "program": {
+ "binary": "bin/app"
+ },
+ "sandbox": {
+ "services": [
+ "fuchsia.logger.LogSink",
+ "fuchsia.game.tennis.TennisService"
+ ]
+ }
+}
diff --git a/bin/tennis/src/game.rs b/bin/tennis/src/game.rs
new file mode 100644
index 0000000..7405945
--- /dev/null
+++ b/bin/tennis/src/game.rs
@@ -0,0 +1,192 @@
+// Copyright 2018 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+use fidl_fuchsia_game_tennis as fidl_tennis;
+use fidl_fuchsia_game_tennis::GameState;
+use fuchsia_syslog::{fx_log, fx_log_info};
+use futures::prelude::*;
+use parking_lot::Mutex;
+use std::sync::{Arc, Weak};
+
+const BOARD_HEIGHT: f64 = 10.0;
+const BOARD_WIDTH: f64 = 20.0;
+const PADDLE_SPEED: f64 = 0.4; // distance paddle travels per step
+const PADDLE_SIZE: f64 = 1.0; // vertical height of paddle
+const BALL_SPEEDUP_MULTIPLIER: f64 = 1.05; // speed multiplier applied on every paddle bounce
+const MAX_BOUNCE_ANGLE: f64 = 1.3; // in radians, bounce angle when hitting very top edge of paddle
+
+pub struct Game {
+ state: GameState,
+ player_1: Option<Player>,
+ player_2: Option<Player>,
+ ball_dx: f64,
+ ball_dy: f64,
+}
+
+#[derive(Clone)]
+struct Player {
+ pub name: String,
+ pub state: Arc<Mutex<PlayerState>>,
+}
+
+#[derive(Clone)]
+pub enum PlayerState {
+ Up,
+ Down,
+ Stop,
+}
+
+fn calc_paddle_movement(pos: &mut f64, state: &PlayerState) {
+ let player_delta = match state {
+ PlayerState::Up => PADDLE_SPEED * -1.0,
+ PlayerState::Down => PADDLE_SPEED,
+ PlayerState::Stop => 0.0,
+ };
+ let new_paddle_location = *pos + player_delta;
+ if new_paddle_location >= 0.0 && new_paddle_location < BOARD_HEIGHT {
+ *pos = new_paddle_location;
+ }
+}
+
+impl Game {
+ /// return clone of internal state
+ pub fn state(&self) -> GameState {
+ fidl_tennis::GameState {
+ ball_x: self.state.ball_x,
+ ball_y: self.state.ball_y,
+ player_1_y: self.state.player_1_y,
+ player_2_y: self.state.player_2_y,
+ player_1_score: self.state.player_1_score,
+ player_2_score: self.state.player_2_score,
+ player_1_name: self.state.player_1_name.clone(),
+ player_2_name: self.state.player_2_name.clone(),
+ time: self.state.time,
+ game_num: self.state.game_num,
+ }
+ }
+ pub fn new() -> Game {
+ Game {
+ player_1: None,
+ player_2: None,
+ ball_dx: 0.0,
+ ball_dy: 0.0,
+ state: GameState {
+ ball_x: 0.0,
+ ball_y: 0.0,
+ game_num: 0,
+ player_1_y: 0.0,
+ player_2_y: 0.0,
+ player_1_score: 0,
+ player_2_score: 0,
+ player_1_name: "".to_string(),
+ player_2_name: "".to_string(),
+ time: 0,
+ },
+ }
+ }
+
+ pub fn players_ready(&self) -> bool {
+ return self.player_1.is_some() && self.player_2.is_some();
+ }
+
+ pub fn register_new_paddle(&mut self, player_name: String) -> Arc<Mutex<PlayerState>> {
+ let paddle = Player {
+ name: player_name.clone(),
+ state: Arc::new(Mutex::new(PlayerState::Stop)),
+ };
+ let res = paddle.state.clone();
+ if self.player_1.is_none() {
+ self.player_1 = Some(paddle);
+ self.state.player_1_name = player_name;
+ } else if self.player_2.is_none() {
+ self.player_2 = Some(paddle);
+ self.state.player_2_name = player_name;
+ } else {
+ panic!("too many clients connected");
+ }
+ return res;
+ }
+
+ pub fn step(&mut self) {
+ if self.players_ready() && self.state.game_num == 0 {
+ self.new_game();
+ } else if !self.players_ready() {
+ // game has not started yet
+ return;
+ }
+
+ fx_log_info!("new step");
+
+ self.state.time += 1;
+
+ calc_paddle_movement(
+ &mut self.state.player_1_y,
+ &self.player_1.as_mut().unwrap().state.lock(),
+ );
+ calc_paddle_movement(
+ &mut self.state.player_2_y,
+ &self.player_2.as_mut().unwrap().state.lock(),
+ );
+
+ let mut new_ball_x = self.state.ball_x + self.ball_dx;
+ let mut new_ball_y = self.state.ball_y + self.ball_dy;
+
+ // reflect off the top/bottom of the board
+ if new_ball_y <= 0.0 || new_ball_y > BOARD_HEIGHT {
+ self.ball_dy = -self.ball_dy;
+ new_ball_y = self.state.ball_y;
+ fx_log_info!("bounce off top or bottom");
+ }
+
+ // reflect off the left/right of the board, if a paddle is in the way
+ if new_ball_x <= 0.0 {
+ // we're about to go off of the left side
+ if new_ball_y > self.state.player_1_y + (PADDLE_SIZE / 2.0)
+ || new_ball_y < self.state.player_1_y - (PADDLE_SIZE / 2.0) {
+ // player 1 missed, so player 2 gets a point and we reset
+ self.state.player_2_score += 1;
+ self.new_game();
+ return;
+ } else {
+ self.ball_dx = -self.ball_dx;
+ new_ball_x = self.state.ball_x;
+ fx_log_info!("bounce off left");
+ }
+ }
+ if new_ball_x > BOARD_WIDTH {
+ // we're about to go off of the right side
+ if new_ball_y > self.state.player_2_y + (PADDLE_SIZE / 2.0)
+ || new_ball_y < self.state.player_2_y - (PADDLE_SIZE / 2.0) {
+ // player 2 missed, so player 1 gets a point and we reset
+ self.state.player_1_score += 1;
+ self.new_game();
+ return;
+ } else {
+ self.ball_dx = -self.ball_dx;
+ new_ball_x = self.state.ball_x;
+ fx_log_info!("bounce off right");
+ }
+ }
+
+ self.state.ball_x = new_ball_x;
+ self.state.ball_y = new_ball_y;
+ }
+
+ fn new_game(&mut self) {
+ self.player_1.as_mut().map(|player| {
+ *player.state.lock() = PlayerState::Stop;
+ });
+ self.player_2.as_mut().map(|player| {
+ *player.state.lock() = PlayerState::Stop;
+ });
+ self.ball_dx = 0.5; // TODO randomize?
+ self.ball_dy = 0.5; // TODO randomize?
+ self.state.ball_x = BOARD_WIDTH / 2.0;
+ self.state.ball_y = BOARD_HEIGHT / 2.0;
+ self.state.game_num += 1;
+ //self.state.player_1_y = BOARD_HEIGHT / 2.0;
+ //self.state.player_2_y = BOARD_HEIGHT / 2.0;
+ self.state.time = 0;
+ }
+}
diff --git a/bin/tennis/src/main.rs b/bin/tennis/src/main.rs
new file mode 100644
index 0000000..c454bc2
--- /dev/null
+++ b/bin/tennis/src/main.rs
@@ -0,0 +1,33 @@
+// Copyright 2018 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#![feature(async_await, await_macro)]
+
+mod game;
+mod tennis_service;
+
+use failure::{Error, ResultExt};
+use fidl::endpoints::ServiceMarker;
+use fidl_fuchsia_game_tennis::TennisServiceMarker;
+use fuchsia_app::server::ServicesServer;
+use fuchsia_syslog::{fx_log, fx_log_info, init_with_tags};
+
+fn main() -> Result<(), Error> {
+ init_with_tags(&["tennis_service"])
+ .expect("tennis syslog init should not fail");
+ fx_log_info!("tennis service started");
+ let mut executor = fuchsia_async::Executor::new()
+ .context("Creating fuchsia_async executor for tennis service failed")?;
+ let tennis = tennis_service::TennisService::new();
+ let done = ServicesServer::new()
+ .add_service((TennisServiceMarker::NAME, move |chan| {
+ tennis.bind(chan);
+ })).start()
+ .context("Creating ServicesServer for tennis service failed")?;
+ executor
+ .run_singlethreaded(done)
+ .context("Attempt to start up tennis services on async::Executor failed")?;
+
+ Ok(())
+}
diff --git a/bin/tennis/src/tennis_service.rs b/bin/tennis/src/tennis_service.rs
new file mode 100644
index 0000000..6127bc5
--- /dev/null
+++ b/bin/tennis/src/tennis_service.rs
@@ -0,0 +1,91 @@
+// Copyright 2018 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+use crate::game::{Game, PlayerState};
+use failure::ResultExt;
+use fidl::endpoints::{ClientEnd, RequestStream, ServerEnd};
+use fidl_fuchsia_game_tennis as fidl_tennis;
+use fuchsia_async as fasync;
+use fuchsia_syslog::{fx_log, fx_log_err, fx_log_info};
+use fuchsia_zircon::DurationNum;
+use futures::prelude::*;
+use parking_lot::Mutex;
+use std::sync::{Arc, Weak};
+
+#[derive(Clone)]
+pub struct TennisService(Arc<Mutex<Game>>);
+
+impl TennisService {
+ pub fn new() -> TennisService {
+ TennisService(Arc::new(Mutex::new(Game::new())))
+ }
+
+ pub fn bind(&self, chan: fuchsia_async::Channel) {
+ let mut self_clone = self.clone();
+ let mut stream = fidl_tennis::TennisServiceRequestStream::from_channel(chan);
+ fuchsia_async::spawn(
+ async move {
+ while let Some(msg) = await!(stream.try_next())
+ .context("error reading value from tennis service request stream")?
+ {
+ match msg {
+ fidl_tennis::TennisServiceRequest::GetState { responder, .. } => {
+ let TennisService(game_arc) = &self_clone;
+ responder.send(&mut game_arc.lock().state());
+ }
+ fidl_tennis::TennisServiceRequest::RegisterPaddle {
+ player_name,
+ paddle,
+ ..
+ } => {
+ fx_log_info!("new paddle registered: {}", player_name);
+ self_clone.register_paddle(player_name, paddle);
+ }
+ }
+ }
+ Ok(())
+ }
+ .unwrap_or_else(|e: failure::Error| fx_log_err!("{:?}", e)),
+ );
+ }
+
+ pub fn register_paddle(
+ &self, player_name: String,
+ paddle: fidl::endpoints::ClientEnd<fidl_fuchsia_game_tennis::PaddleMarker>,
+ ) {
+ let TennisService(game_arc) = self.clone();
+ let mut game = game_arc.lock();
+ let player_state = game.register_new_paddle(player_name);
+ let mut stream = paddle.into_proxy().unwrap().take_event_stream(); // TODO(lard): remove unwrap
+ fasync::spawn(
+ async move {
+ while let Some(event) = await!(stream.try_next())
+ .context("error reading value from paddle event stream")?
+ {
+ let state = match event {
+ fidl_tennis::PaddleEvent::Up { .. } => PlayerState::Up,
+ fidl_tennis::PaddleEvent::Down { .. } => PlayerState::Down,
+ fidl_tennis::PaddleEvent::Stop { .. } => PlayerState::Stop,
+ };
+ *player_state.lock() = state;
+ }
+ Ok(())
+ }
+ .unwrap_or_else(|e: failure::Error| fx_log_err!("{:?}", e)),
+ );
+ if game.players_ready() {
+ fx_log_info!("game is beginning");
+ let game_arc = game_arc.clone();
+ fasync::spawn(
+ async move {
+ loop {
+ game_arc.lock().step();
+ let time_step: i64 = 1000 / 5;
+ await!(fuchsia_async::Timer::new(time_step.millis().after_now()));
+ }
+ },
+ );
+ }
+ }
+}
diff --git a/bin/tennis/tennis_sysmgr.config b/bin/tennis/tennis_sysmgr.config
new file mode 100644
index 0000000..1058a90
--- /dev/null
+++ b/bin/tennis/tennis_sysmgr.config
@@ -0,0 +1,5 @@
+{
+ "services": {
+ "fuchsia.game.tennis.TennisService": "tennis_service"
+ }
+}
diff --git a/bin/tennis/viewer/main.rs b/bin/tennis/viewer/main.rs
new file mode 100644
index 0000000..436e7bd
--- /dev/null
+++ b/bin/tennis/viewer/main.rs
@@ -0,0 +1,108 @@
+// Copyright 2018 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#![deny(warnings)]
+
+#![feature(try_from,async_await,await_macro)]
+
+use failure::{Error, ResultExt};
+use fuchsia_async as fasync;
+use fuchsia_app::client::connect_to_service;
+use fidl_fuchsia_game_tennis::{TennisServiceMarker, GameState};
+use fuchsia_zircon::DurationNum;
+use std::io;
+use std::io::Write;
+
+const DRAW_WIDTH: usize = 80;
+const DRAW_HEIGHT: usize = 25;
+
+const BOARD_WIDTH: f64 = 20.0;
+const BOARD_HEIGHT: f64 = 10.0;
+
+fn main() -> Result<(), Error> {
+ let mut executor = fasync::Executor::new().context("Error creating executor")?;
+ let tennis_service = connect_to_service::<TennisServiceMarker>()?;
+
+ let mut first_print = true;
+
+ println!("connected to tennis service");
+ let resp: Result<(), Error> = executor.run_singlethreaded(async move {
+ loop {
+ let time_step: i64 = 1000 / 5;
+ await!(fuchsia_async::Timer::new(time_step.millis().after_now()));
+
+ let state = await!(tennis_service.get_state())?;
+ if state.game_num == 0 {
+ continue;
+ }
+
+ if first_print {
+ first_print = false;
+ } else {
+ // Print the following to stdout:
+ // - ESC
+ // - [
+ // - The number of lines to move the cursor up, in asci
+ // - A
+ // This is using the ECMA-48 CSI sequences as described here:
+ // http://man7.org/linux/man-pages/man4/console_codes.4.html
+ let mut to_print = Vec::new();
+ to_print.push(0x1B);
+ to_print.push(0x5B);
+ to_print.append(&mut format!("{}", DRAW_HEIGHT).into_bytes().to_vec());
+ to_print.push(0x46);
+
+ io::stdout().write(&to_print)?;
+ }
+
+ print_game(state);
+ }
+ });
+ resp
+}
+
+fn print_game(state: GameState) {
+ let banner_height = 3;
+ let board_draw_height = DRAW_HEIGHT - banner_height;
+
+ let paddle_1_loc =
+ ((state.player_1_y / BOARD_HEIGHT) * (board_draw_height as f64)) as usize;
+ let paddle_2_loc =
+ ((state.player_2_y / BOARD_HEIGHT) * (board_draw_height as f64)) as usize;
+
+ let ball_x_loc = (state.ball_x / BOARD_WIDTH * ((DRAW_WIDTH - 1) as f64)) as usize;
+ let ball_y_loc = (state.ball_y / BOARD_HEIGHT * ((board_draw_height - 1) as f64)) as usize;
+
+ let mut output = "".to_string();
+ output.push_str(&state.player_1_name);
+ output.push_str(&" ".repeat(DRAW_WIDTH - state.player_1_name.len() - state.player_2_name.len()));
+ output.push_str(&state.player_2_name);
+ output.push_str("\n");
+
+ let p1_score = format!("{}", state.player_1_score);
+ let p2_score = format!("{}", state.player_2_score);
+ output.push_str(&p1_score);
+ output.push_str(&" ".repeat(DRAW_WIDTH - p1_score.len() - p2_score.len()));
+ output.push_str(&p2_score);
+ output.push_str("\n");
+
+ for y in 0..board_draw_height {
+ for x in 0..DRAW_WIDTH {
+ // I have no clue why this "as usize" is necessary
+ if (x, y) == (ball_x_loc as usize, ball_y_loc) {
+ output.push_str("0");
+ } else if (x, y) == (0, paddle_1_loc) {
+ output.push_str(")");
+ } else if (x, y) == (DRAW_WIDTH - 1, paddle_2_loc) {
+ output.push_str("(");
+ } else if x == DRAW_WIDTH / 2 {
+ output.push_str("|");
+ } else {
+ output.push_str(" ");
+ }
+ }
+ output.push_str("\n");
+ }
+ println!("{}", output);
+}
diff --git a/examples/rust101/hello/BUILD.gn b/examples/rust101/hello/BUILD.gn
new file mode 100644
index 0000000..e6a1afe
--- /dev/null
+++ b/examples/rust101/hello/BUILD.gn
@@ -0,0 +1,40 @@
+# Copyright 2018 The Fuchsia Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//build/rust/rustc_binary.gni")
+import("//build/package.gni")
+
+package("hello") {
+ deps = [
+ ":hello_bin",
+ ]
+
+ binary = "rust_crates/hello"
+
+ meta = [
+ {
+ path = rebase_path("meta/hello.cmx")
+ dest = "hello.cmx"
+ },
+ ]
+}
+
+rustc_binary("hello_bin") {
+ name = "hello"
+ with_unit_tests = true
+ edition = "2018"
+
+ deps = [
+ "//garnet/public/fidl/fuchsia.net.oldhttp:fuchsia.net.oldhttp-rustc",
+ "//garnet/public/fidl/fuchsia.game.tennis:fuchsia.game.tennis-rustc",
+ "//garnet/public/lib/fidl/rust/fidl",
+ "//garnet/public/rust/crates/fuchsia-app",
+ "//garnet/public/rust/crates/fuchsia-async",
+ "//garnet/public/rust/crates/fuchsia-syslog",
+ "//garnet/public/rust/crates/fuchsia-zircon",
+ "//third_party/rust-crates/rustc_deps:failure",
+ "//third_party/rust-crates/rustc_deps:futures-preview",
+ "//third_party/rust-crates/rustc_deps:parking_lot",
+ ]
+}
diff --git a/examples/rust101/hello/meta/hello.cmx b/examples/rust101/hello/meta/hello.cmx
new file mode 100644
index 0000000..2994d32
--- /dev/null
+++ b/examples/rust101/hello/meta/hello.cmx
@@ -0,0 +1,8 @@
+{
+ "program": {
+ "binary": "bin/app"
+ },
+ "sandbox": {
+ "services": [ "fuchsia.logger.LogSink" ]
+ }
+}
diff --git a/examples/rust101/hello/src/main.rs b/examples/rust101/hello/src/main.rs
new file mode 100644
index 0000000..504a714
--- /dev/null
+++ b/examples/rust101/hello/src/main.rs
@@ -0,0 +1,35 @@
+// Copyright 2018 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#![feature(async_await, await_macro, futures_api)]
+
+use futures::prelude::*;
+use fuchsia_zircon::prelude::*;
+use fuchsia_async as fasync;
+use fuchsia_zircon as zx;
+
+async fn create_future() -> () {
+ println!("This will print second.");
+ let res = await!(wait_1_second());
+ println!("{:?}", res);
+ ()
+}
+
+fn main() {
+ println!("Hello, Fuchsia 2!");
+
+ let mut executor = fuchsia_async::Executor::new()
+ .expect("Creating fuchsia_async executor for tennis service failed");
+
+ let fut = create_future();
+ println!("This will print first!");
+ executor.run_singlethreaded(fut);
+ println!("This will print last!");
+}
+
+async fn wait_1_second() -> () {
+ // TODO use fasync::Timer::new to wait before returning;
+ let time_step: i64 = 1000;
+ await!(fasync::Timer::new(time_step.millis().after_now()));
+}
diff --git a/packages/prod/all b/packages/prod/all
index fa9cabf..3ab25e6 100644
--- a/packages/prod/all
+++ b/packages/prod/all
@@ -91,6 +91,7 @@
"garnet/packages/prod/wlanphy",
"garnet/packages/prod/wlanstack",
"garnet/packages/prod/wlantap",
- "garnet/packages/prod/xi_core"
+ "garnet/packages/prod/xi_core",
+ "garnet/packages/prod/tennis"
]
}
diff --git a/packages/prod/tennis b/packages/prod/tennis
new file mode 100644
index 0000000..2bdfa58
--- /dev/null
+++ b/packages/prod/tennis
@@ -0,0 +1,9 @@
+{
+ "packages": [
+ "//garnet/bin/tennis:tennis_service",
+ "//garnet/bin/tennis:tennis_viewer",
+ "//garnet/bin/tennis:tennis_example_ai",
+ "//garnet/bin/tennis:tennis_sysmgr_config",
+ "//garnet/examples/rust101/hello"
+ ]
+}
diff --git a/public/fidl/fuchsia.game.tennis/BUILD.gn b/public/fidl/fuchsia.game.tennis/BUILD.gn
new file mode 100644
index 0000000..a585e0c
--- /dev/null
+++ b/public/fidl/fuchsia.game.tennis/BUILD.gn
@@ -0,0 +1,13 @@
+# Copyright 2018 The Fuchsia Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//build/fidl/fidl.gni")
+
+fidl("fuchsia.game.tennis") {
+ sdk_category = "partner"
+
+ sources = [
+ "tennis.fidl",
+ ]
+}
diff --git a/public/fidl/fuchsia.game.tennis/tennis.fidl b/public/fidl/fuchsia.game.tennis/tennis.fidl
new file mode 100644
index 0000000..b9e0a27
--- /dev/null
+++ b/public/fidl/fuchsia.game.tennis/tennis.fidl
@@ -0,0 +1,31 @@
+// Copyright 2018 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+library fuchsia.game.tennis;
+
+[Discoverable]
+interface TennisService {
+ 1: GetState() -> (GameState state);
+ 2: RegisterPaddle(string player_name, Paddle paddle);
+};
+
+struct GameState {
+ float64 ballX;
+ float64 ballY;
+ float64 player_1_y; // player 1 is on the left side of the screen
+ float64 player_2_y;
+ int64 player_1_score;
+ int64 player_2_score;
+ int64 time; // start of each game is zero, represents in-game time steps elapsed
+ int64 game_num; // increments by one any time a new game starts, 0 if not enough players yet
+ string player_1_name;
+ string player_2_name;
+};
+
+interface Paddle {
+ 1: NewGame(bool is_player_2);
+ 2: -> Up();
+ 3: -> Down();
+ 4: -> Stop();
+};