13#include <monopp/mono_exception.h>
14#include <monopp/mono_field_invoker.h>
15#include <monopp/mono_internal_call.h>
16#include <monopp/mono_method_invoker.h>
17#include <monopp/mono_property_invoker.h>
30enum class recompile_command :
int
37std::chrono::milliseconds check_interval(50);
40std::atomic<recompile_command> needs_recompile{};
41std::mutex container_mutex;
42std::vector<std::string> needs_to_recompile;
43std::atomic<uint64_t> compilation_version{};
45std::atomic_bool debug_mode{
true};
47auto print_assembly_info(
const mono::mono_assembly& assembly)
50 auto refs = assembly.dump_references();
52 ss << fmt::format(
" ----- References -----");
54 for(
const auto& ref : refs)
56 ss << fmt::format(
"\n{}", ref);
61 auto types = assembly.get_types();
64 ss << fmt::format(
" ----- Types -----");
66 for(
const auto&
type : types)
68 ss << fmt::format(
"\n{}",
type.get_fullname());
69 ss << fmt::format(
"\n sizeof {}",
type.get_sizeof());
70 ss << fmt::format(
"\n alignof {}",
type.get_alignof());
73 auto attribs =
type.get_attributes();
74 for(
const auto& attrib : attribs)
76 ss << fmt::format(
"\n - Attribute : {}", attrib.get_type().get_fullname());
80 auto fields =
type.get_fields();
81 for(
const auto& field : fields)
83 ss << fmt::format(
"\n - Field : {}", field.get_name());
85 auto attribs = field.get_attributes();
86 for(
const auto& attrib : attribs)
88 ss << fmt::format(
"\n -- Attribute : {}", attrib.get_type().get_fullname());
92 auto properties =
type.get_properties();
93 for(
const auto& prop : properties)
95 ss << fmt::format(
"\n - Property : {}", prop.get_name());
97 auto attribs = prop.get_attributes();
98 for(
const auto& attrib : attribs)
100 ss << fmt::format(
"\n -- Attribute : {}", attrib.get_type().get_fullname());
111 auto frame = mono::extract_relevant_stack_frame(e.what());
112 if(!
frame.file_name.empty())
118 APPLOG_ERROR_LOC(loc.file_name(),
int(loc.line()), loc.function_name(), e.what());
124 auto from_debug_info = from;
125 from_debug_info.concat(
".mdb");
126 auto from_comments_xml = from;
127 from_comments_xml.replace_extension(
".xml");
129 auto to_debug_info = to;
130 to_debug_info.concat(
".mdb");
131 auto to_comments_xml = to;
132 to_comments_xml.replace_extension(
".xml");
135 fs::copy_file(from, to, fs::copy_options::overwrite_existing, er);
136 fs::copy_file(from_debug_info, to_debug_info, fs::copy_options::overwrite_existing, er);
137 fs::copy_file(from_comments_xml, to_comments_xml, fs::copy_options::overwrite_existing, er);
139 fs::remove(from, er);
140 fs::remove(from_debug_info, er);
141 fs::remove(from_comments_xml, er);
146 bool is_deploy_mode = ctx.has<
deploy>();
148 mono::compiler_paths result;
153 result.assembly_dir = fs::absolute(mono_dir /
"lib").string();
154 result.config_dir = fs::absolute(mono_dir /
"etc").string();
158 const auto& names = mono::get_common_library_names();
159 const auto& library_paths = mono::get_common_library_paths();
160 const auto& config_paths = mono::get_common_config_paths();
162 for(
size_t i = 0; i < library_paths.size(); ++i)
164 const auto& library_path = library_paths.at(i);
165 const auto& config_path = config_paths.at(i);
166 std::vector<std::string> paths{library_path};
167 auto found_library = fs::find_library(names, paths);
169 if(!found_library.empty())
171 result.assembly_dir = fs::path(library_path).make_preferred().string();
172 result.config_dir = fs::path(config_path).make_preferred().string();
180 const auto& names = mono::get_common_executable_names();
181 const auto& paths = mono::get_common_executable_paths();
183 result.msc_executable = fs::find_program(names, paths).make_preferred().string();
187 APPLOG_TRACE(
"Assembly path - {}", result.assembly_dir);
195 return !paths.assembly_dir.empty() && !paths.config_dir.empty();
200 APPLOG_TRACE(
"{}::{}", hpp::type_name_str(*
this), __func__);
202 auto& ev = ctx.get_cached<
events>();
203 ev.
on_frame_update.connect(sentinel_,
this, &script_system::on_frame_update);
204 ev.on_frame_fixed_update.connect(sentinel_,
this, &script_system::on_frame_fixed_update);
205 ev.on_frame_update.connect(sentinel_, -100000,
this, &script_system::on_frame_late_update);
207 ev.on_play_end.connect(sentinel_, 1000,
this, &script_system::on_play_end);
208 ev.on_pause.connect(sentinel_, 100,
this, &script_system::on_pause);
209 ev.on_resume.connect(sentinel_, -100,
this, &script_system::on_resume);
210 ev.on_skip_next_frame.connect(sentinel_, -100,
this, &script_system::on_skip_next_frame);
212 auto mono_paths = find_mono(ctx);
218 error.msg =
"Failed to locate Mono C#. Please install it from - https://www.mono-project.com/download/stable/";
223 debug_config_.enable_debugging =
true;
225 if(mono::init(mono_paths, debug_config_))
227 bind_internal_calls(ctx);
233 if(!load_engine_domain(ctx,
true))
238 catch(
const mono::mono_exception& e)
250 error.msg =
"Failed to initialize Mono C#. Please install it from - https://www.mono-project.com/download/stable/";
257 APPLOG_TRACE(
"{}::{}", hpp::type_name_str(*
this), __func__);
260 unload_engine_domain();
269 debug_config_.address = address;
270 debug_config_.port = port;
271 debug_config_.loglevel = loglevel;
276 bool is_deploy_mode = ctx.has<
deploy>();
278 if(!is_deploy_mode && recompile)
285 if(!create_compilation_job(ctx,
"engine", debug).get())
291 domain_ = std::make_unique<mono::mono_domain>(
"Ace.Engine");
292 mono::mono_domain::set_current_domain(domain_.get());
297 copy_compiled_lib(engine_script_lib_temp, engine_script_lib);
299 auto assembly = domain_->get_assembly(engine_script_lib.string());
300 domain_->name_assembly(engine_script_lib.string(),
"Unravel.Engine");
303 cache_.update_manager_type = assembly.get_type(
"Unravel.Core",
"SystemManager");
311 mono::mono_domain::set_current_domain(
nullptr);
316 bool is_deploy_mode = ctx.has<
deploy>();
320 if(!is_deploy_mode && recompile)
322 result &= create_compilation_job(ctx,
"app", get_script_debug_mode()).get();
324 has_compilation_errors_ = !result;
327 app_domain_ = std::make_unique<mono::mono_domain>(
"Ace.App");
328 mono::mono_domain::set_current_domain(app_domain_.get());
333 copy_compiled_lib(app_script_lib_temp, app_script_lib);
340 if(assets.size() <= 1)
348 auto assembly = app_domain_->get_assembly(app_script_lib.string());
349 app_domain_->name_assembly(app_script_lib.string(),
"Ace.App");
353 auto engine_assembly = domain_->get_assembly(engine_script_lib.string());
355 auto comp_type = engine_assembly.get_type(
"Unravel.Core",
"ScriptComponent");
357 app_cache_.scriptable_component_types.clear();
358 if(!has_compilation_errors_)
360 app_cache_.scriptable_component_types = assembly.get_types_derived_from(comp_type);
363 catch(
const mono::mono_exception& e)
375 mono::mono_domain::set_current_domain(domain_.get());
404 if(!app_domain_ || !domain_)
410 create_call_ = call_progress::started;
412 for(
auto entity : entities)
419 create_call_ = call_progress::finished;
421 for(
auto entity : entities)
436 catch(
const mono::mono_exception& e)
444 if(!app_domain_ || !domain_)
450 create_call_ = call_progress::started;
453 [&](
auto e,
auto&& comp)
458 create_call_ = call_progress::finished;
461 [&](
auto e,
auto&& comp)
473 catch(
const mono::mono_exception& e)
481 APPLOG_TRACE(
"{}::{}", hpp::type_name_str(*
this), __func__);
483 if(!app_domain_ || !domain_)
490 auto& registry = *scn.registry;
495 registry.on_construct<
active_component>().connect<&on_create_active_component>();
496 registry.on_destroy<
active_component>().connect<&on_destroy_active_component>();
503 APPLOG_TRACE(
"{}::{}", hpp::type_name_str(*
this), __func__);
507 auto& registry = *scn.registry;
514 [&](
auto e,
auto&& comp)
519 catch(
const mono::mono_exception& e)
524 registry.on_construct<active_component>().disconnect<&on_create_active_component>();
525 registry.on_destroy<active_component>().disconnect<&on_destroy_active_component>();
527 registry.on_construct<script_component>().disconnect<&on_create_component>();
528 registry.on_destroy<script_component>().disconnect<&on_destroy_component>();
542 on_frame_update(ctx, step);
551 check_for_recompile(ctx, dt,
true);
558 if(!app_domain_ || !domain_)
565 auto& registry = *scn.registry;
567 registry.view<script_component>().each(
568 [&](
auto e,
auto&& comp)
570 comp.process_pending_deletions();
572 if(ev.is_playing && registry.all_of<active_component>(e))
582 uint64_t frame_count{};
588 auto time_scale = sim.get_time_scale();
591 data.delta_time = dt.count();
592 data.time_scale = time_scale;
593 data.frame_count = sim.get_frame();
595 mono::make_method_invoker<void(update_data)>(cache_.update_manager_type,
"internal_n2m_update");
599 catch(
const mono::mono_exception& e)
603 is_updating_ =
false;
614 if(!app_domain_ || !domain_)
620 auto& scn = ec.get_scene();
621 auto& registry = *scn.registry;
623 registry.view<script_component>().each(
624 [&](
auto e,
auto&& comp)
626 comp.process_pending_deletions();
631 float fixed_delta_time{};
634 if(ev.is_playing && dt > delta_t::zero())
637 auto time_scale = sim.get_time_scale();
640 data.fixed_delta_time = dt.count();
642 mono::make_method_invoker<void(update_data)>(cache_.update_manager_type,
"internal_n2m_fixed_update");
646 catch(
const mono::mono_exception& e)
660 if(!app_domain_ || !domain_)
666 auto& scn = ec.get_scene();
667 auto& registry = *scn.registry;
669 if(ev.is_playing && dt > delta_t::zero())
672 mono::make_method_invoker<void()>(cache_.update_manager_type,
"internal_n2m_late_update");
676 catch(
const mono::mono_exception& e)
684 return app_cache_.scriptable_component_types;
690 return domain_->get_assembly(engine_script_lib.string());
696 return app_domain_->get_assembly(app_script_lib.string());
701 return create_call_ == call_progress::finished;
710 return mono::is_debugger_attached();
715 time_since_last_check_ += dt;
717 if(time_since_last_check_ >= check_interval || needs_recompile == recompile_command::compile_now)
719 time_since_last_check_ = {};
721 recompile_command should_recompile = needs_recompile.exchange(recompile_command::none);
723 if(should_recompile != recompile_command::none)
725 auto container = []()
727 std::lock_guard<std::mutex> lock(container_mutex);
728 auto result = std::move(needs_to_recompile);
732 compilation_jobs_.clear();
734 compilation_version++;
736 auto current_version = compilation_version.load();
737 for(
const auto& protocol : container)
740 .then(tpp::this_thread::get_id(),
741 [
this, &ctx, protocol, emit_callback, current_version](
auto f)
754 if(compilation_version > current_version)
759 has_compilation_errors_ = !
f.get();
760 if(!has_compilation_errors_)
762 ev.on_script_recompile(ctx, protocol, current_version);
766 compilation_jobs_.emplace_back(std::move(job));
776 check_for_recompile(ctx, 100s,
false);
778 auto jobs = std::move(compilation_jobs_);
780 for(
auto& job : jobs)
786auto script_system::create_compilation_job(
rtti::context& ctx,
const std::string& protocol,
bool debug)
787 -> tpp::job_future<bool>
800 return thr.pool->schedule(
802 [&am, flags, protocol]()
804 auto key = get_lib_data_key(protocol);
805 auto output = get_lib_temp_compiled_key(protocol);
816 needs_recompile = now ? recompile_command::compile_now : recompile_command::compile_at_schedule;
818 std::lock_guard<std::mutex> lock(container_mutex);
819 if(std::find(std::begin(needs_to_recompile), std::end(needs_to_recompile), protocol) ==
820 std::end(needs_to_recompile))
822 needs_to_recompile.emplace_back(protocol);
839 return protocol +
"-script.dll";
874 comp->on_sensor_enter(other);
876 catch(
const mono::mono_exception& e)
897 comp->on_sensor_exit(other);
899 catch(
const mono::mono_exception& e)
918 comp->on_collision_enter(
b, manifolds,
true);
926 comp->on_collision_enter(
a, manifolds,
false);
930 catch(
const mono::mono_exception& e)
949 comp->on_collision_exit(
b, manifolds,
true);
957 comp->on_collision_exit(
a, manifolds,
false);
961 catch(
const mono::mono_exception& e)
969 return has_compilation_errors_;
Manages assets, including loading, unloading, and storage.
auto get_assets(const std::string &group={}) const -> std::vector< asset_handle< T > >
Gets all assets in a specified group.
Class that contains core data for audio listeners. There can only be one instance of it per scene.
std::chrono::duration< float > delta_t
#define APPLOG_ERROR_LOC(FILE_LOC, LINE_LOC, FUNC_LOC,...)
#define APPLOG_TRACE(...)
auto get_data_directory(const std::string &prefix={}) -> std::string
auto get_type() -> const std::string &
auto get_compiled_directory(const std::string &prefix={}) -> std::string
path resolve_protocol(const path &_path)
Given the specified path/filename, resolve the final full filename. This will be based on either the ...
void stop_all(const std::string &scope)
Stops all actions within the specified scope.
auto compile< script_library >(asset_manager &am, const fs::path &key, const fs::path &output, uint32_t flags) -> bool
auto validate_paths(const mono::compiler_paths &paths) -> bool
#define APP_SCOPE_PERF(name)
Create a scoped performance timer that only accepts string literals.
Manages the entity-component-system (ECS) operations for the ACE framework.
auto get_scene() -> scene &
Gets the current scene.
hpp::event< void(rtti::context &, delta_t)> on_frame_update
static auto is_debugger_attached() -> bool
static auto get_lib_name(const std::string &protocol) -> std::string
auto is_create_called() const -> bool
static auto get_lib_data_key(const std::string &protocol) -> std::string
static auto get_script_debug_mode() -> bool
void unload_engine_domain()
auto is_update_called() const -> bool
static auto get_lib_compiled_key(const std::string &protocol) -> std::string
static auto get_lib_temp_compiled_key(const std::string &protocol) -> std::string
static void set_script_debug_mode(bool debug)
auto load_engine_domain(rtti::context &ctx, bool recompile) -> bool
auto get_app_assembly() const -> mono::mono_assembly
auto has_compilation_errors() const -> bool
void on_collision_enter(entt::handle a, entt::handle b, const std::vector< manifold_point > &manifolds)
auto init(rtti::context &ctx) -> bool
static void on_create_component(entt::registry &r, entt::entity e)
Called when a physics component is created.
static void set_needs_recompile(const std::string &protocol, bool now=false)
void set_debug_config(const std::string &address, uint32_t port, uint32_t loglevel)
static void copy_compiled_lib(const fs::path &from, const fs::path &to)
static void on_destroy_active_component(entt::registry &r, entt::entity e)
auto load_app_domain(rtti::context &ctx, bool recompile) -> bool
static auto find_mono(const rtti::context &ctx) -> mono::compiler_paths
void on_sensor_exit(entt::handle sensor, entt::handle other)
void on_sensor_enter(entt::handle sensor, entt::handle other)
static void log_exception(const mono::mono_exception &e, const hpp::source_location &loc=hpp::source_location::current())
auto get_engine_assembly() const -> mono::mono_assembly
static void on_create_active_component(entt::registry &r, entt::entity e)
void on_play_begin(rtti::context &ctx)
Called when playback begins.
auto deinit(rtti::context &ctx) -> bool
auto get_all_scriptable_components() const -> const std::vector< mono::mono_type > &
void on_collision_exit(entt::handle a, entt::handle b, const std::vector< manifold_point > &manifolds)
void wait_for_jobs_to_finish(rtti::context &ctx)
static void on_destroy_component(entt::registry &r, entt::entity e)
Called when a physics component is destroyed.