piston-dyon_interactive 0.45.0

A library for interactive coding with the Piston game engine
Documentation
#[macro_use]
extern crate dyon;
extern crate sdl2_window;
extern crate opengl_graphics;
extern crate piston;
extern crate current;
extern crate dyon_interactive;
extern crate kira;
extern crate image;

use std::sync::Arc;
use std::collections::HashMap;
use current::CurrentGuard;
use dyon::{error, load, Module, Dfn, Runtime, Type};
use dyon_interactive::{FontNames, ImageNames};
use image::RgbaImage;
use piston::input::Event;
use piston::window::WindowSettings;
use piston::event_loop::{Events, EventSettings};
use sdl2_window::Sdl2Window;
use opengl_graphics::{OpenGL, Filter, GlGraphics, GlyphCache, Texture, TextureSettings};
use kira::manager::{AudioManager, AudioManagerSettings};
use kira::sound::handle::SoundHandle;
use kira::mixer::{SubTrackHandle, SubTrackSettings};

type Sounds = HashMap<Arc<String>, SoundHandle>;
type Music = HashMap<Arc<String>, SoundHandle>;

fn main() {
    let file = std::env::args_os().nth(1)
        .and_then(|s| s.into_string().ok());
    let file = if let Some(file) = file {
        use std::env::set_current_dir;
        use std::path::PathBuf;

        let path: PathBuf = (&file).into();
        if let Some(parent) = path.parent() {
            if let Err(_) = set_current_dir(parent) {
                file
            } else {
                path.file_name().unwrap().to_str().unwrap().to_owned()
            }
        } else {
            file
        }
    } else {
        println!("dyongame <file.dyon>");
        return;
    };

    let opengl = OpenGL::V3_2;
    let mut window: Sdl2Window =
        WindowSettings::new("dyongame", [512; 2])
        .exit_on_esc(true)
        .samples(4)
        .graphics_api(opengl)
        .build()
        .unwrap();
    let dyon_module = match load_module(&file) {
        None => return,
        Some(m) => Arc::new(m)
    };

    let mut factory = ();
    let mut dyon_runtime = Runtime::new();
    let fira_sans = include_bytes!("../assets/FiraSans-Regular.ttf");
    let hack = include_bytes!("../assets/Hack-Regular.ttf");
    let font_texture_settings = TextureSettings::new().filter(Filter::Nearest);
    let mut glyphs = vec![
        GlyphCache::from_bytes(&fira_sans[..], (), font_texture_settings.clone()).unwrap(),
        GlyphCache::from_bytes(&hack[..], (), font_texture_settings).unwrap()
    ];
    let mut font_names = FontNames(vec![
        Arc::new("FiraSans-Regular".to_owned()),
        Arc::new("Hack-Regular".to_owned()),
    ]);
    let mut images = vec![];
    let mut image_names = ImageNames(vec![]);
    let mut textures = vec![];
    let mut gl = GlGraphics::new(opengl);
    let mut events = Events::new(EventSettings::new());
    let mut audio_manager = AudioManager::new(AudioManagerSettings::default()).unwrap();
    let mut music_track = audio_manager.add_sub_track(SubTrackSettings::default()).unwrap();
    let mut sounds: Sounds = HashMap::new();
    let mut music: Music = HashMap::new();

    let mut e: Option<Event> = None;
    let factory_guard: CurrentGuard<()> = CurrentGuard::new(&mut factory);
    let window_guard = CurrentGuard::new(&mut window);
    let event_guard: CurrentGuard<Option<Event>> = CurrentGuard::new(&mut e);
    let glyphs_guard: CurrentGuard<Vec<GlyphCache>> = CurrentGuard::new(&mut glyphs);
    let font_names_guard: CurrentGuard<FontNames> = CurrentGuard::new(&mut font_names);
    let images_guard: CurrentGuard<Vec<RgbaImage>> = CurrentGuard::new(&mut images);
    let image_names_guard: CurrentGuard<ImageNames> = CurrentGuard::new(&mut image_names);
    let textures_guard: CurrentGuard<Vec<Texture>> = CurrentGuard::new(&mut textures);
    let gl_guard: CurrentGuard<GlGraphics> = CurrentGuard::new(&mut gl);
    let events_guard: CurrentGuard<Events> = CurrentGuard::new(&mut events);
    let audio_manager_guard: CurrentGuard<AudioManager> = CurrentGuard::new(&mut audio_manager);
    let music_track_guard: CurrentGuard<SubTrackHandle> = CurrentGuard::new(&mut music_track);
    let sounds_guard: CurrentGuard<Sounds> = CurrentGuard::new(&mut sounds);
    let music_guard: CurrentGuard<Music> = CurrentGuard::new(&mut music);

    if error(dyon_runtime.run(&dyon_module)) {
        return;
    }

    drop(music_guard);
    drop(sounds_guard);
    drop(music_track_guard);
    drop(audio_manager_guard);
    drop(events_guard);
    drop(gl_guard);
    drop(textures_guard);
    drop(image_names_guard);
    drop(images_guard);
    drop(font_names_guard);
    drop(glyphs_guard);
    drop(event_guard);
    drop(window_guard);
    drop(factory_guard);
}

fn load_module(file: &str) -> Option<Module> {
    use dyon_functions::*;
    use dyon_interactive::add_functions;

    let mut module = Module::new();
    add_functions::<Sdl2Window, (), GlyphCache>(&mut module);
    module.add(Arc::new("render_source".into()), render_source, Dfn::nl(vec![], Type::Str));
    module.add(Arc::new("draw".into()), draw, Dfn::nl(vec![Type::array()], Type::Void));
    module.add(Arc::new("next_event".into()),
        next_event, Dfn::nl(vec![], Type::Bool));
    module.add(Arc::new("bind_sound__name_file".into()),
        bind_sound__name_file, Dfn::nl(vec![Type::Str; 2], Type::Void));
    module.add(Arc::new("bind_music__name_file".into()),
        bind_music__name_file, Dfn::nl(vec![Type::Str; 2], Type::Void));
    module.add(Arc::new("play_sound__name_repeat_volume".into()),
        play_sound__name_repeat_volume, Dfn::nl(vec![Type::Str, Type::F64, Type::F64], Type::Void));
    module.add(Arc::new("play_sound_forever__name_volume".into()),
        play_sound_forever__name_volume, Dfn::nl(vec![Type::Str, Type::F64], Type::Void));
    module.add(Arc::new("play_music__name_repeat".into()),
        play_music__name_repeat, Dfn::nl(vec![Type::Str, Type::F64], Type::Void));
    module.add(Arc::new("play_music_forever__name".into()),
        play_music_forever__name, Dfn::nl(vec![Type::Str], Type::Void));
    module.add(Arc::new("set_music_volume".into()),
        set_music_volume, Dfn::nl(vec![Type::F64], Type::Void));
    module.add(Arc::new("create_texture".into()),
        create_texture, Dfn::nl(vec![Type::F64], Type::F64));
    module.add(Arc::new("update__texture_image".into()),
        update__texture_image, Dfn::nl(vec![Type::F64, Type::F64], Type::Void)
    );

    if error(dyon::load_str(
        "render.dyon",
        Arc::new(include_str!("../src/render.dyon").into()),
        &mut module
    )) {
        return None;
    }

    if error(load(file, &mut module)) {
        None
    } else {
        Some(module)
    }
}

mod dyon_functions {
    use sdl2_window::Sdl2Window;
    use piston::input::{Event, RenderEvent};
    use piston::event_loop::Events;
    use opengl_graphics::{GlGraphics, GlyphCache, Texture, TextureSettings};
    use dyon::{Runtime, Variable};
    use dyon_interactive::{draw_2d, NO_EVENT};
    use current::Current;
    use std::sync::Arc;
    use image::RgbaImage;

    dyon_fn!{fn render_source() -> String {include_str!("../src/render.dyon").into()}}

    pub fn draw(rt: &mut Runtime) -> Result<(), String> {
        let e = unsafe { &*Current::<Option<Event>>::new() };
        let gl = unsafe { &mut *Current::<GlGraphics>::new() };
        let glyphs = unsafe { &mut *Current::<Vec<GlyphCache>>::new() };
        let textures = unsafe { &mut *Current::<Vec<Texture>>::new() };
        if let &Some(ref e) = e {
            if let Some(args) = e.render_args() {
                gl.draw(args.viewport(), |c, g| {
                    draw_2d(rt, glyphs, textures, c, g)
                })
            } else {
                Ok(())
            }
        } else {
            Err(NO_EVENT.into())
        }
    }

    pub fn create_texture(rt: &mut Runtime) -> Result<Variable, String> {
        use dyon::embed::PushVariable;

        let images = unsafe { &*Current::<Vec<RgbaImage>>::new() };
        let textures = unsafe { &mut *Current::<Vec<Texture>>::new() };
        let id: usize = rt.pop()?;
        let new_id = textures.len();
        let image = if let Some(x) = images.get(id) {
            x
        } else {
            return Err("Image id is out of bounds".into());
        };
        textures.push(Texture::from_image(image, &TextureSettings::new()));
        Ok(new_id.push_var())
    }

    #[allow(non_snake_case)]
    pub fn update__texture_image(rt: &mut Runtime) -> Result<(), String> {
        let images = unsafe { &*Current::<Vec<RgbaImage>>::new() };
        let textures = unsafe { &mut *Current::<Vec<Texture>>::new() };
        let image_id: usize = rt.pop()?;
        let texture_id: usize = rt.pop()?;
        let image = if let Some(x) = images.get(image_id) {
            x
        } else {
            return Err("Image id is out of bounds".into());
        };
        let texture = if let Some(x) = textures.get_mut(texture_id) {
            x
        } else {
            return Err("Texture id is out of bounds".into());
        };
        texture.update(&image);
        Ok(())
    }

    dyon_fn!{fn next_event() -> bool {
        let window = unsafe { &mut *Current::<Sdl2Window>::new() };
        let events = unsafe { &mut *Current::<Events>::new() };
        let e = unsafe { &mut *Current::<Option<Event>>::new() };
        if let Some(new_e) = events.next(window) {
            *e = Some(new_e);
            true
        } else {
            *e = None;
            false
        }
    }}

    dyon_fn!{fn bind_sound__name_file(name: Arc<String>, file: Arc<String>) {
        use kira::sound::SoundSettings;
        use AudioManager;
        use Sounds;

        let audio_manager = unsafe { &mut *Current::<AudioManager>::new() };
        let sounds = unsafe { &mut *Current::<Sounds>::new() };
        let sound_handle = audio_manager.load_sound(&**file, SoundSettings::default()).unwrap();
        sounds.insert(name, sound_handle);
    }}

    dyon_fn!{fn bind_music__name_file(name: Arc<String>, file: Arc<String>) {
        use kira::sound::SoundSettings;
        use AudioManager;
        use Music;

        let audio_manager = unsafe { &mut *Current::<AudioManager>::new() };
        let music = unsafe { &mut *Current::<Music>::new() };
        let sound_handle = audio_manager.load_sound(&**file, SoundSettings::default()).unwrap();
        music.insert(name, sound_handle);
    }}

    dyon_fn!{fn play_sound__name_repeat_volume(name: Arc<String>, repeat: f64, volume: f64) {
        use kira::instance::InstanceSettings;
        use kira::arrangement::{
            Arrangement,
            ArrangementSettings,
            LoopArrangementSettings,
            SoundClip
        };
        use Sounds;
        use AudioManager;

        let audio_manager = unsafe { &mut *Current::<AudioManager>::new() };
        let sounds = unsafe { &mut *Current::<Sounds>::new() };
        if let Some(sound_handle) = sounds.get_mut(&name) {
            let instance_settings = InstanceSettings::default().volume(volume);
            if repeat == -1.0 {
                let mut arrangement_handle = audio_manager.add_arrangement(Arrangement::new_loop(
                	&sound_handle,
                	LoopArrangementSettings::default(),
                )).unwrap();
                arrangement_handle.play(instance_settings).unwrap();
            } else if repeat != 0.0 {
                let mut arrangement = Arrangement::new(ArrangementSettings::new());
                let mut start = 0.0;
                for _ in 0..repeat as u32 {
                    arrangement.add_clip(SoundClip::new(&sound_handle, start));
                    start += sound_handle.duration();
                }
                let mut arrangement_handle = audio_manager.add_arrangement(arrangement).unwrap();
                arrangement_handle.play(instance_settings).unwrap();
            }
        }
    }}

    dyon_fn!{fn play_sound_forever__name_volume(name: Arc<String>, volume: f64) {
        use kira::instance::InstanceSettings;
        use kira::arrangement::{
            Arrangement,
            LoopArrangementSettings,
        };
        use Sounds;
        use AudioManager;

        let audio_manager = unsafe { &mut *Current::<AudioManager>::new() };
        let sounds = unsafe { &mut *Current::<Sounds>::new() };
        if let Some(sound_handle) = sounds.get_mut(&name) {
            let instance_settings = InstanceSettings::default().volume(volume);
            let mut arrangement_handle = audio_manager.add_arrangement(Arrangement::new_loop(
            	&sound_handle,
            	LoopArrangementSettings::default(),
            )).unwrap();
            arrangement_handle.play(instance_settings).unwrap();
        }
    }}

    dyon_fn!{fn play_music__name_repeat(name: Arc<String>, repeat: f64) {
        use kira::instance::InstanceSettings;
        use kira::arrangement::{
            Arrangement,
            ArrangementSettings,
            LoopArrangementSettings,
            SoundClip
        };
        use Music;
        use AudioManager;
        use SubTrackHandle;

        let audio_manager = unsafe { &mut *Current::<AudioManager>::new() };
        let music = unsafe { &mut *Current::<Music>::new() };
        let music_track = unsafe { &mut *Current::<SubTrackHandle>::new() };
        if let Some(sound_handle) = music.get_mut(&name) {
            let instance_settings = InstanceSettings::default();
            if repeat == -1.0 {
                let mut arrangement_handle = audio_manager.add_arrangement(Arrangement::new_loop(
                	&sound_handle,
                	LoopArrangementSettings::default().default_track(music_track.id()),
                )).unwrap();
                arrangement_handle.play(instance_settings).unwrap();
            } else if repeat != 0.0 {
                let mut arrangement = Arrangement::new(ArrangementSettings::new()
                    .default_track(music_track.id()));
                let mut start = 0.0;
                for _ in 0..repeat as u32 {
                    arrangement.add_clip(SoundClip::new(&sound_handle, start));
                    start += sound_handle.duration();
                }
                let mut arrangement_handle = audio_manager.add_arrangement(arrangement).unwrap();
                arrangement_handle.play(instance_settings).unwrap();
            }
        }
    }}

    dyon_fn!{fn play_music_forever__name(name: Arc<String>) {
        use kira::instance::InstanceSettings;
        use kira::arrangement::{
            Arrangement,
            LoopArrangementSettings,
        };
        use Music;
        use AudioManager;
        use SubTrackHandle;

        let audio_manager = unsafe { &mut *Current::<AudioManager>::new() };
        let music = unsafe { &mut *Current::<Music>::new() };
        let music_track = unsafe { &mut *Current::<SubTrackHandle>::new() };
        if let Some(sound_handle) = music.get_mut(&name) {
            let instance_settings = InstanceSettings::default();
            let mut arrangement_handle = audio_manager.add_arrangement(Arrangement::new_loop(
            	&sound_handle,
            	LoopArrangementSettings::default().default_track(music_track.id()),
            )).unwrap();
            arrangement_handle.play(instance_settings).unwrap();
        }
    }}

    dyon_fn!{fn set_music_volume(volume: f64) {
        use SubTrackHandle;

        let music_track = unsafe { &mut *Current::<SubTrackHandle>::new() };
        music_track.set_volume(volume).unwrap();
    }}
}