/* vi: set ts=2: +-------------------+ | | Enno Rehling | Eressea PBEM host | Christian Schlittchen | (c) 1998 - 2010 | Katja Zedel | | Henning Peters +-------------------+ This program may not be used, modified or distributed without prior permission by the authors of Eressea. */ #include #include "bind_attrib.h" #include #include #include #include #include #include #include #include #include #include #include /* external libraries */ #include #include #include #include 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); }