2d-sfml-game-engine / HW5 / Part 1 / Client / ScriptManager.cpp
ScriptManager.cpp
Raw
#include "ScriptManager.h"
#include <fstream>
#include <iostream>
#include <cstring>
#include "v8helpers.h"

/** Definition of static container */
std::map<std::string, ContextContainer> ScriptManager::context_containers;

/** Note: function signature is very important */
ScriptManager::ScriptManager(v8::Isolate *isolate, v8::Local<v8::Context> &context)
{
	if(context_containers.empty())
    {
        std::map<std::string, ScriptMetaData> scripts;
        ContextContainer context_container{ "default", isolate, context, scripts};

        // Create a global object template
        v8::Local<v8::ObjectTemplate> global = v8::ObjectTemplate::New(isolate);

        // Add the print function to the global object template
        global->Set(isolate, "print", v8::FunctionTemplate::New(isolate, v8helpers::Print));

        // Create the default context with the global object template
        context_container.context = v8::Context::New(isolate, NULL, global);

        context_containers.emplace(std::make_pair("default", context_container));
    }
}

ContextContainer ScriptManager::getContextContainer(std::string context_name)
{
	return context_containers[context_name];
}

// TODO: Free scripts
ScriptManager::~ScriptManager()
{
    /*
    this->isolate->Dispose();
    delete this->create_params.array_buffer_allocator;
    */
}

std::string read_script(std::filesystem::path path)
{
    std::ifstream in_file(path, std::ios::in);
    const auto script_size = std::filesystem::file_size(path);

    std::string script(script_size, '\0');
    in_file.read(script.data(), script_size);

    return script;
}

void ScriptManager::addContext(v8::Isolate *isolate, v8::Local<v8::Context> &context, std::string context_name)
{
	std::map<std::string, ScriptMetaData> scripts;
	ContextContainer context_container{ context_name, isolate, context, scripts};
	context_containers.emplace(std::make_pair(context_name, context_container));
}

void ScriptManager::addScript(std::string script_id, std::string file_name, std::string context_name)
{
	ScriptMetaData smd;
	smd.script_id = script_id;
	smd.file_name = file_name;
	smd.script = compile(read_script(file_name), context_name);
	context_containers[context_name].scripts.emplace(std::make_pair(script_id, smd));
#if SM_DEBUG
	std::cout << "Added script:\n"
			  << "\tScript_ID: " << smd.script_id
			  << "\tFile_Name: " << smd.file_name
			  << std::endl;
#endif
}

void ScriptManager::runOne(std::string script_id, bool reload, std::string context_name)
{
    ContextContainer container = context_containers[context_name];
    v8::Context::Scope context_scope(container.context);

    ScriptMetaData smd = container.scripts[script_id];

    if (reload)
    {
        smd.script = compile(read_script(smd.file_name), context_name);
    }

    v8::TryCatch try_catch(container.isolate);

    {
        v8::Local<v8::Value> result;
        if (!smd.script->Run(container.context).ToLocal(&result))
        {
            v8::String::Utf8Value error(container.isolate, try_catch.Exception());
            std::cerr << "**********************************************************" << std::endl;
            std::cerr << "Error while executing script: " << script_id << std::endl;
            std::cerr << "Exception: " << *error << std::endl;
            std::cerr << "**********************************************************" << std::endl;
            return;
        }
    }
}

void ScriptManager::runAll(bool reload, std::string context_name)
{
	ContextContainer container = context_containers[context_name];
	for( std::pair<std::string, ScriptMetaData> i : container.scripts )
	{
		runOne(i.second.script_id, reload, context_name);
	}
}

void ScriptManager::reloadAll(std::string context_name)
{
	ContextContainer container = context_containers[context_name];
	for( std::pair<std::string, ScriptMetaData> p : container.scripts )
	{
		p.second.script = compile(p.second.file_name, context_name);
	}
}

v8::Local<v8::Script> ScriptManager::compile(std::string script_src_string, std::string context_name)
{
    ContextContainer container = context_containers[context_name];
    v8::Context::Scope context_scope(container.context);
    v8::Local<v8::String> source = v8::String::NewFromUtf8(container.isolate, script_src_string.c_str(), v8::NewStringType::kNormal).ToLocalChecked();
    v8::Local<v8::Script> script = v8::Script::Compile(container.context, source).ToLocalChecked();
	return script;
}

/**
 * This function demonstrates how you can get an object by using the string
 * associated with the object when added to the Global proxy. Note, despite the
 * return type of Global()->Get(...) being v8::Local<v8::Value>, this is indeed
 * a v8::Local<v8::Object>.
 */
void ScriptManager::getHandleFromScript(const v8::FunctionCallbackInfo<v8::Value>& args)
{
	v8::Isolate *isolate = args.GetIsolate();
	v8::Local<v8::Context> context = isolate->GetCurrentContext();
	v8::Local<v8::Value> object = context->Global()->Get(context, args[0]->ToString()).ToLocalChecked();
	args.GetReturnValue().Set(object);
}