<commit_before>//! Proxy engine
//!
//! This module is intended for processing incoming requests from clients,
//! handling occurred errors during a work, communicating with a message
//! broker and preparing appropriate responses in the certain format.
//!

pub mod rabbitmq;
pub mod router;
pub mod serializer;

use std::cell::{RefCell};
use std::rc::{Rc};
use std::str::{from_utf8};
use std::vec::Vec;

pub use self::router::{Router, Endpoint, extract_endpoints};
pub use self::serializer::{Serializer, JsonMessage};
pub use self::rabbitmq::{RabbitMQClient, RabbitMQFuture};

use super::cli::{CliOptions};
use super::error::{Result, PathfinderError};

use json::{parse as json_parse};
use futures::{Stream};
use futures::future::{Future};
use futures::sync::{mpsc};
use lapin_futures_rustls::lapin::types::{AMQPValue, FieldTable};
use lapin_futures_rustls::lapin::channel::{ConfirmSelectOptions};
use lapin_futures_rustls::lapin::channel::{BasicPublishOptions, BasicProperties, BasicConsumeOptions};
use lapin_futures_rustls::lapin::channel::{QueueDeclareOptions, QueueDeleteOptions, QueueBindOptions};
use tokio_core::reactor::{Handle};
use tungstenite::{Message};
use uuid::{Uuid};

/// Proxy engine for processing messages, handling errors and communicating with a message broker.
pub struct Engine {
    router: Rc<Box<Router>>,
    rabbitmq_client: Rc<RefCell<Box<RabbitMQClient>>>
}

impl Engine {
    /// Returns a new instance of `Engine`.
    pub fn new(cli: &CliOptions, router: Box<Router>) -> Engine {
        Engine {
            router: Rc::new(router),
            rabbitmq_client: Rc::new(RefCell::new(Box::new(RabbitMQClient::new(cli))))
        }
    }

    /// Main handler for generating a response per each incoming request.
    pub fn handle(&self, message: JsonMessage, transmitter: mpsc::UnboundedSender<Message>, handle: &Handle) -> RabbitMQFuture {
        let message_nested = message.clone();
        let url = message_nested["url"].as_str().unwrap();

        let endpoint = self.router.clone().match_url_or_default(&url);
        let endpoint_link = endpoint.clone();
        let endpoint_publish = endpoint.clone();

        let queue_name = Rc::new(format!("{}", Uuid::new_v4()));
        let queue_name_bind = queue_name.clone();
        let queue_name_response = queue_name.clone();
        let queue_name_consumer = queue_name.clone();
        let queue_name_delete = queue_name.clone();

        let request_headers = self.prepare_request_headers(&message_nested, endpoint.clone());
        let client_future = self.rabbitmq_client.borrow().get_future(handle);

        Box::new(client_future
            // 1. Create a channel
            .and_then(move |client| {
                client.create_confirm_channel(ConfirmSelectOptions::default())
            })
            .map_err(|err| {
                let message = format!("Error during creating a channel. Reason -> {}", err);
                error!("{}", message);
                err
            })

            // 2. Declare a response queue
            .and_then(move |channel| {
                let queue_declare_options = QueueDeclareOptions {
                    passive: false,
                    durable: true,
                    exclusive: true,
                    auto_delete: false,
                    ..Default::default()
                };

                channel.queue_declare(&queue_name, &queue_declare_options, &FieldTable::new())
                    .map(|_| channel)
            })
            .map_err(|err| {
                let message = format!("Error during declaring the queue. Reason -> {}", err);
                error!("{}", message);
                err
            })

            // 3. Link the response queue the exchange
            .and_then(move |channel| {
                channel.queue_bind(
                    &queue_name_bind,
                    &endpoint_link.get_response_exchange(),
                    &endpoint_link.get_microservice(),
                    &QueueBindOptions::default(),
                    &FieldTable::new()
                )
                    .map(|_| channel)
            })
            .map_err(|err| {
                let message = format!("Error during linking the response queue with exchange. Reason -> {}", err);
                error!("{}", message);
                err
            })

             // 4. Publish message into the microservice queue and make ensure that it's delivered
            .and_then(move |channel| {
                let publish_message_options = BasicPublishOptions {
                    mandatory: true,
                    immediate: false,
                    ..Default::default()
                };

                let mut message_headers = FieldTable::new();
                for &(ref key, ref value) in request_headers.clone().iter() {
                    let header_name = key.to_string();
                    let header_value = AMQPValue::LongString(value.to_string());
                    message_headers.insert(header_name, header_value);
                }

                let event_name = message["event-name"].as_str().unwrap_or("null");

                let basic_properties = BasicProperties {
                    content_type: Some("application/json".to_string()),
                    headers: Some(message_headers),                       // Headers for the message
                    delivery_mode: Some(2),                               // Message must be persistent
                    reply_to: Some(queue_name_response.to_string()),      // Response queue
                    correlation_id: Some(event_name.clone().to_string()), // Event name
                    ..Default::default()
                };

                channel.basic_publish(
                    &endpoint_publish.get_request_exchange(),
                    &endpoint_publish.get_microservice(),
                    message["content"].dump().as_bytes(),
                    &publish_message_options,
                    basic_properties
                )
                    .map(|_confirmation| channel)
            })
            .map_err(|err| {
                let message = format!("Error during publishing a message. Reason -> {}", err);
                error!("{}", message);
                err
            })

            // 5. Consume a response message from the queue, that was declared on the 2nd step
            .and_then(move |channel| {
                channel.basic_consume(
                    &queue_name_consumer,
                    "response_consumer",
                    &BasicConsumeOptions::default(),
                    &FieldTable::new()
                )
                    .and_then(move |stream| {
                        stream.take(1)
                              .into_future()
                              .map_err(|(err, _)| err)
                              .map(move |(message, _)| (channel, message.unwrap()))
                    })
            })
            .map_err(|err| {
                let message = format!("Error during consuming the response message. Reason -> {}", err);
                error!("{}", message);
                err
            })

            // 6. Prepare a response for a client, serialize and sent via WebSocket transmitter
            .and_then(move |(channel, message)| {
                let raw_data = from_utf8(&message.data).unwrap();
                let json = Rc::new(Box::new(json_parse(raw_data).unwrap()));
                let serializer = Serializer::new();
                let response = serializer.serialize(json.dump()).unwrap();
                transmitter.unbounded_send(response).unwrap();
                channel.basic_ack(message.delivery_tag)
                    .map(move |_| channel)
            })
            .map_err(|err| {
                let message = format!("Error during sending a message to the client. Reason -> {}", err);
                error!("{}", message);
                err
            })

            // 7. Unbind the response queue from the exchange point
            // TODO: Uncomment this code when my PR for queue_unbind will be merged
            //.and_then(move |channel| {
            //    channel.queue_unbind(
            //        &queue_name_unbind,
            //        &response_exchange_name,
            //        &endpoint_unbind.get_microservice(),
            //        &QueueUnbindOptions::default(),
            //        &FieldTable::new()
            //    )
            //        .map(|_| channel)
            //})
            //.map_err(|err| {
            //    let message = format!("Error during linking the response queue with exchange: {}", err);
            //    error!("{}", message);
            //    Err(PathfinderError::Io(err))
            //})

            // 8. Delete the response queue
            .and_then(move |channel| {
                let queue_delete_options = QueueDeleteOptions {
                    if_unused: false,
                    if_empty: false,
                    ..Default::default()
                };

                channel.queue_delete(&queue_name_delete, &queue_delete_options)
                    .map(|_| channel)
            })
            .map_err(|err| {
                let message = format!("Error during deleting the queue. Reason -> {}", err);
                error!("{}", message);
                err
            })

            // 9. Close the channel
            .and_then(move |channel| {
                channel.close(200, "Close the channel.")
            })
            .map_err(|err| {
                let message = format!("Error during closing the channel. Reason -> {}", err);
                error!("{}", message);
                err
            })

            .then(move |result| {
                match result {
                    Ok(_) => Ok(()),
                    Err(_) => Err(PathfinderError::MessageBrokerError(
                        String::from("The request wasn't processed. Please, try once again.")
                    ))
                }
            })
        )
    }

    /// Transforms an error (which is a string) into JSON object in the special format.
    pub fn wrap_an_error(&self, err: &str) -> Message {
        let json_error_message = object!("details" => err);
        let serializer = Serializer::new();
        serializer.serialize(json_error_message.dump()).unwrap()
    }

    /// Serialize a JSON object into message.
    pub fn serialize_message(&self, json: JsonMessage) -> Message {
        let serializer = Serializer::new();
        serializer.serialize(json.dump()).unwrap()
    }

    /// Deserialize a message into JSON object.
    pub fn deserialize_message(&self, message: &Message) -> Result<JsonMessage> {
        let serializer = Serializer::new();
        serializer.deserialize(message)
    }

    /// Prepares a list of key-value pairs for headers in message.
    fn prepare_request_headers(&self, json: &JsonMessage, endpoint: Rc<Box<Endpoint>>) -> Box<Vec<(String, String)>> {
        Box::new(vec![
            (String::from("Microservice-Name"), endpoint.get_microservice()),
            (String::from("Request-URI"), endpoint.get_url()),
            (String::from("Event-Name"), json["event-name"].as_str().unwrap_or("null").to_string()),
            (String::from("Token"), json["token"].as_str().unwrap_or("null").to_string())
        ])
    }
}
<commit_msg>Removed excessive map_err calls for handling RabbitMQ errors<commit_after>//! Proxy engine
//!
//! This module is intended for processing incoming requests from clients,
//! handling occurred errors during a work, communicating with a message
//! broker and preparing appropriate responses in the certain format.
//!

pub mod rabbitmq;
pub mod router;
pub mod serializer;

use std::cell::{RefCell};
use std::rc::{Rc};
use std::str::{from_utf8};
use std::vec::Vec;

pub use self::router::{Router, Endpoint, extract_endpoints};
pub use self::serializer::{Serializer, JsonMessage};
pub use self::rabbitmq::{RabbitMQClient, RabbitMQFuture};

use super::cli::{CliOptions};
use super::error::{Result, PathfinderError};

use json::{parse as json_parse};
use futures::{Stream};
use futures::future::{Future};
use futures::sync::{mpsc};
use lapin_futures_rustls::lapin::types::{AMQPValue, FieldTable};
use lapin_futures_rustls::lapin::channel::{ConfirmSelectOptions};
use lapin_futures_rustls::lapin::channel::{BasicPublishOptions, BasicProperties, BasicConsumeOptions};
use lapin_futures_rustls::lapin::channel::{QueueDeclareOptions, QueueDeleteOptions, QueueBindOptions};
use tokio_core::reactor::{Handle};
use tungstenite::{Message};
use uuid::{Uuid};

/// Proxy engine for processing messages, handling errors and communicating with a message broker.
pub struct Engine {
    router: Rc<Box<Router>>,
    rabbitmq_client: Rc<RefCell<Box<RabbitMQClient>>>
}

impl Engine {
    /// Returns a new instance of `Engine`.
    pub fn new(cli: &CliOptions, router: Box<Router>) -> Engine {
        Engine {
            router: Rc::new(router),
            rabbitmq_client: Rc::new(RefCell::new(Box::new(RabbitMQClient::new(cli))))
        }
    }

    /// Main handler for generating a response per each incoming request.
    pub fn handle(&self, message: JsonMessage, transmitter: mpsc::UnboundedSender<Message>, handle: &Handle) -> RabbitMQFuture {
        let message_nested = message.clone();
        let url = message_nested["url"].as_str().unwrap();

        let endpoint = self.router.clone().match_url_or_default(&url);
        let endpoint_link = endpoint.clone();
        let endpoint_publish = endpoint.clone();

        let queue_name = Rc::new(format!("{}", Uuid::new_v4()));
        let queue_name_bind = queue_name.clone();
        let queue_name_response = queue_name.clone();
        let queue_name_consumer = queue_name.clone();
        let queue_name_delete = queue_name.clone();

        let request_headers = self.prepare_request_headers(&message_nested, endpoint.clone());
        let client_future = self.rabbitmq_client.borrow().get_future(handle);

        Box::new(client_future
            // 1. Create a channel
            .and_then(move |client| {
                client.create_confirm_channel(ConfirmSelectOptions::default())
            })

            // 2. Declare a response queue
            .and_then(move |channel| {
                let queue_declare_options = QueueDeclareOptions {
                    passive: false,
                    durable: true,
                    exclusive: true,
                    auto_delete: false,
                    ..Default::default()
                };

                channel.queue_declare(&queue_name, &queue_declare_options, &FieldTable::new())
                    .map(|_| channel)
            })

            // 3. Link the response queue the exchange
            .and_then(move |channel| {
                channel.queue_bind(
                    &queue_name_bind,
                    &endpoint_link.get_response_exchange(),
                    &endpoint_link.get_microservice(),
                    &QueueBindOptions::default(),
                    &FieldTable::new()
                )
                    .map(|_| channel)
            })

             // 4. Publish message into the microservice queue and make ensure that it's delivered
            .and_then(move |channel| {
                let publish_message_options = BasicPublishOptions {
                    mandatory: true,
                    immediate: false,
                    ..Default::default()
                };

                let mut message_headers = FieldTable::new();
                for &(ref key, ref value) in request_headers.clone().iter() {
                    let header_name = key.to_string();
                    let header_value = AMQPValue::LongString(value.to_string());
                    message_headers.insert(header_name, header_value);
                }

                let event_name = message["event-name"].as_str().unwrap_or("null");

                let basic_properties = BasicProperties {
                    content_type: Some("application/json".to_string()),
                    headers: Some(message_headers),                       // Headers for the message
                    delivery_mode: Some(2),                               // Message must be persistent
                    reply_to: Some(queue_name_response.to_string()),      // Response queue
                    correlation_id: Some(event_name.clone().to_string()), // Event name
                    ..Default::default()
                };

                channel.basic_publish(
                    &endpoint_publish.get_request_exchange(),
                    &endpoint_publish.get_microservice(),
                    message["content"].dump().as_bytes(),
                    &publish_message_options,
                    basic_properties
                )
                    .map(|_confirmation| channel)
            })

            // 5. Consume a response message from the queue, that was declared on the 2nd step
            .and_then(move |channel| {
                channel.basic_consume(
                    &queue_name_consumer,
                    "response_consumer",
                    &BasicConsumeOptions::default(),
                    &FieldTable::new()
                )
                    .and_then(move |stream| {
                        stream.take(1)
                              .into_future()
                              .map_err(|(err, _)| err)
                              .map(move |(message, _)| (channel, message.unwrap()))
                    })
            })

            // 6. Prepare a response for a client, serialize and sent via WebSocket transmitter
            .and_then(move |(channel, message)| {
                let raw_data = from_utf8(&message.data).unwrap();
                let json = Rc::new(Box::new(json_parse(raw_data).unwrap()));
                let serializer = Serializer::new();
                let response = serializer.serialize(json.dump()).unwrap();
                transmitter.unbounded_send(response).unwrap();
                channel.basic_ack(message.delivery_tag)
                    .map(move |_| channel)
            })

            // 7. Unbind the response queue from the exchange point
            // TODO: Uncomment this code when my PR for queue_unbind will be merged
            //.and_then(move |channel| {
            //    channel.queue_unbind(
            //        &queue_name_unbind,
            //        &response_exchange_name,
            //        &endpoint_unbind.get_microservice(),
            //        &QueueUnbindOptions::default(),
            //        &FieldTable::new()
            //    )
            //        .map(|_| channel)
            //})

            // 8. Delete the response queue
            .and_then(move |channel| {
                let queue_delete_options = QueueDeleteOptions {
                    if_unused: false,
                    if_empty: false,
                    ..Default::default()
                };

                channel.queue_delete(&queue_name_delete, &queue_delete_options)
                    .map(|_| channel)
            })

            // 9. Close the channel
            .and_then(move |channel| {
                channel.close(200, "Close the channel.")
            })

            .then(move |result| {
                match result {
                    Ok(_) => Ok(()),
                    Err(err) => {
                        error!("Error in RabbitMQ client. Reason -> {}", err);
                        let message = String::from("The request wasn't processed. Please, try once again.");
                        Err(PathfinderError::MessageBrokerError(message))
                    }
                }
            })
        )
    }

    /// Transforms an error (which is a string) into JSON object in the special format.
    pub fn wrap_an_error(&self, err: &str) -> Message {
        let json_error_message = object!("details" => err);
        let serializer = Serializer::new();
        serializer.serialize(json_error_message.dump()).unwrap()
    }

    /// Serialize a JSON object into message.
    pub fn serialize_message(&self, json: JsonMessage) -> Message {
        let serializer = Serializer::new();
        serializer.serialize(json.dump()).unwrap()
    }

    /// Deserialize a message into JSON object.
    pub fn deserialize_message(&self, message: &Message) -> Result<JsonMessage> {
        let serializer = Serializer::new();
        serializer.deserialize(message)
    }

    /// Prepares a list of key-value pairs for headers in message.
    fn prepare_request_headers(&self, json: &JsonMessage, endpoint: Rc<Box<Endpoint>>) -> Box<Vec<(String, String)>> {
        Box::new(vec![
            (String::from("Microservice-Name"), endpoint.get_microservice()),
            (String::from("Request-URI"), endpoint.get_url()),
            (String::from("Event-Name"), json["event-name"].as_str().unwrap_or("null").to_string()),
            (String::from("Token"), json["token"].as_str().unwrap_or("null").to_string())
        ])
    }
}
<|endoftext|>


<commit_before>// Copyright 2013 Torste Aikio and gl-rs developers.

// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#[feature(globs)];
#[feature(macro_rules)];

extern mod glfw;
extern mod gl;

use std::libc;

use std::cast;
use std::ptr;
use std::str;
use std::sys;
use std::vec;

use std::io;
use std::path::PosixPath;

use gl::types::*;

trait Component {
}

//Do these need to be right here?
struct Position {
    x: f64,
    y: f64
}

impl Component for Position {
}

struct HorizVelocity {
    x: f64
}

impl Component for HorizVelocity {
}

struct VertVelocity {
    y: f64
}

impl Component for VertVelocity {
}

struct Sprite {
    x_size: f64,
    y_size: f64,
    color: [f64, ..4]
}

impl Component for Sprite{
}

static COMPONENT_COUNT: uint = 4;
struct Components {
    position: Option<@mut Position>,
    horiz_velocity: Option<@mut HorizVelocity>,
    vert_velocity: Option<@mut VertVelocity>,
    sprite: Option<@mut Sprite>
}

trait System {
    fn process(&self, entity: @Components) -> ();
}

// We need to figure out how to integrate World with the main game loop
// in Artemis world has a `setDelta` method for timestep
struct World {
    entities: ~[@Components],
    systems: ~[@System]
}

impl World {
    fn new() -> World {
        return World {entities: ~[], systems: ~[]};
    }

    fn process(&self) {
        for system in self.systems.iter() {
            for entity in self.entities.iter() {
                system.process(*entity);
            }
        }
    }
}

struct MovementSystem;

impl System for MovementSystem {
    fn process(&self, entity: @Components) -> () {
        match entity.position {
            Some(pos) => {
                match entity.vert_velocity {
                    Some(v) => pos.y += v.y,
                    None => ()
                }
                match entity.horiz_velocity {
                    Some(v) => pos.x += v.x,
                    None => ()
                }
            },
            None => ()
        }
    }
}

struct EdgeCollisionSystem;

impl System for EdgeCollisionSystem {
    fn process(&self, entity: @Components) -> () {
        match (entity.position, entity.vert_velocity) {
            (Some(pos), Some(vel)) => {
                if pos.y >= 1.0 || pos.y <= 0.0 {
                    vel.y *= -1.0;
                }
            },
            (_, _) => ()
        }
    }
}

struct PaddleCollisionSystem {
    left_paddle: @Components,
    right_paddle: @Components,
    paddle_width: f64,
    paddle_height: f64
}

impl System for PaddleCollisionSystem {
    fn process(&self, entity: @Components) -> () {
        match (entity.position, entity.horiz_velocity) {
            (Some(pos), Some(vel)) => {
                if pos.x >= (4.0-self.paddle_width) {
                    if std::num::abs(pos.y - self.right_paddle.position.unwrap().y) < (self.paddle_height/2.0) {
                        vel.x *= -1.0;
                    }
                }
                if pos.x <= self.paddle_width {
                    if std::num::abs(pos.y - self.left_paddle.position.unwrap().y) < (self.paddle_height/2.0) {
                        vel.x *= -1.0;
                    }
                }
            },
            (_, _) => ()
        }
    }
}

struct RenderSystem {
    program: GLuint,
    position_uniform: GLint,
    scale_uniform: GLint,
    color_uniform: GLint
}

impl System for RenderSystem {
    fn process(&self, entity: @Components) -> () {
        match (entity.position, entity.sprite) {
            (Some(pos), Some(sprite)) => {
                // Set uniforms
                gl::ProgramUniform2f(self.program, self.position_uniform, pos.x as f32, pos.y as f32);
                gl::ProgramUniform2f(self.program, self.scale_uniform, sprite.x_size as f32, sprite.y_size as f32);
                gl::ProgramUniform4f(self.program, self.color_uniform, sprite.color[0] as f32, sprite.color[1] as f32, sprite.color[2] as f32, sprite.color[3] as f32);
                // Draw a rect from the 4 vertices
                gl::DrawArrays(gl::TRIANGLE_STRIP, 0, 4);
            },
            (_, _) => ()
        }
    }
}

enum PaddleSide {
    RIGHT,
    LEFT
}

fn new_ball() -> @Components {
    let components = @Components { position: Some(@mut Position { x: 2.0, y: 1.5 }), horiz_velocity: Some(@mut HorizVelocity { x: 1.0/60.0 }), vert_velocity: Some(@mut VertVelocity { y: 0.0 }), sprite: Some(@mut Sprite {x_size: 0.05, y_size: 0.05, color: [0.3, 0.3, 0.8, 1.0]})};
    return components;
}

fn new_paddle(side: PaddleSide) -> @Components {
    let xpos = match side {
        RIGHT => 3.9,
        LEFT => 0.1
    };
    let components = @Components { position: Some(@mut Position { x: xpos, y: 1.5 }), horiz_velocity: None, vert_velocity: Some(@mut VertVelocity { y: 0.0 }), sprite: Some(@mut Sprite {x_size: 0.1, y_size: 0.4, color: [xpos/4.0, 1.0-(xpos/4.0), 0.3, 1.0]})};
    return components;
}

// Vertex data
static VERTEX_DATA: [GLfloat, ..8] = [
    -1.0,  1.0,
    -1.0, -1.0,
     1.0,  1.0,
     1.0, -1.0
];

#[start]
fn start(argc: int, argv: **u8) -> int {
    std::rt::start_on_main_thread(argc, argv, main)
}

fn compile_shader(src: &str, ty: GLenum) -> GLuint {
    let shader = gl::CreateShader(ty);
    unsafe {
        // Attempt to compile the shader
        src.with_c_str(|ptr| gl::ShaderSource(shader, 1, &ptr, ptr::null()));
        gl::CompileShader(shader);

        // Get the compile status
        let mut status = gl::FALSE as GLint;
        gl::GetShaderiv(shader, gl::COMPILE_STATUS, &mut status);

        // Fail on error
        if status != (gl::TRUE as GLint) {
            let mut len = 0;
            gl::GetShaderiv(shader, gl::INFO_LOG_LENGTH, &mut len);
            let mut buf = vec::from_elem(len as uint - 1, 0u8);     // subtract 1 to skip the trailing null character
            gl::GetShaderInfoLog(shader, len, ptr::mut_null(), vec::raw::to_mut_ptr(buf) as *mut GLchar);
            fail!(str::raw::from_utf8(buf));
        }
    }
    shader
}

fn link_program(vs: GLuint, fs: GLuint, out_color: &str) -> GLuint {
    let program = gl::CreateProgram();
    gl::AttachShader(program, vs);
    gl::AttachShader(program, fs);
    // gl::BindFragDataLocation needs to be called before linking to have effect
    unsafe {
        out_color.with_c_str(|ptr| gl::BindFragDataLocation(program, 0, ptr));
    }
    gl::LinkProgram(program);
    unsafe {
        // Get the link status
        let mut status = gl::FALSE as GLint;
        gl::GetProgramiv(program, gl::LINK_STATUS, &mut status);

        // Fail on error
        if status != (gl::TRUE as GLint) {
            let mut len: GLint = 0;
            gl::GetProgramiv(program, gl::INFO_LOG_LENGTH, &mut len);
            let mut buf = vec::from_elem(len as uint - 1, 0u8);     // subtract 1 to skip the trailing null character
            gl::GetProgramInfoLog(program, len, ptr::mut_null(), vec::raw::to_mut_ptr(buf) as *mut GLchar);
            fail!(str::raw::from_utf8(buf));
        }
    }
    program
}

fn main() {
    do glfw::set_error_callback |_, description| {
        println!("GLFW Error: {}", description);
    }

    do glfw::start {
        // initialize game world
        let left_paddle: @Components = new_paddle(LEFT);
        let right_paddle: @Components = new_paddle(RIGHT);
        let ball: @Components = new_ball();
        let ms: @System = @MovementSystem as @System;
        let es: @System = @EdgeCollisionSystem as @System;
        let ps: @System = @PaddleCollisionSystem{ right_paddle: right_paddle, left_paddle: left_paddle, paddle_height: 0.4, paddle_width: 0.1} as @System;

        let mut world: World = World::new();
        world.entities.push(left_paddle);
        world.entities.push(right_paddle);
        world.entities.push(ball);
        world.systems.push(ms);
        world.systems.push(es);
        world.systems.push(ps);

        // Choose a GL profile that is compatible with OS X 10.7+
        glfw::window_hint::context_version(3, 2);
        glfw::window_hint::opengl_profile(glfw::OpenGlCoreProfile);
        glfw::window_hint::opengl_forward_compat(true);

        let window = glfw::Window::create(1024, 600, "Pong", glfw::Windowed).unwrap();
        window.set_key_callback(key_callback);
        window.make_context_current();

        // Load the OpenGL function pointers
        gl::load_with(glfw::get_proc_address);

        // Create GLSL shaders
        let vs_src = io::read_whole_file_str(&PosixPath("main.vs.glsl")).unwrap();
        let vs = compile_shader(vs_src, gl::VERTEX_SHADER);
        let fs_src = io::read_whole_file_str(&PosixPath("main.fs.glsl")).unwrap();
        let fs = compile_shader(fs_src, gl::FRAGMENT_SHADER);
        let program = link_program(vs, fs, "out_color");

        let mut vao = 0;
        let mut vbo = 0;

        let position_uniform: GLint;
        let scale_uniform: GLint;
        let color_uniform: GLint;

        unsafe {
            position_uniform = "position".with_c_str(|ptr| gl::GetUniformLocation(program, ptr));
            scale_uniform = "scale".with_c_str(|ptr| gl::GetUniformLocation(program, ptr));
            color_uniform = "color".with_c_str(|ptr| gl::GetUniformLocation(program, ptr));
            // Create Vertex Array Object
            gl::GenVertexArrays(1, &mut vao);
            gl::BindVertexArray(vao);

            // Create a Vertex Buffer Object and copy the vertex data to it
            gl::GenBuffers(1, &mut vbo);
            gl::BindBuffer(gl::ARRAY_BUFFER, vbo);
            gl::BufferData(gl::ARRAY_BUFFER,
                           (VERTEX_DATA.len() * sys::size_of::<GLfloat>()) as GLsizeiptr,
                           cast::transmute(&VERTEX_DATA[0]),
                           gl::STATIC_DRAW);

            // Use shader program
            gl::UseProgram(program);

            // Specify the layout of the vertex data
            let vert_attr = "vertex".with_c_str(|ptr| gl::GetAttribLocation(program, ptr));
            gl::EnableVertexAttribArray(vert_attr as GLuint);
            gl::VertexAttribPointer(vert_attr as GLuint, 2, gl::FLOAT,
                                    gl::FALSE as GLboolean, 0, ptr::null());
        }

        let rs: @System = @RenderSystem {program: program, position_uniform: position_uniform, scale_uniform: scale_uniform, color_uniform: color_uniform} as @System;
        world.systems.push(rs);

        while !window.should_close() {
            // Poll events
            glfw::poll_events();

            // Clear the screen to black
            gl::ClearColor(0.8, 0.8, 0.8, 1.0);
            gl::Clear(gl::COLOR_BUFFER_BIT);

            // process game world
            world.process();

            // Swap buffers
            window.swap_buffers();
        }

        // Cleanup
        gl::DeleteProgram(program);
        gl::DeleteShader(fs);
        gl::DeleteShader(vs);
        unsafe {
            gl::DeleteBuffers(1, &vbo);
            gl::DeleteVertexArrays(1, &vao);
        }
    }
}

fn key_callback(window: &glfw::Window, key: glfw::Key, _: libc::c_int, action: glfw::Action, _: glfw::Modifiers) {
    if action == glfw::Press && key == glfw::KeyEscape {
        window.set_should_close(true);
    }
}
<commit_msg>removed unused component trait<commit_after>// Copyright 2013 Torste Aikio and gl-rs developers.

// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#[feature(globs)];
#[feature(macro_rules)];

extern mod glfw;
extern mod gl;

use std::libc;

use std::cast;
use std::ptr;
use std::str;
use std::sys;
use std::vec;

use std::io;
use std::path::PosixPath;

use gl::types::*;

//Do these need to be right here?
struct Position {
    x: f64,
    y: f64
}

struct HorizVelocity {
    x: f64
}

struct VertVelocity {
    y: f64
}

struct Sprite {
    x_size: f64,
    y_size: f64,
    color: [f64, ..4]
}

static COMPONENT_COUNT: uint = 4;
struct Components {
    position: Option<@mut Position>,
    horiz_velocity: Option<@mut HorizVelocity>,
    vert_velocity: Option<@mut VertVelocity>,
    sprite: Option<@mut Sprite>
}

trait System {
    fn process(&self, entity: @Components) -> ();
}

// We need to figure out how to integrate World with the main game loop
// in Artemis world has a `setDelta` method for timestep
struct World {
    entities: ~[@Components],
    systems: ~[@System]
}

impl World {
    fn new() -> World {
        return World {entities: ~[], systems: ~[]};
    }

    fn process(&self) {
        for system in self.systems.iter() {
            for entity in self.entities.iter() {
                system.process(*entity);
            }
        }
    }
}

struct MovementSystem;

impl System for MovementSystem {
    fn process(&self, entity: @Components) -> () {
        match entity.position {
            Some(pos) => {
                match entity.vert_velocity {
                    Some(v) => pos.y += v.y,
                    None => ()
                }
                match entity.horiz_velocity {
                    Some(v) => pos.x += v.x,
                    None => ()
                }
            },
            None => ()
        }
    }
}

struct EdgeCollisionSystem;

impl System for EdgeCollisionSystem {
    fn process(&self, entity: @Components) -> () {
        match (entity.position, entity.vert_velocity) {
            (Some(pos), Some(vel)) => {
                if pos.y >= 1.0 || pos.y <= 0.0 {
                    vel.y *= -1.0;
                }
            },
            (_, _) => ()
        }
    }
}

struct PaddleCollisionSystem {
    left_paddle: @Components,
    right_paddle: @Components,
    paddle_width: f64,
    paddle_height: f64
}

impl System for PaddleCollisionSystem {
    fn process(&self, entity: @Components) -> () {
        match (entity.position, entity.horiz_velocity) {
            (Some(pos), Some(vel)) => {
                if pos.x >= (4.0-self.paddle_width) {
                    if std::num::abs(pos.y - self.right_paddle.position.unwrap().y) < (self.paddle_height/2.0) {
                        vel.x *= -1.0;
                    }
                }
                if pos.x <= self.paddle_width {
                    if std::num::abs(pos.y - self.left_paddle.position.unwrap().y) < (self.paddle_height/2.0) {
                        vel.x *= -1.0;
                    }
                }
            },
            (_, _) => ()
        }
    }
}

struct RenderSystem {
    program: GLuint,
    position_uniform: GLint,
    scale_uniform: GLint,
    color_uniform: GLint
}

impl System for RenderSystem {
    fn process(&self, entity: @Components) -> () {
        match (entity.position, entity.sprite) {
            (Some(pos), Some(sprite)) => {
                // Set uniforms
                gl::ProgramUniform2f(self.program, self.position_uniform, pos.x as f32, pos.y as f32);
                gl::ProgramUniform2f(self.program, self.scale_uniform, sprite.x_size as f32, sprite.y_size as f32);
                gl::ProgramUniform4f(self.program, self.color_uniform, sprite.color[0] as f32, sprite.color[1] as f32, sprite.color[2] as f32, sprite.color[3] as f32);
                // Draw a rect from the 4 vertices
                gl::DrawArrays(gl::TRIANGLE_STRIP, 0, 4);
            },
            (_, _) => ()
        }
    }
}

enum PaddleSide {
    RIGHT,
    LEFT
}

fn new_ball() -> @Components {
    let components = @Components { position: Some(@mut Position { x: 2.0, y: 1.5 }), horiz_velocity: Some(@mut HorizVelocity { x: 1.0/60.0 }), vert_velocity: Some(@mut VertVelocity { y: 0.0 }), sprite: Some(@mut Sprite {x_size: 0.05, y_size: 0.05, color: [0.3, 0.3, 0.8, 1.0]})};
    return components;
}

fn new_paddle(side: PaddleSide) -> @Components {
    let xpos = match side {
        RIGHT => 3.9,
        LEFT => 0.1
    };
    let components = @Components { position: Some(@mut Position { x: xpos, y: 1.5 }), horiz_velocity: None, vert_velocity: Some(@mut VertVelocity { y: 0.0 }), sprite: Some(@mut Sprite {x_size: 0.1, y_size: 0.4, color: [xpos/4.0, 1.0-(xpos/4.0), 0.3, 1.0]})};
    return components;
}

// Vertex data
static VERTEX_DATA: [GLfloat, ..8] = [
    -1.0,  1.0,
    -1.0, -1.0,
     1.0,  1.0,
     1.0, -1.0
];

#[start]
fn start(argc: int, argv: **u8) -> int {
    std::rt::start_on_main_thread(argc, argv, main)
}

fn compile_shader(src: &str, ty: GLenum) -> GLuint {
    let shader = gl::CreateShader(ty);
    unsafe {
        // Attempt to compile the shader
        src.with_c_str(|ptr| gl::ShaderSource(shader, 1, &ptr, ptr::null()));
        gl::CompileShader(shader);

        // Get the compile status
        let mut status = gl::FALSE as GLint;
        gl::GetShaderiv(shader, gl::COMPILE_STATUS, &mut status);

        // Fail on error
        if status != (gl::TRUE as GLint) {
            let mut len = 0;
            gl::GetShaderiv(shader, gl::INFO_LOG_LENGTH, &mut len);
            let mut buf = vec::from_elem(len as uint - 1, 0u8);     // subtract 1 to skip the trailing null character
            gl::GetShaderInfoLog(shader, len, ptr::mut_null(), vec::raw::to_mut_ptr(buf) as *mut GLchar);
            fail!(str::raw::from_utf8(buf));
        }
    }
    shader
}

fn link_program(vs: GLuint, fs: GLuint, out_color: &str) -> GLuint {
    let program = gl::CreateProgram();
    gl::AttachShader(program, vs);
    gl::AttachShader(program, fs);
    // gl::BindFragDataLocation needs to be called before linking to have effect
    unsafe {
        out_color.with_c_str(|ptr| gl::BindFragDataLocation(program, 0, ptr));
    }
    gl::LinkProgram(program);
    unsafe {
        // Get the link status
        let mut status = gl::FALSE as GLint;
        gl::GetProgramiv(program, gl::LINK_STATUS, &mut status);

        // Fail on error
        if status != (gl::TRUE as GLint) {
            let mut len: GLint = 0;
            gl::GetProgramiv(program, gl::INFO_LOG_LENGTH, &mut len);
            let mut buf = vec::from_elem(len as uint - 1, 0u8);     // subtract 1 to skip the trailing null character
            gl::GetProgramInfoLog(program, len, ptr::mut_null(), vec::raw::to_mut_ptr(buf) as *mut GLchar);
            fail!(str::raw::from_utf8(buf));
        }
    }
    program
}

fn main() {
    do glfw::set_error_callback |_, description| {
        println!("GLFW Error: {}", description);
    }

    do glfw::start {
        // initialize game world
        let left_paddle: @Components = new_paddle(LEFT);
        let right_paddle: @Components = new_paddle(RIGHT);
        let ball: @Components = new_ball();
        let ms: @System = @MovementSystem as @System;
        let es: @System = @EdgeCollisionSystem as @System;
        let ps: @System = @PaddleCollisionSystem{ right_paddle: right_paddle, left_paddle: left_paddle, paddle_height: 0.4, paddle_width: 0.1} as @System;

        let mut world: World = World::new();
        world.entities.push(left_paddle);
        world.entities.push(right_paddle);
        world.entities.push(ball);
        world.systems.push(ms);
        world.systems.push(es);
        world.systems.push(ps);

        // Choose a GL profile that is compatible with OS X 10.7+
        glfw::window_hint::context_version(3, 2);
        glfw::window_hint::opengl_profile(glfw::OpenGlCoreProfile);
        glfw::window_hint::opengl_forward_compat(true);

        let window = glfw::Window::create(1024, 600, "Pong", glfw::Windowed).unwrap();
        window.set_key_callback(key_callback);
        window.make_context_current();

        // Load the OpenGL function pointers
        gl::load_with(glfw::get_proc_address);

        // Create GLSL shaders
        let vs_src = io::read_whole_file_str(&PosixPath("main.vs.glsl")).unwrap();
        let vs = compile_shader(vs_src, gl::VERTEX_SHADER);
        let fs_src = io::read_whole_file_str(&PosixPath("main.fs.glsl")).unwrap();
        let fs = compile_shader(fs_src, gl::FRAGMENT_SHADER);
        let program = link_program(vs, fs, "out_color");

        let mut vao = 0;
        let mut vbo = 0;

        let position_uniform: GLint;
        let scale_uniform: GLint;
        let color_uniform: GLint;

        unsafe {
            position_uniform = "position".with_c_str(|ptr| gl::GetUniformLocation(program, ptr));
            scale_uniform = "scale".with_c_str(|ptr| gl::GetUniformLocation(program, ptr));
            color_uniform = "color".with_c_str(|ptr| gl::GetUniformLocation(program, ptr));
            // Create Vertex Array Object
            gl::GenVertexArrays(1, &mut vao);
            gl::BindVertexArray(vao);

            // Create a Vertex Buffer Object and copy the vertex data to it
            gl::GenBuffers(1, &mut vbo);
            gl::BindBuffer(gl::ARRAY_BUFFER, vbo);
            gl::BufferData(gl::ARRAY_BUFFER,
                           (VERTEX_DATA.len() * sys::size_of::<GLfloat>()) as GLsizeiptr,
                           cast::transmute(&VERTEX_DATA[0]),
                           gl::STATIC_DRAW);

            // Use shader program
            gl::UseProgram(program);

            // Specify the layout of the vertex data
            let vert_attr = "vertex".with_c_str(|ptr| gl::GetAttribLocation(program, ptr));
            gl::EnableVertexAttribArray(vert_attr as GLuint);
            gl::VertexAttribPointer(vert_attr as GLuint, 2, gl::FLOAT,
                                    gl::FALSE as GLboolean, 0, ptr::null());
        }

        let rs: @System = @RenderSystem {program: program, position_uniform: position_uniform, scale_uniform: scale_uniform, color_uniform: color_uniform} as @System;
        world.systems.push(rs);

        while !window.should_close() {
            // Poll events
            glfw::poll_events();

            // Clear the screen to black
            gl::ClearColor(0.8, 0.8, 0.8, 1.0);
            gl::Clear(gl::COLOR_BUFFER_BIT);

            // process game world
            world.process();

            // Swap buffers
            window.swap_buffers();
        }

        // Cleanup
        gl::DeleteProgram(program);
        gl::DeleteShader(fs);
        gl::DeleteShader(vs);
        unsafe {
            gl::DeleteBuffers(1, &vbo);
            gl::DeleteVertexArrays(1, &vao);
        }
    }
}

fn key_callback(window: &glfw::Window, key: glfw::Key, _: libc::c_int, action: glfw::Action, _: glfw::Modifiers) {
    if action == glfw::Press && key == glfw::KeyEscape {
        window.set_should_close(true);
    }
}
<|endoftext|>


<commit_before>// Copyright 2013 Torste Aikio and gl-rs developers.

// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#[feature(globs)];
#[feature(macro_rules)];

extern mod glfw;
extern mod gl;

use std::libc;

use std::cast;
use std::ptr;
use std::str;
use std::sys;
use std::vec;

use std::io;
use std::path::PosixPath;

use gl::types::*;

// COMPONENT DEFINITIONS
struct Position {
    x: f64,
    y: f64
}

struct HorizVelocity {
    x: f64
}

struct VertVelocity {
    y: f64
}

struct Sprite {
    x_size: f64,
    y_size: f64,
    color: [f64, ..4]
}

struct Components {
    position: Option<@mut Position>,
    horiz_velocity: Option<@mut HorizVelocity>,
    vert_velocity: Option<@mut VertVelocity>,
    sprite: Option<@mut Sprite>
}

// SYSTEM DEFINITIONS
trait System {
    fn process(&self, entity: @Components) -> ();
}

struct MovementSystem;

impl System for MovementSystem {
    fn process(&self, entity: @Components) -> () {
        match entity.position {
            Some(pos) => {
                match entity.vert_velocity {
                    Some(v) => pos.y += v.y,
                    None => ()
                }
                match entity.horiz_velocity {
                    Some(v) => pos.x += v.x,
                    None => ()
                }
            },
            None => ()
        }
    }
}

struct EdgeCollisionSystem;

impl System for EdgeCollisionSystem {
    fn process(&self, entity: @Components) -> () {
        match (entity.position, entity.vert_velocity) {
            (Some(pos), Some(vel)) => {
                if pos.y >= 1.0 || pos.y <= 0.0 {
                    vel.y *= -1.0;
                }
            },
            (_, _) => ()
        }
    }
}

struct PaddleCollisionSystem {
    left_paddle: @Components,
    right_paddle: @Components,
    paddle_width: f64,
    paddle_height: f64
}

impl System for PaddleCollisionSystem {
    fn process(&self, entity: @Components) -> () {
        match (entity.position, entity.horiz_velocity) {
            (Some(pos), Some(vel)) => {
                if pos.x >= (4.0-self.paddle_width) {
                    if std::num::abs(pos.y - self.right_paddle.position.unwrap().y) < (self.paddle_height/2.0) {
                        vel.x *= -1.0;
                    }
                }
                if pos.x <= self.paddle_width {
                    if std::num::abs(pos.y - self.left_paddle.position.unwrap().y) < (self.paddle_height/2.0) {
                        vel.x *= -1.0;
                    }
                }
            },
            (_, _) => ()
        }
    }
}

struct RenderSystem {
    program: GLuint,
    position_uniform: GLint,
    scale_uniform: GLint,
    color_uniform: GLint
}

impl System for RenderSystem {
    fn process(&self, entity: @Components) -> () {
        match (entity.position, entity.sprite) {
            (Some(pos), Some(sprite)) => {
                // Set uniforms
                gl::ProgramUniform2f(self.program, self.position_uniform, pos.x as f32, pos.y as f32);
                gl::ProgramUniform2f(self.program, self.scale_uniform, sprite.x_size as f32, sprite.y_size as f32);
                gl::ProgramUniform4f(self.program, self.color_uniform, sprite.color[0] as f32, sprite.color[1] as f32, sprite.color[2] as f32, sprite.color[3] as f32);
                // Draw a rect from the 4 vertices
                gl::DrawArrays(gl::TRIANGLE_STRIP, 0, 4);
            },
            (_, _) => ()
        }
    }
}

// WORLD DEFINITION
// We need to figure out how to integrate World with the main game loop
// in Artemis world has a `setDelta` method for timestep
struct World {
    entities: ~[@Components],
    systems: ~[@System]
}

impl World {
    fn new() -> World {
        return World {entities: ~[], systems: ~[]};
    }

    fn process(&self) {
        for system in self.systems.iter() {
            for entity in self.entities.iter() {
                system.process(*entity);
            }
        }
    }
}

//ENTITY CONSTRUCTORS
enum PaddleSide {
    RIGHT,
    LEFT
}

fn new_ball() -> @Components {
    @Components {
        position: Some(@mut Position { x: 2.0, y: 1.5 }),
        horiz_velocity: Some(@mut HorizVelocity { x: 1.0/60.0 }),
        vert_velocity: Some(@mut VertVelocity { y: 0.0 }),
        sprite: Some(@mut Sprite {
            x_size: 0.05,
            y_size: 0.05,
            color: [0.3, 0.3, 0.8, 1.0]
        })
    }
}

fn new_paddle(side: PaddleSide) -> @Components {
    let xpos = match side {
        RIGHT => 3.9,
        LEFT => 0.1
    };
    @Components {
        position: Some(@mut Position { x: xpos, y: 1.5 }),
        horiz_velocity: None,
        vert_velocity: Some(@mut VertVelocity { y: 0.0 }),
        sprite: Some(@mut Sprite {
            x_size: 0.1,
            y_size: 0.4,
            color: [xpos/4.0, 1.0-(xpos/4.0), 0.3, 1.0]
        })
    }
}

// OPENGL ETC STUFF

// Vertex data
static VERTEX_DATA: [GLfloat, ..8] = [
    -1.0,  1.0,
    -1.0, -1.0,
     1.0,  1.0,
     1.0, -1.0
];

#[start]
fn start(argc: int, argv: **u8) -> int {
    std::rt::start_on_main_thread(argc, argv, main)
}

fn compile_shader(src: &str, ty: GLenum) -> GLuint {
    let shader = gl::CreateShader(ty);
    unsafe {
        // Attempt to compile the shader
        src.with_c_str(|ptr| gl::ShaderSource(shader, 1, &ptr, ptr::null()));
        gl::CompileShader(shader);

        // Get the compile status
        let mut status = gl::FALSE as GLint;
        gl::GetShaderiv(shader, gl::COMPILE_STATUS, &mut status);

        // Fail on error
        if status != (gl::TRUE as GLint) {
            let mut len = 0;
            gl::GetShaderiv(shader, gl::INFO_LOG_LENGTH, &mut len);
            let mut buf = vec::from_elem(len as uint - 1, 0u8);     // subtract 1 to skip the trailing null character
            gl::GetShaderInfoLog(shader, len, ptr::mut_null(), vec::raw::to_mut_ptr(buf) as *mut GLchar);
            fail!(str::raw::from_utf8(buf));
        }
    }
    shader
}

fn link_program(vs: GLuint, fs: GLuint, out_color: &str) -> GLuint {
    let program = gl::CreateProgram();
    gl::AttachShader(program, vs);
    gl::AttachShader(program, fs);
    // gl::BindFragDataLocation needs to be called before linking to have effect
    unsafe {
        out_color.with_c_str(|ptr| gl::BindFragDataLocation(program, 0, ptr));
    }
    gl::LinkProgram(program);
    unsafe {
        // Get the link status
        let mut status = gl::FALSE as GLint;
        gl::GetProgramiv(program, gl::LINK_STATUS, &mut status);

        // Fail on error
        if status != (gl::TRUE as GLint) {
            let mut len: GLint = 0;
            gl::GetProgramiv(program, gl::INFO_LOG_LENGTH, &mut len);
            let mut buf = vec::from_elem(len as uint - 1, 0u8);     // subtract 1 to skip the trailing null character
            gl::GetProgramInfoLog(program, len, ptr::mut_null(), vec::raw::to_mut_ptr(buf) as *mut GLchar);
            fail!(str::raw::from_utf8(buf));
        }
    }
    program
}

fn main() {
    do glfw::set_error_callback |_, description| {
        println!("GLFW Error: {}", description);
    }

    do glfw::start {
        // initialize game world
        let left_paddle: @Components = new_paddle(LEFT);
        let right_paddle: @Components = new_paddle(RIGHT);
        let ball: @Components = new_ball();
        let ms: @System = @MovementSystem as @System;
        let es: @System = @EdgeCollisionSystem as @System;
        let ps: @System = @PaddleCollisionSystem{ right_paddle: right_paddle, left_paddle: left_paddle, paddle_height: 0.4, paddle_width: 0.1} as @System;

        let mut world: World = World::new();
        world.entities.push(left_paddle);
        world.entities.push(right_paddle);
        world.entities.push(ball);
        world.systems.push(ms);
        world.systems.push(es);
        world.systems.push(ps);

        // Choose a GL profile that is compatible with OS X 10.7+
        glfw::window_hint::context_version(3, 2);
        glfw::window_hint::opengl_profile(glfw::OpenGlCoreProfile);
        glfw::window_hint::opengl_forward_compat(true);

        let window = glfw::Window::create(1024, 600, "Pong", glfw::Windowed).unwrap();
        window.set_key_callback(key_callback);
        window.make_context_current();

        // Load the OpenGL function pointers
        gl::load_with(glfw::get_proc_address);

        // Create GLSL shaders
        let vs_src = io::read_whole_file_str(&PosixPath("main.vs.glsl")).unwrap();
        let vs = compile_shader(vs_src, gl::VERTEX_SHADER);
        let fs_src = io::read_whole_file_str(&PosixPath("main.fs.glsl")).unwrap();
        let fs = compile_shader(fs_src, gl::FRAGMENT_SHADER);
        let program = link_program(vs, fs, "out_color");

        let mut vao = 0;
        let mut vbo = 0;

        let position_uniform: GLint;
        let scale_uniform: GLint;
        let color_uniform: GLint;

        unsafe {
            position_uniform = "position".with_c_str(|ptr| gl::GetUniformLocation(program, ptr));
            scale_uniform = "scale".with_c_str(|ptr| gl::GetUniformLocation(program, ptr));
            color_uniform = "color".with_c_str(|ptr| gl::GetUniformLocation(program, ptr));
            // Create Vertex Array Object
            gl::GenVertexArrays(1, &mut vao);
            gl::BindVertexArray(vao);

            // Create a Vertex Buffer Object and copy the vertex data to it
            gl::GenBuffers(1, &mut vbo);
            gl::BindBuffer(gl::ARRAY_BUFFER, vbo);
            gl::BufferData(gl::ARRAY_BUFFER,
                           (VERTEX_DATA.len() * sys::size_of::<GLfloat>()) as GLsizeiptr,
                           cast::transmute(&VERTEX_DATA[0]),
                           gl::STATIC_DRAW);

            // Use shader program
            gl::UseProgram(program);

            // Specify the layout of the vertex data
            let vert_attr = "vertex".with_c_str(|ptr| gl::GetAttribLocation(program, ptr));
            gl::EnableVertexAttribArray(vert_attr as GLuint);
            gl::VertexAttribPointer(vert_attr as GLuint, 2, gl::FLOAT,
                                    gl::FALSE as GLboolean, 0, ptr::null());
        }

        let rs: @System = @RenderSystem {program: program, position_uniform: position_uniform, scale_uniform: scale_uniform, color_uniform: color_uniform} as @System;
        world.systems.push(rs);

        while !window.should_close() {
            // Poll events
            glfw::poll_events();

            // Clear the screen to black
            gl::ClearColor(0.8, 0.8, 0.8, 1.0);
            gl::Clear(gl::COLOR_BUFFER_BIT);

            // process game world
            world.process();

            // Swap buffers
            window.swap_buffers();
        }

        // Cleanup
        gl::DeleteProgram(program);
        gl::DeleteShader(fs);
        gl::DeleteShader(vs);
        unsafe {
            gl::DeleteBuffers(1, &vbo);
            gl::DeleteVertexArrays(1, &vao);
        }
    }
}

fn key_callback(window: &glfw::Window, key: glfw::Key, _: libc::c_int, action: glfw::Action, _: glfw::Modifiers) {
    if action == glfw::Press && key == glfw::KeyEscape {
        window.set_should_close(true);
    }
}
<commit_msg>set the upper wall of game area in the correct place<commit_after>// Copyright 2013 Torste Aikio and gl-rs developers.

// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#[feature(globs)];
#[feature(macro_rules)];

extern mod glfw;
extern mod gl;

use std::libc;

use std::cast;
use std::ptr;
use std::str;
use std::sys;
use std::vec;

use std::io;
use std::path::PosixPath;

use gl::types::*;

// COMPONENT DEFINITIONS
struct Position {
    x: f64,
    y: f64
}

struct HorizVelocity {
    x: f64
}

struct VertVelocity {
    y: f64
}

struct Sprite {
    x_size: f64,
    y_size: f64,
    color: [f64, ..4]
}

struct Components {
    position: Option<@mut Position>,
    horiz_velocity: Option<@mut HorizVelocity>,
    vert_velocity: Option<@mut VertVelocity>,
    sprite: Option<@mut Sprite>
}

// SYSTEM DEFINITIONS
trait System {
    fn process(&self, entity: @Components) -> ();
}

struct MovementSystem;

impl System for MovementSystem {
    fn process(&self, entity: @Components) -> () {
        match entity.position {
            Some(pos) => {
                match entity.vert_velocity {
                    Some(v) => pos.y += v.y,
                    None => ()
                }
                match entity.horiz_velocity {
                    Some(v) => pos.x += v.x,
                    None => ()
                }
            },
            None => ()
        }
    }
}

struct EdgeCollisionSystem;

impl System for EdgeCollisionSystem {
    fn process(&self, entity: @Components) -> () {
        match (entity.position, entity.vert_velocity) {
            (Some(pos), Some(vel)) => {
                if pos.y >= 4.0 || pos.y <= 0.0 {
                    vel.y *= -1.0;
                }
            },
            (_, _) => ()
        }
    }
}

struct PaddleCollisionSystem {
    left_paddle: @Components,
    right_paddle: @Components,
    paddle_width: f64,
    paddle_height: f64
}

impl System for PaddleCollisionSystem {
    fn process(&self, entity: @Components) -> () {
        match (entity.position, entity.horiz_velocity) {
            (Some(pos), Some(vel)) => {
                if pos.x >= (4.0-self.paddle_width) {
                    if std::num::abs(pos.y - self.right_paddle.position.unwrap().y) < (self.paddle_height/2.0) {
                        vel.x *= -1.0;
                    }
                }
                if pos.x <= self.paddle_width {
                    if std::num::abs(pos.y - self.left_paddle.position.unwrap().y) < (self.paddle_height/2.0) {
                        vel.x *= -1.0;
                    }
                }
            },
            (_, _) => ()
        }
    }
}

struct RenderSystem {
    program: GLuint,
    position_uniform: GLint,
    scale_uniform: GLint,
    color_uniform: GLint
}

impl System for RenderSystem {
    fn process(&self, entity: @Components) -> () {
        match (entity.position, entity.sprite) {
            (Some(pos), Some(sprite)) => {
                // Set uniforms
                gl::ProgramUniform2f(self.program, self.position_uniform, pos.x as f32, pos.y as f32);
                gl::ProgramUniform2f(self.program, self.scale_uniform, sprite.x_size as f32, sprite.y_size as f32);
                gl::ProgramUniform4f(self.program, self.color_uniform, sprite.color[0] as f32, sprite.color[1] as f32, sprite.color[2] as f32, sprite.color[3] as f32);
                // Draw a rect from the 4 vertices
                gl::DrawArrays(gl::TRIANGLE_STRIP, 0, 4);
            },
            (_, _) => ()
        }
    }
}

// WORLD DEFINITION
// We need to figure out how to integrate World with the main game loop
// in Artemis world has a `setDelta` method for timestep
struct World {
    entities: ~[@Components],
    systems: ~[@System]
}

impl World {
    fn new() -> World {
        return World {entities: ~[], systems: ~[]};
    }

    fn process(&self) {
        for system in self.systems.iter() {
            for entity in self.entities.iter() {
                system.process(*entity);
            }
        }
    }
}

//ENTITY CONSTRUCTORS
enum PaddleSide {
    RIGHT,
    LEFT
}

fn new_ball() -> @Components {
    @Components {
        position: Some(@mut Position { x: 2.0, y: 1.5 }),
        horiz_velocity: Some(@mut HorizVelocity { x: 1.0/60.0 }),
        vert_velocity: Some(@mut VertVelocity { y: 0.0 }),
        sprite: Some(@mut Sprite {
            x_size: 0.05,
            y_size: 0.05,
            color: [0.3, 0.3, 0.8, 1.0]
        })
    }
}

fn new_paddle(side: PaddleSide) -> @Components {
    let xpos = match side {
        RIGHT => 3.9,
        LEFT => 0.1
    };
    @Components {
        position: Some(@mut Position { x: xpos, y: 1.5 }),
        horiz_velocity: None,
        vert_velocity: Some(@mut VertVelocity { y: 0.0 }),
        sprite: Some(@mut Sprite {
            x_size: 0.1,
            y_size: 0.4,
            color: [xpos/4.0, 1.0-(xpos/4.0), 0.3, 1.0]
        })
    }
}

// OPENGL ETC STUFF

// Vertex data
static VERTEX_DATA: [GLfloat, ..8] = [
    -1.0,  1.0,
    -1.0, -1.0,
     1.0,  1.0,
     1.0, -1.0
];

#[start]
fn start(argc: int, argv: **u8) -> int {
    std::rt::start_on_main_thread(argc, argv, main)
}

fn compile_shader(src: &str, ty: GLenum) -> GLuint {
    let shader = gl::CreateShader(ty);
    unsafe {
        // Attempt to compile the shader
        src.with_c_str(|ptr| gl::ShaderSource(shader, 1, &ptr, ptr::null()));
        gl::CompileShader(shader);

        // Get the compile status
        let mut status = gl::FALSE as GLint;
        gl::GetShaderiv(shader, gl::COMPILE_STATUS, &mut status);

        // Fail on error
        if status != (gl::TRUE as GLint) {
            let mut len = 0;
            gl::GetShaderiv(shader, gl::INFO_LOG_LENGTH, &mut len);
            let mut buf = vec::from_elem(len as uint - 1, 0u8);     // subtract 1 to skip the trailing null character
            gl::GetShaderInfoLog(shader, len, ptr::mut_null(), vec::raw::to_mut_ptr(buf) as *mut GLchar);
            fail!(str::raw::from_utf8(buf));
        }
    }
    shader
}

fn link_program(vs: GLuint, fs: GLuint, out_color: &str) -> GLuint {
    let program = gl::CreateProgram();
    gl::AttachShader(program, vs);
    gl::AttachShader(program, fs);
    // gl::BindFragDataLocation needs to be called before linking to have effect
    unsafe {
        out_color.with_c_str(|ptr| gl::BindFragDataLocation(program, 0, ptr));
    }
    gl::LinkProgram(program);
    unsafe {
        // Get the link status
        let mut status = gl::FALSE as GLint;
        gl::GetProgramiv(program, gl::LINK_STATUS, &mut status);

        // Fail on error
        if status != (gl::TRUE as GLint) {
            let mut len: GLint = 0;
            gl::GetProgramiv(program, gl::INFO_LOG_LENGTH, &mut len);
            let mut buf = vec::from_elem(len as uint - 1, 0u8);     // subtract 1 to skip the trailing null character
            gl::GetProgramInfoLog(program, len, ptr::mut_null(), vec::raw::to_mut_ptr(buf) as *mut GLchar);
            fail!(str::raw::from_utf8(buf));
        }
    }
    program
}

fn main() {
    do glfw::set_error_callback |_, description| {
        println!("GLFW Error: {}", description);
    }

    do glfw::start {
        // initialize game world
        let left_paddle: @Components = new_paddle(LEFT);
        let right_paddle: @Components = new_paddle(RIGHT);
        let ball: @Components = new_ball();
        let ms: @System = @MovementSystem as @System;
        let es: @System = @EdgeCollisionSystem as @System;
        let ps: @System = @PaddleCollisionSystem{ right_paddle: right_paddle, left_paddle: left_paddle, paddle_height: 0.4, paddle_width: 0.1} as @System;

        let mut world: World = World::new();
        world.entities.push(left_paddle);
        world.entities.push(right_paddle);
        world.entities.push(ball);
        world.systems.push(ms);
        world.systems.push(es);
        world.systems.push(ps);

        // Choose a GL profile that is compatible with OS X 10.7+
        glfw::window_hint::context_version(3, 2);
        glfw::window_hint::opengl_profile(glfw::OpenGlCoreProfile);
        glfw::window_hint::opengl_forward_compat(true);

        let window = glfw::Window::create(1024, 600, "Pong", glfw::Windowed).unwrap();
        window.set_key_callback(key_callback);
        window.make_context_current();

        // Load the OpenGL function pointers
        gl::load_with(glfw::get_proc_address);

        // Create GLSL shaders
        let vs_src = io::read_whole_file_str(&PosixPath("main.vs.glsl")).unwrap();
        let vs = compile_shader(vs_src, gl::VERTEX_SHADER);
        let fs_src = io::read_whole_file_str(&PosixPath("main.fs.glsl")).unwrap();
        let fs = compile_shader(fs_src, gl::FRAGMENT_SHADER);
        let program = link_program(vs, fs, "out_color");

        let mut vao = 0;
        let mut vbo = 0;

        let position_uniform: GLint;
        let scale_uniform: GLint;
        let color_uniform: GLint;

        unsafe {
            position_uniform = "position".with_c_str(|ptr| gl::GetUniformLocation(program, ptr));
            scale_uniform = "scale".with_c_str(|ptr| gl::GetUniformLocation(program, ptr));
            color_uniform = "color".with_c_str(|ptr| gl::GetUniformLocation(program, ptr));
            // Create Vertex Array Object
            gl::GenVertexArrays(1, &mut vao);
            gl::BindVertexArray(vao);

            // Create a Vertex Buffer Object and copy the vertex data to it
            gl::GenBuffers(1, &mut vbo);
            gl::BindBuffer(gl::ARRAY_BUFFER, vbo);
            gl::BufferData(gl::ARRAY_BUFFER,
                           (VERTEX_DATA.len() * sys::size_of::<GLfloat>()) as GLsizeiptr,
                           cast::transmute(&VERTEX_DATA[0]),
                           gl::STATIC_DRAW);

            // Use shader program
            gl::UseProgram(program);

            // Specify the layout of the vertex data
            let vert_attr = "vertex".with_c_str(|ptr| gl::GetAttribLocation(program, ptr));
            gl::EnableVertexAttribArray(vert_attr as GLuint);
            gl::VertexAttribPointer(vert_attr as GLuint, 2, gl::FLOAT,
                                    gl::FALSE as GLboolean, 0, ptr::null());
        }

        let rs: @System = @RenderSystem {program: program, position_uniform: position_uniform, scale_uniform: scale_uniform, color_uniform: color_uniform} as @System;
        world.systems.push(rs);

        while !window.should_close() {
            // Poll events
            glfw::poll_events();

            // Clear the screen to black
            gl::ClearColor(0.8, 0.8, 0.8, 1.0);
            gl::Clear(gl::COLOR_BUFFER_BIT);

            // process game world
            world.process();

            // Swap buffers
            window.swap_buffers();
        }

        // Cleanup
        gl::DeleteProgram(program);
        gl::DeleteShader(fs);
        gl::DeleteShader(vs);
        unsafe {
            gl::DeleteBuffers(1, &vbo);
            gl::DeleteVertexArrays(1, &vao);
        }
    }
}

fn key_callback(window: &glfw::Window, key: glfw::Key, _: libc::c_int, action: glfw::Action, _: glfw::Modifiers) {
    if action == glfw::Press && key == glfw::KeyEscape {
        window.set_should_close(true);
    }
}
<|endoftext|>


<commit_before>extern crate rand;

use super::std;
use super::std::time::{Duration, Instant};

use super::bus::Bus;
use super::io::{Audio, Display, Input, Pixel};
use super::io::{SCREEN_WIDTH, SCREEN_HEIGHT};
use super::memory::Memory;

// Constants
///////////////////////////////////////////////////////////////////////

const FRAME_RATE:u64 = 17;

// Processor
////////////////////////////////////////////////////////////////////////

#[derive(Clone, Copy)]
enum Key {
    Up,
    Down(Instant),
}

pub struct Processor {
    oc:u16, // Operational Code
    pc:u16, // Program Counter
    sp:u16, // Stack Pointer
    index:u16, // Index

    delay_timer:u8,
    sound_timer:u8,

    draw_flag:bool,

    v:[u8;0x10],
    stack:[u16;0x10],
    screen:[bool;SCREEN_WIDTH*SCREEN_HEIGHT],
    keys:[Key;0x10],
}

impl Default for Processor {
    fn default() -> Processor {
        Processor{
            oc:0x0,
            pc:0x200,
            sp:0x0,
            index:0x0,

            delay_timer:0x0,
            sound_timer:0x0,

            draw_flag:false,

            v:[0x0;0x10],
            stack:[0x0;0x10],
            screen:[false;SCREEN_WIDTH*SCREEN_HEIGHT],
            keys:[Key::Up;0x10],
        }
    }
}

impl Processor {

    // non-self functions

    fn read_address(pointer:u16, memory:&Memory) -> u16 {
        let top = (memory.read_memory(pointer) as u16) << 0x8;
        let bot = (memory.read_memory(pointer + 0x1) & 0xFF) as u16;
        top | bot
    }

    // &self functions

    // &mut self functions

    fn load_opcode(&mut self, memory:&Memory){
        self.oc = Processor::read_address(self.pc, memory);
    }

    fn run_opcode<A, D, I>(&mut self, bus:&mut Bus<A, D, I>)
            where
                A: Audio,
                D: Display,
                I: Input {
        let b1 = (self.oc & 0xF000) >> 0xC;
        let b2 = ((self.oc & 0x0F00) >> 0x8) as usize;
        let b3 = ((self.oc & 0x00F0) >> 0x4) as usize;
        let b4 = self.oc & 0x000F;

        match (b1, b2, b3, b4) {
            (0x0,0x0,0xE,0x0) => { // clear the screen
                self.screen[..]
                        .copy_from_slice(&[false;SCREEN_WIDTH*SCREEN_HEIGHT]);
                self.pc += 2;
            },
            (0x0,0x0,0xE,0xE) => { // return from subroutine
                self.sp -= 1;
                self.pc = self.stack[self.sp as usize];
                self.pc += 2;
            },
            (0x0,_,_,_) => { // call RCA 1802 program at address NNN
                /* TODO */
                panic!("TODO -- 0x0NNN");
            },
            (0x1,_,_,_) => { // jump to address NNN
                self.pc = self.oc & 0x0FFF;
            },
            (0x2,_,_,_) => { // call subroutine at NNN
                self.stack[self.sp as usize] = self.pc;
                self.sp += 1;
                self.pc = self.oc & 0x0FFF;
            },
            (0x3,x,_,_) => { // skip next instruction if VX == NN
                self.pc += if self.v[x] as u16 == (self.oc & 0x00FF) {
                    4
                } else {
                    2
                };
            },
            (0x4,x,_,_) => { // skip next instruction if VX != NN
                self.pc += if self.v[x] as u16 != (self.oc & 0x00FF) {
                    4
                } else {
                    2
                };
            },
            (0x5,x,y,0x0) => { // skip next instruction if VX == VY
                self.pc += if self.v[x] == self.v[y] {
                    4
                } else {
                    2
                };
            },
            (0x6,x,_,_) => { // set VX to NN
                self.v[x] = (self.oc & 0x00FF) as u8;
                self.pc += 2;
            },
            (0x7,x,_,_) => { // add NN to VX
                self.v[x] = self.v[x].wrapping_add((self.oc & 0x00FF) as u8);
                self.pc += 2;
            },
            (0x8,x,y,0x0) => { // set VX to VY
                self.v[x] = self.v[y];
                self.pc += 2;
            },
            (0x8,x,y,0x1) => { // or VX with VY
                self.v[x] |= self.v[y];
                self.pc += 2;
            },
            (0x8,x,y,0x2) => { // and VX with VY
                self.v[x] &= self.v[y];
                self.pc += 2;
            },
            (0x8,x,y,0x3) => { // xor VX with VY
                self.v[x] ^= self.v[y];
                self.pc += 2;
            },
            (0x8,x,y,0x4) => { // add VY to VX. VF = if carry {1} else {0}
                let (value, flag) = self.v[x].overflowing_add(self.v[y]);
                self.v[x] = value;
                self.v[0xF] = if flag {1} else {0};
                self.pc += 2;
            },
            (0x8,x,y,0x5) => { // sub VY from VX. VF = if borrow {0} else {1}
                let (value, flag) = self.v[x].overflowing_sub(self.v[y]);
                self.v[x] = value;
                self.v[0xF] = if flag {0} else {1};
                self.pc += 2;
            },
            (0x8,x,_,0x6) => { // shift VX right. VF set to dropped bit.
                let bit = self.v[x] & 0x1;
                self.v[x] >>= 1;
                self.v[0xF] = bit;
                self.pc += 2;
            },
            (0x8,x,y,0x7) => { // set VX to VY - VX. if borrow {0} else {1}
                let (value, flag) =  self.v[y].overflowing_sub(self.v[x]);
                self.v[x] = value;
                self.v[0xF] = if flag {0} else {1};
                self.pc += 2;
            },
            (0x8,x,_,0xE) => { // shift VX left. VF set to dropped bit.
               let bit = (self.v[x] >> 0x7) & 0x1;
               self.v[x] <<= 1;
               self.v[0xF] = bit;
               self.pc += 2;
            },
            (0x9,x,y,0x0) => { // skip next instruction if VX != VY
                self.pc += if self.v[x] != self.v[y] {
                    4
                } else {
                    2
                };
            },
            (0xA,_,_,_) => { // set I to NNN
                self.index = self.oc & 0x0FFF;
                self.pc += 2;
            },
            (0xB,_,_,_) => { // jump to address NNN + V0
                self.pc = (self.oc & 0x0FFF) + (self.v[0] as u16);
            },
            (0xC,x,_,_) => { // set VX to random number & NN
                self.v[x] = rand::random::<u8>() & (self.oc & 0xFF) as u8;
                self.pc += 2;
            },
            (0xD,x,y,n) => { // complicated
                let x = self.v[x] as usize;
                let y = self.v[y] as usize;
                self.draw_sprite(x, y, n as usize, &bus.memory);
                self.pc += 2;
            },
            (0xE,x,0x9,0xE) => { // skip next if key in VX is pressed;
                match self.keys[self.v[x] as usize] {
                    Key::Down(_) => self.pc += 4,
                    Key::Up => self.pc += 2,
                }
                self.keys[self.v[x] as usize] = Key::Up;
            },
            (0xE,x,0xA,0x1) => { // skip next if key in VX is not pressed;
                match self.keys[self.v[x] as usize] {
                    Key::Down(_)  => self.pc += 2,
                    Key::Up => self.pc += 4,
                }
                self.keys[self.v[x] as usize] = Key::Up;
            },
            (0xF,x,0x0,0x7) => {
                self.v[x] = self.delay_timer;
                self.pc += 2;
            },
            (0xF,x,0x0,0xA) => { // a keypress is awaited, then stored in v[x]
                self.v[x] = bus.input.get_key();
            },
            (0xF,x,0x1,0x5) => { // set delay timer to VX
                self.delay_timer = self.v[x];
                self.pc += 2;
            },
            (0xF,x,0x1,0x8) => {
                self.sound_timer = self.v[x];
                self.pc += 2;
            },
            (0xF,x,0x1,0xE) => {
                let (value, flag) = self.index.overflowing_add(self.v[x] as u16);
                self.index = value;
                self.v[0xF] = if flag {1} else {0};
                self.pc += 2;
            },
            (0xF,x,0x2,0x9) => {
                self.index = (self.v[x] as u16)*5;
                self.pc += 2;
            },
            (0xF,x,0x3,0x3) => {
                let i = self.index;
                let vx = self.v[x];
                bus.memory.write_memory(i,vx/100);
                bus.memory.write_memory(i+1,(vx/10)%100);
                bus.memory.write_memory(i+2,(vx%100)%10);
                self.pc += 2;
            },
            (0xF,x,0x5,0x5) => { // stores V0 to VX (inclusive) starting at I
                let index = self.index;
                bus.memory.set_range(index, &self.v[0..(x+1)]);
                self.pc += 2;
            },
            (0xF,x,0x6,0x5) => { // fills V0 to VX (inclusive) starting from I
                for i in 0..(x+1) as u16{
                    self.v[i as usize] = bus.memory.read_memory(self.index + i);
                }
                self.pc += 2;
            },
            _ => panic!("unknown instruction received"),
        };
    }

    fn decrement_delay_timer(&mut self){
        if self.delay_timer > 0x0 {
            self.delay_timer -= 0x1;
        }
    }

    fn decrement_sound_timer<A:Audio>(&mut self, audio:&A){
        if self.sound_timer > 0x0 {
            if self.sound_timer == 0x1 {
                audio.beep();
            }
            self.sound_timer -= 0x1;
        }
    }

    fn print_screen<D:Display>(&self, display:&mut D){
        for row in 0..SCREEN_HEIGHT {
            for col in 0..SCREEN_WIDTH {
                let pixel = if self.screen[SCREEN_WIDTH*row + col] {
                    Pixel::On
                } else {
                    Pixel::Off
                };
                let _ = display.set(row, col, pixel);
            }
        }
        display.refresh();
    }

    fn draw_sprite(&mut self, x:usize, y:usize, height:usize, memory:&Memory){
        self.v[0xF] = 0x0;
        let location = x + y*SCREEN_WIDTH;
        let index = self.index as usize;
        for i in 0..height {
            if y + i < SCREEN_HEIGHT {
                let byte = memory.read_memory((index + i) as u16) as usize;
                self.draw_byte(location + i*SCREEN_WIDTH, byte);
            }
        }
    }

    fn draw_byte(&mut self, loc:usize, byte:usize){
        for i in 0..8 {
            if (byte & (0x80 >> i)) != 0 {
                self.draw_flag = true;
                if self.screen[loc+i] {self.v[0xF] = 0x1};
                self.screen[loc+i] ^= true;
            }
        }
    }

    fn set_pushed<I:Input>(&mut self, input:&I){
        let timeout = Duration::new(1,0);

        for key in &mut self.keys {
            *key = match *key {
                Key::Down(x) if x.elapsed() > timeout => Key::Up,
                _ => *key,
            };
        }

        for key in input.get_keys(){
            self.keys[key as usize] = Key::Down(Instant::now());
        }
    }
    // pub &mut self functions

    pub fn cycle<A, D, I>(&mut self, bus:&mut Bus<A, D, I>)
            where
                A: Audio,
                D: Display,
                I: Input {
        self.load_opcode(&bus.memory);
        self.run_opcode(bus);
        self.decrement_delay_timer();
        self.decrement_sound_timer(&bus.audio);

        if self.draw_flag {
            self.print_screen(&mut bus.display);
            self.draw_flag = false;
            std::thread::sleep(Duration::from_millis(FRAME_RATE));
        }
        self.set_pushed(&bus.input);
    }
}
<commit_msg>draw flag now set on 00e0 opcode call (clear screen)<commit_after>extern crate rand;

use super::std;
use super::std::time::{Duration, Instant};

use super::bus::Bus;
use super::io::{Audio, Display, Input, Pixel};
use super::io::{SCREEN_WIDTH, SCREEN_HEIGHT};
use super::memory::Memory;

// Constants
///////////////////////////////////////////////////////////////////////

const FRAME_RATE:u64 = 17;

// Processor
////////////////////////////////////////////////////////////////////////

#[derive(Clone, Copy)]
enum Key {
    Up,
    Down(Instant),
}

pub struct Processor {
    oc:u16, // Operational Code
    pc:u16, // Program Counter
    sp:u16, // Stack Pointer
    index:u16, // Index

    delay_timer:u8,
    sound_timer:u8,

    draw_flag:bool,

    v:[u8;0x10],
    stack:[u16;0x10],
    screen:[bool;SCREEN_WIDTH*SCREEN_HEIGHT],
    keys:[Key;0x10],
}

impl Default for Processor {
    fn default() -> Processor {
        Processor{
            oc:0x0,
            pc:0x200,
            sp:0x0,
            index:0x0,

            delay_timer:0x0,
            sound_timer:0x0,

            draw_flag:false,

            v:[0x0;0x10],
            stack:[0x0;0x10],
            screen:[false;SCREEN_WIDTH*SCREEN_HEIGHT],
            keys:[Key::Up;0x10],
        }
    }
}

impl Processor {

    // non-self functions

    fn read_address(pointer:u16, memory:&Memory) -> u16 {
        let top = (memory.read_memory(pointer) as u16) << 0x8;
        let bot = (memory.read_memory(pointer + 0x1) & 0xFF) as u16;
        top | bot
    }

    // &self functions

    // &mut self functions

    fn load_opcode(&mut self, memory:&Memory){
        self.oc = Processor::read_address(self.pc, memory);
    }

    fn run_opcode<A, D, I>(&mut self, bus:&mut Bus<A, D, I>)
            where
                A: Audio,
                D: Display,
                I: Input {
        let b1 = (self.oc & 0xF000) >> 0xC;
        let b2 = ((self.oc & 0x0F00) >> 0x8) as usize;
        let b3 = ((self.oc & 0x00F0) >> 0x4) as usize;
        let b4 = self.oc & 0x000F;

        match (b1, b2, b3, b4) {
            (0x0,0x0,0xE,0x0) => { // clear the screen
                self.screen[..]
                        .copy_from_slice(&[false;SCREEN_WIDTH*SCREEN_HEIGHT]);
                self.draw_flag = true;
                self.pc += 2;
            },
            (0x0,0x0,0xE,0xE) => { // return from subroutine
                self.sp -= 1;
                self.pc = self.stack[self.sp as usize];
                self.pc += 2;
            },
            (0x0,_,_,_) => { // call RCA 1802 program at address NNN
                /* TODO */
                panic!("TODO -- 0x0NNN");
            },
            (0x1,_,_,_) => { // jump to address NNN
                self.pc = self.oc & 0x0FFF;
            },
            (0x2,_,_,_) => { // call subroutine at NNN
                self.stack[self.sp as usize] = self.pc;
                self.sp += 1;
                self.pc = self.oc & 0x0FFF;
            },
            (0x3,x,_,_) => { // skip next instruction if VX == NN
                self.pc += if self.v[x] as u16 == (self.oc & 0x00FF) {
                    4
                } else {
                    2
                };
            },
            (0x4,x,_,_) => { // skip next instruction if VX != NN
                self.pc += if self.v[x] as u16 != (self.oc & 0x00FF) {
                    4
                } else {
                    2
                };
            },
            (0x5,x,y,0x0) => { // skip next instruction if VX == VY
                self.pc += if self.v[x] == self.v[y] {
                    4
                } else {
                    2
                };
            },
            (0x6,x,_,_) => { // set VX to NN
                self.v[x] = (self.oc & 0x00FF) as u8;
                self.pc += 2;
            },
            (0x7,x,_,_) => { // add NN to VX
                self.v[x] = self.v[x].wrapping_add((self.oc & 0x00FF) as u8);
                self.pc += 2;
            },
            (0x8,x,y,0x0) => { // set VX to VY
                self.v[x] = self.v[y];
                self.pc += 2;
            },
            (0x8,x,y,0x1) => { // or VX with VY
                self.v[x] |= self.v[y];
                self.pc += 2;
            },
            (0x8,x,y,0x2) => { // and VX with VY
                self.v[x] &= self.v[y];
                self.pc += 2;
            },
            (0x8,x,y,0x3) => { // xor VX with VY
                self.v[x] ^= self.v[y];
                self.pc += 2;
            },
            (0x8,x,y,0x4) => { // add VY to VX. VF = if carry {1} else {0}
                let (value, flag) = self.v[x].overflowing_add(self.v[y]);
                self.v[x] = value;
                self.v[0xF] = if flag {1} else {0};
                self.pc += 2;
            },
            (0x8,x,y,0x5) => { // sub VY from VX. VF = if borrow {0} else {1}
                let (value, flag) = self.v[x].overflowing_sub(self.v[y]);
                self.v[x] = value;
                self.v[0xF] = if flag {0} else {1};
                self.pc += 2;
            },
            (0x8,x,_,0x6) => { // shift VX right. VF set to dropped bit.
                let bit = self.v[x] & 0x1;
                self.v[x] >>= 1;
                self.v[0xF] = bit;
                self.pc += 2;
            },
            (0x8,x,y,0x7) => { // set VX to VY - VX. if borrow {0} else {1}
                let (value, flag) =  self.v[y].overflowing_sub(self.v[x]);
                self.v[x] = value;
                self.v[0xF] = if flag {0} else {1};
                self.pc += 2;
            },
            (0x8,x,_,0xE) => { // shift VX left. VF set to dropped bit.
               let bit = (self.v[x] >> 0x7) & 0x1;
               self.v[x] <<= 1;
               self.v[0xF] = bit;
               self.pc += 2;
            },
            (0x9,x,y,0x0) => { // skip next instruction if VX != VY
                self.pc += if self.v[x] != self.v[y] {
                    4
                } else {
                    2
                };
            },
            (0xA,_,_,_) => { // set I to NNN
                self.index = self.oc & 0x0FFF;
                self.pc += 2;
            },
            (0xB,_,_,_) => { // jump to address NNN + V0
                self.pc = (self.oc & 0x0FFF) + (self.v[0] as u16);
            },
            (0xC,x,_,_) => { // set VX to random number & NN
                self.v[x] = rand::random::<u8>() & (self.oc & 0xFF) as u8;
                self.pc += 2;
            },
            (0xD,x,y,n) => { // complicated
                let x = self.v[x] as usize;
                let y = self.v[y] as usize;
                self.draw_sprite(x, y, n as usize, &bus.memory);
                self.pc += 2;
            },
            (0xE,x,0x9,0xE) => { // skip next if key in VX is pressed;
                match self.keys[self.v[x] as usize] {
                    Key::Down(_) => self.pc += 4,
                    Key::Up => self.pc += 2,
                }
                self.keys[self.v[x] as usize] = Key::Up;
            },
            (0xE,x,0xA,0x1) => { // skip next if key in VX is not pressed;
                match self.keys[self.v[x] as usize] {
                    Key::Down(_)  => self.pc += 2,
                    Key::Up => self.pc += 4,
                }
                self.keys[self.v[x] as usize] = Key::Up;
            },
            (0xF,x,0x0,0x7) => {
                self.v[x] = self.delay_timer;
                self.pc += 2;
            },
            (0xF,x,0x0,0xA) => { // a keypress is awaited, then stored in v[x]
                self.v[x] = bus.input.get_key();
            },
            (0xF,x,0x1,0x5) => { // set delay timer to VX
                self.delay_timer = self.v[x];
                self.pc += 2;
            },
            (0xF,x,0x1,0x8) => {
                self.sound_timer = self.v[x];
                self.pc += 2;
            },
            (0xF,x,0x1,0xE) => {
                let (value, flag) = self.index.overflowing_add(self.v[x] as u16);
                self.index = value;
                self.v[0xF] = if flag {1} else {0};
                self.pc += 2;
            },
            (0xF,x,0x2,0x9) => {
                self.index = (self.v[x] as u16)*5;
                self.pc += 2;
            },
            (0xF,x,0x3,0x3) => {
                let i = self.index;
                let vx = self.v[x];
                bus.memory.write_memory(i,vx/100);
                bus.memory.write_memory(i+1,(vx/10)%100);
                bus.memory.write_memory(i+2,(vx%100)%10);
                self.pc += 2;
            },
            (0xF,x,0x5,0x5) => { // stores V0 to VX (inclusive) starting at I
                let index = self.index;
                bus.memory.set_range(index, &self.v[0..(x+1)]);
                self.pc += 2;
            },
            (0xF,x,0x6,0x5) => { // fills V0 to VX (inclusive) starting from I
                for i in 0..(x+1) as u16{
                    self.v[i as usize] = bus.memory.read_memory(self.index + i);
                }
                self.pc += 2;
            },
            _ => panic!("unknown instruction received"),
        };
    }

    fn decrement_delay_timer(&mut self){
        if self.delay_timer > 0x0 {
            self.delay_timer -= 0x1;
        }
    }

    fn decrement_sound_timer<A:Audio>(&mut self, audio:&A){
        if self.sound_timer > 0x0 {
            if self.sound_timer == 0x1 {
                audio.beep();
            }
            self.sound_timer -= 0x1;
        }
    }

    fn print_screen<D:Display>(&self, display:&mut D){
        for row in 0..SCREEN_HEIGHT {
            for col in 0..SCREEN_WIDTH {
                let pixel = if self.screen[SCREEN_WIDTH*row + col] {
                    Pixel::On
                } else {
                    Pixel::Off
                };
                let _ = display.set(row, col, pixel);
            }
        }
        display.refresh();
    }

    fn draw_sprite(&mut self, x:usize, y:usize, height:usize, memory:&Memory){
        self.v[0xF] = 0x0;
        let location = x + y*SCREEN_WIDTH;
        let index = self.index as usize;
        for i in 0..height {
            if y + i < SCREEN_HEIGHT {
                let byte = memory.read_memory((index + i) as u16) as usize;
                self.draw_byte(location + i*SCREEN_WIDTH, byte);
            }
        }
    }

    fn draw_byte(&mut self, loc:usize, byte:usize){
        for i in 0..8 {
            if (byte & (0x80 >> i)) != 0 {
                self.draw_flag = true;
                if self.screen[loc+i] {self.v[0xF] = 0x1};
                self.screen[loc+i] ^= true;
            }
        }
    }

    fn set_pushed<I:Input>(&mut self, input:&I){
        let timeout = Duration::new(1,0);

        for key in &mut self.keys {
            *key = match *key {
                Key::Down(x) if x.elapsed() > timeout => Key::Up,
                _ => *key,
            };
        }

        for key in input.get_keys(){
            self.keys[key as usize] = Key::Down(Instant::now());
        }
    }
    // pub &mut self functions

    pub fn cycle<A, D, I>(&mut self, bus:&mut Bus<A, D, I>)
            where
                A: Audio,
                D: Display,
                I: Input {
        self.load_opcode(&bus.memory);
        self.run_opcode(bus);
        self.decrement_delay_timer();
        self.decrement_sound_timer(&bus.audio);

        if self.draw_flag {
            self.print_screen(&mut bus.display);
            self.draw_flag = false;
            std::thread::sleep(Duration::from_millis(FRAME_RATE));
        }
        self.set_pushed(&bus.input);
    }
}
<|endoftext|>


<commit_before>use serde::{ser};
use std::mem::transmute;
use byteorder::{ByteOrder, LittleEndian, WriteBytesExt};
use std::fmt::{Display,Debug};
use std::fmt;
use serialize::{
    serialize_u64
};

// Option to display references as nested
pub static mut NESTED_REFERENCE: bool = false;

#[derive(Serialize, Debug)]
pub struct MftEnumReference{
    #[serde(serialize_with = "serialize_u64")]
    reference: u64,
    entry: u64,
    sequence: u16
}

// Represents a MFT Reference struct
// https://msdn.microsoft.com/en-us/library/bb470211(v=vs.85).aspx
// https://jmharkness.wordpress.com/2011/01/27/mft-file-reference-number/
#[derive(Hash, Eq, PartialEq, Debug)]
pub struct MftReference(pub u64);
impl MftReference{
    pub fn from_entry_and_seq(&mut self, entry: u64, sequence: u16){
        let entry_buffer: [u8; 8] = unsafe {
            transmute(entry.to_le())
        };
        let seq_buffer: [u8; 2] = unsafe {
            transmute(sequence.to_le())
        };
        let mut ref_buffer = vec![];
        ref_buffer.extend_from_slice(&entry_buffer[0..6]);
        ref_buffer.extend_from_slice(&seq_buffer);

        self.0 = LittleEndian::read_u64(&ref_buffer[0..8]);
    }
    pub fn get_from_entry_and_seq(entry: u64, sequence: u16) -> MftReference{
        let entry_buffer: [u8; 8] = unsafe {
            transmute(entry.to_le())
        };
        let seq_buffer: [u8; 2] = unsafe {
            transmute(sequence.to_le())
        };
        let mut ref_buffer = vec![];
        ref_buffer.extend_from_slice(&entry_buffer[0..6]);
        ref_buffer.extend_from_slice(&seq_buffer);

        MftReference(LittleEndian::read_u64(&ref_buffer[0..8]))
    }
    pub fn get_enum_ref(&self)->MftEnumReference{
        let mut raw_buffer = vec![];
        raw_buffer.write_u64::<LittleEndian>(self.0).unwrap();
        MftEnumReference{
            reference: LittleEndian::read_u64(&raw_buffer[0..8]),
            entry: LittleEndian::read_u64(
                &[&raw_buffer[0..6], &[0,0]].concat()
            ),
            sequence: LittleEndian::read_u16(&raw_buffer[6..8])
        }
    }
}
impl Display for MftReference {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f,"{}",self.0)
    }
}
impl Debug for MftReference {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f,"{}",self.0)
    }
}
impl ser::Serialize for MftReference {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
        where S: ser::Serializer
    {
        // If NESTED_REFERENCE we need to serialize the enumerated structure
        if unsafe{NESTED_REFERENCE} {
            serializer.serialize_newtype_struct("mft_reference",&self.get_enum_ref())
        } else {
            // Just serialize the u64 version
            serialize_u64(&self.0,serializer)
        }
    }
}

#[test]
fn test_reference() {
    use std::mem;
    let raw_reference: &[u8] = &[0x73,0x00,0x00,0x00,0x00,0x00,0x68,0x91];

    let mft_reference = MftReference(
        LittleEndian::read_u64(&raw_reference[0..8])
    );
    assert_eq!(mft_reference.0,10477624533077459059);
    assert_eq!(format!("{}", mft_reference),"10477624533077459059");
    // assert_eq!(mft_reference.sequence,37224);

    let mft_reference_01 = MftReference::get_from_entry_and_seq(
        115,
        37224
    );
    assert_eq!(mft_reference_01.0,10477624533077459059);

    let mut mft_reference_02: MftReference = unsafe {
        mem::zeroed()
    };
    assert_eq!(mft_reference_02.0,0);
    mft_reference_02.from_entry_and_seq(
        115,
        37224
    );
    assert_eq!(mft_reference_02.0,10477624533077459059);
}
<commit_msg>removed debug derive<commit_after>use serde::{ser};
use std::mem::transmute;
use byteorder::{ByteOrder, LittleEndian, WriteBytesExt};
use std::fmt::{Display,Debug};
use std::fmt;
use serialize::{
    serialize_u64
};

// Option to display references as nested
pub static mut NESTED_REFERENCE: bool = false;

#[derive(Serialize, Debug)]
pub struct MftEnumReference{
    #[serde(serialize_with = "serialize_u64")]
    reference: u64,
    entry: u64,
    sequence: u16
}

// Represents a MFT Reference struct
// https://msdn.microsoft.com/en-us/library/bb470211(v=vs.85).aspx
// https://jmharkness.wordpress.com/2011/01/27/mft-file-reference-number/
#[derive(Hash, Eq, PartialEq)]
pub struct MftReference(pub u64);
impl MftReference{
    pub fn from_entry_and_seq(&mut self, entry: u64, sequence: u16){
        let entry_buffer: [u8; 8] = unsafe {
            transmute(entry.to_le())
        };
        let seq_buffer: [u8; 2] = unsafe {
            transmute(sequence.to_le())
        };
        let mut ref_buffer = vec![];
        ref_buffer.extend_from_slice(&entry_buffer[0..6]);
        ref_buffer.extend_from_slice(&seq_buffer);

        self.0 = LittleEndian::read_u64(&ref_buffer[0..8]);
    }
    pub fn get_from_entry_and_seq(entry: u64, sequence: u16) -> MftReference{
        let entry_buffer: [u8; 8] = unsafe {
            transmute(entry.to_le())
        };
        let seq_buffer: [u8; 2] = unsafe {
            transmute(sequence.to_le())
        };
        let mut ref_buffer = vec![];
        ref_buffer.extend_from_slice(&entry_buffer[0..6]);
        ref_buffer.extend_from_slice(&seq_buffer);

        MftReference(LittleEndian::read_u64(&ref_buffer[0..8]))
    }
    pub fn get_enum_ref(&self)->MftEnumReference{
        let mut raw_buffer = vec![];
        raw_buffer.write_u64::<LittleEndian>(self.0).unwrap();
        MftEnumReference{
            reference: LittleEndian::read_u64(&raw_buffer[0..8]),
            entry: LittleEndian::read_u64(
                &[&raw_buffer[0..6], &[0,0]].concat()
            ),
            sequence: LittleEndian::read_u16(&raw_buffer[6..8])
        }
    }
}
impl Display for MftReference {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f,"{}",self.0)
    }
}
impl Debug for MftReference {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f,"{}",self.0)
    }
}
impl ser::Serialize for MftReference {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
        where S: ser::Serializer
    {
        // If NESTED_REFERENCE we need to serialize the enumerated structure
        if unsafe{NESTED_REFERENCE} {
            serializer.serialize_newtype_struct("mft_reference",&self.get_enum_ref())
        } else {
            // Just serialize the u64 version
            serialize_u64(&self.0,serializer)
        }
    }
}

#[test]
fn test_reference() {
    use std::mem;
    let raw_reference: &[u8] = &[0x73,0x00,0x00,0x00,0x00,0x00,0x68,0x91];

    let mft_reference = MftReference(
        LittleEndian::read_u64(&raw_reference[0..8])
    );
    assert_eq!(mft_reference.0,10477624533077459059);
    assert_eq!(format!("{}", mft_reference),"10477624533077459059");
    // assert_eq!(mft_reference.sequence,37224);

    let mft_reference_01 = MftReference::get_from_entry_and_seq(
        115,
        37224
    );
    assert_eq!(mft_reference_01.0,10477624533077459059);

    let mut mft_reference_02: MftReference = unsafe {
        mem::zeroed()
    };
    assert_eq!(mft_reference_02.0,0);
    mft_reference_02.from_entry_and_seq(
        115,
        37224
    );
    assert_eq!(mft_reference_02.0,10477624533077459059);
}
<|endoftext|>