mirror of
https://github.com/Ttanasart-pt/Pixel-Composer.git
synced 2025-02-05 09:45:17 +01:00
1016 lines
25 KiB
C++
1016 lines
25 KiB
C++
// apollo_buffer.h:
|
|
#pragma once
|
|
#include "stdafx.h"
|
|
|
|
///~
|
|
enum lua_btype {
|
|
lua_btype_nil,
|
|
lua_btype_bool,
|
|
lua_btype_int32,
|
|
lua_btype_int64,
|
|
lua_btype_real,
|
|
lua_btype_string,
|
|
lua_btype_array,
|
|
lua_btype_struct,
|
|
lua_btype_script,
|
|
lua_btype_method,
|
|
lua_btype_ref,
|
|
};
|
|
|
|
struct buffer {
|
|
int8* pos;
|
|
public:
|
|
buffer(void* origin) : pos((char*)origin) {}
|
|
template<class T> T read() {
|
|
T r = *(T*)pos;
|
|
pos += sizeof(T);
|
|
return r;
|
|
}
|
|
template<class T> void write(T val) {
|
|
*(T*)pos = val;
|
|
pos += sizeof(T);
|
|
}
|
|
template<class T> T* writeStore(T val) {
|
|
auto p = (T*)pos;
|
|
*p = val;
|
|
pos += sizeof(T);
|
|
return p;
|
|
}
|
|
//
|
|
lua_btype read_type() { return (lua_btype)read<char>(); }
|
|
void write_type(lua_btype t) { write<int8>((int8)t); }
|
|
void write_lua_bool(bool val) { write_type(lua_btype_bool); write(val); }
|
|
void write_lua_real(double val) { write_type(lua_btype_real); write(val); }
|
|
void write_lua_int64(long long val) { write_type(lua_btype_int64); write(val); }
|
|
//
|
|
const char* read_string() {
|
|
const char* r = pos;
|
|
while (*pos != 0) pos++;
|
|
pos++;
|
|
return r;
|
|
}
|
|
void write_string(const char* s) {
|
|
if (s != nullptr) for (int i = 0; s[i] != 0; i++) write<char>(s[i]);
|
|
write<char>(0);
|
|
}
|
|
/// Reads a value and pushes it to Lua state
|
|
void read_to(lua_State* q);
|
|
/// Writes i-th value on state's stack
|
|
void write_from(lua_State* q, int i);
|
|
};
|
|
// apollo_method.h:
|
|
#pragma once
|
|
#include "stdafx.h"
|
|
|
|
extern lua_status_t lua_yield_status;
|
|
int lua_script_closure(lua_State* q);
|
|
|
|
namespace apollo_method {
|
|
constexpr const char* metaName = "gml_method";
|
|
struct impl {
|
|
int64 index;
|
|
char nameStart[];
|
|
};
|
|
void init(lua_State* q);
|
|
void create(lua_State* q, int64 index, const char* fname);
|
|
}// apollo_ref.h
|
|
#pragma once
|
|
#include "stdafx.h"
|
|
#include "apollo_method.h"
|
|
|
|
namespace apollo_array {
|
|
struct impl {
|
|
int64 index;
|
|
bool rec;
|
|
};
|
|
constexpr const char* metaName = "gml_array";
|
|
extern gml_script_id getter;
|
|
extern gml_script_id setter;
|
|
extern gml_script_id length;
|
|
void init(lua_State* q);
|
|
void create(lua_State* q, int64 index, bool rec);
|
|
}
|
|
namespace apollo_struct {
|
|
struct impl {
|
|
int64 index;
|
|
bool rec;
|
|
};
|
|
constexpr const char* metaName = "gml_struct";
|
|
extern gml_script_id getter;
|
|
extern gml_script_id setter;
|
|
extern gml_script_id length;
|
|
extern gml_script_id gml_keys;
|
|
void init(lua_State* q);
|
|
void create(lua_State* q, int64 index, bool rec);
|
|
}// apollo_ref_shared.h
|
|
// note: partial!
|
|
|
|
gml_script_id getter;
|
|
gml_script_id setter;
|
|
gml_script_id length;
|
|
#ifndef apollo_ref
|
|
#include "apollo_ref.h"
|
|
extern const char* metaName;
|
|
#endif
|
|
static int toString(lua_State* q);
|
|
|
|
static impl* toImpl(lua_State* q, int index) {
|
|
luaL_checktype(q, index, LUA_TUSERDATA);
|
|
impl* box = (impl*)luaL_checkudata(q, index, metaName);
|
|
if (box == nullptr) luaL_typeerror(q, index, metaName);
|
|
return box;
|
|
}
|
|
static int gc(lua_State* q) {
|
|
auto box = toImpl(q, 1);
|
|
if (!box) return 0;
|
|
ref_recycle.push(box->index);
|
|
return 0;
|
|
}
|
|
static int get(lua_State* q) {
|
|
auto box = toImpl(q, 1);
|
|
if (!box) return 0;
|
|
//
|
|
buffer b(lua_outbuf);
|
|
b.write<gml_script_id>(getter);
|
|
b.write<int32>(3);
|
|
b.write_lua_int64(box->index);
|
|
b.write_from(q, 2);
|
|
b.write_lua_bool(box->rec);
|
|
lua_pop(q, lua_gettop(q));
|
|
lua_yield_status = lua_status_call;
|
|
return lua_yield(q, 0);
|
|
}
|
|
static int set(lua_State* q) {
|
|
auto box = toImpl(q, 1);
|
|
if (!box) return 0;
|
|
//
|
|
buffer b(lua_outbuf);
|
|
b.write<gml_script_id>(setter);
|
|
b.write<int32>(3);
|
|
b.write_lua_int64(box->index);
|
|
b.write_from(q, 2);
|
|
b.write_from(q, 3);
|
|
lua_pop(q, lua_gettop(q));
|
|
lua_yield_status = lua_status_call;
|
|
return lua_yield(q, 0);
|
|
}
|
|
|
|
void create(lua_State* q, int64 index, bool rec) {
|
|
impl* box = (impl*)lua_newuserdata(q, sizeof(impl));
|
|
box->index = index;
|
|
box->rec = rec;
|
|
luaL_getmetatable(q, metaName);
|
|
lua_setmetatable(q, -2);
|
|
}// apollo_shared.h
|
|
#pragma once
|
|
|
|
#if defined(WIN32)
|
|
#define dllx extern "C" __declspec(dllexport)
|
|
#elif defined(GNUC)
|
|
#define dllx extern "C" __attribute__ ((visibility("default")))
|
|
#else
|
|
#define dllx extern "C"
|
|
#endif
|
|
|
|
extern "C" {
|
|
#include "./../Lua/lua.h"
|
|
#include "./../Lua/lualib.h"
|
|
#include "./../Lua/lauxlib.h"
|
|
}
|
|
|
|
#pragma region typedefs
|
|
typedef char int8;
|
|
typedef int int32;
|
|
typedef long long int64;
|
|
typedef unsigned int uint32;
|
|
typedef double real;
|
|
typedef int32 gml_script_id;
|
|
extern void* lua_outbuf;
|
|
#pragma endregion
|
|
|
|
///~
|
|
enum lua_status_t {
|
|
lua_status_amiss = 0,
|
|
lua_status_no_state,
|
|
lua_status_no_func,
|
|
lua_status_done,
|
|
lua_status_error,
|
|
lua_status_call,
|
|
lua_status_yield,
|
|
lua_status_callmethod,
|
|
};
|
|
|
|
#define trace(...) { printf(__VA_ARGS__); printf("\n"); fflush(stdout); }// apollo_buffer.cpp:
|
|
#include "apollo_buffer.h"
|
|
#include "apollo_method.h"
|
|
#include "apollo_ref.h"
|
|
|
|
void buffer::read_to(lua_State* q) {
|
|
switch (read_type()) {
|
|
case lua_btype_bool: lua_pushboolean(q, read<int8>()); break;
|
|
case lua_btype_int32: lua_pushinteger(q, read<int32>()); break;
|
|
case lua_btype_int64: lua_pushinteger(q, read<int64>()); break;
|
|
case lua_btype_real: lua_pushnumber(q, read<real>()); break;
|
|
case lua_btype_string: lua_pushstring(q, read_string()); break;
|
|
case lua_btype_array: {
|
|
lua_newtable(q);
|
|
auto t = lua_gettop(q);
|
|
auto n = read<uint32>();
|
|
for (auto i = 0u; i < n; i++) {
|
|
read_to(q);
|
|
lua_rawseti(q, t, (lua_Integer)i + 1);
|
|
}
|
|
} break;
|
|
case lua_btype_struct: {
|
|
lua_newtable(q);
|
|
auto t = lua_gettop(q);
|
|
auto n = read<uint32>();
|
|
for (auto i = 0u; i < n; i++) {
|
|
lua_pushstring(q, read_string());
|
|
read_to(q);
|
|
lua_rawset(q, t);
|
|
}
|
|
} break;
|
|
case lua_btype_script: {
|
|
lua_pushnumber(q, read<int32>());
|
|
lua_pushcclosure(q, lua_script_closure, 1);
|
|
} break;
|
|
case lua_btype_method: {
|
|
auto index = read<int64>();
|
|
auto name = read_string();
|
|
apollo_method::create(q, index, name);
|
|
} break;
|
|
case lua_btype_ref: {
|
|
auto index = read<int64>();
|
|
auto kind = read<int8>();
|
|
auto rec = read<int8>() != 0;
|
|
if (kind == 0) {
|
|
apollo_array::create(q, index, rec);
|
|
} else {
|
|
apollo_struct::create(q, index, rec);
|
|
}
|
|
} break;
|
|
default: lua_pushnil(q);
|
|
}
|
|
}
|
|
|
|
void buffer::write_from(lua_State* q, int i) {
|
|
switch (lua_type(q, i)) {
|
|
case LUA_TBOOLEAN:
|
|
write_type(lua_btype_bool);
|
|
write<int8>(lua_toboolean(q, i));
|
|
break;
|
|
case LUA_TNUMBER:
|
|
write_type(lua_btype_real);
|
|
write<real>(lua_tonumber(q, i));
|
|
break;
|
|
case LUA_TSTRING:
|
|
write_type(lua_btype_string);
|
|
write_string(lua_tostring(q, i));
|
|
break;
|
|
case LUA_TTABLE: {
|
|
auto len = lua_rawlen(q, i); // lua_objlen in <= 5.1
|
|
if (len > 0) {
|
|
write_type(lua_btype_array);
|
|
write<uint32>(len);
|
|
for (auto k = 1; k <= len; k++) {
|
|
lua_rawgeti(q, i, k);
|
|
write_from(q, lua_gettop(q));
|
|
lua_pop(q, 1);
|
|
}
|
|
} else {
|
|
write_type(lua_btype_struct);
|
|
auto foundAt = writeStore<uint32>(0);
|
|
auto found = 0u;
|
|
lua_pushnil(q);
|
|
while (lua_next(q, i)) {
|
|
auto key = lua_tostring(q, -2);
|
|
if (key) {
|
|
found++;
|
|
write_string(key);
|
|
write_from(q, lua_gettop(q));
|
|
}
|
|
lua_pop(q, 1);
|
|
}
|
|
*foundAt = found;
|
|
}
|
|
} break;
|
|
case LUA_TUSERDATA:
|
|
if (auto mtd = (apollo_method::impl*)luaL_testudata(q, i, apollo_method::metaName)) {
|
|
write_type(lua_btype_method);
|
|
write<int64>(mtd->index);
|
|
} else if (auto arr = (apollo_array::impl*)luaL_testudata(q, i, apollo_array::metaName)) {
|
|
write_type(lua_btype_ref);
|
|
write<int64>(arr->index);
|
|
} else if (auto obj = (apollo_struct::impl*)luaL_testudata(q, i, apollo_struct::metaName)) {
|
|
write_type(lua_btype_ref);
|
|
write<int64>(obj->index);
|
|
}
|
|
else write_type(lua_btype_nil);
|
|
break;
|
|
default: write_type(lua_btype_nil);
|
|
}
|
|
}// apollo_core.cpp:
|
|
/// @author YellowAfterlife
|
|
#define _CRT_SECURE_NO_WARNINGS
|
|
#include "stdafx.h"
|
|
#include <vector>
|
|
#include <queue>
|
|
#include <stack>
|
|
#include <map>
|
|
#ifdef APOLLO_WINAPI
|
|
#include <codecvt>
|
|
#else
|
|
#include <unistd.h>
|
|
#endif
|
|
#include "apollo_buffer.h"
|
|
#include "apollo_method.h"
|
|
#include "apollo_ref.h"
|
|
using namespace std;
|
|
|
|
void* lua_outbuf;
|
|
|
|
#ifdef APOLLO_WINAPI // codepage conversion (Windows)
|
|
wstring_convert<codecvt_utf8_utf16<wchar_t>> ccvt;
|
|
string ccvt_utf8;
|
|
char* ret_string(wstring& ws) {
|
|
ccvt_utf8 = ccvt.to_bytes(ws);
|
|
return (char*)ccvt_utf8.c_str();
|
|
}
|
|
char* ret_string(wchar_t* ws) {
|
|
ccvt_utf8 = ccvt.to_bytes(ws);
|
|
return (char*)ccvt_utf8.c_str();
|
|
}
|
|
#endif
|
|
char* ret_string(const char* s) {
|
|
static char* buf = nullptr;
|
|
static size_t buf_size = 0;
|
|
size_t n = strlen(s) + 1;
|
|
if (buf_size < n) {
|
|
auto tmp = realloc(buf, n);
|
|
if (tmp) {
|
|
buf = (char*)tmp;
|
|
buf_size = n;
|
|
}
|
|
}
|
|
if (n > buf_size) {
|
|
strncpy((char*)buf, s, buf_size - 1);
|
|
buf[buf_size - 1] = 0;
|
|
} else strcpy((char*)buf, s);
|
|
return (char*)buf;
|
|
}
|
|
|
|
#pragma region states
|
|
struct lua_state_t {
|
|
// The actual Lua state (or thread)
|
|
lua_State* state;
|
|
|
|
// If this is a thread, indicates the parent state. Otherwise is null
|
|
lua_State* parent;
|
|
|
|
// This is only for errors thrown via lua_show_error - rethrows into state upon resuming
|
|
char* error_text = nullptr;
|
|
|
|
// A specific set of conditions urges us to have a stack of sub-states:
|
|
// - To be able to suspend Lua calls and return to GML, it must be a coroutine
|
|
// - If an error occurs in a coroutine, it is pronounced dead and can no longer be used
|
|
// - Calls can be nested (GML->Lua->GML->Other function in same Lua state)
|
|
// So we'll maintain a stack of temporary states and create/clean them up as we might
|
|
stack<lua_State*> substates;
|
|
|
|
lua_state_t(lua_State* q, lua_State* p) : state(q), parent(p) {}
|
|
};
|
|
vector<lua_state_t*> lua_state_vec;
|
|
queue<size_t> lua_state_reusable;
|
|
stack<lua_state_t*> lua_state_stack;
|
|
lua_State* lua_state_find(double index) {
|
|
size_t i = (size_t)index;
|
|
return i < lua_state_vec.size() ? lua_state_vec[i]->state : nullptr;
|
|
}
|
|
lua_state_t* lua_state_find_t(double index) {
|
|
size_t i = (size_t)index;
|
|
return i < lua_state_vec.size() ? lua_state_vec[i] : nullptr;
|
|
}
|
|
///
|
|
dllx double lua_show_error(char* text) {
|
|
if (lua_state_stack.empty()) return false;
|
|
lua_state_t* qt = lua_state_stack.top();
|
|
if (qt->error_text == nullptr) {
|
|
char* s = (char*)malloc(strlen(text) + 1);
|
|
strcpy(s, text);
|
|
qt->error_text = s;
|
|
return true;
|
|
} else return false;
|
|
}
|
|
#pragma endregion
|
|
|
|
/// Destroys every single state at once
|
|
dllx double lua_reset() {
|
|
size_t n = lua_state_vec.size();
|
|
for (size_t i = 0; i < n; i++) {
|
|
auto q = lua_state_vec[i];
|
|
if (q != nullptr) {
|
|
// child states are GC-ed, no need to dealloc them
|
|
if (q->parent == nullptr) lua_close(q->state);
|
|
if (q->error_text != nullptr) free(q->error_text);
|
|
delete q;
|
|
}
|
|
}
|
|
lua_state_vec.clear();
|
|
lua_state_reusable = queue<size_t>();
|
|
return true;
|
|
}
|
|
|
|
#pragma region Current working directory
|
|
dllx const char* lua_get_cwd() {
|
|
#ifdef APOLLO_WINAPI
|
|
auto dir = _wgetcwd(NULL, 0);
|
|
if (dir != NULL) {
|
|
char* result = ret_string(dir);
|
|
free(dir);
|
|
return result;
|
|
} else return "";
|
|
#else
|
|
auto dir = getcwd(NULL, 0);
|
|
if (dir != NULL) {
|
|
char* result = ret_string(dir);
|
|
free(dir);
|
|
return result;
|
|
} else return "";
|
|
#endif
|
|
}
|
|
dllx double lua_set_cwd(char* path) {
|
|
#ifdef APOLLO_WINAPI
|
|
int wsize = MultiByteToWideChar(CP_UTF8, 0, path, -1, NULL, 0);
|
|
WCHAR* wpath = new WCHAR[wsize + 1];
|
|
MultiByteToWideChar(CP_UTF8, 0, path, -1, wpath, wsize);
|
|
auto result = (_wchdir(wpath) == 0);
|
|
delete wpath;
|
|
return result;
|
|
#else
|
|
return chdir(path) == 0;
|
|
#endif
|
|
}
|
|
#pragma endregion
|
|
|
|
#pragma region Naming helpers
|
|
#define sprintf_exec(s, i) sprintf(s, "__Apollo_auto_%zu", i)
|
|
#define sprintf_thread(s, i) sprintf(s, "__Apollo_thread_%zu", i)
|
|
#pragma endregion
|
|
|
|
#pragma region State
|
|
size_t lua_state_create_impl(lua_state_t* qt) {
|
|
size_t i;
|
|
if (lua_state_reusable.empty()) {
|
|
i = lua_state_vec.size();
|
|
lua_state_vec.push_back(qt);
|
|
} else {
|
|
i = lua_state_reusable.front();
|
|
lua_state_reusable.pop();
|
|
lua_state_vec[i] = qt;
|
|
}
|
|
return i;
|
|
}
|
|
///
|
|
dllx double lua_state_create() {
|
|
auto q = luaL_newstate();
|
|
auto qt = new lua_state_t(q, nullptr);
|
|
auto i = lua_state_create_impl(qt);
|
|
luaL_openlibs(q);
|
|
apollo_method::init(q);
|
|
apollo_array::init(q);
|
|
apollo_struct::init(q);
|
|
return (double)i;
|
|
}
|
|
///
|
|
dllx double lua_state_destroy(double state_id) {
|
|
size_t i = (size_t)state_id;
|
|
size_t n = lua_state_vec.size();
|
|
if (i < n) {
|
|
auto q = lua_state_vec[i];
|
|
if (q != nullptr) {
|
|
if (q->parent != nullptr) {
|
|
char name[32];
|
|
sprintf_thread(name, i);
|
|
lua_pushnil(q->parent);
|
|
lua_setglobal(q->parent, name);
|
|
} else {
|
|
lua_close(q->state);
|
|
// remove children:
|
|
for (size_t k = 0; k < n; k++) {
|
|
auto qk = lua_state_vec[k];
|
|
if (qk != nullptr && qk->parent == q->state) {
|
|
if (qk->error_text != nullptr) free(qk->error_text);
|
|
delete qk;
|
|
lua_state_vec[k] = nullptr;
|
|
}
|
|
}
|
|
}
|
|
if (q->error_text != nullptr) free(q->error_text);
|
|
delete q;
|
|
lua_state_vec[i] = nullptr;
|
|
return true;
|
|
} else return false;
|
|
} else return false;
|
|
}
|
|
///
|
|
dllx double lua_thread_create(double state_id) {
|
|
auto ot = lua_state_find_t(state_id);
|
|
if (ot == nullptr) return -1;
|
|
auto o = ot->state;
|
|
auto q = lua_newthread(o);
|
|
auto qt = new lua_state_t(q, o);
|
|
auto i = lua_state_create_impl(qt);
|
|
// put the thread from the top of stack in base to a global so that GC won't eat it:
|
|
char name[32];
|
|
sprintf_thread(name, i);
|
|
lua_setglobal(o, name);
|
|
//
|
|
return (double)i;
|
|
}
|
|
///
|
|
dllx double lua_thread_destroy(double state_id) {
|
|
return lua_state_destroy(state_id);
|
|
}
|
|
///
|
|
dllx double lua_state_exists(double state_id) {
|
|
return lua_state_find_t(state_id) != nullptr;
|
|
}
|
|
/// Allows the indexes of all currently destroyed states/threads to be reused for new ones
|
|
dllx double lua_state_reuse_indexes() {
|
|
lua_state_reusable = queue<size_t>();
|
|
auto n = lua_state_vec.size();
|
|
auto r = 0;
|
|
for (size_t i = 0; i < n; i++) {
|
|
if (lua_state_vec[i] == nullptr) {
|
|
lua_state_reusable.push(i);
|
|
r += 1;
|
|
}
|
|
}
|
|
return r;
|
|
}
|
|
#pragma endregion
|
|
|
|
#pragma region function
|
|
dllx double lua_add_function_raw(double state_id, char* name, double script_id) {
|
|
auto q = lua_state_find(state_id);
|
|
if (q == nullptr) return lua_status_no_state;
|
|
lua_pushnumber(q, script_id);
|
|
lua_pushcclosure(q, lua_script_closure, 1);
|
|
lua_setglobal(q, name);
|
|
return lua_status_done;
|
|
}
|
|
#pragma endregion
|
|
|
|
#pragma region sub-state management
|
|
lua_State* lua_state_exec_start(lua_state_t* qt) {
|
|
lua_State* q = lua_newthread(qt->state);
|
|
char v[32];
|
|
sprintf_exec(v, qt->substates.size());
|
|
lua_setglobal(qt->state, v);
|
|
qt->substates.push(q);
|
|
return q;
|
|
}
|
|
void lua_state_exec_end(lua_state_t* qt) {
|
|
qt->substates.pop();
|
|
char v[32];
|
|
sprintf_exec(v, qt->substates.size());
|
|
lua_pushnil(qt->state);
|
|
lua_setglobal(qt->state, v);
|
|
}
|
|
/// Returns how many layers of interop (GML->Lua->GML->...) a state is deep (debug info)
|
|
dllx double lua_state_get_interop_depth(double state_id) {
|
|
lua_state_t* qt = lua_state_find_t(state_id);
|
|
if (qt == nullptr) return 0;
|
|
return (double)qt->substates.size();
|
|
}
|
|
#pragma endregion
|
|
|
|
#pragma region calls
|
|
void lua_state_throw_error(lua_State *q, lua_Debug *ar) {
|
|
lua_sethook(q, lua_state_throw_error, 0, 0);
|
|
auto qt = lua_state_stack.top();
|
|
if (qt->error_text != nullptr) {
|
|
luaL_where(q, 1);
|
|
lua_pushstring(q, qt->error_text);
|
|
lua_concat(q, 2);
|
|
free(qt->error_text);
|
|
qt->error_text = nullptr;
|
|
lua_error(q); // we don't return from there
|
|
}
|
|
}
|
|
lua_status_t lua_state_exec(lua_state_t* qt, lua_State* q, void* data, bool start) {
|
|
lua_outbuf = data;
|
|
buffer b(data);
|
|
int argc = b.read<int32>();
|
|
for (int i = 0; i < argc; i++) b.read_to(q);
|
|
lua_yield_status = lua_status_yield;
|
|
//
|
|
if (qt->error_text != nullptr) {
|
|
// here's the deal: if we want LuaJIT compatibility (5.1/5.2 equivalent),
|
|
// we can't use lua_yieldk, so... I guess just bind a one-use hook?
|
|
lua_sethook(q, lua_state_throw_error, LUA_MASKCOUNT, 1);
|
|
}
|
|
//
|
|
argc = lua_gettop(q);
|
|
if (start) argc -= 1; // if it's the first invocation, first argument is the function to run
|
|
int retc = 0;
|
|
int result = lua_resume(q, NULL, argc, &retc);
|
|
switch (result) {
|
|
case LUA_OK: {
|
|
// we finished execution! let's get the returned values out of there:
|
|
b = buffer(data);
|
|
b.write<int32>(retc);
|
|
for (int i = 0; i < retc; i++) b.write_from(q, i + 1);
|
|
lua_pop(q, retc);
|
|
// remove the call-coroutine and we're good
|
|
lua_state_exec_end(qt);
|
|
lua_state_stack.pop();
|
|
return lua_status_done;
|
|
};
|
|
case LUA_YIELD: {
|
|
if (lua_yield_status != lua_status_yield) {
|
|
return lua_yield_status;
|
|
} else {
|
|
b = buffer(data);
|
|
b.write<int32>(retc);
|
|
for (int i = 0; i < retc; i++) b.write_from(q, i + 1);
|
|
lua_pop(q, retc);
|
|
lua_state_stack.pop();
|
|
return lua_status_yield;
|
|
}
|
|
};
|
|
default: {
|
|
auto text = lua_tostring(q, -1);
|
|
luaL_traceback(q, q, text, 0);
|
|
text = lua_tostring(q, -1);
|
|
buffer b(data);
|
|
b.write_string(text);
|
|
lua_pop(q, lua_gettop(q));
|
|
lua_state_exec_end(qt);
|
|
lua_state_stack.pop();
|
|
return lua_status_error;
|
|
};
|
|
}
|
|
}
|
|
|
|
dllx double lua_state_exec_raw(void* data) {
|
|
if (lua_state_stack.empty()) return lua_status_no_state;
|
|
auto qt = lua_state_stack.top();
|
|
return lua_state_exec(qt, qt->substates.top(), data, false);
|
|
}
|
|
|
|
dllx double lua_call_raw(double state_id, char* name, void* data) {
|
|
auto qt = lua_state_find_t(state_id);
|
|
if (qt == nullptr) return lua_status_no_state;
|
|
auto q = lua_state_exec_start(qt);
|
|
//
|
|
lua_getglobal(q, name);
|
|
if (lua_type(q, -1) != LUA_TFUNCTION) {
|
|
lua_state_exec_end(qt);
|
|
return lua_status_no_func;
|
|
}
|
|
lua_state_stack.push(qt);
|
|
return lua_state_exec(qt, q, data, true);
|
|
}
|
|
#pragma endregion
|
|
|
|
#pragma region add code
|
|
dllx double lua_add_code_raw(double state_id, char* code, void* data) {
|
|
auto qt = lua_state_find_t(state_id);
|
|
if (qt == nullptr) return lua_status_no_state;
|
|
lua_state_stack.push(qt);
|
|
auto q = lua_state_exec_start(qt);
|
|
luaL_loadstring(q, code);
|
|
return lua_state_exec(qt, q, data, true);
|
|
}
|
|
|
|
dllx double lua_add_file_raw(double state_id, char* full_path, void* data) {
|
|
auto qt = lua_state_find_t(state_id);
|
|
if (qt == nullptr) return lua_status_no_state;
|
|
lua_state_stack.push(qt);
|
|
auto q = lua_state_exec_start(qt);
|
|
luaL_loadfile(q, full_path);
|
|
return lua_state_exec(qt, q, data, true);
|
|
}
|
|
#pragma endregion
|
|
|
|
#pragma region globals
|
|
dllx double lua_global_get_raw(double state_id, char* name, void* data) {
|
|
auto q = lua_state_find(state_id);
|
|
if (q == nullptr) return false;
|
|
lua_getglobal(q, name);
|
|
buffer b(data);
|
|
b.write_from(q, 1);
|
|
lua_pop(q, 1);
|
|
return true;
|
|
}
|
|
dllx double lua_global_set_raw(double state_id, char* name, void* data) {
|
|
auto q = lua_state_find(state_id);
|
|
if (q == nullptr) return false;
|
|
buffer b(data);
|
|
b.read_to(q);
|
|
lua_setglobal(q, name);
|
|
return true;
|
|
}
|
|
///
|
|
enum lua_type_t {
|
|
lua_type_none,
|
|
lua_type_nil,
|
|
lua_type_bool,
|
|
lua_type_number,
|
|
lua_type_string,
|
|
lua_type_table,
|
|
lua_type_function,
|
|
lua_type_thread,
|
|
lua_type_userdata,
|
|
lua_type_lightuserdata,
|
|
lua_type_unknown,
|
|
};
|
|
dllx double lua_global_type_raw(double state_id, char* name) {
|
|
auto q = lua_state_find(state_id);
|
|
if (q == nullptr) return -1;
|
|
lua_getglobal(q, name);
|
|
int t = lua_type(q, -1);
|
|
lua_pop(q, 1);
|
|
switch (t) {
|
|
case LUA_TNONE: return lua_type_none;
|
|
case LUA_TNIL: return lua_type_nil;
|
|
case LUA_TBOOLEAN: return lua_type_bool;
|
|
case LUA_TNUMBER: return lua_type_number;
|
|
case LUA_TSTRING: return lua_type_string;
|
|
case LUA_TTABLE: return lua_type_table;
|
|
case LUA_TFUNCTION: return lua_type_function;
|
|
case LUA_TTHREAD: return lua_type_thread;
|
|
case LUA_TUSERDATA: return lua_type_userdata;
|
|
case LUA_TLIGHTUSERDATA: return lua_type_lightuserdata;
|
|
default: return lua_type_unknown;
|
|
}
|
|
}
|
|
#pragma endregion
|
|
|
|
#pragma region coroutines
|
|
dllx double lua_call_start_raw(double state_id, char* name, void* data) {
|
|
auto qt = lua_state_find_t(state_id);
|
|
if (qt == nullptr) return lua_status_no_state;
|
|
auto q = lua_state_exec_start(qt);
|
|
lua_getglobal(q, name);
|
|
if (lua_type(q, -1) != LUA_TFUNCTION) {
|
|
lua_state_exec_end(qt);
|
|
return lua_status_no_func;
|
|
}
|
|
//
|
|
buffer b(data);
|
|
auto argc = b.read<int32>();
|
|
for (int i = 0; i < argc; i++) b.read_to(q);
|
|
//
|
|
return lua_status_done;
|
|
}
|
|
dllx double lua_call_next_raw(double state_id, void* data) {
|
|
auto qt = lua_state_find_t(state_id);
|
|
if (qt == nullptr) return lua_status_no_state;
|
|
if (qt->substates.empty()) return lua_status_no_state;
|
|
//
|
|
lua_state_stack.push(qt);
|
|
return lua_state_exec(qt, qt->substates.top(), data, true);
|
|
}
|
|
#pragma endregion// apollo_init.cpp:
|
|
#include "stdafx.h"
|
|
#include "apollo_ref.h"
|
|
|
|
struct lua_init_t {
|
|
gml_script_id array_getter;
|
|
gml_script_id array_setter;
|
|
gml_script_id array_length;
|
|
gml_script_id struct_getter;
|
|
gml_script_id struct_setter;
|
|
gml_script_id struct_length;
|
|
gml_script_id struct_keys;
|
|
};
|
|
dllx double lua_init_raw(lua_init_t* init) {
|
|
apollo_array::getter = init->array_getter;
|
|
apollo_array::setter = init->array_setter;
|
|
apollo_array::length = init->array_length;
|
|
apollo_struct::getter = init->struct_getter;
|
|
apollo_struct::setter = init->struct_setter;
|
|
apollo_struct::length = init->struct_length;
|
|
apollo_struct::gml_keys = init->struct_keys;
|
|
return true;
|
|
}// apollo_method.cpp:
|
|
#include "apollo_method.h"
|
|
#include "apollo_buffer.h"
|
|
#include <queue>
|
|
namespace apollo_method {
|
|
std::queue<int64> recycle{};
|
|
static impl* toImpl(lua_State* q, int index) {
|
|
luaL_checktype(q, index, LUA_TUSERDATA);
|
|
impl* box = (impl*)luaL_checkudata(q, index, metaName);
|
|
if (box == nullptr) luaL_typeerror(q, index, metaName);
|
|
return box;
|
|
}
|
|
static int gc(lua_State* q) {
|
|
auto box = toImpl(q, 1);
|
|
if (!box) return 0;
|
|
recycle.push(box->index);
|
|
return 0;
|
|
}
|
|
static int toString(lua_State* q) {
|
|
auto box = toImpl(q, 1);
|
|
if (!box) return 0;
|
|
lua_pushstring(q, box->nameStart);
|
|
return 1;
|
|
}
|
|
static int call(lua_State* q) {
|
|
auto box = toImpl(q, 1);
|
|
if (!box) return 0;
|
|
//
|
|
int argc = lua_gettop(q) - 1;
|
|
buffer b(lua_outbuf);
|
|
b.write<int64>(box->index);
|
|
b.write<int32>(argc);
|
|
for (int i = 0; i < argc; i++) b.write_from(q, i + 2);
|
|
lua_pop(q, argc + 1);
|
|
//
|
|
lua_yield_status = lua_status_callmethod;
|
|
return lua_yield(q, 0);
|
|
}
|
|
luaL_Reg meta[] = {
|
|
{"__gc", gc},
|
|
{"__tostring", toString},
|
|
{"__call", call},
|
|
{0, 0}
|
|
};
|
|
void init(lua_State* q) {
|
|
luaL_newmetatable(q, metaName);
|
|
luaL_setfuncs(q, meta, 0);
|
|
lua_pop(q, 1);
|
|
}
|
|
void create(lua_State* q, int64 index, const char* fname) {
|
|
auto fnamen = strlen(fname);
|
|
auto size = sizeof(impl) + fnamen + 1;
|
|
auto ptr = (impl*)lua_newuserdata(q, size);
|
|
ptr->index = index;
|
|
char* nameStart = ptr->nameStart;
|
|
nameStart[fnamen] = 0;
|
|
strncpy(nameStart, fname, fnamen);
|
|
luaL_getmetatable(q, metaName);
|
|
lua_setmetatable(q, -2);
|
|
}
|
|
}
|
|
|
|
dllx double lua_update_method_gc(int64* out, double _max) {
|
|
auto n = apollo_method::recycle.size();
|
|
auto max = (size_t)_max;
|
|
if (n > max) n = max;
|
|
for (auto i = 0u; i < n; i++) {
|
|
out[i] = apollo_method::recycle.front();
|
|
apollo_method::recycle.pop();
|
|
}
|
|
return (double)n;
|
|
}
|
|
|
|
lua_status_t lua_yield_status;
|
|
int lua_script_closure(lua_State* q) {
|
|
// Push the closure context from the pseudoindex (4.2)
|
|
lua_pushvalue(q, lua_upvalueindex(1));
|
|
|
|
// stack: [...args, script]
|
|
int argc = lua_gettop(q) - 1;
|
|
int script = (int)lua_tonumber(q, argc + 1);
|
|
buffer b(lua_outbuf);
|
|
b.write<int32>(script);
|
|
b.write<int32>(argc);
|
|
for (int i = 0; i < argc; i++) b.write_from(q, i + 1);
|
|
lua_pop(q, argc + 1);
|
|
//
|
|
lua_yield_status = lua_status_call;
|
|
return lua_yield(q, 0);
|
|
}// apollo_ref.cpp
|
|
#include "apollo_buffer.h"
|
|
#include "apollo_ref.h"
|
|
|
|
#include <queue>
|
|
static std::queue<int64> ref_recycle{};
|
|
#define apollo_ref
|
|
namespace apollo_array {
|
|
#include "apollo_ref_shared.h"
|
|
static int len(lua_State* q) {
|
|
auto box = toImpl(q, 1);
|
|
if (!box) return 0;
|
|
//
|
|
buffer b(lua_outbuf);
|
|
b.write<gml_script_id>(length);
|
|
b.write<int32>(1);
|
|
b.write_lua_int64(box->index);
|
|
lua_pop(q, lua_gettop(q));
|
|
lua_yield_status = lua_status_call;
|
|
return lua_yield(q, 0);
|
|
}
|
|
|
|
static int toString(lua_State* q) {
|
|
auto box = toImpl(q, 1);
|
|
if (!box) return 0;
|
|
lua_pushfstring(q, "gml_array#%I", box->index);
|
|
return 1;
|
|
}
|
|
|
|
luaL_Reg meta[] = {
|
|
{"__gc", gc},
|
|
{"__tostring", toString},
|
|
{"__index", get},
|
|
{"__newindex", set},
|
|
{"__len", len},
|
|
{0, 0}
|
|
};
|
|
void init(lua_State* q) {
|
|
luaL_newmetatable(q, metaName);
|
|
luaL_setfuncs(q, meta, 0);
|
|
luaL_dostring(q, R"(return function(arr) -- pairs
|
|
local i = 1
|
|
local n = nil
|
|
return function()
|
|
if n == nil then
|
|
n = #arr or 0
|
|
end
|
|
local k = i
|
|
i = i + 1
|
|
if k <= n then
|
|
return k, arr[k]
|
|
else
|
|
return nil, nil
|
|
end
|
|
end, q, nil
|
|
end)");
|
|
lua_setfield(q, -2, "__pairs");
|
|
lua_pop(q, 1);
|
|
}
|
|
}
|
|
|
|
namespace apollo_struct {
|
|
#include "apollo_ref_shared.h"
|
|
gml_script_id gml_keys;
|
|
static int toString(lua_State* q) {
|
|
auto box = toImpl(q, 1);
|
|
if (!box) return 0;
|
|
lua_pushfstring(q, "gml_struct#%I", box->index);
|
|
return 1;
|
|
}
|
|
|
|
static int keys(lua_State* q) {
|
|
auto box = toImpl(q, 1);
|
|
if (!box) return 0;
|
|
//
|
|
buffer b(lua_outbuf);
|
|
b.write<gml_script_id>(gml_keys);
|
|
b.write<int32>(1);
|
|
b.write_lua_int64(box->index);
|
|
lua_pop(q, lua_gettop(q));
|
|
lua_yield_status = lua_status_call;
|
|
return lua_yield(q, 0);
|
|
}
|
|
|
|
luaL_Reg meta[] = {
|
|
{"__gc", gc},
|
|
{"__tostring", toString},
|
|
{"__index", get},
|
|
{"__newindex", set},
|
|
{"__gml_keys", keys},
|
|
{0, 0}
|
|
};
|
|
void init(lua_State* q) {
|
|
luaL_newmetatable(q, metaName);
|
|
luaL_setfuncs(q, meta, 0);
|
|
luaL_dostring(q, R"(return function(q) -- pairs
|
|
local keys = nil
|
|
local i = 1
|
|
local n
|
|
return function()
|
|
if keys == nil then
|
|
keys = getmetatable(q).__gml_keys(q)
|
|
n = #keys
|
|
end
|
|
local k = keys[i]
|
|
i = i + 1
|
|
if k ~= nil then
|
|
return k, q[k]
|
|
else
|
|
return nil, nil
|
|
end
|
|
end, q, nil
|
|
end)");
|
|
lua_setfield(q, -2, "__pairs");
|
|
lua_pop(q, 1);
|
|
}
|
|
}
|
|
#undef apollo_ref
|
|
|
|
dllx double lua_update_ref_gc(int64* out, double _max) {
|
|
auto n = ref_recycle.size();
|
|
auto max = (size_t)_max;
|
|
if (n > max) n = max;
|
|
for (auto i = 0u; i < n; i++) {
|
|
out[i] = ref_recycle.front();
|
|
ref_recycle.pop();
|
|
}
|
|
return (double)n;
|
|
}
|