buildat/src/client/app.cpp
2014-09-27 09:58:16 +03:00

877 lines
24 KiB
C++

// http://www.apache.org/licenses/LICENSE-2.0
// Copyright 2014 Perttu Ahola <celeron55@gmail.com>
#include "app.h"
#include "core/log.h"
#include "client/config.h"
#include "client/state.h"
#include "interface/fs.h"
#include <c55/getopt.h>
#include <c55/os.h>
#include <c55/string_util.h>
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wstrict-aliasing"
#include <Application.h>
#include <Engine.h>
#include <LuaScript.h>
#include <CoreEvents.h>
#include <Input.h>
#include <ResourceCache.h>
#include <Graphics.h>
#pragma GCC diagnostic pop
extern "C" {
#include <lua.h>
#include <lauxlib.h>
}
#include <cereal/archives/portable_binary.hpp>
#include <cereal/types/string.hpp>
#include <signal.h>
#define MODULE "__app"
namespace magic = Urho3D;
extern client::Config g_client_config;
extern bool g_sigint_received;
namespace app {
struct CApp: public App, public magic::Application
{
sp_<client::State> m_state;
magic::LuaScript *m_script;
lua_State *L;
int64_t m_last_script_tick_us;
bool m_reboot_requested = false;
CApp(magic::Context *context):
magic::Application(context),
m_script(nullptr),
L(nullptr),
m_last_script_tick_us(get_timeofday_us())
{
log_v(MODULE, "constructor()");
sv_<ss_> resource_paths = {
g_client_config.cache_path+"/tmp",
g_client_config.share_path+"/extensions", // Could be unsafe
g_client_config.urho3d_path+"/Bin/CoreData",
g_client_config.urho3d_path+"/Bin/Data",
};
auto *fs = interface::getGlobalFilesystem();
ss_ resource_paths_s;
for(const ss_ &path : resource_paths){
if(!resource_paths_s.empty())
resource_paths_s += ";";
resource_paths_s += fs->get_absolute_path(path);
}
engineParameters_["WindowTitle"] = "Buildat Client";
engineParameters_["LogName"] = "client_Urho3D.log";
engineParameters_["FullScreen"] = false;
engineParameters_["Headless"] = false;
engineParameters_["ResourcePaths"] = resource_paths_s.c_str();
engineParameters_["AutoloadPaths"] = "";
// Set up on_update event (this runs every frame)
SubscribeToEvent(magic::E_UPDATE, HANDLER(CApp, on_update));
// Default to not grabbing the mouse
magic::Input *magic_input = GetSubsystem<magic::Input>();
magic_input->SetMouseVisible(true);
// Default to auto-loading resources as they are modified
magic::ResourceCache *magic_cache = GetSubsystem<magic::ResourceCache>();
magic_cache->SetAutoReloadResources(true);
}
~CApp()
{
}
void set_state(sp_<client::State> state)
{
m_state = state;
}
int run()
{
return magic::Application::Run();
}
void shutdown()
{
log_v(MODULE, "shutdown()");
magic::Engine *engine = GetSubsystem<magic::Engine>();
engine->Exit();
}
bool reboot_requested()
{
return m_reboot_requested;
}
void run_script(const ss_ &script)
{
log_v(MODULE, "run_script():\n%s", cs(script));
lua_getfield(L, LUA_GLOBALSINDEX, "__buildat_run_code_in_sandbox");
lua_pushlstring(L, script.c_str(), script.size());
error_logging_pcall(L, 1, 1);
bool status = lua_toboolean(L, -1);
lua_pop(L, 1);
if(status == false){
log_w(MODULE, "run_script(): failed");
} else {
log_v(MODULE, "run_script(): succeeded");
}
}
bool run_script_no_sandbox(const ss_ &script)
{
log_v(MODULE, "run_script_no_sandbox():\n%s", cs(script));
if(luaL_loadstring(L, script.c_str())){
ss_ error = lua_tocppstring(L, -1);
log_e("%s", cs(error));
lua_pop(L, 1);
return false;
}
error_logging_pcall(L, 0, 0);
return true;
}
void handle_packet(const ss_ &name, const ss_ &data)
{
log_v(MODULE, "handle_packet(): %s", cs(name));
lua_getfield(L, LUA_GLOBALSINDEX, "__buildat_handle_packet");
lua_pushlstring(L, name.c_str(), name.size());
lua_pushlstring(L, data.c_str(), data.size());
error_logging_pcall(L, 2, 0);
}
void file_updated_in_cache(const ss_ &file_name,
const ss_ &file_hash, const ss_ &cached_path)
{
log_v(MODULE, "file_updated_in_cache(): %s", cs(file_name));
lua_getfield(L, LUA_GLOBALSINDEX, "__buildat_file_updated_in_cache");
lua_pushlstring(L, file_name.c_str(), file_name.size());
lua_pushlstring(L, file_hash.c_str(), file_hash.size());
lua_pushlstring(L, cached_path.c_str(), cached_path.size());
error_logging_pcall(L, 3, 0);
}
// Non-public methods
void Start()
{
log_v(MODULE, "Start()");
// Set graphics mode
magic::Graphics *magic_graphics = GetSubsystem<magic::Graphics>();
int w = 1024;
int h = 768;
bool fullscreen = false;
bool borderless = false;
bool resizable = true;
bool vsync = true;
bool triple_buffer = false;
int multisampling = 1; // 2 looks much better but is much heavier(?)
magic_graphics->SetMode(w, h, fullscreen, borderless, resizable,
vsync, triple_buffer, multisampling);
// Instantiate and register the Lua script subsystem so that we can use the LuaScriptInstance component
context_->RegisterSubsystem(new magic::LuaScript(context_));
m_script = context_->GetSubsystem<magic::LuaScript>();
L = m_script->GetState();
if(L == nullptr)
throw Exception("m_script.GetState() returned null");
lua_pushlightuserdata(L, (void*)this);
lua_setfield(L, LUA_REGISTRYINDEX, "__buildat_app");
#define DEF_BUILDAT_FUNC(name){\
lua_pushcfunction(L, l_##name);\
lua_setglobal(L, "__buildat_" #name);\
}
DEF_BUILDAT_FUNC(print_log);
DEF_BUILDAT_FUNC(send_packet);
DEF_BUILDAT_FUNC(get_file_content)
DEF_BUILDAT_FUNC(get_file_path)
DEF_BUILDAT_FUNC(get_path)
DEF_BUILDAT_FUNC(extension_path)
DEF_BUILDAT_FUNC(mkdir)
DEF_BUILDAT_FUNC(pcall)
DEF_BUILDAT_FUNC(cereal_binary_input)
DEF_BUILDAT_FUNC(cereal_binary_output)
DEF_BUILDAT_FUNC(connect_server)
DEF_BUILDAT_FUNC(fatal_error)
DEF_BUILDAT_FUNC(disconnect)
ss_ init_lua_path = g_client_config.share_path+"/client/init.lua";
int error = luaL_dofile(L, init_lua_path.c_str());
if(error){
log_w(MODULE, "luaL_dofile: An error occurred: %s\n",
lua_tostring(L, -1));
lua_pop(L, 1);
throw AppStartupError("Could not initialize Lua environment");
}
if(g_client_config.boot_to_menu){
ss_ script =
"local m = require('buildat/extension/__menu')\n"
"if type(m) ~= 'table' then\n"
" error('Failed to load extension __menu')\n"
"end\n"
"m.boot()\n";
if(!run_script_no_sandbox(script)){
throw AppStartupError("Failed to load and run extension __menu");
}
}
}
void on_update(magic::StringHash eventType, magic::VariantMap &eventData)
{
if(g_sigint_received)
shutdown();
if(m_state)
m_state->update();
script_tick();
}
void script_tick()
{
log_t(MODULE, "script_tick()");
int64_t current_us = get_timeofday_us();
int64_t d_us = current_us - m_last_script_tick_us;
if(d_us < 0)
d_us = 0;
m_last_script_tick_us = current_us;
double dtime = d_us / 1000000.0;
lua_getfield(L, LUA_GLOBALSINDEX, "__buildat_tick");
if(lua_isnil(L, -1)){
lua_pop(L, 1);
return;
}
lua_pushnumber(L, dtime);
error_logging_pcall(L, 1, 0);
}
// print_log(level, module, text)
static int l_print_log(lua_State *L)
{
ss_ level = lua_tocppstring(L, 1);
const char *module_c = lua_tostring(L, 2);
const char *text_c = lua_tostring(L, 3);
int loglevel = LOG_INFO;
if(level == "debug")
loglevel = LOG_DEBUG;
else if(level == "verbose")
loglevel = LOG_VERBOSE;
else if(level == "info")
loglevel = LOG_INFO;
else if(level == "warning")
loglevel = LOG_WARNING;
else if(level == "error")
loglevel = LOG_ERROR;
log_(loglevel, module_c, "%s", text_c);
return 0;
}
// send_packet(name: string, data: string)
static int l_send_packet(lua_State *L)
{
ss_ name = lua_tocppstring(L, 1);
ss_ data = lua_tocppstring(L, 2);
lua_getfield(L, LUA_REGISTRYINDEX, "__buildat_app");
CApp *self = (CApp*)lua_touserdata(L, -1);
lua_pop(L, 1);
try {
self->m_state->send_packet(name, data);
return 0;
} catch(std::exception &e){
log_w(MODULE, "Exception in send_packet: %s", e.what());
return 0;
}
}
// get_file_path(name: string) -> path, hash
static int l_get_file_path(lua_State *L)
{
ss_ name = lua_tocppstring(L, 1);
lua_getfield(L, LUA_REGISTRYINDEX, "__buildat_app");
CApp *self = (CApp*)lua_touserdata(L, -1);
lua_pop(L, 1);
try {
ss_ hash;
ss_ path = self->m_state->get_file_path(name, &hash);
lua_pushlstring(L, path.c_str(), path.size());
lua_pushlstring(L, hash.c_str(), hash.size());
return 2;
} catch(std::exception &e){
log_w(MODULE, "Exception in get_file_path: %s", e.what());
return 0;
}
}
// get_file_content(name: string)
static int l_get_file_content(lua_State *L)
{
ss_ name = lua_tocppstring(L, 1);
lua_getfield(L, LUA_REGISTRYINDEX, "__buildat_app");
CApp *self = (CApp*)lua_touserdata(L, -1);
lua_pop(L, 1);
try {
ss_ content = self->m_state->get_file_content(name);
lua_pushlstring(L, content.c_str(), content.size());
return 1;
} catch(std::exception &e){
log_w(MODULE, "Exception in get_file_content: %s", e.what());
return 0;
}
}
// get_path(name: string)
static int l_get_path(lua_State *L)
{
ss_ name = lua_tocppstring(L, 1);
if(name == "share"){
ss_ path = g_client_config.share_path;
lua_pushlstring(L, path.c_str(), path.size());
return 1;
}
if(name == "cache"){
ss_ path = g_client_config.cache_path;
lua_pushlstring(L, path.c_str(), path.size());
return 1;
}
if(name == "tmp"){
ss_ path = g_client_config.cache_path+"/tmp";
lua_pushlstring(L, path.c_str(), path.size());
return 1;
}
log_w(MODULE, "Unknown named path: \"%s\"", cs(name));
return 0;
}
// extension_path(name: string)
static int l_extension_path(lua_State *L)
{
ss_ name = lua_tocppstring(L, 1);
ss_ path = g_client_config.share_path+"/extensions/"+name;
// TODO: Check if extension actually exists and do something suitable if
// not
lua_pushlstring(L, path.c_str(), path.size());
return 1;
}
// mkdir(path: string)
static int l_mkdir(lua_State *L)
{
ss_ path = lua_tocppstring(L, 1);
bool ok = interface::getGlobalFilesystem()->create_directories(path);
if(!ok)
log_w(MODULE, "Failed to create directory: \"%s\"", cs(path));
else
log_v(MODULE, "Created directory: \"%s\"", cs(path));
lua_pushboolean(L, ok);
return 1;
}
static int handle_error(lua_State *L)
{
lua_getglobal(L, "debug");
if(!lua_istable(L, -1)){
log_w(MODULE, "handle_error(): debug is nil");
lua_pop(L, 1);
return 1;
}
lua_getfield(L, -1, "traceback");
if(!lua_isfunction(L, -1)){
log_w(MODULE, "handle_error(): debug.traceback is nil");
lua_pop(L, 2);
return 1;
}
lua_pushvalue(L, 1);
lua_pushinteger(L, 2);
lua_call(L, 2, 1);
return 1;
}
// When calling Lua from C++, this is universally good
static void error_logging_pcall(lua_State *L, int nargs, int nresults)
{
log_t(MODULE, "error_logging_pcall(): nargs=%i, nresults=%i",
nargs, nresults);
//log_d(MODULE, "stack 1: %s", cs(dump_stack(L)));
int start_L = lua_gettop(L);
lua_pushcfunction(L, handle_error);
lua_insert(L, start_L - nargs);
int handle_error_L = start_L - nargs;
//log_d(MODULE, "stack 2: %s", cs(dump_stack(L)));
int r = lua_pcall(L, nargs, nresults, handle_error_L);
lua_remove(L, handle_error_L);
//log_d(MODULE, "stack 3: %s", cs(dump_stack(L)));
if(r != 0){
ss_ traceback = lua_tocppstring(L, -1);
lua_pop(L, 1);
const char *msg =
r == LUA_ERRRUN ? "runtime error" :
r == LUA_ERRMEM ? "ran out of memory" :
r == LUA_ERRERR ? "error handler failed" : "unknown error";
//log_e(MODULE, "Lua %s: %s", msg, cs(traceback));
throw Exception(ss_()+"Lua "+msg+":\n"+traceback);
}
//log_d(MODULE, "stack 4: %s", cs(dump_stack(L)));
}
static void call_global_if_exists(lua_State *L,
const char *global_name, int nargs, int nresults)
{
log_t(MODULE, "call_global_if_exists(): \"%s\"", global_name);
//log_d(MODULE, "stack 1: %s", cs(dump_stack(L)));
int start_L = lua_gettop(L);
lua_getfield(L, LUA_GLOBALSINDEX, global_name);
if(lua_isnil(L, -1)){
lua_pop(L, 1 + nargs);
return;
}
lua_insert(L, start_L - nargs + 1);
error_logging_pcall(L, nargs, nresults);
//log_d(MODULE, "stack 2: %s", cs(dump_stack(L)));
}
// Like lua_pcall, but returns a full traceback on error
// pcall(function) -> status, error
static int l_pcall(lua_State *L)
{
log_d(MODULE, "l_pcall()");
lua_pushcfunction(L, handle_error);
int handle_error_stack_i = lua_gettop(L);
lua_pushvalue(L, 1);
int r = lua_pcall(L, 0, 0, handle_error_stack_i);
int error_stack_i = lua_gettop(L);
if(r == 0){
log_d(MODULE, "l_pcall() returned 0 (no error)");
lua_pushboolean(L, true);
return 1;
}
if(r == LUA_ERRRUN)
log_w(MODULE, "pcall(): Runtime error");
if(r == LUA_ERRMEM)
log_w(MODULE, "pcall(): Out of memory");
if(r == LUA_ERRERR)
log_w(MODULE, "pcall(): Error handler failed");
lua_pushboolean(L, false);
lua_pushvalue(L, error_stack_i);
return 2;
}
static sv_<ss_> dump_stack(lua_State *L)
{
sv_<ss_> result;
int top = lua_gettop(L);
for(int i = 1; i <= top; i++){
int type = lua_type(L, i);
if(type == LUA_TSTRING)
result.push_back(ss_()+"\""+lua_tostring(L, i)+"\"");
else if(type == LUA_TSTRING)
result.push_back(ss_()+"\""+lua_tostring(L, i)+"\"");
else if(type == LUA_TBOOLEAN)
result.push_back(lua_toboolean(L, i) ? "true" : "false");
else if(type == LUA_TNUMBER)
result.push_back(cs(lua_tonumber(L, i)));
else
result.push_back(lua_typename(L, type));
}
return result;
}
static ss_ lua_tocppstring(lua_State *L, int index)
{
if(!lua_isstring(L, index))
throw Exception(ss_()+"lua_tocppstring: Expected string, got "+
lua_typename(L, index));
size_t length;
const char *s = lua_tolstring(L, index, &length);
return ss_(s, length);
}
/* Type format:
{"object",
{"peer", "int32_t"},
{"players", {"unordered_map",
"int32_t",
{"object",
{"peer", "int32_t"},
{"x", "int32_t"},
{"y", "int32_t"},
},
}},
{"playfield", {"object",
{"w", "int32_t"},
{"h", "int32_t"},
{"tiles", {"array", "int32_t"}},
}},
}) */
static constexpr auto known_types =
"byte, int32_t, double, array, unordered_map, object";
// Places result value on top of stack
static void binary_input_read_value(lua_State *L, int type_L,
cereal::PortableBinaryInputArchive &ar)
{
if(type_L < 0) type_L = lua_gettop(L) + type_L + 1;
// Read value definition
bool has_table = false;
ss_ outfield_type;
if(lua_istable(L, type_L)){
has_table = true;
lua_rawgeti(L, type_L, 1);
outfield_type = lua_tocppstring(L, -1);
lua_pop(L, 1);
} else if(lua_isstring(L, type_L)){
outfield_type = lua_tocppstring(L, type_L);
} else {
throw Exception("Value definition table or string expected");
}
log_t(MODULE, "binary_input_read_value(): type=%s", cs(outfield_type));
if(outfield_type == "byte"){
uchar value;
ar(value);
log_t(MODULE, "byte value=%i", (int)value);
lua_pushinteger(L, value);
// value is left on stack
} else if(outfield_type == "int32_t"){
int32_t value;
ar(value);
log_t(MODULE, "int32_t value=%i", value);
lua_pushinteger(L, value);
// value is left on stack
} else if(outfield_type == "double"){
double value;
ar(value);
log_t(MODULE, "double value=%f", value);
lua_pushnumber(L, value);
// value is left on stack
} else if(outfield_type == "string"){
ss_ value;
ar(value);
log_t(MODULE, "string value=%s", cs(value));
lua_pushlstring(L, value.c_str(), value.size());
// value is left on stack
} else if(outfield_type == "array"){
if(!has_table)
throw Exception("array requires parameter table");
lua_newtable(L);
int value_result_table_L = lua_gettop(L);
lua_rawgeti(L, type_L, 2);
int array_type_L = lua_gettop(L);
// Loop through array items
uint64_t num_entries;
ar(num_entries);
for(uint64_t i = 0; i < num_entries; i++){
log_t(MODULE, "array[%s]", cs(i));
binary_input_read_value(L, array_type_L, ar);
lua_rawseti(L, value_result_table_L, i + 1);
}
lua_pop(L, 1); // array_type_L
// value_result_table_L is left on stack
} else if(outfield_type == "unordered_map"){
if(!has_table)
throw Exception("unordered_map requires parameter table");
lua_newtable(L);
int value_result_table_L = lua_gettop(L);
lua_rawgeti(L, type_L, 2);
int map_key_type_L = lua_gettop(L);
lua_rawgeti(L, type_L, 3);
int map_value_type_L = lua_gettop(L);
// Loop through map entries
uint64_t num_entries;
ar(num_entries);
for(uint64_t i = 0; i < num_entries; i++){
log_t(MODULE, "unordered_map[%s]", cs(i));
binary_input_read_value(L, map_key_type_L, ar);
binary_input_read_value(L, map_value_type_L, ar);
lua_rawset(L, value_result_table_L);
}
lua_pop(L, 1); // map_value_type_L
lua_pop(L, 1); // map_key_type_L
// value_result_table_L is left on stack
} else if(outfield_type == "object"){
if(!has_table)
throw Exception("object requires parameter table");
lua_newtable(L);
int value_result_table_L = lua_gettop(L);
// Loop through object fields
size_t field_i = 0;
lua_pushnil(L);
while(lua_next(L, type_L) != 0){
if(field_i != 0){
log_t(MODULE, "object field %zu", field_i);
int field_def_L = lua_gettop(L);
lua_rawgeti(L, field_def_L, 1); // name
lua_rawgeti(L, field_def_L, 2); // type
log_t(MODULE, " = object[\"%s\"]", lua_tostring(L, -2));
binary_input_read_value(L, -1, ar); // Uses type, pushes value
lua_remove(L, -2); // Remove type
lua_rawset(L, value_result_table_L); // Set t[#-2] = #-1
}
lua_pop(L, 1); // Continue iterating by popping table value
field_i++;
}
// value_result_table_L is left on stack
} else {
throw Exception(ss_()+"Unknown type \""+outfield_type+"\""
"; known types are "+known_types);
}
}
static void binary_output_write_value(lua_State *L, int value_L, int type_L,
cereal::PortableBinaryOutputArchive &ar)
{
if(value_L < 0) value_L = lua_gettop(L) + value_L + 1;
if(type_L < 0) type_L = lua_gettop(L) + type_L + 1;
// Read value definition
bool has_table = false;
ss_ outfield_type;
if(lua_istable(L, type_L)){
has_table = true;
lua_rawgeti(L, type_L, 1);
outfield_type = lua_tocppstring(L, -1);
lua_pop(L, 1);
} else if(lua_isstring(L, type_L)){
outfield_type = lua_tocppstring(L, type_L);
} else {
throw Exception("Value definition table or string expected");
}
log_t(MODULE, "binary_output_write_value(): type=%s", cs(outfield_type));
if(outfield_type == "byte"){
uchar value = lua_tointeger(L, value_L);
log_t(MODULE, "byte value=%i", (int)value);
ar(value);
} else if(outfield_type == "int32_t"){
int32_t value = lua_tointeger(L, value_L);
log_t(MODULE, "int32_t value=%i", value);
ar(value);
} else if(outfield_type == "double"){
double value = lua_tonumber(L, value_L);
log_t(MODULE, "double value=%f", value);
ar(value);
} else if(outfield_type == "string"){
ss_ value = lua_tocppstring(L, value_L);
log_t(MODULE, "string value=%s", cs(value));
ar(value);
} else if(outfield_type == "array"){
if(!has_table)
throw Exception("array requires parameter table");
lua_rawgeti(L, type_L, 2);
int array_type_L = lua_gettop(L);
// Loop through array items
uint64_t num_entries = 0;
lua_pushnil(L);
while(lua_next(L, value_L) != 0){
lua_pop(L, 1); // Continue iterating by popping table value
num_entries++;
}
ar(num_entries);
lua_pushnil(L);
int i = 1;
while(lua_next(L, value_L) != 0){
log_t(MODULE, "array[%i]", i);
binary_output_write_value(L, -1, array_type_L, ar);
lua_pop(L, 1); // Continue iterating by popping table value
i++;
}
lua_pop(L, 1); // array_type_L
// value_result_table_L is left on stack
} else if(outfield_type == "unordered_map"){
if(!has_table)
throw Exception("unordered_map requires parameter table");
lua_rawgeti(L, type_L, 2);
int map_key_type_L = lua_gettop(L);
lua_rawgeti(L, type_L, 3);
int map_value_type_L = lua_gettop(L);
// Loop through map entries
uint64_t num_entries = 0;
lua_pushnil(L);
while(lua_next(L, value_L) != 0){
lua_pop(L, 1); // Continue iterating by popping table value
num_entries++;
}
ar(num_entries);
lua_pushnil(L);
while(lua_next(L, value_L) != 0){
int key_L = lua_gettop(L) - 1;
int value_L = lua_gettop(L);
log_t(MODULE, "unordered_map[%s]", lua_tostring(L, key_L));
binary_output_write_value(L, key_L, map_key_type_L, ar);
binary_output_write_value(L, value_L, map_value_type_L, ar);
lua_pop(L, 1); // Continue iterating by popping table value
}
lua_pop(L, 1); // map_value_type_L
lua_pop(L, 1); // map_key_type_L
// value_result_table_L is left on stack
} else if(outfield_type == "object"){
if(!has_table)
throw Exception("object requires parameter table");
// Loop through object fields
size_t field_i = 0;
lua_pushnil(L);
while(lua_next(L, type_L) != 0){
if(field_i != 0){
log_t(MODULE, "object field %zu", field_i);
int field_def_L = lua_gettop(L);
lua_rawgeti(L, field_def_L, 2); // type
lua_rawgeti(L, field_def_L, 1); // name
log_t(MODULE, " = object[\"%s\"]", lua_tostring(L, -1));
// Get value_L[name]; name is replaced by value
lua_rawget(L, value_L);
// Recurse into this value
binary_output_write_value(L, -1, -2, ar);
lua_pop(L, 1); // Pop value
lua_pop(L, 1); // Pop type
}
lua_pop(L, 1); // Continue iterating by popping table value
field_i++;
}
} else {
throw Exception(ss_()+"Unknown type \""+outfield_type+"\""
"; known types are "+known_types);
}
}
// cereal_binary_input(data: string, types: table) -> table of values
static int l_cereal_binary_input(lua_State *L)
{
size_t data_len = 0;
const char *data_c = lua_tolstring(L, 1, &data_len);
ss_ data(data_c, data_len);
int type_L = 2;
std::istringstream is(data, std::ios::binary);
cereal::PortableBinaryInputArchive ar(is);
binary_input_read_value(L, type_L, ar);
return 1;
}
// cereal_binary_output(values: table, types: table) -> data
static int l_cereal_binary_output(lua_State *L)
{
int value_L = 1;
int type_L = 2;
std::ostringstream os(std::ios::binary);
{
cereal::PortableBinaryOutputArchive ar(os);
binary_output_write_value(L, value_L, type_L, ar);
}
ss_ data = os.str();
lua_pushlstring(L, data.c_str(), data.size());
return 1;
}
// connect_server(address: string) -> status: bool, error: string or nil
static int l_connect_server(lua_State *L)
{
lua_getfield(L, LUA_REGISTRYINDEX, "__buildat_app");
CApp *self = (CApp*)lua_touserdata(L, -1);
lua_pop(L, 1);
ss_ address = lua_tocppstring(L, 1);
if(address.empty()){
log_w(MODULE, "connect_server(): Cannot connect to empty address");
lua_pushboolean(L, false);
return 2;
}
ss_ host;
ss_ port;
c55::Strfnd f(address);
if(address[0] == '['){
f.next("[");
host = f.next("]");
f.next(":");
port = f.next("");
} else {
host = f.next(":");
port = f.next("");
}
if(host == ""){
log_w(MODULE, "connect_server(): Cannot connect to empty host");
lua_pushboolean(L, false);
return 2;
}
if(port == ""){
port = "20000";
}
ss_ error;
bool ok = self->m_state->connect(host, port, &error);
lua_pushboolean(L, ok);
if(ok)
lua_pushnil(L);
else
lua_pushstring(L, error.c_str());
return 2;
}
// faatal_error(error: string)
static int l_fatal_error(lua_State *L)
{
ss_ error = lua_tocppstring(L, 1);
log_e(MODULE, "Fatal error: %s", cs(error));
throw Exception("Fatal error from Lua");
return 0;
}
// disconnect()
static int l_disconnect(lua_State *L)
{
lua_getfield(L, LUA_REGISTRYINDEX, "__buildat_app");
CApp *self = (CApp*)lua_touserdata(L, -1);
lua_pop(L, 1);
if(g_client_config.boot_to_menu){
// If menu, reboot client into menu
self->m_reboot_requested = true;
self->shutdown();
} else {
// If no menu, shutdown client
self->shutdown();
}
return 0;
}
};
App* createApp(magic::Context *context)
{
return new CApp(context);
}
}
// vim: set noet ts=4 sw=4: