<commit_before>//! Messages to and from the server.
use std::borrow::ToOwned;
use std::fmt::{Display, Formatter, Result as FmtResult};
use std::str::FromStr;

use error;
use error::{Error, ErrorKind};
use proto::{Command, ChannelExt};

/// IRC Message data.
#[derive(Clone, PartialEq, Debug)]
pub struct Message {
    /// Message tags as defined by [IRCv3.2](http://ircv3.net/specs/core/message-tags-3.2.html).
    pub tags: Option<Vec<Tag>>,
    /// The message prefix (or source) as defined by [RFC 2812](http://tools.ietf.org/html/rfc2812).
    pub prefix: Option<String>,
    /// The IRC command.
    pub command: Command,
}

impl Message {
    /// Creates a new Message.
    pub fn new(
        prefix: Option<&str>,
        command: &str,
        args: Vec<&str>,
        suffix: Option<&str>,
    ) -> error::Result<Message> {
        Message::with_tags(None, prefix, command, args, suffix)
    }

    /// Creates a new Message optionally including IRCv3.2 message tags.
    pub fn with_tags(
        tags: Option<Vec<Tag>>,
        prefix: Option<&str>,
        command: &str,
        args: Vec<&str>,
        suffix: Option<&str>,
    ) -> error::Result<Message> {
        Ok(Message {
            tags: tags,
            prefix: prefix.map(|s| s.to_owned()),
            command: Command::new(command, args, suffix)?,
        })
    }

    /// Gets the nickname of the message source, if it exists.
    pub fn source_nickname(&self) -> Option<&str> {
        // <prefix> ::= <servername> | <nick> [ '!' <user> ] [ '@' <host> ]
        // <servername> ::= <host>
        self.prefix.as_ref().and_then(|s| match (
            s.find('!'),
            s.find('@'),
            s.find('.'),
        ) {
            (Some(i), _, _) => Some(&s[..i]), // <nick> '!' <user> [ '@' <host> ]
            (None, Some(i), _) => Some(&s[..i]), // <nick> '@' <host>
            (None, None, None) => Some(s), // <nick>
            _ => None, // <servername>
        })
    }

    /// Gets the likely intended place to respond to this message.
    /// If the type of the message is a `PRIVMSG` or `NOTICE` and the message is sent to a channel,
    /// the result will be that channel. In all other cases, this will call `source_nickname`.
    pub fn response_target(&self) -> Option<&str> {
        match self.command {
            Command::PRIVMSG(ref target, _) if target.is_channel_name() => Some(target),
            Command::NOTICE(ref target, _) if target.is_channel_name() => Some(target),
            _ => self.source_nickname()
        }
    }

    /// Converts a Message into a String according to the IRC protocol.
    pub fn to_string(&self) -> String {
        let mut ret = String::new();
        if let Some(ref tags) = self.tags {
            ret.push('@');
            for tag in tags {
                ret.push_str(&tag.0);
                if let Some(ref value) = tag.1 {
                    ret.push('=');
                    ret.push_str(value);
                }
                ret.push(';');
            }
            ret.pop();
            ret.push(' ');
        }
        if let Some(ref prefix) = self.prefix {
            ret.push(':');
            ret.push_str(prefix);
            ret.push(' ');
        }
        let cmd: String = From::from(&self.command);
        ret.push_str(&cmd);
        ret.push_str("\r\n");
        ret
    }
}

impl From<Command> for Message {
    fn from(cmd: Command) -> Message {
        Message {
            tags: None,
            prefix: None,
            command: cmd,
        }
    }
}

impl FromStr for Message {
    type Err = Error;

    fn from_str(s: &str) -> Result<Message, Self::Err> {
        let mut state = s;
        if s.is_empty() {
            return Err(ErrorKind::ParseEmpty.into());
        }
        let tags = if state.starts_with('@') {
            let tags = state.find(' ').map(|i| &state[1..i]);
            state = state.find(' ').map_or("", |i| &state[i + 1..]);
            tags.map(|ts| {
                ts.split(';')
                    .filter(|s| !s.is_empty())
                    .map(|s: &str| {
                        let mut iter = s.splitn(2, '=');
                        let (fst, snd) = (iter.next(), iter.next());
                        Tag(fst.unwrap_or("").to_owned(), snd.map(|s| s.to_owned()))
                    })
                    .collect::<Vec<_>>()
            })
        } else {
            None
        };
        let prefix = if state.starts_with(':') {
            let prefix = state.find(' ').map(|i| &state[1..i]);
            state = state.find(' ').map_or("", |i| &state[i + 1..]);
            prefix
        } else {
            None
        };
        let suffix = if state.contains(" :") {
            let suffix = state.find(" :").map(|i| &state[i + 2..state.len() - 2]);
            state = state.find(" :").map_or("", |i| &state[..i + 1]);
            suffix
        } else {
            None
        };
        let command = match state.find(' ').map(|i| &state[..i]) {
            Some(cmd) => {
                state = state.find(' ').map_or("", |i| &state[i + 1..]);
                cmd
            }
            _ => return Err(ErrorKind::InvalidCommand.into()),
        };
        if suffix.is_none() {
            state = &state[..state.len() - 2]
        }
        let args: Vec<_> = state.splitn(14, ' ').filter(|s| !s.is_empty()).collect();
        Message::with_tags(tags, prefix, command, args, suffix)
            .map_err(|_| ErrorKind::InvalidCommand.into())
    }
}

impl<'a> From<&'a str> for Message {
    fn from(s: &'a str) -> Message {
        s.parse().unwrap()
    }
}

impl Display for Message {
    fn fmt(&self, f: &mut Formatter) -> FmtResult {
        write!(f, "{}", self.to_string())
    }
}

/// A message tag as defined by [IRCv3.2](http://ircv3.net/specs/core/message-tags-3.2.html).
#[derive(Clone, PartialEq, Debug)]
pub struct Tag(pub String, pub Option<String>);

#[cfg(test)]
mod test {
    use super::{Message, Tag};
    use proto::Command::{PRIVMSG, Raw};

    #[test]
    fn new() {
        let message = Message {
            tags: None,
            prefix: None,
            command: PRIVMSG(format!("test"), format!("Testing!")),
        };
        assert_eq!(
            Message::new(None, "PRIVMSG", vec!["test"], Some("Testing!")).unwrap(),
            message
        )
    }

    #[test]
    fn source_nickname() {
        assert_eq!(
            Message::new(None, "PING", vec![], Some("data"))
                .unwrap()
                .source_nickname(),
            None
        );

        assert_eq!(
            Message::new(Some("irc.test.net"), "PING", vec![], Some("data"))
                .unwrap()
                .source_nickname(),
            None
        );

        assert_eq!(
            Message::new(Some("test!test@test"), "PING", vec![], Some("data"))
                .unwrap()
                .source_nickname(),
            Some("test")
        );

        assert_eq!(
            Message::new(Some("test@test"), "PING", vec![], Some("data"))
                .unwrap()
                .source_nickname(),
            Some("test")
        );

        assert_eq!(
            Message::new(Some("test!test@irc.test.com"), "PING", vec![], Some("data"))
                .unwrap()
                .source_nickname(),
            Some("test")
        );

        assert_eq!(
            Message::new(Some("test!test@127.0.0.1"), "PING", vec![], Some("data"))
                .unwrap()
                .source_nickname(),
            Some("test")
        );

        assert_eq!(
            Message::new(Some("test@test.com"), "PING", vec![], Some("data"))
                .unwrap()
                .source_nickname(),
            Some("test")
        );

        assert_eq!(
            Message::new(Some("test"), "PING", vec![], Some("data"))
                .unwrap()
                .source_nickname(),
            Some("test")
        );
    }

    #[test]
    fn to_string() {
        let message = Message {
            tags: None,
            prefix: None,
            command: PRIVMSG(format!("test"), format!("Testing!")),
        };
        assert_eq!(&message.to_string()[..], "PRIVMSG test :Testing!\r\n");
        let message = Message {
            tags: None,
            prefix: Some(format!("test!test@test")),
            command: PRIVMSG(format!("test"), format!("Still testing!")),
        };
        assert_eq!(
            &message.to_string()[..],
            ":test!test@test PRIVMSG test :Still testing!\r\n"
        );
    }

    #[test]
    fn from_string() {
        let message = Message {
            tags: None,
            prefix: None,
            command: PRIVMSG(format!("test"), format!("Testing!")),
        };
        assert_eq!(
            "PRIVMSG test :Testing!\r\n".parse::<Message>().unwrap(),
            message
        );
        let message = Message {
            tags: None,
            prefix: Some(format!("test!test@test")),
            command: PRIVMSG(format!("test"), format!("Still testing!")),
        };
        assert_eq!(
            ":test!test@test PRIVMSG test :Still testing!\r\n"
                .parse::<Message>()
                .unwrap(),
            message
        );
        let message = Message {
            tags: Some(vec![
                Tag(format!("aaa"), Some(format!("bbb"))),
                Tag(format!("ccc"), None),
                Tag(format!("example.com/ddd"), Some(format!("eee"))),
            ]),
            prefix: Some(format!("test!test@test")),
            command: PRIVMSG(format!("test"), format!("Testing with tags!")),
        };
        assert_eq!(
            "@aaa=bbb;ccc;example.com/ddd=eee :test!test@test PRIVMSG test :Testing with \
                    tags!\r\n"
                .parse::<Message>()
                .unwrap(),
            message
        )
    }

    #[test]
    fn from_and_to_string() {
        let message = "@aaa=bbb;ccc;example.com/ddd=eee :test!test@test PRIVMSG test :Testing with \
                       tags!\r\n";
        assert_eq!(message.parse::<Message>().unwrap().to_string(), message);
    }

    #[test]
    fn to_message() {
        let message = Message {
            tags: None,
            prefix: None,
            command: PRIVMSG(format!("test"), format!("Testing!")),
        };
        let msg: Message = "PRIVMSG test :Testing!\r\n".into();
        assert_eq!(msg, message);
        let message = Message {
            tags: None,
            prefix: Some(format!("test!test@test")),
            command: PRIVMSG(format!("test"), format!("Still testing!")),
        };
        let msg: Message = ":test!test@test PRIVMSG test :Still testing!\r\n".into();
        assert_eq!(msg, message);
    }

    #[test]
    fn to_message_with_colon_in_arg() {
        // Apparently, UnrealIRCd (and perhaps some others) send some messages that include
        // colons within individual parameters. So, let's make sure it parses correctly.
        let message = Message {
            tags: None,
            prefix: Some(format!("test!test@test")),
            command: Raw(
                format!("COMMAND"),
                vec![format!("ARG:test")],
                Some(format!("Testing!")),
            ),
        };
        let msg: Message = ":test!test@test COMMAND ARG:test :Testing!\r\n".into();
        assert_eq!(msg, message);
    }

    #[test]
    #[should_panic]
    fn to_message_invalid_format() {
        let _: Message = ":invalid :message".into();
    }
}
<commit_msg>Improved the documentation for irc::proto::message a bit.<commit_after>//! A module providing a data structure for messages to and from IRC servers.
use std::borrow::ToOwned;
use std::fmt::{Display, Formatter, Result as FmtResult};
use std::str::FromStr;

use error;
use error::{Error, ErrorKind};
use proto::{Command, ChannelExt};

/// A data structure representing an IRC message according to the protocol specification.
#[derive(Clone, PartialEq, Debug)]
pub struct Message {
    /// Message tags as defined by [IRCv3.2](http://ircv3.net/specs/core/message-tags-3.2.html).
    pub tags: Option<Vec<Tag>>,
    /// The message prefix (or source) as defined by [RFC 2812](http://tools.ietf.org/html/rfc2812).
    pub prefix: Option<String>,
    /// The IRC command.
    pub command: Command,
}

impl Message {
    /// Creates a new message from the given components.
    pub fn new(
        prefix: Option<&str>,
        command: &str,
        args: Vec<&str>,
        suffix: Option<&str>,
    ) -> error::Result<Message> {
        Message::with_tags(None, prefix, command, args, suffix)
    }

    /// Creates a new IRCv3.2 message from the given components, including message tags.
    pub fn with_tags(
        tags: Option<Vec<Tag>>,
        prefix: Option<&str>,
        command: &str,
        args: Vec<&str>,
        suffix: Option<&str>,
    ) -> error::Result<Message> {
        Ok(Message {
            tags: tags,
            prefix: prefix.map(|s| s.to_owned()),
            command: Command::new(command, args, suffix)?,
        })
    }

    /// Gets the nickname of the message source, if it exists.
    pub fn source_nickname(&self) -> Option<&str> {
        // <prefix> ::= <servername> | <nick> [ '!' <user> ] [ '@' <host> ]
        // <servername> ::= <host>
        self.prefix.as_ref().and_then(|s| match (
            s.find('!'),
            s.find('@'),
            s.find('.'),
        ) {
            (Some(i), _, _) => Some(&s[..i]), // <nick> '!' <user> [ '@' <host> ]
            (None, Some(i), _) => Some(&s[..i]), // <nick> '@' <host>
            (None, None, None) => Some(s), // <nick>
            _ => None, // <servername>
        })
    }

    /// Gets the likely intended place to respond to this message.
    /// If the type of the message is a `PRIVMSG` or `NOTICE` and the message is sent to a channel,
    /// the result will be that channel. In all other cases, this will call `source_nickname`.
    pub fn response_target(&self) -> Option<&str> {
        match self.command {
            Command::PRIVMSG(ref target, _) if target.is_channel_name() => Some(target),
            Command::NOTICE(ref target, _) if target.is_channel_name() => Some(target),
            _ => self.source_nickname()
        }
    }

    /// Converts a Message into a String according to the IRC protocol.
    pub fn to_string(&self) -> String {
        let mut ret = String::new();
        if let Some(ref tags) = self.tags {
            ret.push('@');
            for tag in tags {
                ret.push_str(&tag.0);
                if let Some(ref value) = tag.1 {
                    ret.push('=');
                    ret.push_str(value);
                }
                ret.push(';');
            }
            ret.pop();
            ret.push(' ');
        }
        if let Some(ref prefix) = self.prefix {
            ret.push(':');
            ret.push_str(prefix);
            ret.push(' ');
        }
        let cmd: String = From::from(&self.command);
        ret.push_str(&cmd);
        ret.push_str("\r\n");
        ret
    }
}

impl From<Command> for Message {
    fn from(cmd: Command) -> Message {
        Message {
            tags: None,
            prefix: None,
            command: cmd,
        }
    }
}

impl FromStr for Message {
    type Err = Error;

    fn from_str(s: &str) -> Result<Message, Self::Err> {
        let mut state = s;
        if s.is_empty() {
            return Err(ErrorKind::ParseEmpty.into());
        }
        let tags = if state.starts_with('@') {
            let tags = state.find(' ').map(|i| &state[1..i]);
            state = state.find(' ').map_or("", |i| &state[i + 1..]);
            tags.map(|ts| {
                ts.split(';')
                    .filter(|s| !s.is_empty())
                    .map(|s: &str| {
                        let mut iter = s.splitn(2, '=');
                        let (fst, snd) = (iter.next(), iter.next());
                        Tag(fst.unwrap_or("").to_owned(), snd.map(|s| s.to_owned()))
                    })
                    .collect::<Vec<_>>()
            })
        } else {
            None
        };
        let prefix = if state.starts_with(':') {
            let prefix = state.find(' ').map(|i| &state[1..i]);
            state = state.find(' ').map_or("", |i| &state[i + 1..]);
            prefix
        } else {
            None
        };
        let suffix = if state.contains(" :") {
            let suffix = state.find(" :").map(|i| &state[i + 2..state.len() - 2]);
            state = state.find(" :").map_or("", |i| &state[..i + 1]);
            suffix
        } else {
            None
        };
        let command = match state.find(' ').map(|i| &state[..i]) {
            Some(cmd) => {
                state = state.find(' ').map_or("", |i| &state[i + 1..]);
                cmd
            }
            _ => return Err(ErrorKind::InvalidCommand.into()),
        };
        if suffix.is_none() {
            state = &state[..state.len() - 2]
        }
        let args: Vec<_> = state.splitn(14, ' ').filter(|s| !s.is_empty()).collect();
        Message::with_tags(tags, prefix, command, args, suffix)
            .map_err(|_| ErrorKind::InvalidCommand.into())
    }
}

impl<'a> From<&'a str> for Message {
    fn from(s: &'a str) -> Message {
        s.parse().unwrap()
    }
}

impl Display for Message {
    fn fmt(&self, f: &mut Formatter) -> FmtResult {
        write!(f, "{}", self.to_string())
    }
}

/// A message tag as defined by [IRCv3.2](http://ircv3.net/specs/core/message-tags-3.2.html).
#[derive(Clone, PartialEq, Debug)]
pub struct Tag(pub String, pub Option<String>);

#[cfg(test)]
mod test {
    use super::{Message, Tag};
    use proto::Command::{PRIVMSG, Raw};

    #[test]
    fn new() {
        let message = Message {
            tags: None,
            prefix: None,
            command: PRIVMSG(format!("test"), format!("Testing!")),
        };
        assert_eq!(
            Message::new(None, "PRIVMSG", vec!["test"], Some("Testing!")).unwrap(),
            message
        )
    }

    #[test]
    fn source_nickname() {
        assert_eq!(
            Message::new(None, "PING", vec![], Some("data"))
                .unwrap()
                .source_nickname(),
            None
        );

        assert_eq!(
            Message::new(Some("irc.test.net"), "PING", vec![], Some("data"))
                .unwrap()
                .source_nickname(),
            None
        );

        assert_eq!(
            Message::new(Some("test!test@test"), "PING", vec![], Some("data"))
                .unwrap()
                .source_nickname(),
            Some("test")
        );

        assert_eq!(
            Message::new(Some("test@test"), "PING", vec![], Some("data"))
                .unwrap()
                .source_nickname(),
            Some("test")
        );

        assert_eq!(
            Message::new(Some("test!test@irc.test.com"), "PING", vec![], Some("data"))
                .unwrap()
                .source_nickname(),
            Some("test")
        );

        assert_eq!(
            Message::new(Some("test!test@127.0.0.1"), "PING", vec![], Some("data"))
                .unwrap()
                .source_nickname(),
            Some("test")
        );

        assert_eq!(
            Message::new(Some("test@test.com"), "PING", vec![], Some("data"))
                .unwrap()
                .source_nickname(),
            Some("test")
        );

        assert_eq!(
            Message::new(Some("test"), "PING", vec![], Some("data"))
                .unwrap()
                .source_nickname(),
            Some("test")
        );
    }

    #[test]
    fn to_string() {
        let message = Message {
            tags: None,
            prefix: None,
            command: PRIVMSG(format!("test"), format!("Testing!")),
        };
        assert_eq!(&message.to_string()[..], "PRIVMSG test :Testing!\r\n");
        let message = Message {
            tags: None,
            prefix: Some(format!("test!test@test")),
            command: PRIVMSG(format!("test"), format!("Still testing!")),
        };
        assert_eq!(
            &message.to_string()[..],
            ":test!test@test PRIVMSG test :Still testing!\r\n"
        );
    }

    #[test]
    fn from_string() {
        let message = Message {
            tags: None,
            prefix: None,
            command: PRIVMSG(format!("test"), format!("Testing!")),
        };
        assert_eq!(
            "PRIVMSG test :Testing!\r\n".parse::<Message>().unwrap(),
            message
        );
        let message = Message {
            tags: None,
            prefix: Some(format!("test!test@test")),
            command: PRIVMSG(format!("test"), format!("Still testing!")),
        };
        assert_eq!(
            ":test!test@test PRIVMSG test :Still testing!\r\n"
                .parse::<Message>()
                .unwrap(),
            message
        );
        let message = Message {
            tags: Some(vec![
                Tag(format!("aaa"), Some(format!("bbb"))),
                Tag(format!("ccc"), None),
                Tag(format!("example.com/ddd"), Some(format!("eee"))),
            ]),
            prefix: Some(format!("test!test@test")),
            command: PRIVMSG(format!("test"), format!("Testing with tags!")),
        };
        assert_eq!(
            "@aaa=bbb;ccc;example.com/ddd=eee :test!test@test PRIVMSG test :Testing with \
                    tags!\r\n"
                .parse::<Message>()
                .unwrap(),
            message
        )
    }

    #[test]
    fn from_and_to_string() {
        let message = "@aaa=bbb;ccc;example.com/ddd=eee :test!test@test PRIVMSG test :Testing with \
                       tags!\r\n";
        assert_eq!(message.parse::<Message>().unwrap().to_string(), message);
    }

    #[test]
    fn to_message() {
        let message = Message {
            tags: None,
            prefix: None,
            command: PRIVMSG(format!("test"), format!("Testing!")),
        };
        let msg: Message = "PRIVMSG test :Testing!\r\n".into();
        assert_eq!(msg, message);
        let message = Message {
            tags: None,
            prefix: Some(format!("test!test@test")),
            command: PRIVMSG(format!("test"), format!("Still testing!")),
        };
        let msg: Message = ":test!test@test PRIVMSG test :Still testing!\r\n".into();
        assert_eq!(msg, message);
    }

    #[test]
    fn to_message_with_colon_in_arg() {
        // Apparently, UnrealIRCd (and perhaps some others) send some messages that include
        // colons within individual parameters. So, let's make sure it parses correctly.
        let message = Message {
            tags: None,
            prefix: Some(format!("test!test@test")),
            command: Raw(
                format!("COMMAND"),
                vec![format!("ARG:test")],
                Some(format!("Testing!")),
            ),
        };
        let msg: Message = ":test!test@test COMMAND ARG:test :Testing!\r\n".into();
        assert_eq!(msg, message);
    }

    #[test]
    #[should_panic]
    fn to_message_invalid_format() {
        let _: Message = ":invalid :message".into();
    }
}
<|endoftext|>


<commit_before>use std::path::Path;

use builder::context::Context;
use builder::distrib::{Unknown,Distribution};
use builder::error::{Error};
use builder::commands::{composer, gem, npm, pip};
use builder::packages;
use build_step::BuildStep;
use container::util::clean_dir;
use container::mount::{unmount, mount_system_dirs, mount_proc};
use file_util::{create_dir, copy};

pub struct Guard<'a> {
    pub ctx: Context<'a>,
    pub distro: Box<Distribution>,
}

impl<'a> Guard<'a> {
    pub fn build(ctx: Context) -> Result<(), Error> {
        Guard {
            ctx: ctx,
            distro: Box::new(Unknown),
        }.run_process()
    }

    fn run_process(&mut self) -> Result<(), Error> {
        try!(self.start());

        for b in self.ctx.container_config.setup.iter() {
            debug!("Building step: {:?}", b);
            try!(self.ctx.timelog.mark(format_args!("Step: {:?}", b))
                .map_err(|e| format!("Can't write timelog: {}", e)));
            try!(b.build(self, true)
                .map_err(|e| Error::Step(b.0.clone(), e)));
        }

        try!(self.finish());
        Ok(())
    }

    pub fn start(&mut self) -> Result<(), String> {
        try!(mount_system_dirs());
        try!(mount_proc(&Path::new("/proc")));
        try!(copy("/proc/self/uid_map", "/vagga/container/uid_map")
            .map_err(|e| format!("Error copying uid_map: {}", e)));
        try!(copy("/proc/self/gid_map", "/vagga/container/gid_map")
            .map_err(|e| format!("Error copying gid_map: {}", e)));
        try_msg!(create_dir("/vagga/root/etc", false),
             "Error creating /etc dir: {err}");
        try!(copy("/etc/resolv.conf", "/vagga/root/etc/resolv.conf")
            .map_err(|e| format!("Error copying /etc/resolv.conf: {}", e)));
        try!(self.ctx.timelog.mark(format_args!("Prepare"))
            .map_err(|e| format!("Can't write timelog: {}", e)));
        Ok(())
    }

    pub fn finish(&mut self) -> Result<(), String> {
        // Pip
        if self.ctx.featured_packages.contains(&packages::PipPy2) ||
           self.ctx.featured_packages.contains(&packages::PipPy3)
        {
            try!(pip::freeze(&mut self.ctx));
        }
        // Npm
        if self.ctx.featured_packages.contains(&packages::Npm) {
            try!(npm::list(&mut self.ctx));
        }
        // Composer
        if self.ctx.featured_packages.contains(&packages::Composer) {
            try!(composer::finish(&mut self.ctx));
        }
        // Gem
        if self.ctx.featured_packages.contains(&packages::Bundler) {
            try!(gem::list(&mut self.ctx));
        }

        try!(self.distro.finish(&mut self.ctx));

        let base = Path::new("/vagga/root");

        for path in self.ctx.mounted.iter().rev() {
            try!(unmount(path));
        }

        for dir in self.ctx.remove_dirs.iter() {
            try!(clean_dir(&base.join(dir), true)
                .map_err(|e| format!("Error removing dir: {}", e)));
        }

        for dir in self.ctx.empty_dirs.iter() {
            try!(clean_dir(&base.join(dir), false));
        }

        for dir in self.ctx.ensure_dirs.iter() {
            let fulldir = base.join(dir);
            try_msg!(create_dir(&fulldir, true),
                "Error creating dir: {err}");
        }

        try!(self.ctx.timelog.mark(format_args!("Finish"))
            .map_err(|e| format!("Can't write timelog: {}", e)));

        return Ok(());
    }
}
<commit_msg>Also track `last_use` when building container (fixes #303)<commit_after>use std::path::Path;
use std::fs::File;

use builder::context::Context;
use builder::distrib::{Unknown,Distribution};
use builder::error::{Error};
use builder::commands::{composer, gem, npm, pip};
use builder::packages;
use build_step::BuildStep;
use container::util::clean_dir;
use container::mount::{unmount, mount_system_dirs, mount_proc};
use file_util::{create_dir, copy};

pub struct Guard<'a> {
    pub ctx: Context<'a>,
    pub distro: Box<Distribution>,
}

impl<'a> Guard<'a> {
    pub fn build(ctx: Context) -> Result<(), Error> {
        Guard {
            ctx: ctx,
            distro: Box::new(Unknown),
        }.run_process()
    }

    fn run_process(&mut self) -> Result<(), Error> {
        try!(self.start());

        for b in self.ctx.container_config.setup.iter() {
            debug!("Building step: {:?}", b);
            try!(self.ctx.timelog.mark(format_args!("Step: {:?}", b))
                .map_err(|e| format!("Can't write timelog: {}", e)));
            try!(b.build(self, true)
                .map_err(|e| Error::Step(b.0.clone(), e)));
        }

        try!(self.finish());
        Ok(())
    }

    pub fn start(&mut self) -> Result<(), String> {
        try!(mount_system_dirs());
        try!(mount_proc(&Path::new("/proc")));
        try!(copy("/proc/self/uid_map", "/vagga/container/uid_map")
            .map_err(|e| format!("Error copying uid_map: {}", e)));
        try!(copy("/proc/self/gid_map", "/vagga/container/gid_map")
            .map_err(|e| format!("Error copying gid_map: {}", e)));
        try_msg!(create_dir("/vagga/root/etc", false),
             "Error creating /etc dir: {err}");
        try!(copy("/etc/resolv.conf", "/vagga/root/etc/resolv.conf")
            .map_err(|e| format!("Error copying /etc/resolv.conf: {}", e)));
        try!(self.ctx.timelog.mark(format_args!("Prepare"))
            .map_err(|e| format!("Can't write timelog: {}", e)));
        Ok(())
    }

    pub fn finish(&mut self) -> Result<(), String> {
        // Pip
        if self.ctx.featured_packages.contains(&packages::PipPy2) ||
           self.ctx.featured_packages.contains(&packages::PipPy3)
        {
            try!(pip::freeze(&mut self.ctx));
        }
        // Npm
        if self.ctx.featured_packages.contains(&packages::Npm) {
            try!(npm::list(&mut self.ctx));
        }
        // Composer
        if self.ctx.featured_packages.contains(&packages::Composer) {
            try!(composer::finish(&mut self.ctx));
        }
        // Gem
        if self.ctx.featured_packages.contains(&packages::Bundler) {
            try!(gem::list(&mut self.ctx));
        }

        try!(self.distro.finish(&mut self.ctx));

        let base = Path::new("/vagga/root");

        for path in self.ctx.mounted.iter().rev() {
            try!(unmount(path));
        }

        for dir in self.ctx.remove_dirs.iter() {
            try!(clean_dir(&base.join(dir), true)
                .map_err(|e| format!("Error removing dir: {}", e)));
        }

        for dir in self.ctx.empty_dirs.iter() {
            try!(clean_dir(&base.join(dir), false));
        }

        for dir in self.ctx.ensure_dirs.iter() {
            let fulldir = base.join(dir);
            try_msg!(create_dir(&fulldir, true),
                "Error creating dir: {err}");
        }

        File::create("/vagga/container/last_use")
            .map_err(|e| warn!("Can't write image usage info: {}", e)).ok();

        try!(self.ctx.timelog.mark(format_args!("Finish"))
            .map_err(|e| format!("Can't write timelog: {}", e)));

        return Ok(());
    }
}
<|endoftext|>


<commit_before>use opengl_graphics::{GlGraphics, Texture, TextureSettings};
use graphics::{Context, Transformed};
use graphics::draw_state::DrawState;
use graphics::Image;
use image::{self, DynamicImage};
use find_folder;
use graphics::color::gamma_srgb_to_linear;
use image::Pixel;
use image::Rgba;

pub const PLAYER_IDLE: (f64, f64) = (0.0, 6.0);
pub const SKELINGTON_IDLE: (f64, f64) = (6.0, 6.0);
pub const STINKBAG_IDLE: (f64, f64) = (12.0, 6.0);
pub const BOTTLE_FOOD: f64 = 18.0;
pub const TOMATO_FOOD: f64 = 19.0;
pub const EXIT_TILE: f64 = 20.0;
pub const WALL_TILE: [f64; 11] = [21.0, 22.0, 23.0, 24.0, 25.0, 26.0, 27.0, 28.0, 29.0, 30.0, 31.0];
pub const FLOOR_TILE: [f64; 8] = [32.0, 33.0, 34.0, 35.0, 36.0, 37.0, 38.0, 39.0];
pub const PLAYER_ATTACK: (f64, f64) = (40.0, 2.0);
pub const SKELINGTON_ATTACK: (f64, f64) = (42.0, 2.0);
pub const STINKBAG_ATTACK: (f64, f64) = (44.0, 2.0);
pub const PLAYER_HIT: (f64, f64) = (46.0, 2.0);
pub const DAMAGEABLE_TILES: [f64; 7] = [48.0, 49.0, 50.0, 51.0, 52.0, 53.0, 54.0];

pub struct Sprites {
    texture: Texture,
}

impl Sprites {
    /// Loading sprite sheet from image.
    pub fn new() -> Sprites {
        let img = Sprites::load_image("Scavengers_SpriteSheet.png");
        let new_img = Sprites::convert_image_from_srgb_to_linear(img);
        let texture = Sprites::create_texture_from_image(new_img);
        Sprites { texture: texture }
    }

    /// Get the right slice of the sprite. Sprite is 8x8
    fn animated_sprites_coord(state: f64, start: f64) -> [f64; 4] {
        let x = (state + start) % 8.0;
        let y = (((state + start) as i32) / 8) as f64;
        [x * 64.0, y * 64.0, 64.0, 64.0]
    }

    /// Draw an animated sprite with current state
    pub fn draw_chars(&self, x: f64, y: f64, i: f64, st: f64, c: &Context, gl: &mut GlGraphics) {
        let sprite_coord = Sprites::animated_sprites_coord(st, i);
        let transf = c.transform.trans(x, y);
        Image::new()
            .src_rect(sprite_coord)
            .draw(&self.texture, &DrawState::new_alpha(), transf, gl);
    }

    // private helpers

    fn load_image(path: &str) -> image::ImageBuffer<Rgba<u8>, Vec<u8>> {
        let resources = find_folder::Search::ParentsThenKids(3, 3)
            .for_folder("resources")
            .unwrap();
        let sprite = resources.join(path);
        let img = image::open(&sprite).unwrap();
        let img = match img {
            DynamicImage::ImageRgba8(img) => img,
            x => x.to_rgba(),
        };
        img
    }

    fn convert_image_from_srgb_to_linear(img: image::ImageBuffer<Rgba<u8>, Vec<u8>>)
                                         -> image::ImageBuffer<Rgba<u8>, Vec<u8>> {
        let mut new_img = img.clone();

        for (x, y, pixel) in img.enumerate_pixels() {
            let (r, g, b, a) = pixel.channels4();
            let r = r as f32 / 255.0;
            let g = g as f32 / 255.0;
            let b = b as f32 / 255.0;
            let a = a as f32 / 255.0;
            let new_color = gamma_srgb_to_linear([r, g, b, a]);
            let r = (new_color[0] * 255.0) as u8;
            let g = (new_color[1] * 255.0) as u8;
            let b = (new_color[2] * 255.0) as u8;
            let a = (new_color[3] * 255.0) as u8;
            let new_pixel = image::Pixel::from_channels(r, g, b, a);
            new_img.put_pixel(x, y, new_pixel);
        }

        new_img
    }

    fn create_texture_from_image(img: image::ImageBuffer<Rgba<u8>, Vec<u8>>) -> Texture {
        let mut texture_settings = TextureSettings::new();
        texture_settings.set_convert_gamma(true);
        texture_settings.set_compress(true);
        Texture::from_image(&img, &texture_settings)
    }
}
<commit_msg>fix textures color space<commit_after>use opengl_graphics::{GlGraphics, Texture, TextureSettings};
use graphics::{Context, Transformed};
use graphics::draw_state::DrawState;
use graphics::Image;
use image::{self, DynamicImage};
use find_folder;
use graphics::color::gamma_srgb_to_linear;
use image::Pixel;
use image::Rgba;

pub const PLAYER_IDLE: (f64, f64) = (0.0, 6.0);
pub const SKELINGTON_IDLE: (f64, f64) = (6.0, 6.0);
pub const STINKBAG_IDLE: (f64, f64) = (12.0, 6.0);
pub const BOTTLE_FOOD: f64 = 18.0;
pub const TOMATO_FOOD: f64 = 19.0;
pub const EXIT_TILE: f64 = 20.0;
pub const WALL_TILE: [f64; 11] = [21.0, 22.0, 23.0, 24.0, 25.0, 26.0, 27.0, 28.0, 29.0, 30.0, 31.0];
pub const FLOOR_TILE: [f64; 8] = [32.0, 33.0, 34.0, 35.0, 36.0, 37.0, 38.0, 39.0];
pub const PLAYER_ATTACK: (f64, f64) = (40.0, 2.0);
pub const SKELINGTON_ATTACK: (f64, f64) = (42.0, 2.0);
pub const STINKBAG_ATTACK: (f64, f64) = (44.0, 2.0);
pub const PLAYER_HIT: (f64, f64) = (46.0, 2.0);
pub const DAMAGEABLE_TILES: [f64; 7] = [48.0, 49.0, 50.0, 51.0, 52.0, 53.0, 54.0];

pub struct Sprites {
    texture: Texture,
}

impl Sprites {
    /// Loading sprite sheet from image.
    pub fn new() -> Sprites {
        let img = Sprites::load_image("Scavengers_SpriteSheet.png");
        let new_img = Sprites::convert_image_from_srgb_to_linear(img);
        let texture = Sprites::create_texture_from_image(new_img);
        Sprites { texture: texture }
    }

    /// Draw an animated sprite with current state
    pub fn draw_chars(&self, x: f64, y: f64, i: f64, st: f64, c: &Context, gl: &mut GlGraphics) {
        let sprite_coord = Sprites::animated_sprites_coord(st, i);
        let transf = c.transform.trans(x, y);
        Image::new()
            .src_rect(sprite_coord)
            .draw(&self.texture, &DrawState::new_alpha(), transf, gl);
    }

    // private helpers

    fn load_image(path: &str) -> image::ImageBuffer<Rgba<u8>, Vec<u8>> {
        let resources = find_folder::Search::ParentsThenKids(3, 3)
            .for_folder("resources")
            .unwrap();
        let sprite = resources.join(path);
        let img = image::open(&sprite).unwrap();
        let img = match img {
            DynamicImage::ImageRgba8(img) => img,
            x => x.to_rgba(),
        };
        img
    }

    fn convert_image_from_srgb_to_linear(img: image::ImageBuffer<Rgba<u8>, Vec<u8>>)
                                         -> image::ImageBuffer<Rgba<u8>, Vec<u8>> {
        let mut new_img = img.clone();

        for (x, y, pixel) in img.enumerate_pixels() {
            let (r, g, b, a) = pixel.channels4();
            let r = r as f32 / 255.0;
            let g = g as f32 / 255.0;
            let b = b as f32 / 255.0;
            let a = a as f32 / 255.0;
            let new_color = gamma_srgb_to_linear([r, g, b, a]);
            let r = (new_color[0] * 255.0) as u8;
            let g = (new_color[1] * 255.0) as u8;
            let b = (new_color[2] * 255.0) as u8;
            let a = (new_color[3] * 255.0) as u8;
            let new_pixel = image::Pixel::from_channels(r, g, b, a);
            new_img.put_pixel(x, y, new_pixel);
        }

        new_img
    }

    fn create_texture_from_image(img: image::ImageBuffer<Rgba<u8>, Vec<u8>>) -> Texture {
        let mut texture_settings = TextureSettings::new();
        texture_settings.set_convert_gamma(true);
        texture_settings.set_compress(true);
        Texture::from_image(&img, &texture_settings)
    }

    /// Get the right slice of the sprite. Sprite is 8x8
    fn animated_sprites_coord(state: f64, start: f64) -> [f64; 4] {
        let x = (state + start) % 8.0;
        let y = (((state + start) as i32) / 8) as f64;
        [x * 64.0, y * 64.0, 64.0, 64.0]
    }
}
<|endoftext|>


<commit_before>use std::error::Error;
use std::process::exit;
use std::sync::Arc;

use serenity::async_trait;
use serenity::client::bridge::gateway::ShardManager;
use serenity::client::EventHandler;
use serenity::model::channel::{Channel, GuildChannel, Message, MessageType};
use serenity::model::event::GuildMemberUpdateEvent;
use serenity::model::gateway::{Activity, GatewayIntents};
use serenity::model::guild::{Guild, GuildUnavailable, Member, PartialGuild, Role};
use serenity::model::id::{ChannelId, GuildId, RoleId};
use serenity::model::prelude::application_command::ApplicationCommand;
use serenity::model::prelude::{CurrentUser, Interaction, Ready, User};
use serenity::prelude::{Context, Mentionable};
use serenity::Client;
use tokio::sync::Mutex;
use tracing::error;
use typemap_rev::TypeMap;

use crate::config::Config;
use crate::discord;
use crate::discord::message_processors::{get_all_processors, MessageProcessors};
use crate::discord::scheduled::run_scheduled_tasks;
use crate::discord::slash_commands::SlashCommands;
use crate::discord::util::{invisible_success_reply, verify_guild_message, GuildMessage};
use crate::flags::Flags;
use crate::service::guild::GuildService;
use crate::service::guild_statistic::GuildStatisticService;
use crate::service::join::JoinService;
use crate::service::mute::MuteService;
use crate::service::setting::SettingService;
use crate::service::shard_statistic::ShardStatisticService;
use crate::service::tag::TagService;
use tokio::time::Duration;

const SHARD_LATENCY_POLLING_INTERVAL: u64 = 60;

pub struct DiscordBot {
    client: Client,
}

async fn feed_shard_statistics(shard_manager: Arc<Mutex<ShardManager>>, services: Arc<TypeMap>) {
    let shard_statistic_service =
        if let Some(shard_statistic_service) = services.get::<ShardStatisticService>() {
            shard_statistic_service
        } else {
            return;
        };

    loop {
        tokio::time::sleep(Duration::from_secs(SHARD_LATENCY_POLLING_INTERVAL)).await;
        shard_statistic_service
            .update_latencies(&*shard_manager.lock().await)
            .await;
    }
}

impl DiscordBot {
    pub async fn new(
        config: Arc<Config>,
        flags: Arc<Flags>,
        services: Arc<TypeMap>,
    ) -> Result<DiscordBot, Box<dyn Error>> {
        let slash_commands = discord::slash_commands::get_all_commands();

        let handler = DiscordEventHandler {
            create_slash_commands: flags.create_slash_commands,
            config: config.clone(),
            services: services.clone(),
            slash_commands,
            message_processors: get_all_processors(),
        };

        let application_id: u64 = config.oauth_client_id.parse()?;
        let client = Client::builder(&(config.discord_token))
            .event_handler(handler)
            .intents(
                GatewayIntents::GUILDS
                    | GatewayIntents::GUILD_MEMBERS
                    | GatewayIntents::GUILD_MESSAGES,
            )
            .application_id(application_id)
            .await?;

        if let Some(guild_service) = services.get::<GuildService>() {
            guild_service
                .insert_http(client.cache_and_http.http.clone())
                .await;
        }

        tokio::spawn(feed_shard_statistics(
            client.shard_manager.clone(),
            services.clone(),
        ));

        run_scheduled_tasks(client.cache_and_http.http.clone(), services.clone());

        Ok(DiscordBot { client })
    }

    pub async fn connect(&mut self) -> Result<(), Arc<serenity::Error>> {
        Ok(self.client.start_autosharded().await.map_err(Arc::new)?)
    }
}

struct DiscordEventHandler {
    create_slash_commands: bool,
    config: Arc<Config>,
    slash_commands: SlashCommands,
    message_processors: MessageProcessors,
    services: Arc<TypeMap>,
}

async fn initialize_slash_commands(
    ctx: &Context,
    slash_commands: &SlashCommands,
) -> Result<(), serenity::Error> {
    ApplicationCommand::set_global_application_commands(&ctx.http, |commands| {
        for (&_name, slash_command) in &slash_commands.0 {
            commands
                .create_application_command(move |command| slash_command.create_command(command));
        }
        commands
    })
    .await
    .map(|_| ())
}

#[async_trait]
impl EventHandler for DiscordEventHandler {
    async fn guild_create(&self, _ctx: Context, guild: Guild) {
        if let Some(statistic_service) = self.services.get::<GuildStatisticService>() {
            statistic_service.add_guild(&guild).await;
        }
    }

    async fn guild_update(&self, _ctx: Context, new: PartialGuild) {
        if let Some(guild_service) = self.services.get::<GuildService>() {
            guild_service.invalidate_cached_guild(new.id).await;
        }
    }

    async fn guild_delete(&self, _ctx: Context, incomplete: GuildUnavailable) {
        if let Some(statistic_service) = self.services.get::<GuildStatisticService>() {
            statistic_service.remove_guild(&incomplete).await;
        }

        if !incomplete.unavailable {
            if let Some(guild_service) = self.services.get::<GuildService>() {
                guild_service.invalidate_cached_guild(incomplete.id).await;
            }
        }
    }

    async fn guild_member_addition(&self, ctx: Context, new_member: Member) {
        let guild_id = new_member.guild_id;

        if let Some(statistic_service) = self.services.get::<GuildStatisticService>() {
            statistic_service
                .increment_guild_member_count(guild_id)
                .await;
        }

        let setting = if let Some(setting_service) = self.services.get::<SettingService>() {
            setting_service.get_setting(guild_id).await
        } else {
            return;
        };

        let guild = if let Some(guild_service) = self.services.get::<GuildService>() {
            match guild_service.get_guild(guild_id).await {
                Ok(guild) => guild,
                Err(_) => return,
            }
        } else {
            return;
        };

        if setting.invite_link_remover && new_member.user.name.contains("discord.gg/") {
            let _ = guild_id
                .kick_with_reason(
                    &*ctx.http,
                    new_member.user.id,
                    "Username contains invite link",
                )
                .await
                .map_err(|err| {
                    error!("failed to issue discord kick {}", err);
                    err
                });
            return;
        }

        if setting.welcome_message {
            let content = setting
                .message
                .replace("$user", &format!("{:?}", new_member.mention()))
                .replace("$guild", &guild.name);

            let content = if setting.holding_room {
                content.replace("$minute", &setting.holding_room_minutes.to_string())
            } else {
                content
            };

            let channel_id = ChannelId(setting.welcome_message_channel_id as u64);
            let _ = channel_id
                .send_message(&*ctx.http, |message| message.content(content))
                .await
                .map_err(|err| {
                    error!("failed to send welcome message {}", err);
                    err
                });
        }

        if setting.holding_room {
            if let Some(join_service) = self.services.get::<JoinService>() {
                join_service
                    .issue_join(guild_id, new_member.user.id, setting.holding_room_minutes)
                    .await;
            }
        }

        if setting.join_captcha {
            if let Ok(dm_channel) = new_member.user.id.create_dm_channel(&ctx.http).await {
                let _ = dm_channel
                    .send_message(&ctx.http, |message| {
                        message.content(format!("Welcome to {}! To enter you must complete this captcha.\n{}/captcha/{}/{}", &guild.name, self.config.self_url, guild_id.0, new_member.user.id.0))
                    })
                    .await
                    .map_err(|err| {
                        error!("failed to send captcha challenge DM {}", err);
                        err
                    });
            }
        }

        let mute_service = if let Some(service) = self.services.get::<MuteService>() {
            service
        } else {
            return;
        };

        let mute_records = mute_service
            .fetch_valid_mutes(guild_id, new_member.user.id)
            .await;

        if mute_records.is_empty() {
            return;
        }

        let role = match mute_service
            .fetch_muted_role(&ctx.http, &*self.services, guild_id)
            .await
        {
            Ok(role) => role,
            Err(_) => {
                return;
            }
        };

        let _ = ctx
            .http
            .add_member_role(
                guild_id.0,
                new_member.user.id.0,
                role.0,
                Some("Preventing mute evasion"),
            )
            .await
            .map_err(|err| {
                error!("failed to issue discord member role add {}", err);
                err
            });
    }

    async fn guild_member_update(&self, _ctx: Context, new: GuildMemberUpdateEvent) {
        if let Some(guild_service) = self.services.get::<GuildService>() {
            guild_service
                .invalidate_cached_guild_member(new.guild_id, new.user.id)
                .await;
        }
    }

    async fn guild_member_removal(&self, _ctx: Context, guild_id: GuildId, kicked: User) {
        if let Some(statistic_service) = self.services.get::<GuildStatisticService>() {
            statistic_service
                .decrement_guild_member_count(guild_id)
                .await;
        }

        if let Some(join_service) = self.services.get::<JoinService>() {
            join_service.delete_user_joins(guild_id, kicked.id).await;
        }

        if let Some(guild_service) = self.services.get::<GuildService>() {
            guild_service
                .invalidate_cached_guild_member(guild_id, kicked.id)
                .await;
        }
    }

    async fn ready(&self, ctx: Context, data_about_bot: Ready) {
        let shard = data_about_bot.shard.unwrap_or([0, 1]);
        // Watching over users. [0 / 1]
        let shard_status = format!("over users. [{} / {}]", shard[0], shard[1]);
        let activity = Activity::watching(shard_status);
        ctx.set_activity(activity).await;

        if self.create_slash_commands {
            if let Err(err) = initialize_slash_commands(&ctx, &self.slash_commands).await {
                error!("failed to create slash commands {}", err);
                exit(-1);
            }
        }
    }

    async fn interaction_create(&self, ctx: Context, interaction: Interaction) {
        if let Interaction::Autocomplete(autocomplete_interaction) = interaction {
            // only tag command has autocomplete so no need to filter based on command name
            if let Some(tag_service) = self.services.get::<TagService>() {
                let guild_id = if let Some(id) = autocomplete_interaction.guild_id {
                    id
                } else {
                    return;
                };

                let tags = tag_service.get_tag_names(guild_id).await;
                let _ = autocomplete_interaction
                    .create_autocomplete_response(&*ctx.http, |response| {
                        for tag in tags {
                            response.add_string_choice(&tag, &tag);
                        }

                        response
                    })
                    .await
                    .map_err(|err| {
                        error!("failed to create autocomplete response {}", err);
                        err
                    });
            }

            return;
        }

        if let Interaction::ApplicationCommand(command) = interaction {
            if command.guild_id.is_none() || command.member.is_none() {
                invisible_success_reply(
                    &*ctx.http,
                    &command,
                    "Jim's commands can only be used in servers.",
                )
                .await;
                return;
            }

            let name = &command.data.name;
            let slash_command_handler = self.slash_commands.0.get(name.as_str());
            if let Some(slash_command_handler) = slash_command_handler {
                if let Err(err) = slash_command_handler
                    .handle_command(&ctx, &command, &self.config, self.services.as_ref())
                    .await
                {
                    error!(
                        name = name.as_str(),
                        "failed to handle slash command {}", err
                    );
                }
            }
        }
    }

    async fn message(&self, ctx: Context, message: Message) {
        if message.author.bot {
            return;
        }

        if message.webhook_id.is_some() {
            return;
        }

        if message.content.is_empty() {
            return;
        }

        if message.kind != MessageType::Regular || message.kind != MessageType::InlineReply {
            return;
        }

        let (guild_id, member) =
            if let Ok(GuildMessage { guild_id, member }) = verify_guild_message(&message) {
                (guild_id, member)
            } else {
                return;
            };

        let setting_service = if let Some(service) = self.services.get::<SettingService>() {
            service
        } else {
            return;
        };

        let setting = setting_service.get_setting(guild_id).await;

        let guild_service = if let Some(service) = self.services.get::<GuildService>() {
            service
        } else {
            return;
        };

        let permissions =
            if let Ok(permissions) = guild_service.get_permissions(&member.roles, guild_id).await {
                permissions
            } else {
                return;
            };

        for (i, processor) in self.message_processors.0.iter().enumerate() {
            match processor
                .handle_message(
                    &ctx,
                    &message,
                    guild_id,
                    member,
                    permissions,
                    &*setting,
                    &*self.config,
                    &*self.services,
                )
                .await
            {
                Ok(true) => break,
                Ok(false) => continue,
                Err(err) => {
                    error!("failed to run message processor id: {}, err: {}", i, err);
                    break;
                }
            }
        }

        return;
    }

    async fn guild_role_create(&self, _ctx: Context, new: Role) {
        if let Some(guild_service) = self.services.get::<GuildService>() {
            guild_service
                .invalidate_cached_guild_roles(new.guild_id)
                .await;
        }
    }

    async fn guild_role_update(&self, _ctx: Context, new: Role) {
        if let Some(guild_service) = self.services.get::<GuildService>() {
            guild_service
                .invalidate_cached_guild_roles(new.guild_id)
                .await;
        }
    }

    async fn guild_role_delete(&self, _ctx: Context, guild_id: GuildId, _removed_role_id: RoleId) {
        if let Some(guild_service) = self.services.get::<GuildService>() {
            guild_service.invalidate_cached_guild_roles(guild_id).await;
        }
    }

    async fn user_update(&self, _ctx: Context, new: CurrentUser) {
        if let Some(guild_service) = self.services.get::<GuildService>() {
            guild_service.invalidate_cached_user(new.id).await;
        }
    }

    async fn channel_create(&self, _ctx: Context, new: &GuildChannel) {
        if let Some(guild_service) = self.services.get::<GuildService>() {
            guild_service
                .invalidate_cached_guild_channels(new.guild_id)
                .await;
        }
    }

    async fn channel_update(&self, _ctx: Context, new: Channel) {
        if let Some(guild_channel) = new.guild() {
            if let Some(guild_service) = self.services.get::<GuildService>() {
                guild_service
                    .invalidate_cached_guild_channels(guild_channel.guild_id)
                    .await;
            }
        }
    }

    async fn channel_delete(&self, _ctx: Context, new: &GuildChannel) {
        if let Some(guild_service) = self.services.get::<GuildService>() {
            guild_service
                .invalidate_cached_guild_channels(new.guild_id)
                .await;
        }
    }
}
<commit_msg>Fix welcome message and message processors not working properly<commit_after>use std::error::Error;
use std::process::exit;
use std::sync::Arc;

use serenity::async_trait;
use serenity::client::bridge::gateway::ShardManager;
use serenity::client::EventHandler;
use serenity::model::channel::{Channel, GuildChannel, Message, MessageType};
use serenity::model::event::GuildMemberUpdateEvent;
use serenity::model::gateway::{Activity, GatewayIntents};
use serenity::model::guild::{Guild, GuildUnavailable, Member, PartialGuild, Role};
use serenity::model::id::{ChannelId, GuildId, RoleId};
use serenity::model::prelude::application_command::ApplicationCommand;
use serenity::model::prelude::{CurrentUser, Interaction, Ready, User};
use serenity::prelude::{Context, Mentionable};
use serenity::Client;
use tokio::sync::Mutex;
use tracing::error;
use typemap_rev::TypeMap;

use crate::config::Config;
use crate::discord;
use crate::discord::message_processors::{get_all_processors, MessageProcessors};
use crate::discord::scheduled::run_scheduled_tasks;
use crate::discord::slash_commands::SlashCommands;
use crate::discord::util::{invisible_success_reply, verify_guild_message, GuildMessage};
use crate::flags::Flags;
use crate::service::guild::GuildService;
use crate::service::guild_statistic::GuildStatisticService;
use crate::service::join::JoinService;
use crate::service::mute::MuteService;
use crate::service::setting::SettingService;
use crate::service::shard_statistic::ShardStatisticService;
use crate::service::tag::TagService;
use tokio::time::Duration;

const SHARD_LATENCY_POLLING_INTERVAL: u64 = 60;

pub struct DiscordBot {
    client: Client,
}

async fn feed_shard_statistics(shard_manager: Arc<Mutex<ShardManager>>, services: Arc<TypeMap>) {
    let shard_statistic_service =
        if let Some(shard_statistic_service) = services.get::<ShardStatisticService>() {
            shard_statistic_service
        } else {
            return;
        };

    loop {
        tokio::time::sleep(Duration::from_secs(SHARD_LATENCY_POLLING_INTERVAL)).await;
        shard_statistic_service
            .update_latencies(&*shard_manager.lock().await)
            .await;
    }
}

impl DiscordBot {
    pub async fn new(
        config: Arc<Config>,
        flags: Arc<Flags>,
        services: Arc<TypeMap>,
    ) -> Result<DiscordBot, Box<dyn Error>> {
        let slash_commands = discord::slash_commands::get_all_commands();

        let handler = DiscordEventHandler {
            create_slash_commands: flags.create_slash_commands,
            config: config.clone(),
            services: services.clone(),
            slash_commands,
            message_processors: get_all_processors(),
        };

        let application_id: u64 = config.oauth_client_id.parse()?;
        let client = Client::builder(&(config.discord_token))
            .event_handler(handler)
            .intents(
                GatewayIntents::GUILDS
                    | GatewayIntents::GUILD_MEMBERS
                    | GatewayIntents::GUILD_MESSAGES,
            )
            .application_id(application_id)
            .await?;

        if let Some(guild_service) = services.get::<GuildService>() {
            guild_service
                .insert_http(client.cache_and_http.http.clone())
                .await;
        }

        tokio::spawn(feed_shard_statistics(
            client.shard_manager.clone(),
            services.clone(),
        ));

        run_scheduled_tasks(client.cache_and_http.http.clone(), services.clone());

        Ok(DiscordBot { client })
    }

    pub async fn connect(&mut self) -> Result<(), Arc<serenity::Error>> {
        Ok(self.client.start_autosharded().await.map_err(Arc::new)?)
    }
}

struct DiscordEventHandler {
    create_slash_commands: bool,
    config: Arc<Config>,
    slash_commands: SlashCommands,
    message_processors: MessageProcessors,
    services: Arc<TypeMap>,
}

async fn initialize_slash_commands(
    ctx: &Context,
    slash_commands: &SlashCommands,
) -> Result<(), serenity::Error> {
    ApplicationCommand::set_global_application_commands(&ctx.http, |commands| {
        for (&_name, slash_command) in &slash_commands.0 {
            commands
                .create_application_command(move |command| slash_command.create_command(command));
        }
        commands
    })
    .await
    .map(|_| ())
}

#[async_trait]
impl EventHandler for DiscordEventHandler {
    async fn guild_create(&self, _ctx: Context, guild: Guild) {
        if let Some(statistic_service) = self.services.get::<GuildStatisticService>() {
            statistic_service.add_guild(&guild).await;
        }
    }

    async fn guild_update(&self, _ctx: Context, new: PartialGuild) {
        if let Some(guild_service) = self.services.get::<GuildService>() {
            guild_service.invalidate_cached_guild(new.id).await;
        }
    }

    async fn guild_delete(&self, _ctx: Context, incomplete: GuildUnavailable) {
        if let Some(statistic_service) = self.services.get::<GuildStatisticService>() {
            statistic_service.remove_guild(&incomplete).await;
        }

        if !incomplete.unavailable {
            if let Some(guild_service) = self.services.get::<GuildService>() {
                guild_service.invalidate_cached_guild(incomplete.id).await;
            }
        }
    }

    async fn guild_member_addition(&self, ctx: Context, new_member: Member) {
        let guild_id = new_member.guild_id;

        if let Some(statistic_service) = self.services.get::<GuildStatisticService>() {
            statistic_service
                .increment_guild_member_count(guild_id)
                .await;
        }

        let setting = if let Some(setting_service) = self.services.get::<SettingService>() {
            setting_service.get_setting(guild_id).await
        } else {
            return;
        };

        let guild = if let Some(guild_service) = self.services.get::<GuildService>() {
            match guild_service.get_guild(guild_id).await {
                Ok(guild) => guild,
                Err(_) => return,
            }
        } else {
            return;
        };

        if setting.invite_link_remover && new_member.user.name.contains("discord.gg/") {
            let _ = guild_id
                .kick_with_reason(
                    &*ctx.http,
                    new_member.user.id,
                    "Username contains invite link",
                )
                .await
                .map_err(|err| {
                    error!("failed to issue discord kick {}", err);
                    err
                });
            return;
        }

        if setting.welcome_message {
            let content = setting
                .message
                .replace("$user", &format!("{}", new_member.mention()))
                .replace("$guild", &guild.name);

            let content = if setting.holding_room {
                content.replace("$minute", &setting.holding_room_minutes.to_string())
            } else {
                content
            };

            let channel_id = ChannelId(setting.welcome_message_channel_id as u64);
            let _ = channel_id
                .send_message(&*ctx.http, |message| message.content(content))
                .await
                .map_err(|err| {
                    error!("failed to send welcome message {}", err);
                    err
                });
        }

        if setting.holding_room {
            if let Some(join_service) = self.services.get::<JoinService>() {
                join_service
                    .issue_join(guild_id, new_member.user.id, setting.holding_room_minutes)
                    .await;
            }
        }

        if setting.join_captcha {
            if let Ok(dm_channel) = new_member.user.id.create_dm_channel(&ctx.http).await {
                let _ = dm_channel
                    .send_message(&ctx.http, |message| {
                        message.content(format!("Welcome to {}! To enter you must complete this captcha.\n{}/captcha/{}/{}", &guild.name, self.config.self_url, guild_id.0, new_member.user.id.0))
                    })
                    .await
                    .map_err(|err| {
                        error!("failed to send captcha challenge DM {}", err);
                        err
                    });
            }
        }

        let mute_service = if let Some(service) = self.services.get::<MuteService>() {
            service
        } else {
            return;
        };

        let mute_records = mute_service
            .fetch_valid_mutes(guild_id, new_member.user.id)
            .await;

        if mute_records.is_empty() {
            return;
        }

        let role = match mute_service
            .fetch_muted_role(&ctx.http, &*self.services, guild_id)
            .await
        {
            Ok(role) => role,
            Err(_) => {
                return;
            }
        };

        let _ = ctx
            .http
            .add_member_role(
                guild_id.0,
                new_member.user.id.0,
                role.0,
                Some("Preventing mute evasion"),
            )
            .await
            .map_err(|err| {
                error!("failed to issue discord member role add {}", err);
                err
            });
    }

    async fn guild_member_update(&self, _ctx: Context, new: GuildMemberUpdateEvent) {
        if let Some(guild_service) = self.services.get::<GuildService>() {
            guild_service
                .invalidate_cached_guild_member(new.guild_id, new.user.id)
                .await;
        }
    }

    async fn guild_member_removal(&self, _ctx: Context, guild_id: GuildId, kicked: User) {
        if let Some(statistic_service) = self.services.get::<GuildStatisticService>() {
            statistic_service
                .decrement_guild_member_count(guild_id)
                .await;
        }

        if let Some(join_service) = self.services.get::<JoinService>() {
            join_service.delete_user_joins(guild_id, kicked.id).await;
        }

        if let Some(guild_service) = self.services.get::<GuildService>() {
            guild_service
                .invalidate_cached_guild_member(guild_id, kicked.id)
                .await;
        }
    }

    async fn ready(&self, ctx: Context, data_about_bot: Ready) {
        let shard = data_about_bot.shard.unwrap_or([0, 1]);
        // Watching over users. [0 / 1]
        let shard_status = format!("over users. [{} / {}]", shard[0], shard[1]);
        let activity = Activity::watching(shard_status);
        ctx.set_activity(activity).await;

        if self.create_slash_commands {
            if let Err(err) = initialize_slash_commands(&ctx, &self.slash_commands).await {
                error!("failed to create slash commands {}", err);
                exit(-1);
            }
        }
    }

    async fn interaction_create(&self, ctx: Context, interaction: Interaction) {
        if let Interaction::Autocomplete(autocomplete_interaction) = interaction {
            // only tag command has autocomplete so no need to filter based on command name
            if let Some(tag_service) = self.services.get::<TagService>() {
                let guild_id = if let Some(id) = autocomplete_interaction.guild_id {
                    id
                } else {
                    return;
                };

                let tags = tag_service.get_tag_names(guild_id).await;
                let _ = autocomplete_interaction
                    .create_autocomplete_response(&*ctx.http, |response| {
                        for tag in tags {
                            response.add_string_choice(&tag, &tag);
                        }

                        response
                    })
                    .await
                    .map_err(|err| {
                        error!("failed to create autocomplete response {}", err);
                        err
                    });
            }

            return;
        }

        if let Interaction::ApplicationCommand(command) = interaction {
            if command.guild_id.is_none() || command.member.is_none() {
                invisible_success_reply(
                    &*ctx.http,
                    &command,
                    "Jim's commands can only be used in servers.",
                )
                .await;
                return;
            }

            let name = &command.data.name;
            let slash_command_handler = self.slash_commands.0.get(name.as_str());
            if let Some(slash_command_handler) = slash_command_handler {
                if let Err(err) = slash_command_handler
                    .handle_command(&ctx, &command, &self.config, self.services.as_ref())
                    .await
                {
                    error!(
                        name = name.as_str(),
                        "failed to handle slash command {}", err
                    );
                }
            }
        }
    }

    async fn message(&self, ctx: Context, message: Message) {
        if message.author.bot {
            return;
        }

        if message.webhook_id.is_some() {
            return;
        }

        if message.content.is_empty() {
            return;
        }

        if message.kind != MessageType::Regular && message.kind != MessageType::InlineReply {
            return;
        }

        let (guild_id, member) =
            if let Ok(GuildMessage { guild_id, member }) = verify_guild_message(&message) {
                (guild_id, member)
            } else {
                return;
            };

        let setting_service = if let Some(service) = self.services.get::<SettingService>() {
            service
        } else {
            return;
        };

        let setting = setting_service.get_setting(guild_id).await;

        let guild_service = if let Some(service) = self.services.get::<GuildService>() {
            service
        } else {
            return;
        };

        let permissions =
            if let Ok(permissions) = guild_service.get_permissions(message.author.id, &member.roles, guild_id).await {
                permissions
            } else {
                return;
            };

        for (i, processor) in self.message_processors.0.iter().enumerate() {
            match processor
                .handle_message(
                    &ctx,
                    &message,
                    guild_id,
                    member,
                    permissions,
                    &*setting,
                    &*self.config,
                    &*self.services,
                )
                .await
            {
                Ok(true) => break,
                Ok(false) => continue,
                Err(err) => {
                    error!("failed to run message processor id: {}, err: {}", i, err);
                    break;
                }
            }
        }

        return;
    }

    async fn guild_role_create(&self, _ctx: Context, new: Role) {
        if let Some(guild_service) = self.services.get::<GuildService>() {
            guild_service
                .invalidate_cached_guild_roles(new.guild_id)
                .await;
        }
    }

    async fn guild_role_update(&self, _ctx: Context, new: Role) {
        if let Some(guild_service) = self.services.get::<GuildService>() {
            guild_service
                .invalidate_cached_guild_roles(new.guild_id)
                .await;
        }
    }

    async fn guild_role_delete(&self, _ctx: Context, guild_id: GuildId, _removed_role_id: RoleId) {
        if let Some(guild_service) = self.services.get::<GuildService>() {
            guild_service.invalidate_cached_guild_roles(guild_id).await;
        }
    }

    async fn user_update(&self, _ctx: Context, new: CurrentUser) {
        if let Some(guild_service) = self.services.get::<GuildService>() {
            guild_service.invalidate_cached_user(new.id).await;
        }
    }

    async fn channel_create(&self, _ctx: Context, new: &GuildChannel) {
        if let Some(guild_service) = self.services.get::<GuildService>() {
            guild_service
                .invalidate_cached_guild_channels(new.guild_id)
                .await;
        }
    }

    async fn channel_update(&self, _ctx: Context, new: Channel) {
        if let Some(guild_channel) = new.guild() {
            if let Some(guild_service) = self.services.get::<GuildService>() {
                guild_service
                    .invalidate_cached_guild_channels(guild_channel.guild_id)
                    .await;
            }
        }
    }

    async fn channel_delete(&self, _ctx: Context, new: &GuildChannel) {
        if let Some(guild_service) = self.services.get::<GuildService>() {
            guild_service
                .invalidate_cached_guild_channels(new.guild_id)
                .await;
        }
    }
}
<|endoftext|>


<commit_before>use cgmath;
use character::controls::CharacterInputState;
use critter::{CharacterSprite, CritterData};
use data;
use game::constants::{ASPECT_RATIO, CHARACTER_SHEET_TOTAL_WIDTH, RUN_SPRITE_OFFSET, SPRITE_OFFSET};
use gfx;
use gfx_app::{ColorFormat, DepthFormat};
use gfx_app::mouse_controls::MouseInputState;
use graphics::{camera::CameraInputState, Dimensions, get_orientation_from_center, load_texture, orientation::{Orientation, Stance}, overlaps};
use shaders::{CharacterSheet, critter_pipeline, Position, Projection, VertexData};
use specs;
use specs::prelude::{Read, ReadStorage, WriteStorage};
use zombie::{ZombieDrawable, zombies::Zombies};

pub mod controls;

const SHADER_VERT: &[u8] = include_bytes!("../shaders/character.v.glsl");
const SHADER_FRAG: &[u8] = include_bytes!("../shaders/character.f.glsl");

#[derive(Clone)]
pub struct CharacterDrawable {
  projection: Projection,
  pub position: Position,
  orientation: Orientation,
  pub stance: Stance,
  direction: Orientation,
}

impl CharacterDrawable {
  pub fn new() -> CharacterDrawable {
    let view = Dimensions::get_view_matrix();
    CharacterDrawable {
      projection: Projection {
        model: view.into(),
        view: view.into(),
        proj: cgmath::perspective(cgmath::Deg(75.0f32), ASPECT_RATIO, 0.1, 4000.0).into(),
      },
      position: Position::new([0.0, 0.0]),
      orientation: Orientation::Right,
      stance: Stance::Walking,
      direction: Orientation::Right,
    }
  }

  pub fn update(&mut self, world_to_clip: &Projection, ci: &CharacterInputState, mouse_input: &MouseInputState, dimensions: &Dimensions, zombies: &Vec<ZombieDrawable>) {
    self.projection = *world_to_clip;

    if zombies.iter()
              .any(|z| self.stance != Stance::NormalDeath &&
                overlaps(Position::new([ci.x_movement, ci.y_movement]), z.position, 10.0, 10.0)) {
      self.stance = Stance::NormalDeath;
      println!("Player died, press Esc to quit");
    }

    if self.stance != Stance::NormalDeath {
      if ci.is_shooting && mouse_input.left_click_point.is_some() && !ci.is_colliding {
        self.stance = Stance::Firing;
        self.orientation = get_orientation_from_center(mouse_input, dimensions);
      } else if ci.is_colliding {
        self.stance = Stance::Still;
      } else {
        self.stance = Stance::Walking;
        self.orientation = ci.orientation;
      }
    }
  }
}

impl Default for CharacterDrawable {
  fn default() -> Self {
    CharacterDrawable::new()
  }
}

impl specs::prelude::Component for CharacterDrawable {
  type Storage = specs::storage::VecStorage<CharacterDrawable>;
}

pub struct CharacterDrawSystem<R: gfx::Resources> {
  bundle: gfx::pso::bundle::Bundle<R, critter_pipeline::Data<R>>,
  data: Vec<CritterData>,
}

impl<R: gfx::Resources> CharacterDrawSystem<R> {
  pub fn new<F>(factory: &mut F,
                rtv: gfx::handle::RenderTargetView<R, ColorFormat>,
                dsv: gfx::handle::DepthStencilView<R, DepthFormat>) -> CharacterDrawSystem<R>
                where F: gfx::Factory<R> {
    use gfx::traits::FactoryExt;

    let charter_bytes = &include_bytes!("../../assets/character.png")[..];

    let vertex_data: [VertexData; 4] = [
      VertexData::new([-20.0, -28.0], [0.0, 1.0]),
      VertexData::new([20.0, -28.0], [1.0, 1.0]),
      VertexData::new([20.0, 28.0], [1.0, 0.0]),
      VertexData::new([-20.0, 28.0], [0.0, 0.0]),
    ];

    let index_data: [u16; 6] = [0, 1, 2, 2, 3, 0];

    let (vertex_buf, slice) = factory.create_vertex_buffer_with_slice(&vertex_data, &index_data[..]);

    let char_texture = load_texture(factory, charter_bytes);
    let pso = factory
      .create_pipeline_simple(SHADER_VERT,
                              SHADER_FRAG,
                              critter_pipeline::new())
      .unwrap();

    let pipeline_data = critter_pipeline::Data {
      vbuf: vertex_buf,
      projection_cb: factory.create_constant_buffer(1),
      position_cb: factory.create_constant_buffer(1),
      character_sprite_cb: factory.create_constant_buffer(1),
      charactersheet: (char_texture, factory.create_sampler_linear()),
      out_color: rtv,
      out_depth: dsv,
    };

    let data = data::load_character();

    CharacterDrawSystem {
      bundle: gfx::Bundle::new(slice, pso, pipeline_data),
      data,
    }
  }

  fn get_next_sprite(&self, character_idx: usize, character_fire_idx: usize, drawable: &mut CharacterDrawable) -> CharacterSheet {
    let char_sprite =
      if drawable.orientation == Orientation::Still && drawable.stance == Stance::Walking {
        let sprite_idx = (drawable.direction as usize * 28 + RUN_SPRITE_OFFSET) as usize;
        (&self.data[sprite_idx], sprite_idx)
      } else if drawable.stance == Stance::Walking {
        drawable.direction = drawable.orientation;
        let sprite_idx = (drawable.orientation as usize * 28 + character_idx + RUN_SPRITE_OFFSET) as usize;
        (&self.data[sprite_idx], sprite_idx)
      } else {
        let sprite_idx = (drawable.orientation as usize * 8 + character_fire_idx) as usize;
        (&self.data[sprite_idx], sprite_idx)
      };

    let elements_x = CHARACTER_SHEET_TOTAL_WIDTH / (char_sprite.0.data[2] + SPRITE_OFFSET);
    CharacterSheet {
      x_div: elements_x,
      y_div: 0.0,
      row_idx: 0,
      index: char_sprite.1 as f32,
    }
  }

  pub fn draw<C>(&mut self,
                 mut drawable: &mut CharacterDrawable,
                 character: &CharacterSprite,
                 encoder: &mut gfx::Encoder<R, C>)
                 where C: gfx::CommandBuffer<R> {
    encoder.update_constant_buffer(&self.bundle.data.projection_cb, &drawable.projection);
    encoder.update_constant_buffer(&self.bundle.data.position_cb, &drawable.position);
    encoder.update_constant_buffer(&self.bundle.data.character_sprite_cb,
                                   &self.get_next_sprite(character.character_idx,
                                                         character.character_fire_idx,
                                                         &mut drawable));
    self.bundle.encode(encoder);
  }
}

#[derive(Debug)]
pub struct PreDrawSystem;

impl PreDrawSystem {
  pub fn new() -> PreDrawSystem {
    PreDrawSystem {}
  }
}

impl<'a> specs::prelude::System<'a> for PreDrawSystem {
  #[cfg_attr(feature = "cargo-clippy", allow(type_complexity))]
  type SystemData = (WriteStorage<'a, CharacterDrawable>,
                     ReadStorage<'a, CameraInputState>,
                     ReadStorage<'a, CharacterInputState>,
                     ReadStorage<'a, MouseInputState>,
                     ReadStorage<'a, Zombies>,
                     Read<'a, Dimensions>);

  fn run(&mut self, (mut character, camera_input, character_input, mouse_input, zombies, dim): Self::SystemData) {
    use specs::join::Join;

    for (c, camera, ci, mi, zs) in (&mut character, &camera_input, &character_input, &mouse_input, &zombies).join() {
      let world_to_clip = dim.world_to_projection(camera);
      c.update(&world_to_clip, ci, mi, &dim, &zs.zombies);
    }
  }
}
<commit_msg>Fix character hit detection to zombie<commit_after>use cgmath;
use character::controls::CharacterInputState;
use critter::{CharacterSprite, CritterData};
use data;
use game::constants::{ASPECT_RATIO, CHARACTER_SHEET_TOTAL_WIDTH, RUN_SPRITE_OFFSET, SPRITE_OFFSET};
use gfx;
use gfx_app::{ColorFormat, DepthFormat};
use gfx_app::mouse_controls::MouseInputState;
use graphics::{camera::CameraInputState, Dimensions, get_orientation_from_center, load_texture, orientation::{Orientation, Stance}, overlaps};
use shaders::{CharacterSheet, critter_pipeline, Position, Projection, VertexData};
use specs;
use specs::prelude::{Read, ReadStorage, WriteStorage};
use zombie::{ZombieDrawable, zombies::Zombies};

pub mod controls;

const SHADER_VERT: &[u8] = include_bytes!("../shaders/character.v.glsl");
const SHADER_FRAG: &[u8] = include_bytes!("../shaders/character.f.glsl");

#[derive(Clone)]
pub struct CharacterDrawable {
  projection: Projection,
  pub position: Position,
  orientation: Orientation,
  pub stance: Stance,
  direction: Orientation,
}

impl CharacterDrawable {
  pub fn new() -> CharacterDrawable {
    let view = Dimensions::get_view_matrix();
    CharacterDrawable {
      projection: Projection {
        model: view.into(),
        view: view.into(),
        proj: cgmath::perspective(cgmath::Deg(75.0f32), ASPECT_RATIO, 0.1, 4000.0).into(),
      },
      position: Position::new([0.0, 0.0]),
      orientation: Orientation::Right,
      stance: Stance::Walking,
      direction: Orientation::Right,
    }
  }

  pub fn update(&mut self, world_to_clip: &Projection, ci: &CharacterInputState, mouse_input: &MouseInputState, dimensions: &Dimensions, zombies: &Vec<ZombieDrawable>) {
    self.projection = *world_to_clip;

    if zombies.iter()
              .any(|z| {
                let zombie_pos = Position::new([ci.x_movement - z.position.position[0], ci.y_movement - z.position.position[1]]);
                self.stance != Stance::NormalDeath &&
                  z.stance != Stance::NormalDeath &&
                  z.stance != Stance::CriticalDeath &&
                  overlaps(Position::new([ci.x_movement, ci.y_movement]), zombie_pos, 10.0, 20.0)
              }) {
      self.stance = Stance::NormalDeath;
      println!("Player died, press Esc to quit");
    }

    if self.stance != Stance::NormalDeath {
      if ci.is_shooting && mouse_input.left_click_point.is_some() && !ci.is_colliding {
        self.stance = Stance::Firing;
        self.orientation = get_orientation_from_center(mouse_input, dimensions);
      } else if ci.is_colliding {
        self.stance = Stance::Still;
      } else {
        self.stance = Stance::Walking;
        self.orientation = ci.orientation;
      }
    }
  }
}

impl Default for CharacterDrawable {
  fn default() -> Self {
    CharacterDrawable::new()
  }
}

impl specs::prelude::Component for CharacterDrawable {
  type Storage = specs::storage::VecStorage<CharacterDrawable>;
}

pub struct CharacterDrawSystem<R: gfx::Resources> {
  bundle: gfx::pso::bundle::Bundle<R, critter_pipeline::Data<R>>,
  data: Vec<CritterData>,
}

impl<R: gfx::Resources> CharacterDrawSystem<R> {
  pub fn new<F>(factory: &mut F,
                rtv: gfx::handle::RenderTargetView<R, ColorFormat>,
                dsv: gfx::handle::DepthStencilView<R, DepthFormat>) -> CharacterDrawSystem<R>
                where F: gfx::Factory<R> {
    use gfx::traits::FactoryExt;

    let charter_bytes = &include_bytes!("../../assets/character.png")[..];

    let vertex_data: [VertexData; 4] = [
      VertexData::new([-20.0, -28.0], [0.0, 1.0]),
      VertexData::new([20.0, -28.0], [1.0, 1.0]),
      VertexData::new([20.0, 28.0], [1.0, 0.0]),
      VertexData::new([-20.0, 28.0], [0.0, 0.0]),
    ];

    let index_data: [u16; 6] = [0, 1, 2, 2, 3, 0];

    let (vertex_buf, slice) = factory.create_vertex_buffer_with_slice(&vertex_data, &index_data[..]);

    let char_texture = load_texture(factory, charter_bytes);
    let pso = factory
      .create_pipeline_simple(SHADER_VERT,
                              SHADER_FRAG,
                              critter_pipeline::new())
      .unwrap();

    let pipeline_data = critter_pipeline::Data {
      vbuf: vertex_buf,
      projection_cb: factory.create_constant_buffer(1),
      position_cb: factory.create_constant_buffer(1),
      character_sprite_cb: factory.create_constant_buffer(1),
      charactersheet: (char_texture, factory.create_sampler_linear()),
      out_color: rtv,
      out_depth: dsv,
    };

    let data = data::load_character();

    CharacterDrawSystem {
      bundle: gfx::Bundle::new(slice, pso, pipeline_data),
      data,
    }
  }

  fn get_next_sprite(&self, character_idx: usize, character_fire_idx: usize, drawable: &mut CharacterDrawable) -> CharacterSheet {
    let char_sprite =
      if drawable.orientation == Orientation::Still && drawable.stance == Stance::Walking {
        let sprite_idx = (drawable.direction as usize * 28 + RUN_SPRITE_OFFSET) as usize;
        (&self.data[sprite_idx], sprite_idx)
      } else if drawable.stance == Stance::Walking {
        drawable.direction = drawable.orientation;
        let sprite_idx = (drawable.orientation as usize * 28 + character_idx + RUN_SPRITE_OFFSET) as usize;
        (&self.data[sprite_idx], sprite_idx)
      } else {
        let sprite_idx = (drawable.orientation as usize * 8 + character_fire_idx) as usize;
        (&self.data[sprite_idx], sprite_idx)
      };

    let elements_x = CHARACTER_SHEET_TOTAL_WIDTH / (char_sprite.0.data[2] + SPRITE_OFFSET);
    CharacterSheet {
      x_div: elements_x,
      y_div: 0.0,
      row_idx: 0,
      index: char_sprite.1 as f32,
    }
  }

  pub fn draw<C>(&mut self,
                 mut drawable: &mut CharacterDrawable,
                 character: &CharacterSprite,
                 encoder: &mut gfx::Encoder<R, C>)
                 where C: gfx::CommandBuffer<R> {
    encoder.update_constant_buffer(&self.bundle.data.projection_cb, &drawable.projection);
    encoder.update_constant_buffer(&self.bundle.data.position_cb, &drawable.position);
    encoder.update_constant_buffer(&self.bundle.data.character_sprite_cb,
                                   &self.get_next_sprite(character.character_idx,
                                                         character.character_fire_idx,
                                                         &mut drawable));
    self.bundle.encode(encoder);
  }
}

#[derive(Debug)]
pub struct PreDrawSystem;

impl PreDrawSystem {
  pub fn new() -> PreDrawSystem {
    PreDrawSystem {}
  }
}

impl<'a> specs::prelude::System<'a> for PreDrawSystem {
  #[cfg_attr(feature = "cargo-clippy", allow(type_complexity))]
  type SystemData = (WriteStorage<'a, CharacterDrawable>,
                     ReadStorage<'a, CameraInputState>,
                     ReadStorage<'a, CharacterInputState>,
                     ReadStorage<'a, MouseInputState>,
                     ReadStorage<'a, Zombies>,
                     Read<'a, Dimensions>);

  fn run(&mut self, (mut character, camera_input, character_input, mouse_input, zombies, dim): Self::SystemData) {
    use specs::join::Join;

    for (c, camera, ci, mi, zs) in (&mut character, &camera_input, &character_input, &mouse_input, &zombies).join() {
      let world_to_clip = dim.world_to_projection(camera);
      c.update(&world_to_clip, ci, mi, &dim, &zs.zombies);
    }
  }
}
<|endoftext|>


<commit_before>//! `sled` is a high-performance embedded database with
//! an API that is similar to a `BTreeMap<[u8], [u8]>`,
//! but with several additional capabilities for
//! assisting creators of stateful systems.
//!
//! It is fully thread-safe, and all operations are
//! atomic. Multiple `Tree`s are supported with the
//! [`Db::open_tree`](struct.Db.html#method.open_tree) method.
//!
//! ACID transactions involving reads and writes to
//! multiple items are supported with the
//! [`Tree::transaction`](struct.Tree.html#method.transaction)
//! method. Transactions may also operate over
//! multiple `Tree`s (see
//! [`Tree::transaction`](struct.Tree.html#method.transaction)
//! docs for more info).
//!
//! Users may also subscribe to updates on individual
//! `Tree`s by using the
//! [`Tree::watch_prefix`](struct.Tree.html#method.watch_prefix)
//! method, which returns a blocking `Iterator` over
//! updates to keys that begin with the provided
//! prefix. You may supply an empty prefix to subscribe
//! to everything.
//!
//! [Merge operators](https://github.com/spacejam/sled/wiki/merge-operators)
//! (aka read-modify-write operators) are supported. A
//! merge operator is a function that specifies
//! how new data can be merged into an existing value
//! without requiring both a read and a write.
//! Using the
//! [`Tree::merge`](struct.Tree.html#method.merge)
//! method, you may "push" data to a `Tree` value
//! and have the provided merge operator combine
//! it with the existing value, if there was one.
//! They are set on a per-`Tree` basis, and essentially
//! allow any sort of data structure to be built
//! using merges as an atomic high-level operation.
//!
//! `sled` is built by experienced database engineers
//! who think users should spend less time tuning and
//! working against high-friction APIs. Expect
//! significant ergonomic and performance improvements
//! over time. Most surprises are bugs, so please
//! [let us know](mailto:t@jujit.su?subject=sled%20sucks!!!) if something
//! is high friction.
//!
//! # Examples
//!
//! ```
//! use sled::Db;
//!
//! let t = Db::open("my_db").unwrap();
//! t.insert(b"yo!", b"v1");
//! assert_eq!(&t.get(b"yo!").unwrap().unwrap(), b"v1");
//!
//! // Atomic compare-and-swap.
//! t.cas(
//!     b"yo!",       // key
//!     Some(b"v1"),  // old value, None for not present
//!     Some(b"v2"),  // new value, None for delete
//! ).unwrap();
//!
//! // Iterates over key-value pairs, starting at the given key.
//! let scan_key = b"a non-present key before yo!";
//! let mut iter = t.range(scan_key..);
//! assert_eq!(&iter.next().unwrap().unwrap().0, b"yo!");
//! assert_eq!(iter.next(), None);
//!
//! t.remove(b"yo!");
//! assert_eq!(t.get(b"yo!"), Ok(None));
//! ```
#![cfg_attr(test, deny(warnings))]
#![deny(missing_docs)]
#![deny(future_incompatible)]
#![deny(nonstandard_style)]
#![deny(rust_2018_compatibility)]
#![deny(rust_2018_idioms)]

mod batch;
mod binary_search;
mod context;
mod data;
mod db;
mod frag;
mod iter;
mod ivec;
mod materializer;
mod meta;
mod node;
mod prefix;
mod subscription;
mod tree;
mod tx;

#[cfg(any(windows, target_os = "linux", target_os = "macos"))]
mod flusher;

const DEFAULT_TREE_ID: &[u8] = b"__sled__default";

pub use {
    self::{
        batch::Batch,
        db::Db,
        iter::Iter,
        ivec::IVec,
        subscription::{Event, Subscriber},
        tree::Tree,
        tx::{
            TransactionError, TransactionResult, Transactional,
            TransactionalTree,
        },
    },
    pagecache::{Config, ConfigBuilder, Error, Result},
};

use {
    self::{
        binary_search::binary_search_lub,
        context::Context,
        data::Data,
        frag::Frag,
        node::Node,
        prefix::{
            prefix_cmp, prefix_cmp_encoded, prefix_decode, prefix_encode,
            prefix_reencode,
        },
        subscription::Subscriptions,
    },
    log::{debug, error, trace},
    pagecache::{
        debug_delay, pin, Materializer, Measure, PageCache, PageId,
        RecoveryGuard, M,
    },
    serde::{Deserialize, Serialize},
};

type TreePtr<'g> = pagecache::PagePtr<'g, Frag>;

/// Allows arbitrary logic to be injected into mere operations of the
/// `PageCache`.
pub type MergeOperator = fn(
    key: &[u8],
    last_value: Option<&[u8]>,
    new_merge: &[u8],
) -> Option<Vec<u8>>;
<commit_msg>Fix doctest<commit_after>//! `sled` is a high-performance embedded database with
//! an API that is similar to a `BTreeMap<[u8], [u8]>`,
//! but with several additional capabilities for
//! assisting creators of stateful systems.
//!
//! It is fully thread-safe, and all operations are
//! atomic. Multiple `Tree`s are supported with the
//! [`Db::open_tree`](struct.Db.html#method.open_tree) method.
//!
//! ACID transactions involving reads and writes to
//! multiple items are supported with the
//! [`Tree::transaction`](struct.Tree.html#method.transaction)
//! method. Transactions may also operate over
//! multiple `Tree`s (see
//! [`Tree::transaction`](struct.Tree.html#method.transaction)
//! docs for more info).
//!
//! Users may also subscribe to updates on individual
//! `Tree`s by using the
//! [`Tree::watch_prefix`](struct.Tree.html#method.watch_prefix)
//! method, which returns a blocking `Iterator` over
//! updates to keys that begin with the provided
//! prefix. You may supply an empty prefix to subscribe
//! to everything.
//!
//! [Merge operators](https://github.com/spacejam/sled/wiki/merge-operators)
//! (aka read-modify-write operators) are supported. A
//! merge operator is a function that specifies
//! how new data can be merged into an existing value
//! without requiring both a read and a write.
//! Using the
//! [`Tree::merge`](struct.Tree.html#method.merge)
//! method, you may "push" data to a `Tree` value
//! and have the provided merge operator combine
//! it with the existing value, if there was one.
//! They are set on a per-`Tree` basis, and essentially
//! allow any sort of data structure to be built
//! using merges as an atomic high-level operation.
//!
//! `sled` is built by experienced database engineers
//! who think users should spend less time tuning and
//! working against high-friction APIs. Expect
//! significant ergonomic and performance improvements
//! over time. Most surprises are bugs, so please
//! [let us know](mailto:t@jujit.su?subject=sled%20sucks!!!) if something
//! is high friction.
//!
//! # Examples
//!
//! ```
//! use sled::Db;
//!
//! let t = Db::open("my_db").unwrap();
//! t.insert(b"yo!", b"v1");
//! assert_eq!(&t.get(b"yo!").unwrap().unwrap(), b"v1");
//!
//! // Atomic compare-and-swap.
//! t.cas(
//!     b"yo!",       // key
//!     Some(b"v1"),  // old value, None for not present
//!     Some(b"v2"),  // new value, None for delete
//! ).unwrap();
//!
//! // Iterates over key-value pairs, starting at the given key.
//! let scan_key: &[u8] = b"a non-present key before yo!";
//! let mut iter = t.range(scan_key..);
//! assert_eq!(&iter.next().unwrap().unwrap().0, b"yo!");
//! assert_eq!(iter.next(), None);
//!
//! t.remove(b"yo!");
//! assert_eq!(t.get(b"yo!"), Ok(None));
//! ```
#![cfg_attr(test, deny(warnings))]
#![deny(missing_docs)]
#![deny(future_incompatible)]
#![deny(nonstandard_style)]
#![deny(rust_2018_compatibility)]
#![deny(rust_2018_idioms)]

mod batch;
mod binary_search;
mod context;
mod data;
mod db;
mod frag;
mod iter;
mod ivec;
mod materializer;
mod meta;
mod node;
mod prefix;
mod subscription;
mod tree;
mod tx;

#[cfg(any(windows, target_os = "linux", target_os = "macos"))]
mod flusher;

const DEFAULT_TREE_ID: &[u8] = b"__sled__default";

pub use {
    self::{
        batch::Batch,
        db::Db,
        iter::Iter,
        ivec::IVec,
        subscription::{Event, Subscriber},
        tree::Tree,
        tx::{
            TransactionError, TransactionResult, Transactional,
            TransactionalTree,
        },
    },
    pagecache::{Config, ConfigBuilder, Error, Result},
};

use {
    self::{
        binary_search::binary_search_lub,
        context::Context,
        data::Data,
        frag::Frag,
        node::Node,
        prefix::{
            prefix_cmp, prefix_cmp_encoded, prefix_decode, prefix_encode,
            prefix_reencode,
        },
        subscription::Subscriptions,
    },
    log::{debug, error, trace},
    pagecache::{
        debug_delay, pin, Materializer, Measure, PageCache, PageId,
        RecoveryGuard, M,
    },
    serde::{Deserialize, Serialize},
};

type TreePtr<'g> = pagecache::PagePtr<'g, Frag>;

/// Allows arbitrary logic to be injected into mere operations of the
/// `PageCache`.
pub type MergeOperator = fn(
    key: &[u8],
    last_value: Option<&[u8]>,
    new_merge: &[u8],
) -> Option<Vec<u8>>;
<|endoftext|>