14#include <monopp/mono_exception.h>
15#include <monopp/mono_field_invoker.h>
16#include <monopp/mono_internal_call.h>
17#include <monopp/mono_logger.h>
18#include <monopp/mono_method_invoker.h>
19#include <monopp/mono_property_invoker.h>
33enum class recompile_command :
int
40std::chrono::milliseconds check_interval(50);
43std::atomic<recompile_command> needs_recompile{};
44std::mutex container_mutex;
45std::vector<std::string> needs_to_recompile;
46std::atomic<uint64_t> compilation_version{};
48std::atomic_bool debug_mode{
true};
50auto print_assembly_info(
const mono::mono_assembly& assembly)
53 auto refs = assembly.dump_references();
55 ss << fmt::format(
" ----- References -----");
57 for(
const auto& ref : refs)
59 ss << fmt::format(
"\n{}", ref);
64 auto types = assembly.get_types();
67 ss << fmt::format(
" ----- Types -----");
69 for(
const auto&
type : types)
71 ss << fmt::format(
"\n{}",
type.get_fullname());
72 ss << fmt::format(
"\n sizeof {}",
type.get_sizeof());
73 ss << fmt::format(
"\n alignof {}",
type.get_alignof());
76 auto attribs =
type.get_attributes();
77 for(
const auto& attrib : attribs)
79 ss << fmt::format(
"\n - Attribute : {}", attrib.get_type().get_fullname());
83 auto fields =
type.get_fields();
84 for(
const auto& field : fields)
86 ss << fmt::format(
"\n - Field : {}", field.get_name());
88 auto attribs = field.get_attributes();
89 for(
const auto& attrib : attribs)
91 ss << fmt::format(
"\n -- Attribute : {}", attrib.get_type().get_fullname());
95 auto properties =
type.get_properties();
96 for(
const auto& prop : properties)
98 ss << fmt::format(
"\n - Property : {}", prop.get_name());
100 auto attribs = prop.get_attributes();
101 for(
const auto& attrib : attribs)
103 ss << fmt::format(
"\n -- Attribute : {}", attrib.get_type().get_fullname());
114 auto frame = mono::extract_relevant_stack_frame(e.what());
115 if(!
frame.file_name.empty())
121 APPLOG_ERROR_LOC(loc.file_name(),
int(loc.line()), loc.function_name(), e.what());
127 auto from_debug_info = from;
128 from_debug_info.concat(
".mdb");
129 auto from_comments_xml = from;
130 from_comments_xml.replace_extension(
".xml");
132 auto to_debug_info = to;
133 to_debug_info.concat(
".mdb");
134 auto to_comments_xml = to;
135 to_comments_xml.replace_extension(
".xml");
138 fs::copy_file(from, to, fs::copy_options::overwrite_existing, er);
139 fs::copy_file(from_debug_info, to_debug_info, fs::copy_options::overwrite_existing, er);
140 fs::copy_file(from_comments_xml, to_comments_xml, fs::copy_options::overwrite_existing, er);
142 fs::remove(from, er);
143 fs::remove(from_debug_info, er);
144 fs::remove(from_comments_xml, er);
149 bool is_deploy_mode = ctx.has<
deploy>();
151 mono::compiler_paths result;
156 result.assembly_dir = fs::absolute(mono_dir /
"lib").string();
157 result.config_dir = fs::absolute(mono_dir /
"etc").string();
161 const auto& names = mono::get_common_library_names();
162 const auto& library_paths = mono::get_common_library_paths();
163 const auto& config_paths = mono::get_common_config_paths();
165 for(
size_t i = 0; i < library_paths.size(); ++i)
167 const auto& library_path = library_paths.at(i);
168 const auto& config_path = config_paths.at(i);
169 std::vector<std::string> paths{library_path};
170 auto found_library = fs::find_library(names, paths);
172 if(!found_library.empty())
174 result.assembly_dir = fs::path(library_path).make_preferred().string();
175 result.config_dir = fs::path(config_path).make_preferred().string();
183 const auto& names = mono::get_common_executable_names();
184 const auto& paths = mono::get_common_executable_paths();
186 result.msc_executable = fs::find_program(names, paths).make_preferred().string();
190 APPLOG_TRACE(
"Assembly path - {}", result.assembly_dir);
198 return !paths.assembly_dir.empty() && !paths.config_dir.empty();
203 APPLOG_TRACE(
"{}::{}", hpp::type_name_str(*
this), __func__);
205 auto& ev = ctx.get_cached<
events>();
206 ev.
on_frame_update.connect(sentinel_,
this, &script_system::on_frame_update);
207 ev.on_frame_fixed_update.connect(sentinel_,
this, &script_system::on_frame_fixed_update);
208 ev.on_frame_update.connect(sentinel_, -100000,
this, &script_system::on_frame_late_update);
210 ev.on_play_end.connect(sentinel_, 1000,
this, &script_system::on_play_end);
211 ev.on_pause.connect(sentinel_, 100,
this, &script_system::on_pause);
212 ev.on_resume.connect(sentinel_, -100,
this, &script_system::on_resume);
213 ev.on_skip_next_frame.connect(sentinel_, -100,
this, &script_system::on_skip_next_frame);
215 auto mono_paths = find_mono(ctx);
221 error.msg =
"Failed to locate Mono C#. Please install it from - https://www.mono-project.com/download/stable/";
226 debug_config_.enable_debugging =
true;
228 mono::set_log_handler(
"info",
229 [](
const std::string& msg)
233 mono::set_log_handler(
"trace",
234 [](
const std::string& msg)
238 mono::set_log_handler(
"warning",
239 [](
const std::string& msg)
243 mono::set_log_handler(
"error",
244 [](
const std::string& msg)
249 if(mono::init(mono_paths, debug_config_))
251 bind_internal_calls(ctx);
257 if(!load_engine_domain(ctx,
true))
262 catch(
const mono::mono_exception& e)
274 error.msg =
"Failed to initialize Mono C#. Please install it from - https://www.mono-project.com/download/stable/";
281 APPLOG_TRACE(
"{}::{}", hpp::type_name_str(*
this), __func__);
284 unload_engine_domain();
293 debug_config_.address = address;
294 debug_config_.port = port;
295 debug_config_.loglevel = loglevel;
300 bool is_deploy_mode = ctx.has<
deploy>();
302 if(!is_deploy_mode && recompile)
309 if(!create_compilation_job(ctx,
"engine", debug).get())
315 domain_ = std::make_unique<mono::mono_domain>(
"Unravel.Engine");
316 mono::mono_domain::set_current_domain(domain_.get());
321 copy_compiled_lib(engine_script_lib_temp, engine_script_lib);
323 auto assembly = domain_->get_assembly(engine_script_lib.string());
326 cache_.update_manager_type = assembly.get_type(
"Unravel.Core",
"SystemManager");
329 cache_.update_method = cache_.update_manager_type.get_method(
"internal_n2m_update", 1);
330 cache_.fixed_update_method = cache_.update_manager_type.get_method(
"internal_n2m_fixed_update", 1);
331 cache_.late_update_method = cache_.update_manager_type.get_method(
"internal_n2m_late_update", 0);
339 mono::mono_domain::set_current_domain(
nullptr);
344 bool is_deploy_mode = ctx.has<
deploy>();
348 if(!is_deploy_mode && recompile)
350 result &= create_compilation_job(ctx,
"app", get_script_debug_mode()).get();
352 has_compilation_errors_ = !result;
355 app_domain_ = std::make_unique<mono::mono_domain>(
"Unravel.App");
356 mono::mono_domain::set_current_domain(app_domain_.get());
361 copy_compiled_lib(app_script_lib_temp, app_script_lib);
368 if(assets.size() <= 1)
376 auto assembly = app_domain_->get_assembly(app_script_lib.string());
380 auto engine_assembly = domain_->get_assembly(engine_script_lib.string());
382 auto comp_type = engine_assembly.get_type(
"Unravel.Core",
"ScriptComponent");
384 app_cache_.scriptable_component_types.clear();
385 if(!has_compilation_errors_)
387 app_cache_.scriptable_component_types = assembly.get_types_derived_from(comp_type);
390 catch(
const mono::mono_exception& e)
402 mono::mono_domain::set_current_domain(domain_.get());
431 if(!app_domain_ || !domain_)
437 create_call_ = call_progress::started;
439 for(
auto entity : entities)
446 create_call_ = call_progress::finished;
448 for(
auto entity : entities)
463 catch(
const mono::mono_exception& e)
471 if(!app_domain_ || !domain_)
477 create_call_ = call_progress::started;
480 [&](
auto e,
auto&& comp)
485 create_call_ = call_progress::finished;
488 [&](
auto e,
auto&& comp)
500 catch(
const mono::mono_exception& e)
508 APPLOG_TRACE(
"{}::{}", hpp::type_name_str(*
this), __func__);
510 if(!app_domain_ || !domain_)
517 auto& registry = *scn.registry;
522 registry.on_construct<
active_component>().connect<&on_create_active_component>();
523 registry.on_destroy<
active_component>().connect<&on_destroy_active_component>();
532 APPLOG_TRACE(
"{}::{}", hpp::type_name_str(*
this), __func__);
536 auto& registry = *scn.registry;
553 registry.on_construct<
active_component>().disconnect<&on_create_active_component>();
554 registry.on_destroy<
active_component>().disconnect<&on_destroy_active_component>();
556 registry.on_construct<
script_component>().disconnect<&on_create_component>();
557 registry.on_destroy<
script_component>().disconnect<&on_destroy_component>();
573 on_frame_update(ctx, step);
582 check_for_recompile(ctx, dt,
true);
589 if(!app_domain_ || !domain_)
596 auto& registry = *scn.registry;
598 registry.view<script_component>().each(
599 [&](
auto e,
auto&& comp)
601 comp.process_pending_deletions();
603 if(ev.is_playing && registry.all_of<active_component>(e))
614 uint64_t frame_count{};
620 auto time_scale = sim.get_time_scale();
623 data.time = elapsed_time_.count();
624 data.delta_time = dt.count();
625 data.time_scale = time_scale;
626 data.frame_count = sim.get_frame();
629 auto method_thunk = mono::make_method_invoker<void(update_data)>(cache_.update_method);
635 catch(
const mono::mono_exception& e)
639 is_updating_ =
false;
650 if(!app_domain_ || !domain_)
656 auto& scn = ec.get_scene();
657 auto& registry = *scn.registry;
659 registry.view<script_component>().each(
660 [&](
auto e,
auto&& comp)
662 comp.process_pending_deletions();
667 float fixed_delta_time{};
670 if(ev.is_playing && dt > delta_t::zero())
673 auto time_scale = sim.get_time_scale();
676 data.fixed_delta_time = dt.count();
679 auto method_thunk = mono::make_method_invoker<void(update_data)>(cache_.fixed_update_method);
683 catch(
const mono::mono_exception& e)
697 if(!app_domain_ || !domain_)
703 auto& scn = ec.get_scene();
704 auto& registry = *scn.registry;
706 if(ev.is_playing && dt > delta_t::zero())
709 auto method_thunk = mono::make_method_invoker<void()>(cache_.late_update_method);
713 catch(
const mono::mono_exception& e)
721 return app_cache_.scriptable_component_types;
727 return domain_->get_assembly(engine_script_lib.string());
733 return app_domain_->get_assembly(app_script_lib.string());
738 return create_call_ == call_progress::finished;
747 return mono::is_debugger_attached();
752 time_since_last_check_ += dt;
754 if(time_since_last_check_ >= check_interval || needs_recompile == recompile_command::compile_now)
756 time_since_last_check_ = {};
758 recompile_command should_recompile = needs_recompile.exchange(recompile_command::none);
760 if(should_recompile != recompile_command::none)
762 auto container = []()
764 std::lock_guard<std::mutex> lock(container_mutex);
765 auto result = std::move(needs_to_recompile);
769 compilation_jobs_.clear();
771 compilation_version++;
773 auto current_version = compilation_version.load();
774 for(
const auto& protocol : container)
777 .then(tpp::this_thread::get_id(),
778 [
this, &ctx, protocol, emit_callback, current_version](
auto f)
791 if(compilation_version > current_version)
796 has_compilation_errors_ = !
f.get();
797 if(!has_compilation_errors_)
799 ev.on_script_recompile(ctx, protocol, current_version);
803 compilation_jobs_.emplace_back(std::move(job));
813 check_for_recompile(ctx, 100s,
false);
815 auto jobs = std::move(compilation_jobs_);
817 for(
auto& job : jobs)
823auto script_system::create_compilation_job(
rtti::context& ctx,
824 const std::string& protocol,
825 bool debug) -> tpp::job_future<bool>
836 return thr.pool->schedule(
838 [&am, flags, protocol]()
840 auto key = get_lib_data_key(protocol);
841 auto output = get_lib_temp_compiled_key(protocol);
852 needs_recompile = now ? recompile_command::compile_now : recompile_command::compile_at_schedule;
854 std::lock_guard<std::mutex> lock(container_mutex);
855 if(std::find(std::begin(needs_to_recompile), std::end(needs_to_recompile), protocol) ==
856 std::end(needs_to_recompile))
858 needs_to_recompile.emplace_back(protocol);
875 return protocol +
"-script.dll";
910 comp->on_sensor_enter(other);
912 catch(
const mono::mono_exception& e)
933 comp->on_sensor_exit(other);
935 catch(
const mono::mono_exception& e)
954 comp->on_collision_enter(
b, manifolds,
true);
962 comp->on_collision_enter(
a, manifolds,
false);
966 catch(
const mono::mono_exception& e)
985 comp->on_collision_exit(
b, manifolds,
true);
993 comp->on_collision_exit(
a, manifolds,
false);
997 catch(
const mono::mono_exception& e)
1005 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_WARNING(...)
#define APPLOG_ERROR(...)
#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.
Hash specialization for batch_key to enable use in std::unordered_map.
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.