server/src/bind_attrib.c

415 lines
11 KiB
C

/* vi: set ts=2:
+-------------------+
| | Enno Rehling <enno@eressea.de>
| Eressea PBEM host | Christian Schlittchen <corwin@amber.kn-bremen.de>
| (c) 1998 - 2010 | Katja Zedel <katze@felidae.kn-bremen.de>
| | Henning Peters <faroul@beyond.kn-bremen.de>
+-------------------+
This program may not be used, modified or distributed
without prior permission by the authors of Eressea.
*/
#include <platform.h>
#include "bind_attrib.h"
#include <kernel/config.h>
#include <kernel/unit.h>
#include <kernel/faction.h>
#include <kernel/ship.h>
#include <kernel/building.h>
#include <kernel/region.h>
#include <kernel/objtypes.h>
#include <util/attrib.h>
#include <util/log.h>
#include <util/resolve.h>
#include <storage.h>
/* external libraries */
#include <bson.h>
#include <lua.h>
#include <tolua.h>
#include <errno.h>
static void init_ext(attrib * a)
{
lua_State *L = (lua_State *) global.vm_state;
lua_pushstring(L, "callbacks");
lua_rawget(L, LUA_GLOBALSINDEX);
if (lua_istable(L, -1)) {
lua_pushstring(L, "attrib_init");
lua_rawget(L, LUA_GLOBALSINDEX);
if (lua_isfunction(L, -1)) {
lua_rawgeti(L, LUA_REGISTRYINDEX, a->data.i);
if (lua_pcall(L, 1, 0, 0) != 0) {
const char *error = lua_tostring(L, -1);
log_error("attrib_init '%d': %s.\n", a->data.i, error);
}
}
}
}
static void free_ext(attrib * a)
{
lua_State *L = (lua_State *) global.vm_state;
if (a->data.i > 0) {
luaL_unref(L, LUA_REGISTRYINDEX, a->data.i);
}
}
static int age_ext(attrib * a)
{
return AT_AGE_KEEP;
}
static void write_ext_i(lua_State * L, const char *name, bson_buffer * bb)
{
int type = lua_type(L, -1);
switch (type) {
case LUA_TNUMBER:
{
double value = tolua_tonumber(L, -1, 0);
bson_append_double(bb, name, value);
}
break;
case LUA_TSTRING:
{
const char *value = tolua_tostring(L, -1, 0);
bson_append_string(bb, name, value);
}
break;
case LUA_TTABLE:
{
int n = luaL_getn(L, -1);
if (n) {
bson_buffer *arr = bson_append_start_array(bb, name);
int i;
for (i = 0; i != n; ++i) {
char num[12];
bson_numstr(num, i);
lua_rawgeti(L, -1, i + 1);
write_ext_i(L, num, arr);
lua_pop(L, 1);
}
bson_append_finish_object(arr);
} else {
bson_buffer *sub = bson_append_start_object(bb, name);
lua_pushnil(L); /* first key */
while (lua_next(L, -2) != 0) {
const char *key;
/* uses 'key' (at index -2) and 'value' (at index -1) */
lua_pushvalue(L, -2);
key = lua_tolstring(L, -1, 0);
lua_pushvalue(L, -2);
if (key) {
write_ext_i(L, key, sub);
}
/* removes 'value'; keeps 'key' for next iteration */
lua_pop(L, 3);
}
bson_append_finish_object(sub);
}
}
break;
case LUA_TUSERDATA:
{
tolua_Error tolua_err;
if (tolua_isusertype(L, -1, "unit", 0, &tolua_err)) {
unit *u = (unit *) tolua_tousertype(L, -1, 0);
bson_oid_t oid;
oid.ints[0] = TYP_UNIT;
oid.ints[1] = u->no;
bson_append_oid(bb, name, &oid);
} else if (tolua_isusertype(L, -1, "region", 0, &tolua_err)) {
region *r = (region *) tolua_tousertype(L, -1, 0);
bson_oid_t oid;
oid.ints[0] = TYP_REGION;
oid.ints[1] = r->uid;
bson_append_oid(bb, name, &oid);
} else if (tolua_isusertype(L, -1, "ship", 0, &tolua_err)) {
ship *sh = (ship *) tolua_tousertype(L, -1, 0);
bson_oid_t oid;
oid.ints[0] = TYP_SHIP;
oid.ints[1] = sh->no;
bson_append_oid(bb, name, &oid);
} else if (tolua_isusertype(L, -1, "building", 0, &tolua_err)) {
building *b = (building *) tolua_tousertype(L, -1, 0);
bson_oid_t oid;
oid.ints[0] = TYP_BUILDING;
oid.ints[1] = b->no;
bson_append_oid(bb, name, &oid);
} else {
log_error("unsuported type.\n");
bson_append_null(bb, name);
}
}
break;
default:
bson_append_null(bb, name);
break;
}
}
static void
write_ext(const attrib * a, const void *owner, struct storage *store)
{
lua_State *L = (lua_State *) global.vm_state;
if (a->data.i > 0) {
int handle = a->data.i;
bson_buffer bb;
bson b;
bson_buffer_init(&bb);
lua_rawgeti(L, LUA_REGISTRYINDEX, handle);
write_ext_i(L, "_data", &bb);
bson_from_buffer(&b, &bb);
store->w_int(store, bson_size(&b));
store->w_bin(store, b.data, bson_size(&b));
bson_destroy(&b);
}
}
static int read_ext_i(lua_State * L, bson_iterator * it, bson_type type)
{
switch (type) {
case bson_double:
{
lua_pushnumber(L, bson_iterator_double(it));
}
break;
case bson_string:
{
lua_pushstring(L, bson_iterator_string(it));
}
break;
case bson_array:
{
bson_iterator sub;
int err;
bson_iterator_subiterator(it, &sub);
lua_newtable(L);
if (bson_iterator_more(&sub)) {
bson_type ctype;
for (ctype = bson_iterator_next(&sub); bson_iterator_more(&sub);
ctype = bson_iterator_next(&sub)) {
int i = atoi(bson_iterator_key(&sub));
err = read_ext_i(L, &sub, ctype);
if (err) {
lua_pop(L, 1);
return err;
}
lua_rawseti(L, -2, i + 1);
}
}
}
break;
case bson_object:
{
bson_iterator sub;
int err;
bson_iterator_subiterator(it, &sub);
lua_newtable(L);
if (bson_iterator_more(&sub)) {
bson_type ctype;
for (ctype = bson_iterator_next(&sub); bson_iterator_more(&sub);
ctype = bson_iterator_next(&sub)) {
lua_pushstring(L, bson_iterator_key(&sub));
err = read_ext_i(L, &sub, ctype);
if (err) {
lua_pop(L, 1);
return err;
}
lua_rawset(L, -3);
}
}
}
break;
case bson_oid:
{
bson_oid_t *oid = bson_iterator_oid(it);
if (oid->ints[0] == TYP_UNIT) {
unit *u = findunit(oid->ints[1]);
if (u)
tolua_pushusertype(L, u, "unit");
else
lua_pushnil(L);
} else if (oid->ints[0] == TYP_REGION) {
region *r = findregionbyid(oid->ints[1]);
if (r)
tolua_pushusertype(L, r, "region");
else
lua_pushnil(L);
} else if (oid->ints[0] == TYP_SHIP) {
ship *sh = findship(oid->ints[1]);
if (sh)
tolua_pushusertype(L, sh, "ship");
else
lua_pushnil(L);
} else if (oid->ints[0] == TYP_BUILDING) {
building *b = findbuilding(oid->ints[1]);
if (b)
tolua_pushusertype(L, b, "building");
else
lua_pushnil(L);
} else {
log_error("unknown oid %d %d %d\n", oid->ints[0], oid->ints[1], oid->ints[2]);
lua_pushnil(L);
}
}
break;
case bson_null:
lua_pushnil(L);
break;
case bson_eoo:
return EFAULT;
default:
return EINVAL;
}
return 0;
}
static int resolve_bson(variant data, void *address)
{
lua_State *L = (lua_State *) global.vm_state;
bson b;
int err;
bson_iterator it;
attrib *a = (attrib *) address;
char *buffer = data.v;
bson_init(&b, buffer, 1);
bson_iterator_init(&it, b.data);
err = read_ext_i(L, &it, bson_iterator_next(&it));
a->data.i = luaL_ref(L, LUA_REGISTRYINDEX);
bson_destroy(&b);
return err ? AT_READ_FAIL : AT_READ_OK;
}
static int read_ext(attrib * a, void *owner, struct storage *store)
{
variant data;
int len = store->r_int(store);
data.v = bson_malloc(len);
store->r_bin(store, data.v, (size_t) len);
a->data.v = 0;
ur_add(data, a, resolve_bson);
return AT_READ_OK;
}
attrib_type at_lua_ext = {
"lua", init_ext, free_ext, age_ext, write_ext, read_ext
};
static attrib **get_attribs(lua_State * L, int idx)
{
attrib **ap = NULL;
tolua_Error tolua_err;
if (tolua_isusertype(L, idx, TOLUA_CAST "unit", 0, &tolua_err)) {
unit *u = (unit *) tolua_tousertype(L, idx, 0);
if (u)
ap = &u->attribs;
} else if (tolua_isusertype(L, idx, TOLUA_CAST "region", 0, &tolua_err)) {
region *r = (region *) tolua_tousertype(L, idx, 0);
if (r)
ap = &r->attribs;
} else if (tolua_isusertype(L, idx, TOLUA_CAST "faction", 0, &tolua_err)) {
faction *f = (faction *) tolua_tousertype(L, idx, 0);
if (f)
ap = &f->attribs;
} else if (lua_isstring(L, idx)) {
const char *str = tolua_tostring(L, idx, NULL);
if (str && strcmp(str, "global") == 0) {
ap = &global.attribs;
}
}
return ap;
}
static int tolua_attrib_create(lua_State * L)
{
attrib **ap = get_attribs(L, 1);
if (ap) {
attrib *a = a_new(&at_lua_ext);
int handle;
lua_pushvalue(L, 2);
handle = luaL_ref(L, LUA_REGISTRYINDEX);
a->data.i = handle;
a_add(ap, a);
tolua_pushusertype(L, (void *)a, TOLUA_CAST "attrib");
return 1;
}
return 0;
}
int tolua_attrib_data(lua_State * L)
{
attrib *a = (attrib *) tolua_tousertype(L, 1, 0);
if (a && a->data.i) {
lua_rawgeti(L, LUA_REGISTRYINDEX, a->data.i);
return 1;
}
return 0;
}
attrib *tolua_get_lua_ext(struct attrib * alist)
{
while (alist && alist->type != &at_lua_ext)
alist = alist->next;
return alist;
}
int tolua_attriblist_next(lua_State * L)
{
attrib **attrib_ptr = (attrib **) lua_touserdata(L, lua_upvalueindex(1));
attrib *a = *attrib_ptr;
if (a != NULL) {
tolua_pushusertype(L, (void *)a, TOLUA_CAST "attrib");
*attrib_ptr = tolua_get_lua_ext(a->next);
return 1;
} else
return 0; /* no more values to return */
}
int tolua_attrib_get(lua_State * L)
{
attrib **ap = get_attribs(L, 1);
if (ap) {
attrib *a = tolua_get_lua_ext(*ap);
attrib **attrib_ptr = (attrib **) lua_newuserdata(L, sizeof(attrib *));
luaL_getmetatable(L, "attrib");
lua_setmetatable(L, -2);
*attrib_ptr = a;
lua_pushcclosure(L, tolua_attriblist_next, 1);
return 1;
} else
return 0;
}
void tolua_attrib_open(lua_State * L)
{
at_register(&at_lua_ext);
tolua_usertype(L, TOLUA_CAST "attrib");
tolua_module(L, NULL, 0);
tolua_beginmodule(L, NULL);
{
tolua_cclass(L, TOLUA_CAST "attrib", TOLUA_CAST "attrib", TOLUA_CAST "",
NULL);
tolua_beginmodule(L, TOLUA_CAST "attrib");
{
tolua_function(L, TOLUA_CAST "create", &tolua_attrib_create);
tolua_function(L, TOLUA_CAST "get", &tolua_attrib_get);
tolua_variable(L, TOLUA_CAST "data", &tolua_attrib_data, NULL);
}
tolua_endmodule(L);
}
tolua_endmodule(L);
}