Skip to content

Commit

Permalink
move lua class serializers down into the classes themselves
Browse files Browse the repository at this point in the history
lua classes now get to register serializers if they want them

much cleaner decoupled code, and now LuaSerializer doesn't depend on the
entire Object tree, Game, Pi, etc
  • Loading branch information
robn committed Feb 6, 2015
1 parent 85c1f2f commit 0a0d9cb
Show file tree
Hide file tree
Showing 7 changed files with 237 additions and 135 deletions.
4 changes: 2 additions & 2 deletions SAVEBUMP.txt
Expand Up @@ -13,5 +13,5 @@ Regular tasks
- data/libs/NameGen.lua: Add new authors to name generator

One-time tasks

[ There is nothing we know we need to do at the next savebump. ]
- Remove serializer registration for ModelSkin (see XXX comment at bottom for
scengraph/LuaModelSkin.cpp)
71 changes: 71 additions & 0 deletions src/LuaBody.cpp
Expand Up @@ -12,6 +12,15 @@
#include "Pi.h"
#include "Game.h"

#include "ModelBody.h"
#include "Ship.h"
#include "Player.h"
#include "SpaceStation.h"
#include "Planet.h"
#include "Star.h"
#include "CargoBody.h"
#include "Missile.h"

/*
* Class: Body
*
Expand Down Expand Up @@ -385,6 +394,57 @@ static int l_body_find_nearest_to(lua_State *l)
return 1;
}

static std::string _body_serializer(LuaWrappable *o)
{
static char buf[256];
Body *b = static_cast<Body*>(o);
snprintf(buf, sizeof(buf), "%u\n", Pi::game->GetSpace()->GetIndexForBody(b));
return std::string(buf);
}

static bool _body_deserializer(const char *pos, const char **next)
{
Uint32 n = strtoul(pos, const_cast<char**>(next), 0);
if (pos == *next) return false;
(*next)++; // skip newline

Body *body = Pi::game->GetSpace()->GetBodyByIndex(n);

switch (body->GetType()) {
case Object::BODY:
LuaObject<Body>::PushToLua(body);
break;
case Object::MODELBODY:
LuaObject<Body>::PushToLua(dynamic_cast<ModelBody*>(body));
break;
case Object::SHIP:
LuaObject<Ship>::PushToLua(dynamic_cast<Ship*>(body));
break;
case Object::PLAYER:
LuaObject<Player>::PushToLua(dynamic_cast<Player*>(body));
break;
case Object::SPACESTATION:
LuaObject<SpaceStation>::PushToLua(dynamic_cast<SpaceStation*>(body));
break;
case Object::PLANET:
LuaObject<Planet>::PushToLua(dynamic_cast<Planet*>(body));
break;
case Object::STAR:
LuaObject<Star>::PushToLua(dynamic_cast<Star*>(body));
break;
case Object::CARGOBODY:
LuaObject<Star>::PushToLua(dynamic_cast<CargoBody*>(body));
break;
case Object::MISSILE:
LuaObject<Missile>::PushToLua(dynamic_cast<Missile*>(body));
break;
default:
return false;
}

return true;
}

template <> const char *LuaObject<Body>::s_type = "Body";

template <> void LuaObject<Body>::RegisterClass()
Expand All @@ -411,4 +471,15 @@ template <> void LuaObject<Body>::RegisterClass()

LuaObjectBase::CreateClass(s_type, l_parent, l_methods, l_attrs, 0);
LuaObjectBase::RegisterPromotion(l_parent, s_type, LuaObject<Body>::DynamicCastPromotionTest);
LuaObjectBase::RegisterSerializer(s_type, SerializerPair(_body_serializer, _body_deserializer));

// we're also the serializer for our subclasses
LuaObjectBase::RegisterSerializer("ModelBody", SerializerPair(_body_serializer, _body_deserializer));
LuaObjectBase::RegisterSerializer("Ship", SerializerPair(_body_serializer, _body_deserializer));
LuaObjectBase::RegisterSerializer("Player", SerializerPair(_body_serializer, _body_deserializer));
LuaObjectBase::RegisterSerializer("SpaceStation", SerializerPair(_body_serializer, _body_deserializer));
LuaObjectBase::RegisterSerializer("Planet", SerializerPair(_body_serializer, _body_deserializer));
LuaObjectBase::RegisterSerializer("Star", SerializerPair(_body_serializer, _body_deserializer));
LuaObjectBase::RegisterSerializer("CargoBody", SerializerPair(_body_serializer, _body_deserializer));
LuaObjectBase::RegisterSerializer("Missile", SerializerPair(_body_serializer, _body_deserializer));
}
46 changes: 46 additions & 0 deletions src/LuaObject.cpp
Expand Up @@ -94,14 +94,17 @@
static bool instantiated = false;

static std::map< std::string, std::map<std::string,PromotionTest> > *promotions;
static std::map< std::string, SerializerPair > *serializers;

static void _teardown() {
delete promotions;
delete serializers;
}

static inline void _instantiate() {
if (!instantiated) {
promotions = new std::map< std::string, std::map<std::string,PromotionTest> >;
serializers = new std::map< std::string, SerializerPair >;

// XXX atexit is not a very nice way to deal with this in C++
atexit(_teardown);
Expand Down Expand Up @@ -867,6 +870,49 @@ void LuaObjectBase::RegisterPromotion(const char *base_type, const char *target_
(*promotions)[base_type][target_type] = test_fn;
}

void LuaObjectBase::RegisterSerializer(const char *type, SerializerPair pair)
{
(*serializers)[type] = pair;
}

std::string LuaObjectBase::Serialize()
{
static char buf[256];

lua_State *l = Lua::manager->GetLuaState();

auto i = serializers->find(m_type);
if (i == serializers->end()) {
luaL_error(l, "No registered serializer for type %s\n", m_type);
abort();
}

snprintf(buf, sizeof(buf), "%s\n", m_type);

return std::string(buf) + (*i).second.serialize(GetObject());
}

bool LuaObjectBase::Deserialize(const char *stream, const char **next)
{
static char buf[256];

const char *end = strchr(stream, '\n');
int len = end - stream;
end++; // skip newline

snprintf(buf, sizeof(buf), "%.*s", len, stream);

lua_State *l = Lua::manager->GetLuaState();

auto i = serializers->find(buf);
if (i == serializers->end()) {
luaL_error(l, "No registered deserializer for type %s\n", buf);
abort();
}

return (*i).second.deserialize(end, next);
}

void *LuaObjectBase::Allocate(size_t n) {
lua_State *l = Lua::manager->GetLuaState();
return lua_newuserdata(l, n);
Expand Down
25 changes: 25 additions & 0 deletions src/LuaObject.h
Expand Up @@ -11,6 +11,7 @@
#include "LuaWrappable.h"
#include "RefCounted.h"
#include "DeleteEmitter.h"
#include "Serializer.h"
#include <typeinfo>
#include <tuple>

Expand Down Expand Up @@ -68,6 +69,25 @@
// type for promotion test callbacks
typedef bool (*PromotionTest)(LuaWrappable *o);

// type for serializer function pair
struct SerializerPair {
typedef std::string (*Serializer)(LuaWrappable *o);
typedef bool (*Deserializer)(const char *stream, const char **next);

SerializerPair() :
serialize(nullptr),
deserialize(nullptr)
{}

SerializerPair(Serializer _serialize, Deserializer _deserialize) :
serialize(_serialize),
deserialize(_deserialize)
{}

Serializer serialize;
Deserializer deserialize;
};


// wrapper baseclass, and extra bits for getting at certain parts of the
// LuaObject layer
Expand Down Expand Up @@ -124,6 +144,11 @@ class LuaObjectBase {
// object will be of target_type
static void RegisterPromotion(const char *base_type, const char *target_type, PromotionTest test_fn);

static void RegisterSerializer(const char *type, SerializerPair pair);

std::string Serialize();
static bool Deserialize(const char *stream, const char **next);

// allocate n bytes from Lua memory and leave it an associated userdata on
// the stack. this is a wrapper around lua_newuserdata
static void *Allocate(size_t n);
Expand Down
143 changes: 10 additions & 133 deletions src/LuaSerializer.cpp
Expand Up @@ -3,15 +3,6 @@

#include "LuaSerializer.h"
#include "LuaObject.h"
#include "galaxy/StarSystem.h"
#include "Body.h"
#include "Ship.h"
#include "SpaceStation.h"
#include "Planet.h"
#include "Star.h"
#include "Player.h"
#include "Pi.h"
#include "Game.h"

// every module can save one object. that will usually be a table. we call
// each serializer in turn and capture its return value we build a table like
Expand All @@ -33,8 +24,8 @@


// pickler can handle simple types (boolean, number, string) and will drill
// down into tables. it can do userdata for a specific set of types - Body and
// its kids and SystemPath. anything else will cause a lua error
// down into tables. it can do userdata assuming the appropriate Lua wrapper
// class has registered a serializer and deseriaizer
//
// pickle format is newline-seperated. each line begins with a type value,
// followed by data for that type as follows
Expand All @@ -46,8 +37,8 @@
// n - end of table
// r - reference to previously-seen table. followed by the table id
// uXXXX - userdata. XXXX is type, followed by newline, followed by data
// Body - data is a single stringified number for Serializer::LookupBody
// SystemPath - data is four stringified numbers, newline separated
// everything after u is passed down to LuaObject::Serialize to
// generate using per-class serializers
// oXXXX - object. XXX is type, followed by newline, followed by one
// pickled item (typically t[able])

Expand Down Expand Up @@ -187,35 +178,7 @@ void LuaSerializer::pickle(lua_State *l, int to_serialize, std::string &out, con
if (!o)
Error("Lua serializer '%s' tried to serialize an invalid '%s' object", key, lo->GetType());

// XXX object wrappers should really have Serialize/Unserialize
// methods to deal with this
if (lo->Isa("SystemPath")) {
SystemPath *sbp = static_cast<SystemPath*>(o);
snprintf(buf, sizeof(buf), "SystemPath\n%d\n%d\n%d\n%u\n%u\n",
sbp->sectorX, sbp->sectorY, sbp->sectorZ, sbp->systemIndex, sbp->bodyIndex);
out += buf;
break;
}

if (lo->Isa("Body")) {
Body *b = static_cast<Body*>(o);
snprintf(buf, sizeof(buf), "Body\n%u\n", Pi::game->GetSpace()->GetIndexForBody(b));
out += buf;
break;
}

if (lo->Isa("SceneGraph.ModelSkin")) {
SceneGraph::ModelSkin *skin = static_cast<SceneGraph::ModelSkin*>(o);
Serializer::Writer wr;
skin->Save(wr);
const std::string &ser = wr.GetData();
snprintf(buf, sizeof(buf), "ModelSkin\n%lu\n", ser.size());
out += buf;
out += ser;
break;
}

Error("Lua serializer '%s' tried to serialize unsupported '%s' userdata value", key, lo->GetType());
out += lo->Serialize();
break;
}

Expand Down Expand Up @@ -307,97 +270,11 @@ const char *LuaSerializer::unpickle(lua_State *l, const char *pos)
}

case 'u': {
const char *end = strchr(pos, '\n');
if (!end) throw SavedGameCorruptException();
int len = end - pos;
end++; // skip newline

if (len == 10 && strncmp(pos, "SystemPath", 10) == 0) {
pos = end;

Sint32 sectorX = strtol(pos, const_cast<char**>(&end), 0);
if (pos == end) throw SavedGameCorruptException();
pos = end+1; // skip newline

Sint32 sectorY = strtol(pos, const_cast<char**>(&end), 0);
if (pos == end) throw SavedGameCorruptException();
pos = end+1; // skip newline

Sint32 sectorZ = strtol(pos, const_cast<char**>(&end), 0);
if (pos == end) throw SavedGameCorruptException();
pos = end+1; // skip newline

Uint32 systemNum = strtoul(pos, const_cast<char**>(&end), 0);
if (pos == end) throw SavedGameCorruptException();
pos = end+1; // skip newline

Uint32 sbodyId = strtoul(pos, const_cast<char**>(&end), 0);
if (pos == end) throw SavedGameCorruptException();
pos = end+1; // skip newline

const SystemPath sbp(sectorX, sectorY, sectorZ, systemNum, sbodyId);
LuaObject<SystemPath>::PushToLua(sbp);

break;
}

if (len == 4 && strncmp(pos, "Body", 4) == 0) {
pos = end;

Uint32 n = strtoul(pos, const_cast<char**>(&end), 0);
if (pos == end) throw SavedGameCorruptException();
pos = end+1; // skip newline

Body *body = Pi::game->GetSpace()->GetBodyByIndex(n);
if (pos == end) throw SavedGameCorruptException();

switch (body->GetType()) {
case Object::BODY:
LuaObject<Body>::PushToLua(body);
break;
case Object::SHIP:
LuaObject<Ship>::PushToLua(dynamic_cast<Ship*>(body));
break;
case Object::SPACESTATION:
LuaObject<SpaceStation>::PushToLua(dynamic_cast<SpaceStation*>(body));
break;
case Object::PLANET:
LuaObject<Planet>::PushToLua(dynamic_cast<Planet*>(body));
break;
case Object::STAR:
LuaObject<Star>::PushToLua(dynamic_cast<Star*>(body));
break;
case Object::PLAYER:
LuaObject<Player>::PushToLua(dynamic_cast<Player*>(body));
break;
default:
throw SavedGameCorruptException();
}

break;
}

if (len == 9 && strncmp(pos, "ModelSkin", 9) == 0) {
pos = end;

Uint32 serlen = strtoul(pos, const_cast<char**>(&end), 0);
if (pos == end) throw SavedGameCorruptException();
pos = end+1; // skip newline

std::string buf(pos, serlen);
const char *bufp = buf.c_str();
Serializer::Reader rd(ByteRange(bufp, bufp + buf.size()));
SceneGraph::ModelSkin skin;
skin.Load(rd);

LuaObject<SceneGraph::ModelSkin>::PushToLua(skin);

pos += serlen;

break;
}

throw SavedGameCorruptException();
const char *end;
if (!LuaObjectBase::Deserialize(pos, &end))
throw SavedGameCorruptException();
pos = end;
break;
}

case 'o': {
Expand Down

0 comments on commit 0a0d9cb

Please sign in to comment.