<commit_before>use crate::error::Error;
use crate::structs::{Config, Member, PublicKey, RateLimit, Repo, Sector, SectorGroup, Team};
use glob::glob;
use hyper::body::HttpBody;
use hyper::{header, Body, Client, Request};
use hyper_tls::HttpsConnector;
use std::collections::HashMap;
use std::fs::File;
use std::io::prelude::*;

pub struct GithubClient {
    pub conf: Config,
}

impl GithubClient {
    pub fn new(config: &Config) -> GithubClient {
        if std::env::var("SSL_CERT_FILE").is_err() {
            std::env::set_var("SSL_CERT_FILE", &config.cert_path);
        }
        GithubClient { conf: config.clone() }
    }

    fn get_cache_path(&self, url: &str) -> std::path::PathBuf {
        let mut path = std::path::PathBuf::new();
        path.push(&self.conf.cache_dir);
        path.push(url);
        path
    }

    fn load_contents_from_cache(&self, url: &str) -> Result<(std::fs::Metadata, String), Error> {
        let path = self.get_cache_path(url);
        let metadata = std::fs::metadata(path.to_str().unwrap())?;
        let mut f = File::open(path.to_str().unwrap())?;
        let mut contents = String::new();
        f.read_to_string(&mut contents)?;
        Ok((metadata, contents))
    }

    fn store_contents_to_cache(&self, url: &str, contents: &str) -> Result<(), Error> {
        let path = self.get_cache_path(url);
        std::fs::create_dir_all(path.parent().unwrap_or(std::path::Path::new("/")))?;
        let mut f = File::create(path.to_str().unwrap())?;
        f.write_all(contents.as_bytes())?;
        Ok(())
    }

    async fn get_contents_from_url(&self, url: &str) -> Result<String, Error> {
        let mut all_contents: Vec<serde_json::value::Value> = Vec::new();
        let mut page = 1;
        loop {
            let mut new_array = self.get_contents_from_url_page(url, page).await?;
            if new_array.is_empty() {
                break;
            }
            all_contents.append(&mut new_array);
            page += 1;
        }
        let contents = serde_json::ser::to_string(&all_contents)?;
        self.store_contents_to_cache(url, &contents)?;
        Ok(contents)
    }

    fn build_request(&self, url: &str) -> Result<Request<Body>, Error> {
        let token = String::from("token ") + &self.conf.token;
        Request::get(url).header(header::AUTHORIZATION, token.as_str())
                         .header(header::USER_AGENT, "sectora")
                         .body(Body::empty())
                         .map_err(Error::from)
    }

    fn build_page_request(&self, url: &str, page: u64) -> Result<Request<Body>, Error> {
        let sep = if url.contains('?') { '&' } else { '?' };
        let url_p = format!("{}{}page={}", url, sep, page);
        self.build_request(&url_p)
    }

    async fn run_request(&self, req: Request<Body>) -> Result<Vec<u8>, Error> {
        let client = Client::builder().build(HttpsConnector::new());
        let mut resp = client.request(req).await?;
        let mut buff: Vec<u8> = Vec::new();
        while let Some(chunk) = resp.body_mut().data().await {
            buff.write_all(&chunk?)?;
        }
        Ok(buff)
    }

    async fn get_contents_from_url_page(&self, url: &str, page: u64) -> Result<Vec<serde_json::Value>, Error> {
        let req = self.build_page_request(url, page)?;
        let resp = self.run_request(req).await?;
        Ok(serde_json::from_slice(&resp)?)
    }

    async fn get_contents(&self, url: &str) -> Result<String, Error> {
        match self.load_contents_from_cache(url) {
            Ok((metadata, cache_contents)) => match std::time::SystemTime::now().duration_since(metadata.modified()?) {
                Ok(caching_duration) => {
                    if caching_duration.as_secs() > self.conf.cache_duration {
                        match self.get_contents_from_url(url).await {
                            Ok(contents_from_url) => Ok(contents_from_url),
                            Err(_) => Ok(cache_contents),
                        }
                    } else {
                        Ok(cache_contents)
                    }
                }
                Err(_) => Ok(cache_contents),
            },
            Err(_) => self.get_contents_from_url(url).await,
        }
    }

    pub async fn get_user_public_key(&self, user: &str) -> Result<String, Error> {
        let url = format!("{}/users/{}/keys", self.conf.endpoint, user);
        let contents = self.get_contents(&url).await?;
        let keys = serde_json::from_str::<Vec<PublicKey>>(&contents)?;
        Ok(keys.iter().map(|k| k.key.clone()).collect::<Vec<_>>().join("\n"))
    }

    pub async fn check_pam(&self, user: &str) -> Result<bool, Error> {
        let sectors = self.get_sectors().await?;
        Ok(sectors.iter().any(|team| team.members.contains_key(user)))
    }

    pub async fn get_sectors(&self) -> Result<Vec<SectorGroup>, Error> {
        let mut sectors: Vec<SectorGroup> = self.get_teams_result().await?;
        sectors.append(&mut self.get_repos_result().await?);
        Ok(sectors)
    }

    async fn get_teams_result(&self) -> Result<Vec<SectorGroup>, Error> {
        let gh_teams = self.get_team_map(&self.conf.org).await?;
        let mut teams = Vec::new();
        for team_conf in &self.conf.team {
            if let Some(gh_team) = gh_teams.get(&team_conf.name) {
                teams.push(SectorGroup { sector: Sector::from(gh_team.clone()),
                                         gid: team_conf.gid,
                                         group: team_conf.group.clone(),
                                         members: self.get_team_members(gh_team.id).await? });
            }
        }
        Ok(teams)
    }

    async fn get_team_map(&self, org: &str) -> Result<HashMap<String, Team>, Error> {
        let url = format!("{}/orgs/{}/teams", self.conf.endpoint, org);
        let contents = self.get_contents(&url).await?;
        let teams = serde_json::from_str::<Vec<Team>>(&contents)?;
        Ok(teams.iter().map(|t| (t.name.clone(), t.clone())).collect())
    }

    async fn get_team_members(&self, mid: u64) -> Result<HashMap<String, Member>, Error> {
        let url = format!("{}/teams/{}/members", self.conf.endpoint, mid);
        let contents = self.get_contents(&url).await?;
        let members = serde_json::from_str::<Vec<Member>>(&contents)?;
        Ok(members.iter().map(|m| (m.login.clone(), m.clone())).collect())
    }

    async fn get_repos_result(&self) -> Result<Vec<SectorGroup>, Error> {
        let gh_repos = self.get_repo_map(&self.conf.org).await?;
        let mut repos = Vec::new();
        for repo_conf in &self.conf.repo {
            if let Some(gh_repo) = gh_repos.get(&repo_conf.name) {
                repos.push(SectorGroup { sector: Sector::from(gh_repo.clone()),
                                         gid: repo_conf.gid,
                                         group: repo_conf.group.clone(),
                                         members: self.get_repo_collaborators(&self.conf.org, &gh_repo.name)
                                                      .await? });
            }
        }
        Ok(repos)
    }

    async fn get_repo_map(&self, org: &str) -> Result<HashMap<String, Repo>, Error> {
        let url = format!("{}/orgs/{}/repos", self.conf.endpoint, org);
        let contents = self.get_contents(&url).await?;
        let repos = serde_json::from_str::<Vec<Repo>>(&contents)?;
        Ok(repos.iter().map(|t| (t.name.clone(), t.clone())).collect())
    }

    async fn get_repo_collaborators(&self, org: &str, repo_name: &str) -> Result<HashMap<String, Member>, Error> {
        let url = format!("{}/repos/{}/{}/collaborators?affiliation=outside",
                          self.conf.endpoint, org, repo_name);
        let contents = self.get_contents(&url).await?;
        let members = serde_json::from_str::<Vec<Member>>(&contents)?;
        Ok(members.iter().map(|m| (m.login.clone(), m.clone())).collect())
    }

    pub async fn get_rate_limit(&self) -> Result<RateLimit, Error> {
        let url = format!("{}/rate_limit", self.conf.endpoint);
        let req = self.build_request(&url)?;
        let resp = futures::executor::block_on(self.run_request(req))?;
        Ok(serde_json::from_slice(&resp)?)
    }

    pub async fn clear_all_caches(&self) -> Result<(), Error> {
        let mut path = self.get_cache_path("");
        path.push("**/*");
        for entry in glob(&path.to_str().unwrap()).unwrap() {
            match entry {
                Ok(path) => {
                    if path.is_file() {
                        std::fs::remove_file(path)?
                    }
                }
                Err(e) => println!("{:?}", e),
            }
        }
        Ok(())
    }
}
<commit_msg>Make http client reusable<commit_after>use crate::error::Error;
use crate::structs::{Config, Member, PublicKey, RateLimit, Repo, Sector, SectorGroup, Team};
use glob::glob;
use hyper::body::HttpBody;
use hyper::client::HttpConnector;
use hyper::{header, Body, Client, Request};
use hyper_tls::HttpsConnector;
use std::collections::HashMap;
use std::fs::File;
use std::io::prelude::*;

pub struct GithubClient {
    client: Client<HttpsConnector<HttpConnector>>,
    pub conf: Config,
}

impl GithubClient {
    pub fn new(config: &Config) -> GithubClient {
        if std::env::var("SSL_CERT_FILE").is_err() {
            std::env::set_var("SSL_CERT_FILE", &config.cert_path);
        }
        let client = Client::builder().build(HttpsConnector::new());
        GithubClient { client,
                       conf: config.clone() }
    }

    fn get_cache_path(&self, url: &str) -> std::path::PathBuf {
        let mut path = std::path::PathBuf::new();
        path.push(&self.conf.cache_dir);
        path.push(url);
        path
    }

    fn load_contents_from_cache(&self, url: &str) -> Result<(std::fs::Metadata, String), Error> {
        let path = self.get_cache_path(url);
        let metadata = std::fs::metadata(path.to_str().unwrap())?;
        let mut f = File::open(path.to_str().unwrap())?;
        let mut contents = String::new();
        f.read_to_string(&mut contents)?;
        Ok((metadata, contents))
    }

    fn store_contents_to_cache(&self, url: &str, contents: &str) -> Result<(), Error> {
        let path = self.get_cache_path(url);
        std::fs::create_dir_all(path.parent().unwrap_or(std::path::Path::new("/")))?;
        let mut f = File::create(path.to_str().unwrap())?;
        f.write_all(contents.as_bytes())?;
        Ok(())
    }

    async fn get_contents_from_url(&self, url: &str) -> Result<String, Error> {
        let mut all_contents: Vec<serde_json::value::Value> = Vec::new();
        let mut page = 1;
        loop {
            let mut new_array = self.get_contents_from_url_page(url, page).await?;
            if new_array.is_empty() {
                break;
            }
            all_contents.append(&mut new_array);
            page += 1;
        }
        let contents = serde_json::ser::to_string(&all_contents)?;
        self.store_contents_to_cache(url, &contents)?;
        Ok(contents)
    }

    fn build_request(&self, url: &str) -> Result<Request<Body>, Error> {
        let token = String::from("token ") + &self.conf.token;
        Request::get(url).header(header::AUTHORIZATION, token.as_str())
                         .header(header::USER_AGENT, "sectora")
                         .body(Body::empty())
                         .map_err(Error::from)
    }

    fn build_page_request(&self, url: &str, page: u64) -> Result<Request<Body>, Error> {
        let sep = if url.contains('?') { '&' } else { '?' };
        let url_p = format!("{}{}page={}", url, sep, page);
        self.build_request(&url_p)
    }

    async fn run_request(&self, req: Request<Body>) -> Result<Vec<u8>, Error> {
        let mut resp = self.client.request(req).await?;
        let mut buff: Vec<u8> = Vec::new();
        while let Some(chunk) = resp.body_mut().data().await {
            buff.write_all(&chunk?)?;
        }
        Ok(buff)
    }

    async fn get_contents_from_url_page(&self, url: &str, page: u64) -> Result<Vec<serde_json::Value>, Error> {
        let req = self.build_page_request(url, page)?;
        let resp = self.run_request(req).await?;
        Ok(serde_json::from_slice(&resp)?)
    }

    async fn get_contents(&self, url: &str) -> Result<String, Error> {
        match self.load_contents_from_cache(url) {
            Ok((metadata, cache_contents)) => match std::time::SystemTime::now().duration_since(metadata.modified()?) {
                Ok(caching_duration) => {
                    if caching_duration.as_secs() > self.conf.cache_duration {
                        match self.get_contents_from_url(url).await {
                            Ok(contents_from_url) => Ok(contents_from_url),
                            Err(_) => Ok(cache_contents),
                        }
                    } else {
                        Ok(cache_contents)
                    }
                }
                Err(_) => Ok(cache_contents),
            },
            Err(_) => self.get_contents_from_url(url).await,
        }
    }

    pub async fn get_user_public_key(&self, user: &str) -> Result<String, Error> {
        let url = format!("{}/users/{}/keys", self.conf.endpoint, user);
        let contents = self.get_contents(&url).await?;
        let keys = serde_json::from_str::<Vec<PublicKey>>(&contents)?;
        Ok(keys.iter().map(|k| k.key.clone()).collect::<Vec<_>>().join("\n"))
    }

    pub async fn check_pam(&self, user: &str) -> Result<bool, Error> {
        let sectors = self.get_sectors().await?;
        Ok(sectors.iter().any(|team| team.members.contains_key(user)))
    }

    pub async fn get_sectors(&self) -> Result<Vec<SectorGroup>, Error> {
        let mut sectors: Vec<SectorGroup> = self.get_teams_result().await?;
        sectors.append(&mut self.get_repos_result().await?);
        Ok(sectors)
    }

    async fn get_teams_result(&self) -> Result<Vec<SectorGroup>, Error> {
        let gh_teams = self.get_team_map(&self.conf.org).await?;
        let mut teams = Vec::new();
        for team_conf in &self.conf.team {
            if let Some(gh_team) = gh_teams.get(&team_conf.name) {
                teams.push(SectorGroup { sector: Sector::from(gh_team.clone()),
                                         gid: team_conf.gid,
                                         group: team_conf.group.clone(),
                                         members: self.get_team_members(gh_team.id).await? });
            }
        }
        Ok(teams)
    }

    async fn get_team_map(&self, org: &str) -> Result<HashMap<String, Team>, Error> {
        let url = format!("{}/orgs/{}/teams", self.conf.endpoint, org);
        let contents = self.get_contents(&url).await?;
        let teams = serde_json::from_str::<Vec<Team>>(&contents)?;
        Ok(teams.iter().map(|t| (t.name.clone(), t.clone())).collect())
    }

    async fn get_team_members(&self, mid: u64) -> Result<HashMap<String, Member>, Error> {
        let url = format!("{}/teams/{}/members", self.conf.endpoint, mid);
        let contents = self.get_contents(&url).await?;
        let members = serde_json::from_str::<Vec<Member>>(&contents)?;
        Ok(members.iter().map(|m| (m.login.clone(), m.clone())).collect())
    }

    async fn get_repos_result(&self) -> Result<Vec<SectorGroup>, Error> {
        let gh_repos = self.get_repo_map(&self.conf.org).await?;
        let mut repos = Vec::new();
        for repo_conf in &self.conf.repo {
            if let Some(gh_repo) = gh_repos.get(&repo_conf.name) {
                repos.push(SectorGroup { sector: Sector::from(gh_repo.clone()),
                                         gid: repo_conf.gid,
                                         group: repo_conf.group.clone(),
                                         members: self.get_repo_collaborators(&self.conf.org, &gh_repo.name)
                                                      .await? });
            }
        }
        Ok(repos)
    }

    async fn get_repo_map(&self, org: &str) -> Result<HashMap<String, Repo>, Error> {
        let url = format!("{}/orgs/{}/repos", self.conf.endpoint, org);
        let contents = self.get_contents(&url).await?;
        let repos = serde_json::from_str::<Vec<Repo>>(&contents)?;
        Ok(repos.iter().map(|t| (t.name.clone(), t.clone())).collect())
    }

    async fn get_repo_collaborators(&self, org: &str, repo_name: &str) -> Result<HashMap<String, Member>, Error> {
        let url = format!("{}/repos/{}/{}/collaborators?affiliation=outside",
                          self.conf.endpoint, org, repo_name);
        let contents = self.get_contents(&url).await?;
        let members = serde_json::from_str::<Vec<Member>>(&contents)?;
        Ok(members.iter().map(|m| (m.login.clone(), m.clone())).collect())
    }

    pub async fn get_rate_limit(&self) -> Result<RateLimit, Error> {
        let url = format!("{}/rate_limit", self.conf.endpoint);
        let req = self.build_request(&url)?;
        let resp = futures::executor::block_on(self.run_request(req))?;
        Ok(serde_json::from_slice(&resp)?)
    }

    pub async fn clear_all_caches(&self) -> Result<(), Error> {
        let mut path = self.get_cache_path("");
        path.push("**/*");
        for entry in glob(&path.to_str().unwrap()).unwrap() {
            match entry {
                Ok(path) => {
                    if path.is_file() {
                        std::fs::remove_file(path)?
                    }
                }
                Err(e) => println!("{:?}", e),
            }
        }
        Ok(())
    }
}
<|endoftext|>


<commit_before>use graphics::Context;
use opengl_graphics::GlGraphics;
use vecmath::*;

use world;
use physics;

pub struct PlayerSystem;

fn get_aim_direction(control_state: &world::ControlState) -> Option<Vector2<f32>> {

    let mut aim: Vector2<f32> = [0.0, 0.0];

    if control_state.aim_up {
        aim = vec2_add(aim, [0.0, -1.0]);
    }

    if control_state.aim_down {
        aim = vec2_add(aim, [0.0, 1.0]);
    }

    if control_state.aim_left {
        aim = vec2_add(aim, [-1.0, 0.0]);
    }

    if control_state.aim_right {
        aim = vec2_add(aim, [1.0, 0.0]);
    }

    if aim != [0.0, 0.0] {
        Some(vec2_normalized(aim))
    } else {
        None
    }
}

impl world::System for PlayerSystem {

    fn update(&mut self, control_state: &world::ControlState, components: &mut world::Components, entities: &mut Vec<world::Entity>) {

        use world::PlayerState;

        for entity in entities.iter() {
            if let (Some(player_id), Some(body_id), Some(animator_id)) = (entity.player_controller, entity.dynamic_body, entity.sprite_animator) {

                {
                    // Update orientation state ..
                    update_walk_state(entity, components, entities);
                }

                if let Some(aim_direction) = get_aim_direction(control_state) {
                    let player = components.player_controller.get_mut(player_id);
                    player.aim_direction = aim_direction;

                    // TODO shoot stuff

                }

                {
                    update_animation(entity, components, entities);
                }

                let player = components.player_controller.get(player_id);
                let body = components.dynamic_body.get_mut(body_id);

                println!("{:?}", player.state);

                if player.state != PlayerState::Flying {
                    let mut velocity: Vector2<f32> = [0.0, 0.0];

                    if control_state.move_up {
                        velocity = vec2_add(velocity, [0.0, -1.0]);
                    }

                    if control_state.move_down {
                        velocity = vec2_add(velocity, [0.0, 1.0]);
                    }

                    if control_state.move_left {
                        velocity = vec2_add(velocity, [-1.0, 0.0]);
                    }

                    if control_state.move_right {
                        velocity = vec2_add(velocity, [1.0, 0.0]);
                    }

                    if velocity != [0.0, 0.0] {
                        velocity = vec2_normalized(velocity);
                        velocity = vec2_scale(velocity, player.move_speed);
                    }

                    body.vx = velocity[0];
                    body.vy = velocity[1];

                }
            }
        }
    }
    fn render(&mut self, context: &Context, gl: &mut GlGraphics, components: &mut world::Components, entities: &mut Vec<world::Entity>) {
        // not implemented
    }

}

fn aim_up_anim(player: &world::PlayerController, speed: f32) -> world::SpriteAnimation {
    if speed > 0.1 {
        player.walk_anim_aim_up.clone()
    } else {
        player.idle_anim_aim_up.clone()
    }
}

fn aim_up_forward_anim(player: &world::PlayerController, speed: f32) -> world::SpriteAnimation {
    if speed > 0.1 {
        player.walk_anim_aim_up_forward.clone()
    } else {
        player.idle_anim_aim_up_forward.clone()
    }
}

fn aim_down_forward_anim(player: &world::PlayerController, speed: f32) -> world::SpriteAnimation {
    if speed > 0.1 {
        player.walk_anim_aim_down_forward.clone()
    } else {
        player.idle_anim_aim_down_forward.clone()
    }
}

fn aim_down_anim(player: &world::PlayerController, speed: f32) -> world::SpriteAnimation {
    if speed > 0.1 {
        player.walk_anim_aim_down.clone()
    } else {
        player.idle_anim_aim_down.clone()
    }
}

fn aim_forward_anim(player: &world::PlayerController, speed: f32) -> world::SpriteAnimation {
    if speed > 0.1 {
        player.walk_anim.clone()
    } else {
        player.idle_anim.clone()
    }
}

//
// TODO - replace this with some generic Animation State Machine
//
fn update_animation(player_entity: &world::Entity, components: &mut world::Components, entities: &Vec<world::Entity>) {

    use world::PlayerState;

    let player = components.player_controller.get(player_entity.player_controller.unwrap());
    let body = components.dynamic_body.get_mut(player_entity.dynamic_body.unwrap());
    let animator = components.sprite_animator.get_mut(player_entity.sprite_animator.unwrap());
    let sprite = &mut components.sprite_renderer.get_mut(player_entity.sprite_renderer.unwrap()).sprite;

    let speed_sqr = body.vx * body.vx + body.vy * body.vy;

    match player.state {
        PlayerState::Flying => {

            if player.aim_direction == [0.0, -1.0] {
                animator.animation = aim_up_anim(player, speed_sqr);
            } else if player.aim_direction == [0.0, 1.0] {
                animator.animation = aim_down_anim(player, speed_sqr);
            } else if player.aim_direction[1] < 0.0 {
                animator.animation = aim_up_forward_anim(player, speed_sqr);
            } else if player.aim_direction[1] > 0.0 {
                animator.animation = aim_down_forward_anim(player, speed_sqr);
            } else {
                animator.animation = aim_forward_anim(player, speed_sqr);
            }

            if player.aim_direction[0] > 0.0 {
                sprite.set_flip_x(false);
            } else if player.aim_direction[0] < 0.0 {
                sprite.set_flip_x(true);
            }

        },
        PlayerState::OnLeftWall => {

            if player.aim_direction == [1.0, 0.0] {
                animator.animation = aim_up_anim(player, speed_sqr);
            } else if player.aim_direction == [-1.0, 0.0] {
                animator.animation = aim_down_anim(player, speed_sqr);
            } else if player.aim_direction[0] < 0.0 {
                animator.animation = aim_down_forward_anim(player, speed_sqr);
            } else if player.aim_direction[0] > 0.0 {
                animator.animation = aim_up_forward_anim(player, speed_sqr);
            } else {
                animator.animation = aim_forward_anim(player, speed_sqr);
            }

            if player.aim_direction[1] < 0.0 {
                sprite.set_flip_x(true);
            } else if player.aim_direction[1] > 0.0 {
                sprite.set_flip_x(false);
            }

        },
        PlayerState::OnRightWall => {

            if player.aim_direction == [-1.0, 0.0] {
                animator.animation = aim_up_anim(player, speed_sqr);
            } else if player.aim_direction == [1.0, 0.0] {
                animator.animation = aim_down_anim(player, speed_sqr);
            } else if player.aim_direction[0] > 0.0 {
                animator.animation = aim_down_forward_anim(player, speed_sqr);
            } else if player.aim_direction[0] < 0.0 {
                animator.animation = aim_up_forward_anim(player, speed_sqr);
            } else {
                animator.animation = aim_forward_anim(player, speed_sqr);
            }

            if player.aim_direction[1] > 0.0 {
                sprite.set_flip_x(true);
            } else if player.aim_direction[1] < 0.0 {
                sprite.set_flip_x(false);
            }

        },
        PlayerState::OnCeiling => {

            if player.aim_direction == [0.0, 1.0] {
                animator.animation = aim_up_anim(player, speed_sqr);
            } else if player.aim_direction == [0.0, -1.0] {
                animator.animation = aim_down_anim(player, speed_sqr);
            } else if player.aim_direction[1] > 0.0 {
                animator.animation = aim_up_forward_anim(player, speed_sqr);
            } else if player.aim_direction[1] < 0.0 {
                animator.animation = aim_down_forward_anim(player, speed_sqr);
            } else {
                animator.animation = aim_forward_anim(player, speed_sqr);
            }

            if player.aim_direction[0] > 0.0 {
                sprite.set_flip_x(true);
            } else if player.aim_direction[0] < 0.0 {
                sprite.set_flip_x(false);
            }

        },
        PlayerState::OnFloor => {

            if player.aim_direction == [0.0, -1.0] {
                animator.animation = aim_up_anim(player, speed_sqr);
            } else if player.aim_direction == [0.0, 1.0] {
                animator.animation = aim_down_anim(player, speed_sqr);
            } else if player.aim_direction[1] < 0.0 {
                animator.animation = aim_up_forward_anim(player, speed_sqr);
            } else if player.aim_direction[1] > 0.0 {
                animator.animation = aim_down_forward_anim(player, speed_sqr);
            } else {
                animator.animation = aim_forward_anim(player, speed_sqr);
            }

            if player.aim_direction[0] > 0.0 {
                sprite.set_flip_x(false);
            } else if player.aim_direction[0] < 0.0 {
                sprite.set_flip_x(true);
            }

        },
    }
}

fn update_walk_state(player_entity: &world::Entity, components: &mut world::Components, entities: &Vec<world::Entity>) {

    use world::PlayerState;

    let new_state = get_walk_state(player_entity, components, entities);

    let sprite = &mut components.sprite_renderer.get_mut(player_entity.sprite_renderer.unwrap()).sprite;

    match new_state {
        PlayerState::Flying => { sprite.set_rotation(0.0) },
        PlayerState::OnLeftWall => { sprite.set_rotation(90.0) },
        PlayerState::OnRightWall => { sprite.set_rotation(270.0) },
        PlayerState::OnCeiling => { sprite.set_rotation(180.0) },
        PlayerState::OnFloor => { sprite.set_rotation(0.0) },
    }

    let player = components.player_controller.get_mut(player_entity.player_controller.unwrap());
    player.state = new_state;
}

fn get_walk_state(player_entity: &world::Entity, components: &world::Components, entities: &Vec<world::Entity>) -> world::PlayerState {

    let player = components.player_controller.get(player_entity.player_controller.unwrap());
    let position = components.position.get(player_entity.position.unwrap());
    let ground_check = &player.ground_check;

    // find all intersections of ground_check with static geometry
    let mut neighbouring_tiles = Vec::new();

    for entity_2 in entities.iter() {

        if let (Some(collider_2_id), Some(position_2_id)) = (entity_2.collider, entity_2.position) {

            // Don't check for collisions with self!
            if player_entity.collider.unwrap() == collider_2_id {
                continue;
            }

            let collider_2 = components.collider.get(collider_2_id);
            let position_2 = components.position.get(position_2_id);

            if physics::aabb_intersect(ground_check, position, collider_2, position_2) {
                neighbouring_tiles.push(position_2.clone());
            }
        }
    }

    if neighbouring_tiles.len() > 0 {

        let up = [0.0, -1.0];
        let down = [0.0, 1.0];
        let left = [-1.0, 0.0];
        let right = [1.0, 0.0];

        let directions = vec![
            (world::PlayerState::OnFloor, down),
            (world::PlayerState::OnCeiling, up),
            (world::PlayerState::OnRightWall, right),
            (world::PlayerState::OnLeftWall, left),
        ];

        let sum = neighbouring_tiles.iter().fold([0.0, 0.0], |v, p| [v[0] + p.x, v[1] + p.y]);
        let avg = vec2_scale(sum, 1.0 / neighbouring_tiles.len() as f32);

        let wall_to_player = vec2_normalized(vec2_sub([position.x, position.y], avg));

        let (state, _) = directions.iter()
            .map(|&(ref state, dir)| (state, vec2_len(vec2_sub(wall_to_player, dir))))
            .max_by(|&(_, dist)| (dist * 10000.0) as i32)
            .unwrap();

        return state.clone();

    }
    world::PlayerState::Flying
}

<commit_msg>Cleanup<commit_after>use graphics::Context;
use opengl_graphics::GlGraphics;
use vecmath::*;

use world;
use physics;

pub struct PlayerSystem;

impl world::System for PlayerSystem {

    fn update(&mut self, control_state: &world::ControlState, components: &mut world::Components, entities: &mut Vec<world::Entity>) {

        for entity in entities.iter() {
            if let (Some(player_id), Some(body_id), Some(animator_id)) = (entity.player_controller, entity.dynamic_body, entity.sprite_animator) {

                // Update orientation
                update_walk_state(entity, components, entities);

                // Update aim direction
                if let Some(aim_direction) = get_aim_direction(control_state) {
                    let player = components.player_controller.get_mut(player_id);
                    player.aim_direction = aim_direction;

                    // TODO shoot stuff!
                }

                // Update animation given orientation, aim direction, and speed
                update_animation(entity, components, entities);

                update_control(entity, control_state, components, entities);

            }
        }
    }
    fn render(&mut self, context: &Context, gl: &mut GlGraphics, components: &mut world::Components, entities: &mut Vec<world::Entity>) {
        // not implemented
    }

}

fn get_aim_direction(control_state: &world::ControlState) -> Option<Vector2<f32>> {

    let mut aim: Vector2<f32> = [0.0, 0.0];

    if control_state.aim_up {
        aim = vec2_add(aim, [0.0, -1.0]);
    }

    if control_state.aim_down {
        aim = vec2_add(aim, [0.0, 1.0]);
    }

    if control_state.aim_left {
        aim = vec2_add(aim, [-1.0, 0.0]);
    }

    if control_state.aim_right {
        aim = vec2_add(aim, [1.0, 0.0]);
    }

    if aim != [0.0, 0.0] {
        Some(vec2_normalized(aim))
    } else {
        None
    }
}

fn aim_up_anim(player: &world::PlayerController, speed: f32) -> world::SpriteAnimation {
    if speed > 0.1 {
        player.walk_anim_aim_up.clone()
    } else {
        player.idle_anim_aim_up.clone()
    }
}

fn aim_up_forward_anim(player: &world::PlayerController, speed: f32) -> world::SpriteAnimation {
    if speed > 0.1 {
        player.walk_anim_aim_up_forward.clone()
    } else {
        player.idle_anim_aim_up_forward.clone()
    }
}

fn aim_down_forward_anim(player: &world::PlayerController, speed: f32) -> world::SpriteAnimation {
    if speed > 0.1 {
        player.walk_anim_aim_down_forward.clone()
    } else {
        player.idle_anim_aim_down_forward.clone()
    }
}

fn aim_down_anim(player: &world::PlayerController, speed: f32) -> world::SpriteAnimation {
    if speed > 0.1 {
        player.walk_anim_aim_down.clone()
    } else {
        player.idle_anim_aim_down.clone()
    }
}

fn aim_forward_anim(player: &world::PlayerController, speed: f32) -> world::SpriteAnimation {
    if speed > 0.1 {
        player.walk_anim.clone()
    } else {
        player.idle_anim.clone()
    }
}

fn update_control(player_entity: &world::Entity, control_state: &world::ControlState, components: &mut world::Components, entities: &Vec<world::Entity>) {

    use world::PlayerState;

    let player = components.player_controller.get(player_entity.player_controller.unwrap());
    let body = components.dynamic_body.get_mut(player_entity.dynamic_body.unwrap());

    if player.state != PlayerState::Flying {
        let mut velocity: Vector2<f32> = [0.0, 0.0];

        if control_state.move_up {
            velocity = vec2_add(velocity, [0.0, -1.0]);
        }

        if control_state.move_down {
            velocity = vec2_add(velocity, [0.0, 1.0]);
        }

        if control_state.move_left {
            velocity = vec2_add(velocity, [-1.0, 0.0]);
        }

        if control_state.move_right {
            velocity = vec2_add(velocity, [1.0, 0.0]);
        }

        if velocity != [0.0, 0.0] {
            velocity = vec2_normalized(velocity);
            velocity = vec2_scale(velocity, player.move_speed);
        }

        body.vx = velocity[0];
        body.vy = velocity[1];

    }

}

//
// TODO - probably replace this with some generic Animation State Machine
//
fn update_animation(player_entity: &world::Entity, components: &mut world::Components, entities: &Vec<world::Entity>) {

    use world::PlayerState;

    let player = components.player_controller.get(player_entity.player_controller.unwrap());
    let body = components.dynamic_body.get_mut(player_entity.dynamic_body.unwrap());
    let animator = components.sprite_animator.get_mut(player_entity.sprite_animator.unwrap());
    let sprite = &mut components.sprite_renderer.get_mut(player_entity.sprite_renderer.unwrap()).sprite;

    let speed2 = body.vx * body.vx + body.vy * body.vy;

    match player.state {
        PlayerState::Flying => {

            if player.aim_direction == [0.0, -1.0] {
                animator.animation = aim_up_anim(player, speed2);
            } else if player.aim_direction == [0.0, 1.0] {
                animator.animation = aim_down_anim(player, speed2);
            } else if player.aim_direction[1] < 0.0 {
                animator.animation = aim_up_forward_anim(player, speed2);
            } else if player.aim_direction[1] > 0.0 {
                animator.animation = aim_down_forward_anim(player, speed2);
            } else {
                animator.animation = aim_forward_anim(player, speed2);
            }

            if player.aim_direction[0] > 0.0 {
                sprite.set_flip_x(false);
            } else if player.aim_direction[0] < 0.0 {
                sprite.set_flip_x(true);
            }

        },
        PlayerState::OnLeftWall => {

            if player.aim_direction == [1.0, 0.0] {
                animator.animation = aim_up_anim(player, speed2);
            } else if player.aim_direction == [-1.0, 0.0] {
                animator.animation = aim_down_anim(player, speed2);
            } else if player.aim_direction[0] < 0.0 {
                animator.animation = aim_down_forward_anim(player, speed2);
            } else if player.aim_direction[0] > 0.0 {
                animator.animation = aim_up_forward_anim(player, speed2);
            } else {
                animator.animation = aim_forward_anim(player, speed2);
            }

            if player.aim_direction[1] < 0.0 {
                sprite.set_flip_x(true);
            } else if player.aim_direction[1] > 0.0 {
                sprite.set_flip_x(false);
            }

        },
        PlayerState::OnRightWall => {

            if player.aim_direction == [-1.0, 0.0] {
                animator.animation = aim_up_anim(player, speed2);
            } else if player.aim_direction == [1.0, 0.0] {
                animator.animation = aim_down_anim(player, speed2);
            } else if player.aim_direction[0] > 0.0 {
                animator.animation = aim_down_forward_anim(player, speed2);
            } else if player.aim_direction[0] < 0.0 {
                animator.animation = aim_up_forward_anim(player, speed2);
            } else {
                animator.animation = aim_forward_anim(player, speed2);
            }

            if player.aim_direction[1] > 0.0 {
                sprite.set_flip_x(true);
            } else if player.aim_direction[1] < 0.0 {
                sprite.set_flip_x(false);
            }

        },
        PlayerState::OnCeiling => {

            if player.aim_direction == [0.0, 1.0] {
                animator.animation = aim_up_anim(player, speed2);
            } else if player.aim_direction == [0.0, -1.0] {
                animator.animation = aim_down_anim(player, speed2);
            } else if player.aim_direction[1] > 0.0 {
                animator.animation = aim_up_forward_anim(player, speed2);
            } else if player.aim_direction[1] < 0.0 {
                animator.animation = aim_down_forward_anim(player, speed2);
            } else {
                animator.animation = aim_forward_anim(player, speed2);
            }

            if player.aim_direction[0] > 0.0 {
                sprite.set_flip_x(true);
            } else if player.aim_direction[0] < 0.0 {
                sprite.set_flip_x(false);
            }

        },
        PlayerState::OnFloor => {

            if player.aim_direction == [0.0, -1.0] {
                animator.animation = aim_up_anim(player, speed2);
            } else if player.aim_direction == [0.0, 1.0] {
                animator.animation = aim_down_anim(player, speed2);
            } else if player.aim_direction[1] < 0.0 {
                animator.animation = aim_up_forward_anim(player, speed2);
            } else if player.aim_direction[1] > 0.0 {
                animator.animation = aim_down_forward_anim(player, speed2);
            } else {
                animator.animation = aim_forward_anim(player, speed2);
            }

            if player.aim_direction[0] > 0.0 {
                sprite.set_flip_x(false);
            } else if player.aim_direction[0] < 0.0 {
                sprite.set_flip_x(true);
            }

        },
    }
}

fn update_walk_state(player_entity: &world::Entity, components: &mut world::Components, entities: &Vec<world::Entity>) {

    use world::PlayerState;

    let new_state = get_walk_state(player_entity, components, entities);

    let sprite = &mut components.sprite_renderer.get_mut(player_entity.sprite_renderer.unwrap()).sprite;

    match new_state {
        PlayerState::Flying => { sprite.set_rotation(0.0) },
        PlayerState::OnLeftWall => { sprite.set_rotation(90.0) },
        PlayerState::OnRightWall => { sprite.set_rotation(270.0) },
        PlayerState::OnCeiling => { sprite.set_rotation(180.0) },
        PlayerState::OnFloor => { sprite.set_rotation(0.0) },
    }

    let player = components.player_controller.get_mut(player_entity.player_controller.unwrap());
    player.state = new_state;
}

fn get_walk_state(player_entity: &world::Entity, components: &world::Components, entities: &Vec<world::Entity>) -> world::PlayerState {

    let player = components.player_controller.get(player_entity.player_controller.unwrap());
    let position = components.position.get(player_entity.position.unwrap());
    let ground_check = &player.ground_check;

    // find all intersections of ground_check with static geometry
    let mut neighbouring_tiles = Vec::new();

    for entity_2 in entities.iter() {

        if let (Some(collider_2_id), Some(position_2_id)) = (entity_2.collider, entity_2.position) {

            // Don't check for collisions with self!
            if player_entity.collider.unwrap() == collider_2_id {
                continue;
            }

            let collider_2 = components.collider.get(collider_2_id);
            let position_2 = components.position.get(position_2_id);

            if physics::aabb_intersect(ground_check, position, collider_2, position_2) {
                neighbouring_tiles.push(position_2.clone());
            }
        }
    }

    if neighbouring_tiles.len() > 0 {

        let up = [0.0, -1.0];
        let down = [0.0, 1.0];
        let left = [-1.0, 0.0];
        let right = [1.0, 0.0];

        let directions = vec![
            (world::PlayerState::OnFloor, down),
            (world::PlayerState::OnCeiling, up),
            (world::PlayerState::OnRightWall, right),
            (world::PlayerState::OnLeftWall, left),
        ];

        let sum = neighbouring_tiles.iter().fold([0.0, 0.0], |v, p| [v[0] + p.x, v[1] + p.y]);
        let avg = vec2_scale(sum, 1.0 / neighbouring_tiles.len() as f32);

        let wall_to_player = vec2_normalized(vec2_sub([position.x, position.y], avg));

        let (state, _) = directions.iter()
            .map(|&(ref state, dir)| (state, vec2_len(vec2_sub(wall_to_player, dir))))
            .max_by(|&(_, dist)| (dist * 10000.0) as i32)
            .unwrap();

        return state.clone();

    }
    world::PlayerState::Flying
}

<|endoftext|>


<commit_before>#![allow(non_snake_case)]
#![allow(non_upper_case_globals)]

use error_chain::ChainedError;
use libc::*;
use gl;
use gl::types::*;
use std::cmp;
use std::ffi::{CStr, CString};
use std::mem;
use std::ptr;
use std::sync::RwLock;

use capture;
use command;
use cvar;
use dl;
use encode;
use engine::Engine;
use errors::*;
use function::Function;
use sdl;

lazy_static!{
    static ref POINTERS: RwLock<Pointers> = RwLock::new(Pointers::default());
}

#[derive(Debug, Default)]
pub struct Pointers {
    RunListenServer: Function<unsafe extern "C" fn(*mut c_void,
                                                   *mut c_char,
                                                   *mut c_char,
                                                   *mut c_char,
                                                   *mut c_void,
                                                   *mut c_void)
                                                   -> c_int>,

    Cmd_AddCommand: Function<unsafe extern "C" fn(*const c_char, *mut c_void)>,
    Cmd_Argc: Function<unsafe extern "C" fn() -> c_int>,
    Cmd_Argv: Function<unsafe extern "C" fn(c_int) -> *const c_char>,
    Con_Printf: Function<unsafe extern "C" fn(*const c_char)>,
    Cvar_RegisterVariable: Function<unsafe extern "C" fn(*mut cvar::cvar_t)>,
    Host_FilterTime: Function<unsafe extern "C" fn(c_float) -> c_int>,
    Memory_Init: Function<unsafe extern "C" fn(*mut c_void, c_int)>,
    Sys_VID_FlipScreen: Function<unsafe extern "C" fn()>,
    VideoMode_GetCurrentVideoMode:
        Function<unsafe extern "C" fn(*mut c_int, *mut c_int, *mut c_int)>,
    VideoMode_IsWindowed: Function<unsafe extern "C" fn() -> c_int>,

    cls: Option<*mut client_static_t>,
    host_frametime: Option<*mut c_double>,
    window_rect: Option<*mut RECT>,
}

// TODO: think about how to deal with unsafety here.
// The compiler complaining about *mut not being Send/Sync is perfectly valid here.
// Our safe RwLock cannot guarantee there isn't some game thread accessing the values
// at the same time. Although reading/writing to a pointer is already unsafe?
unsafe impl Send for Pointers {}
unsafe impl Sync for Pointers {}

#[repr(C)]
#[derive(Debug, Clone, Copy)]
struct RECT {
    left: c_int,
    right: c_int,
    top: c_int,
    bottom: c_int,
}

#[repr(C)]
struct client_static_t {
    stuff: [u8; 0x4060],
    demoplayback: c_int,
}

/// The "main" function of hw.so, called inside `CEngineAPI::Run()`.
///
/// The game runs within this function and shortly after it exits hw.so is unloaded.
/// Note: `_restart` also causes this function to exit, in this case the launcher
/// unloads and reloads hw.so and this function is called again as if it was a fresh start.
#[export_name = "_Z15RunListenServerPvPcS0_S0_PFP14IBaseInterfacePKcPiES7_"]
pub unsafe extern "C" fn RunListenServer(instance: *mut c_void,
                                         basedir: *mut c_char,
                                         cmdline: *mut c_char,
                                         postRestartCmdLineArgs: *mut c_char,
                                         launcherFactory: *mut c_void,
                                         filesystemFactory: *mut c_void)
                                         -> c_int {
    // hw.so just loaded, either for the first time or potentially at a different address.
    // Refresh all pointers.
    if let Err(ref e) = refresh_pointers().chain_err(|| "error refreshing pointers") {
        panic!("{}", e.display());
    }

    // Initialize the encoding.
    encode::initialize();

    // Initialize the capturing.
    capture::initialize();

    let rv = real!(RunListenServer)(instance,
                                    basedir,
                                    cmdline,
                                    postRestartCmdLineArgs,
                                    launcherFactory,
                                    filesystemFactory);

    // Since hw.so is getting unloaded, reset all pointers.
    reset_pointers();

    rv
}

/// Calculates the frame time and limits the FPS.
#[no_mangle]
pub unsafe extern "C" fn Host_FilterTime(time: c_float) -> c_int {
    real!(Host_FilterTime)(time)
}

/// Initializes the hunk memory.
///
/// After the hunk memory has been initialized we can register console commands and variables.
/// It's also a good place to get OpenGL function pointers.
#[no_mangle]
pub unsafe extern "C" fn Memory_Init(buf: *mut c_void, size: c_int) {
    real!(Memory_Init)(buf, size);

    register_cvars_and_commands();

    gl::ReadPixels::load_with(|s| sdl::get_proc_address(s) as _);
    if !gl::ReadPixels::is_loaded() {
        panic!("could not load glReadPixels()");
    }
    gl::PixelStorei::load_with(|s| sdl::get_proc_address(s) as _);
    if !gl::PixelStorei::is_loaded() {
        panic!("could not load glPixelStorei()");
    }
}

/// Flips the screen.
#[export_name = "_Z18Sys_VID_FlipScreenv"]
pub unsafe extern "C" fn Sys_VID_FlipScreen() {
    // Print all errors that happened.
    loop {
        match capture::get_error() {
            Some(e) => con_print(&format!("{}", e.display())),
            None => break,
        }
    }

    if capture::is_capturing() {
        capture::capture_block_start(); // Profiling.

        let (w, h) = get_resolution();
        let mut buf = capture::get_buffer((w, h));

        // Our buffer expects 1-byte alignment.
        gl::PixelStorei(gl::PACK_ALIGNMENT, 1);

        // Get the pixels!
        gl::ReadPixels(0,
                       0,
                       w as GLsizei,
                       h as GLsizei,
                       gl::RGB,
                       gl::UNSIGNED_BYTE,
                       buf.as_mut_slice().as_mut_ptr() as _);

        capture::capture(buf, *POINTERS.read().unwrap().host_frametime.unwrap());

        capture::capture_block_end(); // Profiling.
    }

    real!(Sys_VID_FlipScreen)();

    // TODO: check if we're called from SCR_UpdateScreen().
}

/// Obtains and stores all necessary function and variable addresses.
fn refresh_pointers() -> Result<()> {
    let hw = dl::open("hw.so", RTLD_NOW | RTLD_NOLOAD)
        .chain_err(|| "couldn't load hw.so")?;

    let mut pointers = POINTERS.write().unwrap();

    unsafe {
        find!(pointers,
              hw,
              RunListenServer,
              "_Z15RunListenServerPvPcS0_S0_PFP14IBaseInterfacePKcPiES7_");
        find!(pointers, hw, Cmd_AddCommand, "Cmd_AddCommand");
        find!(pointers, hw, Cmd_Argc, "Cmd_Argc");
        find!(pointers, hw, Cmd_Argv, "Cmd_Argv");
        find!(pointers, hw, Con_Printf, "Con_Printf");
        find!(pointers, hw, Cvar_RegisterVariable, "Cvar_RegisterVariable");
        find!(pointers, hw, Host_FilterTime, "Host_FilterTime");
        find!(pointers, hw, Memory_Init, "Memory_Init");
        find!(pointers, hw, Sys_VID_FlipScreen, "_Z18Sys_VID_FlipScreenv");
        find!(pointers,
              hw,
              VideoMode_GetCurrentVideoMode,
              "VideoMode_GetCurrentVideoMode");
        find!(pointers, hw, VideoMode_IsWindowed, "VideoMode_IsWindowed");

        pointers.cls = Some(hw.sym("cls")
                              .chain_err(|| "couldn't find cls")? as _);
        pointers.host_frametime = Some(hw.sym("host_frametime")
                                         .chain_err(|| "couldn't find host_frametime")? as _);
        pointers.window_rect = Some(hw.sym("window_rect")
                                      .chain_err(|| "couldn't find window_rect")? as _);
    }

    Ok(())
}

/// Resets all pointers to their default values.
fn reset_pointers() {
    *POINTERS.write().unwrap() = Pointers::default();
}

/// Registers console commands and variables.
///
/// # Safety
/// Unsafe because this function should only be called from the main game thread.
unsafe fn register_cvars_and_commands() {
    for cmd in &command::COMMANDS {
        register_command(cmd.name(), cmd.callback());
    }

    let mut engine = Engine::new();
    for cvar in &cvar::CVARS {
        if let Err(ref e) =
            cvar.get(&engine)
                .register(&mut engine)
                .chain_err(|| "error registering a console variable") {
            panic!("{}", e.display());
        }
    }
}

/// Registers a console command.
///
/// # Safety
/// Unsafe because this function should only be called from the main game thread.
unsafe fn register_command(name: &'static [u8], callback: unsafe extern "C" fn()) {
    real!(Cmd_AddCommand)(name as *const _ as *const _, callback as *mut c_void);
}

/// Registers a console variable.
///
/// # Safety
/// Unsafe because this function should only be called from the main game thread.
pub unsafe fn register_variable(cvar: &mut cvar::cvar_t) {
    real!(Cvar_RegisterVariable)(cvar);
}

/// Prints the given string to the game console.
///
/// `string` must not contain null bytes.
///
/// # Safety
/// Unsafe because this function should only be called from the main game thread.
pub unsafe fn con_print(string: &str) {
    real!(Con_Printf)(CString::new(string.replace('%', "%%"))
                          .expect("string cannot contain null bytes")
                          .as_ptr())
}

/// Gets the console command argument count.
///
/// # Safety
/// Unsafe because this function should only be called from the main game thread.
pub unsafe fn cmd_argc() -> u32 {
    let argc = real!(Cmd_Argc)();
    cmp::max(0, argc) as u32
}

/// Gets a console command argument.
///
/// # Safety
/// Unsafe because this function should only be called from the main game thread.
pub unsafe fn cmd_argv(index: u32) -> String {
    let index = cmp::min(index, i32::max_value() as u32) as i32;
    let arg = real!(Cmd_Argv)(index);
    CStr::from_ptr(arg).to_string_lossy().into_owned()
}

/// Returns the current game resolution.
///
/// # Safety
/// Unsafe because this function should only be called from the main game thread.
pub unsafe fn get_resolution() -> (u32, u32) {
    let mut width;
    let mut height;

    if real!(VideoMode_IsWindowed)() != 0 {
        let window_rect = *POINTERS.read().unwrap().window_rect.unwrap();
        width = window_rect.right - window_rect.left;
        height = window_rect.bottom - window_rect.top;
    } else {
        width = mem::uninitialized();
        height = mem::uninitialized();
        real!(VideoMode_GetCurrentVideoMode)(&mut width, &mut height, ptr::null_mut());
    }

    width = cmp::max(0, width);
    height = cmp::max(0, height);

    (width as u32, height as u32)
}

// command!(cap_test, |engine| {
//     let args = engine.args();
//
//     let mut buf = String::new();
//     buf.push_str(&format!("Args len: {}\n", args.len()));
//
//     for arg in args {
//         buf.push_str(&arg);
//         buf.push('\n');
//     }
//
//     engine.con_print(&buf);
// });
<commit_msg>Find realtime<commit_after>#![allow(non_snake_case)]
#![allow(non_upper_case_globals)]

use error_chain::ChainedError;
use libc::*;
use gl;
use gl::types::*;
use std::cmp;
use std::ffi::{CStr, CString};
use std::mem;
use std::ptr;
use std::sync::RwLock;

use capture;
use command;
use cvar;
use dl;
use encode;
use engine::Engine;
use errors::*;
use function::Function;
use sdl;

lazy_static!{
    static ref POINTERS: RwLock<Pointers> = RwLock::new(Pointers::default());
}

#[derive(Debug, Default)]
pub struct Pointers {
    RunListenServer: Function<unsafe extern "C" fn(*mut c_void,
                                                   *mut c_char,
                                                   *mut c_char,
                                                   *mut c_char,
                                                   *mut c_void,
                                                   *mut c_void)
                                                   -> c_int>,

    Cmd_AddCommand: Function<unsafe extern "C" fn(*const c_char, *mut c_void)>,
    Cmd_Argc: Function<unsafe extern "C" fn() -> c_int>,
    Cmd_Argv: Function<unsafe extern "C" fn(c_int) -> *const c_char>,
    Con_Printf: Function<unsafe extern "C" fn(*const c_char)>,
    Cvar_RegisterVariable: Function<unsafe extern "C" fn(*mut cvar::cvar_t)>,
    Host_FilterTime: Function<unsafe extern "C" fn(c_float) -> c_int>,
    Memory_Init: Function<unsafe extern "C" fn(*mut c_void, c_int)>,
    Sys_VID_FlipScreen: Function<unsafe extern "C" fn()>,
    VideoMode_GetCurrentVideoMode:
        Function<unsafe extern "C" fn(*mut c_int, *mut c_int, *mut c_int)>,
    VideoMode_IsWindowed: Function<unsafe extern "C" fn() -> c_int>,

    cls: Option<*mut client_static_t>,
    host_frametime: Option<*mut c_double>,
    realtime: Option<*mut c_double>,
    window_rect: Option<*mut RECT>,
}

// TODO: think about how to deal with unsafety here.
// The compiler complaining about *mut not being Send/Sync is perfectly valid here.
// Our safe RwLock cannot guarantee there isn't some game thread accessing the values
// at the same time. Although reading/writing to a pointer is already unsafe?
unsafe impl Send for Pointers {}
unsafe impl Sync for Pointers {}

#[repr(C)]
#[derive(Debug, Clone, Copy)]
struct RECT {
    left: c_int,
    right: c_int,
    top: c_int,
    bottom: c_int,
}

#[repr(C)]
struct client_static_t {
    stuff: [u8; 0x4060],
    demoplayback: c_int,
}

/// The "main" function of hw.so, called inside `CEngineAPI::Run()`.
///
/// The game runs within this function and shortly after it exits hw.so is unloaded.
/// Note: `_restart` also causes this function to exit, in this case the launcher
/// unloads and reloads hw.so and this function is called again as if it was a fresh start.
#[export_name = "_Z15RunListenServerPvPcS0_S0_PFP14IBaseInterfacePKcPiES7_"]
pub unsafe extern "C" fn RunListenServer(instance: *mut c_void,
                                         basedir: *mut c_char,
                                         cmdline: *mut c_char,
                                         postRestartCmdLineArgs: *mut c_char,
                                         launcherFactory: *mut c_void,
                                         filesystemFactory: *mut c_void)
                                         -> c_int {
    // hw.so just loaded, either for the first time or potentially at a different address.
    // Refresh all pointers.
    if let Err(ref e) = refresh_pointers().chain_err(|| "error refreshing pointers") {
        panic!("{}", e.display());
    }

    // Initialize the encoding.
    encode::initialize();

    // Initialize the capturing.
    capture::initialize();

    let rv = real!(RunListenServer)(instance,
                                    basedir,
                                    cmdline,
                                    postRestartCmdLineArgs,
                                    launcherFactory,
                                    filesystemFactory);

    // Since hw.so is getting unloaded, reset all pointers.
    reset_pointers();

    rv
}

/// Calculates the frame time and limits the FPS.
#[no_mangle]
pub unsafe extern "C" fn Host_FilterTime(time: c_float) -> c_int {
    real!(Host_FilterTime)(time)
}

/// Initializes the hunk memory.
///
/// After the hunk memory has been initialized we can register console commands and variables.
/// It's also a good place to get OpenGL function pointers.
#[no_mangle]
pub unsafe extern "C" fn Memory_Init(buf: *mut c_void, size: c_int) {
    real!(Memory_Init)(buf, size);

    register_cvars_and_commands();

    gl::ReadPixels::load_with(|s| sdl::get_proc_address(s) as _);
    if !gl::ReadPixels::is_loaded() {
        panic!("could not load glReadPixels()");
    }
    gl::PixelStorei::load_with(|s| sdl::get_proc_address(s) as _);
    if !gl::PixelStorei::is_loaded() {
        panic!("could not load glPixelStorei()");
    }
}

/// Flips the screen.
#[export_name = "_Z18Sys_VID_FlipScreenv"]
pub unsafe extern "C" fn Sys_VID_FlipScreen() {
    // Print all errors that happened.
    loop {
        match capture::get_error() {
            Some(e) => con_print(&format!("{}", e.display())),
            None => break,
        }
    }

    if capture::is_capturing() {
        capture::capture_block_start(); // Profiling.

        let (w, h) = get_resolution();
        let mut buf = capture::get_buffer((w, h));

        // Our buffer expects 1-byte alignment.
        gl::PixelStorei(gl::PACK_ALIGNMENT, 1);

        // Get the pixels!
        gl::ReadPixels(0,
                       0,
                       w as GLsizei,
                       h as GLsizei,
                       gl::RGB,
                       gl::UNSIGNED_BYTE,
                       buf.as_mut_slice().as_mut_ptr() as _);

        capture::capture(buf, *POINTERS.read().unwrap().host_frametime.unwrap());

        capture::capture_block_end(); // Profiling.
    }

    real!(Sys_VID_FlipScreen)();

    // TODO: check if we're called from SCR_UpdateScreen().
}

/// Obtains and stores all necessary function and variable addresses.
fn refresh_pointers() -> Result<()> {
    let hw = dl::open("hw.so", RTLD_NOW | RTLD_NOLOAD)
        .chain_err(|| "couldn't load hw.so")?;

    let mut pointers = POINTERS.write().unwrap();

    unsafe {
        find!(pointers,
              hw,
              RunListenServer,
              "_Z15RunListenServerPvPcS0_S0_PFP14IBaseInterfacePKcPiES7_");
        find!(pointers, hw, Cmd_AddCommand, "Cmd_AddCommand");
        find!(pointers, hw, Cmd_Argc, "Cmd_Argc");
        find!(pointers, hw, Cmd_Argv, "Cmd_Argv");
        find!(pointers, hw, Con_Printf, "Con_Printf");
        find!(pointers, hw, Cvar_RegisterVariable, "Cvar_RegisterVariable");
        find!(pointers, hw, Host_FilterTime, "Host_FilterTime");
        find!(pointers, hw, Memory_Init, "Memory_Init");
        find!(pointers, hw, Sys_VID_FlipScreen, "_Z18Sys_VID_FlipScreenv");
        find!(pointers,
              hw,
              VideoMode_GetCurrentVideoMode,
              "VideoMode_GetCurrentVideoMode");
        find!(pointers, hw, VideoMode_IsWindowed, "VideoMode_IsWindowed");

        pointers.cls = Some(hw.sym("cls")
                              .chain_err(|| "couldn't find cls")? as _);
        pointers.host_frametime = Some(hw.sym("host_frametime")
                                         .chain_err(|| "couldn't find host_frametime")? as _);
        pointers.realtime = Some(hw.sym("realtime")
                                   .chain_err(|| "couldn't find realtime")? as _);
        pointers.window_rect = Some(hw.sym("window_rect")
                                      .chain_err(|| "couldn't find window_rect")? as _);
    }

    Ok(())
}

/// Resets all pointers to their default values.
fn reset_pointers() {
    *POINTERS.write().unwrap() = Pointers::default();
}

/// Registers console commands and variables.
///
/// # Safety
/// Unsafe because this function should only be called from the main game thread.
unsafe fn register_cvars_and_commands() {
    for cmd in &command::COMMANDS {
        register_command(cmd.name(), cmd.callback());
    }

    let mut engine = Engine::new();
    for cvar in &cvar::CVARS {
        if let Err(ref e) =
            cvar.get(&engine)
                .register(&mut engine)
                .chain_err(|| "error registering a console variable") {
            panic!("{}", e.display());
        }
    }
}

/// Registers a console command.
///
/// # Safety
/// Unsafe because this function should only be called from the main game thread.
unsafe fn register_command(name: &'static [u8], callback: unsafe extern "C" fn()) {
    real!(Cmd_AddCommand)(name as *const _ as *const _, callback as *mut c_void);
}

/// Registers a console variable.
///
/// # Safety
/// Unsafe because this function should only be called from the main game thread.
pub unsafe fn register_variable(cvar: &mut cvar::cvar_t) {
    real!(Cvar_RegisterVariable)(cvar);
}

/// Prints the given string to the game console.
///
/// `string` must not contain null bytes.
///
/// # Safety
/// Unsafe because this function should only be called from the main game thread.
pub unsafe fn con_print(string: &str) {
    real!(Con_Printf)(CString::new(string.replace('%', "%%"))
                          .expect("string cannot contain null bytes")
                          .as_ptr())
}

/// Gets the console command argument count.
///
/// # Safety
/// Unsafe because this function should only be called from the main game thread.
pub unsafe fn cmd_argc() -> u32 {
    let argc = real!(Cmd_Argc)();
    cmp::max(0, argc) as u32
}

/// Gets a console command argument.
///
/// # Safety
/// Unsafe because this function should only be called from the main game thread.
pub unsafe fn cmd_argv(index: u32) -> String {
    let index = cmp::min(index, i32::max_value() as u32) as i32;
    let arg = real!(Cmd_Argv)(index);
    CStr::from_ptr(arg).to_string_lossy().into_owned()
}

/// Returns the current game resolution.
///
/// # Safety
/// Unsafe because this function should only be called from the main game thread.
pub unsafe fn get_resolution() -> (u32, u32) {
    let mut width;
    let mut height;

    if real!(VideoMode_IsWindowed)() != 0 {
        let window_rect = *POINTERS.read().unwrap().window_rect.unwrap();
        width = window_rect.right - window_rect.left;
        height = window_rect.bottom - window_rect.top;
    } else {
        width = mem::uninitialized();
        height = mem::uninitialized();
        real!(VideoMode_GetCurrentVideoMode)(&mut width, &mut height, ptr::null_mut());
    }

    width = cmp::max(0, width);
    height = cmp::max(0, height);

    (width as u32, height as u32)
}

// command!(cap_test, |engine| {
//     let args = engine.args();
//
//     let mut buf = String::new();
//     buf.push_str(&format!("Args len: {}\n", args.len()));
//
//     for arg in args {
//         buf.push_str(&arg);
//         buf.push('\n');
//     }
//
//     engine.con_print(&buf);
// });
<|endoftext|>


<commit_before>#[derive(Debug,Clone,PartialEq,Eq,PartialOrd,Ord)]
struct Region
{
	id: usize,
	count: usize,
	player: String,
}

<commit_msg>region has neighbours and super, player is Option<commit_after>#[derive(Debug,Clone,PartialEq,Eq,PartialOrd,Ord)]
struct Region
{
	id: usize,
	count: usize,
	super_region: usize,
	player: Option<String>,
	neighbours: Vec<usize>,
}

<|endoftext|>


<commit_before>use ::libc::{self, c_int, O_EXCL, O_RDWR, O_CREAT};
use std::io;
use std::os::unix::io::{AsRawFd, FromRawFd};
use std::fs::{self, File, OpenOptions};
use std::os::unix::ffi::OsStrExt;
use std::path::Path;
use std::ffi::{OsStr, CString};
use ::util::tmpname;

// Stolen from std.
fn cstr(path: &Path) -> io::Result<CString> {
    path.as_os_str().to_cstring().ok_or(
        io::Error::new(io::ErrorKind::InvalidInput, "path contained a null"))
}

fn create_unix(dir: &Path) -> io::Result<File> {
    loop {
        let name = tmpname();
        let tmp_path = dir.join(OsStr::from_bytes(&name));
        return match unsafe {
            libc::open(try!(cstr(&tmp_path)).as_ptr(), O_CLOEXEC | O_EXCL | O_RDWR | O_CREAT, 0o600)
        } {
            -1 => {
                let err = io::Error::last_os_error();
                if err.kind() == io::ErrorKind::AlreadyExists {
                    continue;
                } else {
                    Err(err)
                }
            }
            fd => {
                let f = FromRawFd::from_raw_fd(fd);
                // I should probably tell the user this failed but the temporary file creation
                // didn't really fail...
                let _ = fs::remove_file(tmp_path);
                Ok(f)
            }
        }
    }
}

#[cfg(target_os = "linux")]
fn create_linux(dir: &Path) -> io::Result<File> {
    const O_TMPFILE: libc::c_int = 4259840;
    let dir = try!(cstr(dir));

    match unsafe {
        libc::open(dir.as_ptr(), O_CLOEXEC | O_EXCL | O_TMPFILE | O_RDWR, 0o600)
    } {
        -1 => Err(io::Error::last_os_error()),
        fd => Ok(FromRawFd::from_raw_fd(fd)),
    }
}

#[cfg(target_os = "linux")]
pub fn create(dir: &Path) -> io::Result<File> {
    create_linux(dir).or_else(|_| create_unix(dir))
}

#[cfg(not(target_os = "linux"))]
#[inline(always)]
pub fn create(dir: &Path, num_readers: usize) -> io::Result<File> {
    create_unix(dir, num_readers)
}

pub fn reopen(f: &File) -> io::Result<File> {
    OpenOptions::new().read(true).write(true).create(false).open(format!("/dev/fd/{}", f.as_raw_fd()))
}
<commit_msg>Add cloexec flag.<commit_after>use ::libc::{self, c_int, O_EXCL, O_RDWR, O_CREAT};
use std::io;
use std::os::unix::io::{AsRawFd, FromRawFd};
use std::fs::{self, File, OpenOptions};
use std::os::unix::ffi::OsStrExt;
use std::path::Path;
use std::ffi::{OsStr, CString};
use ::util::tmpname;

const O_CLOEXEC: libc::c_int = 0o2000000;
const O_TMPFILE: libc::c_int = 0o20200000;

// Stolen from std.
fn cstr(path: &Path) -> io::Result<CString> {
    path.as_os_str().to_cstring().ok_or(
        io::Error::new(io::ErrorKind::InvalidInput, "path contained a null"))
}

fn create_unix(dir: &Path) -> io::Result<File> {
    loop {
        let name = tmpname();
        let tmp_path = dir.join(OsStr::from_bytes(&name));
        return match unsafe {
            libc::open(try!(cstr(&tmp_path)).as_ptr(), O_CLOEXEC | O_EXCL | O_RDWR | O_CREAT, 0o600)
        } {
            -1 => {
                let err = io::Error::last_os_error();
                if err.kind() == io::ErrorKind::AlreadyExists {
                    continue;
                } else {
                    Err(err)
                }
            }
            fd => {
                let f = FromRawFd::from_raw_fd(fd);
                // I should probably tell the user this failed but the temporary file creation
                // didn't really fail...
                let _ = fs::remove_file(tmp_path);
                Ok(f)
            }
        }
    }
}

#[cfg(target_os = "linux")]
fn create_linux(dir: &Path) -> io::Result<File> {
    let dir = try!(cstr(dir));

    match unsafe {
        libc::open(dir.as_ptr(), O_CLOEXEC | O_EXCL | O_TMPFILE | O_RDWR, 0o600)
    } {
        -1 => Err(io::Error::last_os_error()),
        fd => Ok(FromRawFd::from_raw_fd(fd)),
    }
}

#[cfg(target_os = "linux")]
pub fn create(dir: &Path) -> io::Result<File> {
    // Fallback on unix create in case the kernel version is < 3.11.
    create_linux(dir).or_else(|_| create_unix(dir))
}

#[cfg(not(target_os = "linux"))]
#[inline(always)]
pub fn create(dir: &Path, num_readers: usize) -> io::Result<File> {
    create_unix(dir, num_readers)
}

pub fn reopen(f: &File) -> io::Result<File> {
    OpenOptions::new().read(true).write(true).create(false).open(format!("/dev/fd/{}", f.as_raw_fd()))
}
<|endoftext|>


<commit_before>use glium::VertexBuffer;
use glium::IndexBuffer;
use glium::Program;
use glium::DrawParameters;
use glium::Surface;
use glium::Frame;
use glium::Rect;
use glium::Texture2d;
use glium::Blend;
use glium::index::PrimitiveType;
use glium::backend::Facade;

use nalgebra::Mat4;
use nalgebra::OrthoMat3;

use mem::MemSection;
use mem::AddressSpace;

pub const LCD_WIDTH: u32    = 160;
pub const LCD_HEIGHT: u32   = 144;
pub const LCD_ASPECT: f32   = (LCD_WIDTH as f32) / (LCD_HEIGHT as f32);

pub const BG_SIZE: u32      = 256;

/// Period of the V-Blank in ns. V-Blank frequency is ~59.7 Hz
pub const VBLANK_PERIOD: u64 = 16_750_419;

/// Period of the H-Blank in ns. H-Blank frequency is ~9198 Hz
pub const HBLANK_PERIOD: u64 = 108_719;

static SIMPLE_VERT: &'static str = r#"
#version 140

in vec2 coord;
in vec2 tcoord;

out vec2 tex_coord;

uniform mat4 projection;
uniform vec2 translate;

void main() {
    tex_coord = tcoord;
    gl_Position = projection * vec4(coord.x + translate.x, coord.y + translate.y, 0.0, 1.0);
}
"#;

static COLOR_FRAG: &'static str = r#"
#version 140

in vec2 tex_coord;

out vec4 frag_color;

uniform vec4 color;
uniform vec2 translate;

void main() {
    frag_color = color;
}
"#;

static TEXTURE_FRAG: &'static str = r#"
#version 140

in vec2 tex_coord;

out vec4 frag_color;

uniform sampler2D tex;

void main() {
    frag_color = texture(tex, tex_coord);
}
"#;

#[derive(Copy, Clone)]
struct Vertex {
    coord: [f32; 2],
    tcoord: [f32; 2],
}

implement_vertex!(Vertex, coord, tcoord);

pub struct GbDisplay {
    simple_surface: VertexBuffer<Vertex>,
    simple_surface_idx: IndexBuffer<u32>,
    color_prog: Program,
    tex_prog: Program,
    projection: Mat4<f32>,
    ly_counter: u8,
}

impl GbDisplay {

    pub fn new<F>(display: &F) -> GbDisplay where F: Facade {
        let (vertbuf, idxbuf) = {
            let topleft = Vertex {
                coord: [0.0, 0.0],
                tcoord: [0.0, 1.0],
            };
            let topright = Vertex {
                coord: [BG_SIZE as f32, 0.0],
                tcoord: [1.0, 1.0],
            };
            let bottomright = Vertex {
                coord: [BG_SIZE as f32, BG_SIZE as f32],
                tcoord: [1.0, 0.0],
            };
            let bottomleft = Vertex {
                coord: [0.0, BG_SIZE as f32],
                tcoord: [0.0, 0.0],
            };
            let vertices = vec![topleft, topright, bottomright, bottomleft];
            let indices = vec![0, 1, 3, 1, 2, 3];
            (
                VertexBuffer::new(display, &vertices).unwrap(),
                IndexBuffer::immutable(display, PrimitiveType::TrianglesList, &indices).unwrap()
            )
        };
        let colorprog = Program::from_source(display, SIMPLE_VERT, COLOR_FRAG, None).unwrap();
        let texprog = Program::from_source(display, SIMPLE_VERT, TEXTURE_FRAG, None).unwrap();
        let projection = {
            let orthomat = OrthoMat3::new(LCD_WIDTH as f32, LCD_HEIGHT as f32, 0.0, 1.0);
            // Reverse y coord, and translate origin to top left
            let adjust = Mat4::new(
                1.0f32, 0.0, 0.0, (LCD_WIDTH as f32) / -2.0,
                0.0, -1.0, 0.0, (LCD_HEIGHT as f32) / 2.0,
                0.0, 0.0, 1.0, 0.0,
                0.0, 0.0, 0.0, 1.0
                );
            orthomat.to_mat() * adjust
        };
        GbDisplay {
            simple_surface: vertbuf,
            simple_surface_idx: idxbuf,
            color_prog: colorprog,
            tex_prog: texprog,
            projection: projection,
            ly_counter: 0,
        }
    }

    pub fn inc_ly_counter(&mut self) -> u8 {
        if self.ly_counter >= 153 {
            self.ly_counter = 0;
        } else {
            self.ly_counter += 1;
        }
        self.ly_counter
    }

    pub fn set_ly_vblank(&mut self) -> u8 {
        self.ly_counter = 144;
        self.ly_counter
    }

    pub fn clear_viewport(&mut self, frame: &mut Frame, view: Rect, color: (f32, f32, f32, f32)) {
        let params = DrawParameters {
            viewport: Some(view),
            .. Default::default()
        };
        let uniforms = uniform! {
            projection: self.projection,
            translate: (0.0f32, 0.0),
            color: color,
        };
        frame.draw(&self.simple_surface, &self.simple_surface_idx, &self.color_prog, &uniforms, &params);
    }

    pub fn draw<F>(&mut self, display: &F, frame: &mut Frame, view: Rect, mem: &AddressSpace) where F: Facade {
        let lcdc_reg = mem[0xFF40];
        let lcd_on                  = (lcdc_reg & 0x80) != 0;
        let win_map_addr            = if (lcdc_reg & 0x40) == 0 { 0x9800 } else { 0x9C00 };
        let win_on                  = (lcdc_reg & 0x20) != 0;
        let (tile_data, signed_idx) = if (lcdc_reg & 0x10) == 0 { (0x8800, false) }
                                        else { (0x8000, true) };
        let bg_map_addr             = if (lcdc_reg & 0x08) == 0 { 0x9800 } else { 0x9C00 };
        let sprite_height           = if (lcdc_reg & 0x04) == 0 { 8 } else { 16 };
        let sprite_on               = (lcdc_reg & 0x02) != 0;
        let bg_on                   = (lcdc_reg & 0x01) != 0;
        let scroll_y = mem[0xFF42];
        let scroll_x = mem[0xFF43];
        let bg_palette = build_palette(mem[0xFF47]);
        let sprite_palette0 = build_palette(mem[0xFF48]);
        let sprite_palette1 = build_palette(mem[0xFF49]);
        let win_y = mem[0xFF4A];
        let win_x = mem[0xFF4B];

        let params = DrawParameters {
            blend: Blend::alpha_blending(),
            viewport: Some(view),
            .. Default::default()
        };

        // Draw BG
        if bg_on {
            let opts = TileOpts {
                map_addr: bg_map_addr,
                tile_addr: tile_data,
                signed_idx: signed_idx,
                palette: bg_palette,
            };
            let bg_tex = build_tile_tex(display, mem, &opts);
            let uniforms = uniform! {
                projection: self.projection,
                translate: (scroll_x as f32, scroll_y as f32),
                tex: &bg_tex,
            };
            frame.draw(&self.simple_surface, &self.simple_surface_idx, &self.tex_prog, &uniforms, &params);
        }

        // Draw window
        if win_on {
            let opts = TileOpts {
                map_addr: win_map_addr,
                tile_addr: tile_data,
                signed_idx: signed_idx,
                palette: bg_palette,
            };
            let win_tex = build_tile_tex(display, mem, &opts);
            let uniforms = uniform! {
                projection: self.projection,
                translate: (win_x as f32, win_y as f32),
                tex: &win_tex,
            };
            frame.draw(&self.simple_surface, &self.simple_surface_idx, &self.tex_prog, &uniforms, &params);
        }
    }
}

const PALETTE_COLOR0: (f32, f32, f32, f32) = (1.0, 1.0, 1.0, 1.0);
const PALETTE_COLOR1: (f32, f32, f32, f32) = (0.25, 0.25, 0.25, 0.25);
const PALETTE_COLOR2: (f32, f32, f32, f32) = (0.75, 0.75, 0.75, 0.75);
const PALETTE_COLOR3: (f32, f32, f32, f32) = (0.0, 0.0, 0.0, 0.0);

fn build_palette(bits: u8) -> [(f32, f32, f32, f32); 4] {
    let c0 = bits & 0x03;
    let c1 = bits & 0x0C >> 2;
    let c2 = bits & 0x30 >> 4;
    let c3 = bits & 0xC0 >> 6;
    let mut colors = [PALETTE_COLOR0; 4];
    colors[c0 as usize] = PALETTE_COLOR0;
    colors[c1 as usize] = PALETTE_COLOR1;
    colors[c2 as usize] = PALETTE_COLOR2;
    colors[c3 as usize] = PALETTE_COLOR3;
    colors
}

struct TileOpts {
    map_addr: u16,
    tile_addr: u16,
    signed_idx: bool,
    palette: [(f32, f32, f32, f32); 4],
}

fn build_tile_tex<F>(display: &F, mem: &AddressSpace, opts: &TileOpts) -> Texture2d where F: Facade {
    // Data layout: data[y][x], origin top left
    let mut data: Vec<Vec<(f32, f32, f32, f32)>> = Vec::with_capacity(256);
    for i in 0..256 {
        data.push(Vec::with_capacity(256));
    }
    for i in 0..1024 {
        let offset: i32 = if opts.signed_idx {
            // Abuse conversion operations
            let tmp = mem[opts.map_addr + i] as i8;
            tmp as i32
        } else {
            mem[opts.map_addr + i] as i32
        };
        let addr = (opts.tile_addr as i32 + offset) as u16;
        let y_coord = i / 32;
        // Pixel format: big endian pixel pos
        for j in 0..8 {
            let lo = mem[addr + j*2];
            let hi = mem[addr + j*2 + 1];
            for k in (0..8).rev() {
                let mask = 1 << k;
                let color = ((lo & mask) >> k) | (((hi & mask) >> k) << 1);
                data[(y_coord + j) as usize].push(opts.palette[color as usize]);
            }
        }
    }
    Texture2d::new(display, data).unwrap()
}

pub fn calculate_viewport(width: u32, height: u32) -> Rect {
    let aspect = (width as f32) / (height as f32);
    if aspect > LCD_ASPECT {
        let fixwidth = ((height as f32) * LCD_ASPECT) as u32;
        Rect {
            left: (width - fixwidth) / 2,
            bottom: 0,
            width: fixwidth,
            height: height,
        }
    } else {
        let fixheight = ((width as f32) / LCD_ASPECT) as u32;
        Rect {
            left: 0,
            bottom: (height - fixheight) / 2,
            width: width,
            height: fixheight,
        }
    }
}
<commit_msg>Correct behavior of palette generator<commit_after>use glium::VertexBuffer;
use glium::IndexBuffer;
use glium::Program;
use glium::DrawParameters;
use glium::Surface;
use glium::Frame;
use glium::Rect;
use glium::Texture2d;
use glium::Blend;
use glium::index::PrimitiveType;
use glium::backend::Facade;

use nalgebra::Mat4;
use nalgebra::OrthoMat3;

use mem::MemSection;
use mem::AddressSpace;

pub const LCD_WIDTH: u32    = 160;
pub const LCD_HEIGHT: u32   = 144;
pub const LCD_ASPECT: f32   = (LCD_WIDTH as f32) / (LCD_HEIGHT as f32);

pub const BG_SIZE: u32      = 256;

/// Period of the V-Blank in ns. V-Blank frequency is ~59.7 Hz
pub const VBLANK_PERIOD: u64 = 16_750_419;

/// Period of the H-Blank in ns. H-Blank frequency is ~9198 Hz
pub const HBLANK_PERIOD: u64 = 108_719;

static SIMPLE_VERT: &'static str = r#"
#version 140

in vec2 coord;
in vec2 tcoord;

out vec2 tex_coord;

uniform mat4 projection;
uniform vec2 translate;

void main() {
    tex_coord = tcoord;
    gl_Position = projection * vec4(coord.x + translate.x, coord.y + translate.y, 0.0, 1.0);
}
"#;

static COLOR_FRAG: &'static str = r#"
#version 140

in vec2 tex_coord;

out vec4 frag_color;

uniform vec4 color;
uniform vec2 translate;

void main() {
    frag_color = color;
}
"#;

static TEXTURE_FRAG: &'static str = r#"
#version 140

in vec2 tex_coord;

out vec4 frag_color;

uniform sampler2D tex;

void main() {
    frag_color = texture(tex, tex_coord);
}
"#;

#[derive(Copy, Clone)]
struct Vertex {
    coord: [f32; 2],
    tcoord: [f32; 2],
}

implement_vertex!(Vertex, coord, tcoord);

pub struct GbDisplay {
    simple_surface: VertexBuffer<Vertex>,
    simple_surface_idx: IndexBuffer<u32>,
    color_prog: Program,
    tex_prog: Program,
    projection: Mat4<f32>,
    ly_counter: u8,
}

impl GbDisplay {

    pub fn new<F>(display: &F) -> GbDisplay where F: Facade {
        let (vertbuf, idxbuf) = {
            let topleft = Vertex {
                coord: [0.0, 0.0],
                tcoord: [0.0, 1.0],
            };
            let topright = Vertex {
                coord: [BG_SIZE as f32, 0.0],
                tcoord: [1.0, 1.0],
            };
            let bottomright = Vertex {
                coord: [BG_SIZE as f32, BG_SIZE as f32],
                tcoord: [1.0, 0.0],
            };
            let bottomleft = Vertex {
                coord: [0.0, BG_SIZE as f32],
                tcoord: [0.0, 0.0],
            };
            let vertices = vec![topleft, topright, bottomright, bottomleft];
            let indices = vec![0, 1, 3, 1, 2, 3];
            (
                VertexBuffer::new(display, &vertices).unwrap(),
                IndexBuffer::immutable(display, PrimitiveType::TrianglesList, &indices).unwrap()
            )
        };
        let colorprog = Program::from_source(display, SIMPLE_VERT, COLOR_FRAG, None).unwrap();
        let texprog = Program::from_source(display, SIMPLE_VERT, TEXTURE_FRAG, None).unwrap();
        let projection = {
            let orthomat = OrthoMat3::new(LCD_WIDTH as f32, LCD_HEIGHT as f32, 0.0, 1.0);
            // Reverse y coord, and translate origin to top left
            let adjust = Mat4::new(
                1.0f32, 0.0, 0.0, (LCD_WIDTH as f32) / -2.0,
                0.0, -1.0, 0.0, (LCD_HEIGHT as f32) / 2.0,
                0.0, 0.0, 1.0, 0.0,
                0.0, 0.0, 0.0, 1.0
                );
            orthomat.to_mat() * adjust
        };
        GbDisplay {
            simple_surface: vertbuf,
            simple_surface_idx: idxbuf,
            color_prog: colorprog,
            tex_prog: texprog,
            projection: projection,
            ly_counter: 0,
        }
    }

    pub fn inc_ly_counter(&mut self) -> u8 {
        if self.ly_counter >= 153 {
            self.ly_counter = 0;
        } else {
            self.ly_counter += 1;
        }
        self.ly_counter
    }

    pub fn set_ly_vblank(&mut self) -> u8 {
        self.ly_counter = 144;
        self.ly_counter
    }

    pub fn clear_viewport(&mut self, frame: &mut Frame, view: Rect, color: (f32, f32, f32, f32)) {
        let params = DrawParameters {
            viewport: Some(view),
            .. Default::default()
        };
        let uniforms = uniform! {
            projection: self.projection,
            translate: (0.0f32, 0.0),
            color: color,
        };
        frame.draw(&self.simple_surface, &self.simple_surface_idx, &self.color_prog, &uniforms, &params);
    }

    pub fn draw<F>(&mut self, display: &F, frame: &mut Frame, view: Rect, mem: &AddressSpace) where F: Facade {
        let lcdc_reg = mem[0xFF40];
        let lcd_on                  = (lcdc_reg & 0x80) != 0;
        let win_map_addr            = if (lcdc_reg & 0x40) == 0 { 0x9800 } else { 0x9C00 };
        let win_on                  = (lcdc_reg & 0x20) != 0;
        let (tile_data, signed_idx) = if (lcdc_reg & 0x10) == 0 { (0x8800, false) }
                                        else { (0x8000, true) };
        let bg_map_addr             = if (lcdc_reg & 0x08) == 0 { 0x9800 } else { 0x9C00 };
        let sprite_height           = if (lcdc_reg & 0x04) == 0 { 8 } else { 16 };
        let sprite_on               = (lcdc_reg & 0x02) != 0;
        let bg_on                   = (lcdc_reg & 0x01) != 0;
        let scroll_y = mem[0xFF42];
        let scroll_x = mem[0xFF43];
        let bg_palette = build_palette(mem[0xFF47]);
        let sprite_palette0 = build_palette(mem[0xFF48]);
        let sprite_palette1 = build_palette(mem[0xFF49]);
        let win_y = mem[0xFF4A];
        let win_x = mem[0xFF4B];

        let params = DrawParameters {
            blend: Blend::alpha_blending(),
            viewport: Some(view),
            .. Default::default()
        };

        // Draw BG
        if bg_on {
            let opts = TileOpts {
                map_addr: bg_map_addr,
                tile_addr: tile_data,
                signed_idx: signed_idx,
                palette: bg_palette,
            };
            let bg_tex = build_tile_tex(display, mem, &opts);
            let uniforms = uniform! {
                projection: self.projection,
                translate: (scroll_x as f32, scroll_y as f32),
                tex: &bg_tex,
            };
            frame.draw(&self.simple_surface, &self.simple_surface_idx, &self.tex_prog, &uniforms, &params);
        }

        // Draw window
        if win_on {
            let opts = TileOpts {
                map_addr: win_map_addr,
                tile_addr: tile_data,
                signed_idx: signed_idx,
                palette: bg_palette,
            };
            let win_tex = build_tile_tex(display, mem, &opts);
            let uniforms = uniform! {
                projection: self.projection,
                translate: (win_x as f32, win_y as f32),
                tex: &win_tex,
            };
            frame.draw(&self.simple_surface, &self.simple_surface_idx, &self.tex_prog, &uniforms, &params);
        }
    }
}

const PALETTE_COLOR0: (f32, f32, f32, f32) = (1.0, 1.0, 1.0, 1.0);
const PALETTE_COLOR1: (f32, f32, f32, f32) = (0.25, 0.25, 0.25, 1.0);
const PALETTE_COLOR2: (f32, f32, f32, f32) = (0.75, 0.75, 0.75, 1.0);
const PALETTE_COLOR3: (f32, f32, f32, f32) = (0.0, 0.0, 0.0, 1.0);
const PALETTE_COLORTEST: (f32, f32, f32, f32) = (1.0, 0.5, 0.75, 1.0);

fn build_palette(bits: u8) -> [(f32, f32, f32, f32); 4] {
    let c0 = (bits & 0x03);
    let c1 = (bits & 0x0C) >> 2;
    let c2 = (bits & 0x30) >> 4;
    let c3 = (bits & 0xC0) >> 6;
    let mut colors = [PALETTE_COLOR0; 4];
    colors[0] = match c0 {
        0 => PALETTE_COLOR0,
        1 => PALETTE_COLOR1,
        2 => PALETTE_COLOR2,
        3 => PALETTE_COLOR3,
        _ => PALETTE_COLORTEST,
    };
    colors[1] = match c1 {
        0 => PALETTE_COLOR0,
        1 => PALETTE_COLOR1,
        2 => PALETTE_COLOR2,
        3 => PALETTE_COLOR3,
        _ => PALETTE_COLORTEST,
    };
    colors[2] = match c2 {
        0 => PALETTE_COLOR0,
        1 => PALETTE_COLOR1,
        2 => PALETTE_COLOR2,
        3 => PALETTE_COLOR3,
        _ => PALETTE_COLORTEST,
    };
    colors[3] = match c3 {
        0 => PALETTE_COLOR0,
        1 => PALETTE_COLOR1,
        2 => PALETTE_COLOR2,
        3 => PALETTE_COLOR3,
        _ => PALETTE_COLORTEST,
    };
    colors
}

struct TileOpts {
    map_addr: u16,
    tile_addr: u16,
    signed_idx: bool,
    palette: [(f32, f32, f32, f32); 4],
}

fn build_tile_tex<F>(display: &F, mem: &AddressSpace, opts: &TileOpts) -> Texture2d where F: Facade {
    // Data layout: data[y][x], origin top left
    let mut data: Vec<Vec<(f32, f32, f32, f32)>> = Vec::with_capacity(256);
    for i in 0..256 {
        data.push(Vec::with_capacity(256));
    }
    for i in 0..1024 {
        let offset: i32 = if opts.signed_idx {
            // Abuse conversion operations
            let tmp = mem[opts.map_addr + i] as i8;
            tmp as i32
        } else {
            mem[opts.map_addr + i] as i32
        };
        let addr = (opts.tile_addr as i32 + offset) as u16;
        let y_coord = i / 32;
        // Pixel format: big endian pixel pos
        for j in 0..8 {
            let lo = mem[addr + j*2];
            let hi = mem[addr + j*2 + 1];
            for k in (0..8).rev() {
                let mask = 1 << k;
                let color = ((lo & mask) >> k) | (((hi & mask) >> k) << 1);
                data[(y_coord + j) as usize].push(opts.palette[color as usize]);
            }
        }
    }
    Texture2d::new(display, data).unwrap()
}

pub fn calculate_viewport(width: u32, height: u32) -> Rect {
    let aspect = (width as f32) / (height as f32);
    if aspect > LCD_ASPECT {
        let fixwidth = ((height as f32) * LCD_ASPECT) as u32;
        Rect {
            left: (width - fixwidth) / 2,
            bottom: 0,
            width: fixwidth,
            height: height,
        }
    } else {
        let fixheight = ((width as f32) / LCD_ASPECT) as u32;
        Rect {
            left: 0,
            bottom: (height - fixheight) / 2,
            width: width,
            height: fixheight,
        }
    }
}
<|endoftext|>


<commit_before>use ::*;

fn out<S: ToString>(screen : &mut RawTerminal<Stdout>, output : S) {
    write!(*screen, "{}", output.to_string()).unwrap();
}

fn curs(screen : &mut RawTerminal<Stdout>, coord : Coord) {
    // Coordinate translation assumes floor is being rendered at (1, 1)
    out(screen, termion::cursor::Goto(coord.col+1, coord.row+1));
}

fn color<C: Color>(screen : &mut RawTerminal<Stdout>, clr : C) {
    out(screen, Fg(clr));
}

pub fn render_loop(stop         : Arc<Mutex<bool>>,
                   floor        : Arc<Mutex<Floor>>,
                   dirty_coords : Arc<Mutex<Vec<Coord>>>,
                   messages     : Arc<Mutex<Vec<String>>>,
                   player       : Arc<Mutex<Player>>,
                   monsters     : Arc<Mutex<Vec<Monster>>>) {

    let mut screen = &mut stdout().into_raw_mode().unwrap();
    out(screen, termion::cursor::Hide); // Hide the cursor
    out(screen, termion::clear::All);   // Clear the screen

    // Draw the entire floor
    curs(screen, Coord::new(0,0));
    {
        let floor = floor.lock().unwrap();
        for row in &floor.tiles {
            for tile in row {
                if let Some(ref wall) = tile.wall {
                    out(screen, wall);
                } else {
                    out(screen, " ");
                }
            }
            out(screen, "\r\n");
        }
    }

    // Render Loop
    loop {
        sleep(Duration::from_millis(10));
        {
            if *stop.lock().unwrap() {
                break;
            }
        }

        // Lock floor first...
        let floor = floor.lock().unwrap();

        // Redraw any dirty coordinates with floor tiles
        let mut dirty_coords = dirty_coords.lock().unwrap();
        for coord in dirty_coords.drain(..) {
            curs(screen, coord);
            out(screen, floor.get_symbol(&coord));
        }

        // Render Player
        {
            let mut player = player.lock().unwrap();
            if player.dirty {
                player.dirty = false;
                // Player's sword
                curs(screen, player.sword_coord);
                color(screen, Red);
                out(screen, sword_symbol(&player.facing));
                // Player himself
                curs(screen, player.coord);
                color(screen, Blue);
                out(screen, &player.symbol);
                color(screen, Reset);
            }
            // Player Score
            let score_string = format!("Score: {}", player.score);
            curs(screen, Coord::new((floor.cols - score_string.len()) as u16,
                                    floor.rows as u16));
            out(screen, score_string);
        }

        // Render Monsters
        {
            let monsters = monsters.lock().unwrap();
            for monster in monsters.iter() {
                curs(screen, monster.coord);
                color(screen, Green);
                out(screen, &monster.symbol);
                color(screen, Reset);
            }
        }

        // Game Title
        curs(screen, Coord::new(0, floor.rows as u16));
        out(screen, "Rusty Sword - Game of Infamy!");

        // Messages
        curs(screen, Coord::new(0, (floor.rows + 2) as u16));
        let mut messages = messages.lock().unwrap();
        if messages.len() > 4 {
            messages.remove(0);
        }
        for msg in messages.iter() {
            if msg.contains("spawned") {
                color(screen, Green);
            } else if msg.contains("You killed") {
                color(screen, Blue);
            } else if msg.contains("were eaten") {
                color(screen, Red);
            } else {
                color(screen, LightWhite);
            }
            out(screen, msg);
            out(screen, format!("{}\n\r", clear::UntilNewline));
            color(screen, Reset);
        }

        screen.flush().unwrap();
    }

    // Nice cleanup: Move cursor below the floor, so we can see how we finished
    {
        let floor = floor.lock().unwrap();
        curs(screen, Coord::new(0, (floor.rows+7) as u16));
    }
    out(screen, termion::cursor::Show); // Show the cursor again
    screen.flush().unwrap();
}

<commit_msg>Make it easier to add render_loop arguments as we go.<commit_after>use ::*;

fn out<S: ToString>(screen : &mut RawTerminal<Stdout>, output : S) {
    write!(*screen, "{}", output.to_string()).unwrap();
}

fn curs(screen : &mut RawTerminal<Stdout>, coord : Coord) {
    // Coordinate translation assumes floor is being rendered at (1, 1)
    out(screen, termion::cursor::Goto(coord.col+1, coord.row+1));
}

fn color<C: Color>(screen : &mut RawTerminal<Stdout>, clr : C) {
    out(screen, Fg(clr));
}

pub fn render_loop(
    stop         : Arc<Mutex<bool>>,
    floor        : Arc<Mutex<Floor>>,
    dirty_coords : Arc<Mutex<Vec<Coord>>>,
    messages     : Arc<Mutex<Vec<String>>>,
    player       : Arc<Mutex<Player>>,
    monsters     : Arc<Mutex<Vec<Monster>>>,
) {
    let mut screen = &mut stdout().into_raw_mode().unwrap();
    out(screen, termion::cursor::Hide); // Hide the cursor
    out(screen, termion::clear::All);   // Clear the screen

    // Draw the entire floor
    curs(screen, Coord::new(0,0));
    {
        let floor = floor.lock().unwrap();
        for row in &floor.tiles {
            for tile in row {
                if let Some(ref wall) = tile.wall {
                    out(screen, wall);
                } else {
                    out(screen, " ");
                }
            }
            out(screen, "\r\n");
        }
    }

    // Render Loop
    loop {
        sleep(Duration::from_millis(10));
        {
            if *stop.lock().unwrap() {
                break;
            }
        }

        // Lock floor first...
        let floor = floor.lock().unwrap();

        // Redraw any dirty coordinates with floor tiles
        let mut dirty_coords = dirty_coords.lock().unwrap();
        for coord in dirty_coords.drain(..) {
            curs(screen, coord);
            out(screen, floor.get_symbol(&coord));
        }

        // Render Player
        {
            let mut player = player.lock().unwrap();
            if player.dirty {
                player.dirty = false;
                // Player's sword
                curs(screen, player.sword_coord);
                color(screen, Red);
                out(screen, sword_symbol(&player.facing));
                // Player himself
                curs(screen, player.coord);
                color(screen, Blue);
                out(screen, &player.symbol);
                color(screen, Reset);
            }
            // Player Score
            let score_string = format!("Score: {}", player.score);
            curs(screen, Coord::new((floor.cols - score_string.len()) as u16,
                                    floor.rows as u16));
            out(screen, score_string);
        }

        // Render Monsters
        {
            let monsters = monsters.lock().unwrap();
            for monster in monsters.iter() {
                curs(screen, monster.coord);
                color(screen, Green);
                out(screen, &monster.symbol);
                color(screen, Reset);
            }
        }

        // Game Title
        curs(screen, Coord::new(0, floor.rows as u16));
        out(screen, "Rusty Sword - Game of Infamy!");

        // Messages
        curs(screen, Coord::new(0, (floor.rows + 2) as u16));
        let mut messages = messages.lock().unwrap();
        if messages.len() > 4 {
            messages.remove(0);
        }
        for msg in messages.iter() {
            if msg.contains("spawned") {
                color(screen, Green);
            } else if msg.contains("You killed") {
                color(screen, Blue);
            } else if msg.contains("were eaten") {
                color(screen, Red);
            } else {
                color(screen, LightWhite);
            }
            out(screen, msg);
            out(screen, format!("{}\n\r", clear::UntilNewline));
            color(screen, Reset);
        }

        screen.flush().unwrap();
    }

    // Nice cleanup: Move cursor below the floor, so we can see how we finished
    {
        let floor = floor.lock().unwrap();
        curs(screen, Coord::new(0, (floor.rows+7) as u16));
    }
    out(screen, termion::cursor::Show); // Show the cursor again
    screen.flush().unwrap();
}

<|endoftext|>