1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
extern crate core;
#[macro_use]
extern crate log;
#[macro_use]
extern crate lazy_static;

use crate::data::Game;
use crate::lazy_static::__Deref;
use std::collections::HashSet;
use std::sync::{Mutex, MutexGuard};
use stdweb::js;

#[cfg(test)]
mod tests {
    #[test]
    fn it_works() {
        assert_eq!(2 + 2, 4);
    }
}

pub mod data;
pub mod logging;

lazy_static! {
    static ref GAME: Mutex<Game> = Mutex::new(Game::new());
}

pub fn game<'a>() -> MutexGuard<'a, Game> {
    GAME.lock().unwrap()
}

pub fn init_screeps_connection(game_loop: &'static dyn Fn(&Game)) {
    std::panic::set_hook(Box::new(|info| {
        let msg = &info.to_string();
        let panic_message = msg.to_owned();
        /*
        In case this might be useful
        // Add the error stack to our message.
        //
        // This ensures that even if the `console` implementation doesn't
        // include stacks for `console.error`, the stack is still available
        // for the user. Additionally, Firefox's console tries to clean up
        // stack traces, and ruins Rust symbols in the process
        // (https://bugzilla.mozilla.org/show_bug.cgi?id=1519569) but since
        // it only touches the logged message's associated stack, and not
        // the message's contents, by including the stack in the message
        // contents we make sure it is available to the user.
        msg.push_str("\n\nStack:\n\n");
        let e = Error::new();
        let stack = e.stack();
        msg.push_str(&stack);

        // Safari's devtools, on the other hand, _do_ mess with logged
        // messages' contents, so we attempt to break their heuristics for
        // doing that by appending some whitespace.
        // https://github.com/rustwasm/console_error_panic_hook/issues/7
        msg.push_str("\n\n");
        */
        js! { @(no_return)
            console.error( @{msg} );
        }
        panic!(panic_message);
    }));
    logging::setup_logging(logging::Info);
    info!(
        "Global Reset! Compile took: {}",
        screeps::game::cpu::get_used()
    );

    let _game_loop = move || {
        debug!("loop starting! CPU: {}", screeps::game::cpu::get_used());

        game().refresh_state();

        game_loop(game().deref());

        let time = screeps::game::time();

        if time % 128 == 3 {
            info!("running memory cleanup");
            cleanup_memory().expect("expected Memory.creeps format to be a regular memory object");
        }

        debug!("done! cpu: {}", screeps::game::cpu::get_used())
    };

    js! {
        // show more error frames in js (default: 10)
        Error.stackTraceLimit = 25;

        var game_loop = @{_game_loop};

        module.exports.loop = function() {
            // Provide actual error traces.

            if(Game.cpu.bucket < 500){
                console.log("we are running out of time!" + JSON.stringify(Game.cpu));
                return;
            }
            try {
                game_loop();
            } catch (error) {
                // console_error function provided by 'screeps-game-api'
                console_error("caught exception:", error);
                if (error.stack) {
                    console_error("stack trace:", error.stack);
                }
                console_error("resetting VM next tick.");
                // reset the VM since we don't know if everything was cleaned up and don't
                // want an inconsistent state.
                module.exports.loop = wasm_initialize;
            }
        }
    }
}

fn cleanup_memory() -> Result<(), Box<dyn (::std::error::Error)>> {
    let alive_creeps: HashSet<String> = screeps::game::creeps::keys().into_iter().collect();

    let screeps_memory = match screeps::memory::root().dict("creeps")? {
        Some(v) => v,
        None => {
            warn!("not cleaning game creep memory: no Memory.creeps dict");
            return Ok(());
        }
    };

    for mem_name in screeps_memory.keys() {
        if !alive_creeps.contains(&mem_name) {
            debug!("cleaning up creep memory of dead creep {}", mem_name);
            screeps_memory.del(&mem_name);
        }
    }

    Ok(())
}