njin
Engine 2D gọn nhẹ để tự tay làm game theo ý mình. Vibe coding cũng ổn luôn
Loading...
Searching...
No Matches
njin_script.h
1#pragma once
2#include "_math.h"
3#include "_types.h"
4#include "njin_json.h"
5#include <entt/entity/fwd.hpp>
6#include <functional>
7#include <initializer_list>
8#include <span>
9#include <string>
10#include <type_traits>
11#include <utility>
12#include <variant>
13
14namespace njin {
15struct context;
16
19
22using script_value = std::variant<std::monostate, bool, f64, std::string, vec2, vec3, entt::entity>;
23
30 bool allow_io = false;
31};
32
37void script_init(context &ctx, const script_desc &desc = {});
38
46bool script_run_file(context &ctx, const char *path);
47
53bool script_run_string(context &ctx, const char *code, const char *name = "string");
54
57 bool ok = false;
59};
60
67script_result script_call(context &ctx, const char *function, std::initializer_list<script_value> args = {});
68
74script_result script_call(context &ctx, const char *function, std::span<const script_value> args);
75
77using script_fn = std::function<script_value(context &, std::span<const script_value>)>;
78
85void script_register(context &ctx, const char *name, script_fn fn);
86
87namespace script_detail {
88template <class> inline constexpr bool always_false = false;
89
91template <class T> std::remove_cvref_t<T> to(const script_value &v) {
92 using U = std::remove_cvref_t<T>;
93 if constexpr (std::is_same_v<U, bool>) {
94 if (const bool *b = std::get_if<bool>(&v))
95 return *b;
96 if (const f64 *d = std::get_if<f64>(&v))
97 return *d != 0.0;
98 return false;
99 } else if constexpr (std::is_same_v<U, entt::entity>) {
100 if (const entt::entity *e = std::get_if<entt::entity>(&v))
101 return *e;
102 if (const f64 *d = std::get_if<f64>(&v))
103 return entt::entity((u32)*d);
104 return entt::entity(~u32{0});
105 } else if constexpr (std::is_arithmetic_v<U>) {
106 if (const f64 *d = std::get_if<f64>(&v))
107 return (U)*d;
108 if (const bool *b = std::get_if<bool>(&v))
109 return (U)(*b ? 1 : 0);
110 return U{};
111 } else if constexpr (std::is_same_v<U, std::string> || std::is_same_v<U, vec2> || std::is_same_v<U, vec3>) {
112 if (const U *p = std::get_if<U>(&v))
113 return *p;
114 return U{};
115 } else if constexpr (std::is_same_v<U, script_value>) {
116 return v;
117 } else {
118 static_assert(always_false<U>, "script_register: kiểu tham số không hỗ trợ");
119 }
120}
121
123template <class T> script_value from(T &&value) {
124 using U = std::remove_cvref_t<T>;
125 if constexpr (std::is_same_v<U, script_value>)
126 return std::forward<T>(value);
127 else if constexpr (std::is_same_v<U, bool> || std::is_same_v<U, vec2> || std::is_same_v<U, vec3> ||
128 std::is_same_v<U, entt::entity> || std::is_same_v<U, std::string>)
129 return script_value{std::forward<T>(value)};
130 else if constexpr (std::is_arithmetic_v<U>)
131 return script_value{(f64)value};
132 else if constexpr (std::is_convertible_v<U, const char *>)
133 return script_value{std::string(value)};
134 else
135 static_assert(always_false<U>, "script_register: kiểu trả về không hỗ trợ");
136}
137
138inline const script_value &arg(std::span<const script_value> args, usize i) {
139 static const script_value none{};
140 return i < args.size() ? args[i] : none;
141}
142
143template <class R, class... A, usize... I>
144script_value call(const std::function<R(context &, A...)> &f, context &ctx, std::span<const script_value> args,
145 std::index_sequence<I...>) {
146 if constexpr (std::is_void_v<R>) {
147 f(ctx, to<A>(arg(args, I))...);
148 return {};
149 } else {
150 return from(f(ctx, to<A>(arg(args, I))...));
151 }
152}
153
154template <class R, class... A, usize... I>
155script_value call(const std::function<R(A...)> &f, std::span<const script_value> args, std::index_sequence<I...>) {
156 if constexpr (std::is_void_v<R>) {
157 f(to<A>(arg(args, I))...);
158 return {};
159 } else {
160 return from(f(to<A>(arg(args, I))...));
161 }
162}
163
164template <class R, class... A> script_fn wrap(std::function<R(context &, A...)> f) {
165 return [f = std::move(f)](context &ctx, std::span<const script_value> args) {
166 return call(f, ctx, args, std::index_sequence_for<A...>{});
167 };
168}
169
170template <class R, class... A> script_fn wrap(std::function<R(A...)> f) {
171 return [f = std::move(f)](context &, std::span<const script_value> args) {
172 return call(f, args, std::index_sequence_for<A...>{});
173 };
174}
175} // namespace script_detail
176
187template <class F>
188 requires(!std::is_convertible_v<F, script_fn>)
189void script_register(context &ctx, const char *name, F &&fn) {
190 script_register(ctx, name, script_detail::wrap(std::function{std::forward<F>(fn)}));
191}
192
197void script_set_global(context &ctx, const char *name, const script_value &value);
198
203script_value script_get_global(context &ctx, const char *name);
204
229bool script_attach(context &ctx, entt::entity entity, const char *path);
230
234void script_detach(context &ctx, entt::entity entity);
235
238bool script_attached(const context &ctx, entt::entity entity);
239
245script_value script_field(context &ctx, entt::entity entity, const char *key);
246
252void script_set_field(context &ctx, entt::entity entity, const char *key, const script_value &value);
253
255i32 script_count(const context &ctx);
256
264void script_set_save_id(context &ctx, entt::entity entity, const char *id);
265
268std::string script_save_id(context &ctx, entt::entity entity);
269
286
302i32 script_load_state(context &ctx, const json_value &state);
304} // namespace njin
void script_init(context &ctx, const script_desc &desc={})
Tạo máy Lua với tùy chọn desc.
std::variant< std::monostate, bool, f64, std::string, vec2, vec3, entt::entity > script_value
Một giá trị đi qua lại giữa C++ và Lua: nil, bool, số, chuỗi, vec2, vec3 hay entity.
Definition njin_script.h:22
void script_register(context &ctx, const char *name, script_fn fn)
Đăng ký một hàm C++ dưới tên name (toàn cục bên Lua; có dấu chấm thì đặt trong bảng,...
i32 script_count(const context &ctx)
Số entity đang có script.
json_value script_save_state(context &ctx)
Trạng thái của mọi script có tên lưu, để ghi vào save game: các trường của self (số,...
std::function< script_value(context &, std::span< const script_value >)> script_fn
Hàm của game gọi được từ Lua, dạng chung: nhận mọi tham số, trả một giá trị.
Definition njin_script.h:77
script_value script_get_global(context &ctx, const char *name)
Đọc một biến toàn cục bên Lua.
void script_set_field(context &ctx, entt::entity entity, const char *key, const script_value &value)
Ghi trường key của bảng self.
void script_detach(context &ctx, entt::entity entity)
Gỡ script khỏi entity (gọi on_destroy).
bool script_attach(context &ctx, entt::entity entity, const char *path)
Gắn một script vào entity.
i32 script_load_state(context &ctx, const json_value &state)
Đổ trạng thái đã lưu bằng script_save_state() vào các script đang gắn có cùng tên lưu,...
bool script_run_string(context &ctx, const char *code, const char *name="string")
Chạy một đoạn mã Lua.
script_value script_field(context &ctx, entt::entity entity, const char *key)
Đọc trường key của bảng self của script gắn trên entity.
bool script_run_file(context &ctx, const char *path)
Chạy một file Lua (đường dẫn như mọi tài nguyên: cạnh file chạy hay thư mục làm việc).
bool script_attached(const context &ctx, entt::entity entity)
Entity có script không.
void script_set_global(context &ctx, const char *name, const script_value &value)
Đặt một biến toàn cục bên Lua (tên có dấu chấm đặt trong bảng).
void script_set_save_id(context &ctx, entt::entity entity, const char *id)
Đặt tên lưu (self.save_id) cho script gắn trên entity: khóa để script_load_state() tìm lại đúng entit...
script_result script_call(context &ctx, const char *function, std::initializer_list< script_value > args={})
Gọi một hàm Lua toàn cục theo tên.
std::string script_save_id(context &ctx, entt::entity entity)
Tên lưu của script gắn trên entity.
double f64
Số thực 64 bit.
Definition _types.h:29
std::int32_t i32
Số nguyên có dấu 32 bit.
Definition _types.h:14
std::size_t usize
Số nguyên không dấu cỡ con trỏ, dùng cho kích thước và chỉ số.
Definition _types.h:34
std::uint32_t u32
Số nguyên không dấu 32 bit.
Definition _types.h:22
Một giá trị JSON: null, bool, số, chuỗi, mảng hoặc object.
Definition njin_json.h:33
Tùy chọn của máy Lua, cho script_init().
Definition njin_script.h:25
bool allow_io
Cho script đọc ghi file và chạy file khác (io, dofile, loadfile, os.remove...).
Definition njin_script.h:30
Kết quả của script_call().
Definition njin_script.h:56
script_value value
Giá trị trả về đầu tiên (nil nếu không trả gì).
Definition njin_script.h:58
bool ok
Hàm có và chạy không lỗi.
Definition njin_script.h:57