517 lines
16 KiB
C++
517 lines
16 KiB
C++
#include "Scene.h"
|
|
#ifdef ONLINE_MODE
|
|
#include "card.h"
|
|
#include "json-c/json.h"
|
|
//#pragma optimize("",off)
|
|
/****************************************************************************
|
|
Server's JSON structure:
|
|
Note: even levels are Objects (associative arrays actually),
|
|
odd levels are enumerated array.
|
|
|
|
Themes Object (level 0)
|
|
(
|
|
[ThemeId] => Theme Array(level 1)
|
|
(
|
|
[0] => Name
|
|
[1] => Thumbnail
|
|
[2] => Roles Object (level 2)
|
|
(
|
|
[RoleId] => Role Array (level 3)
|
|
(
|
|
[0] => Name
|
|
[1] => Thumbnail
|
|
[2] => Assets Object (level 4)
|
|
(
|
|
[AssetId] => Asset Array (level 5)
|
|
(
|
|
[0] => Name
|
|
[1] => Thumbnail
|
|
[2] => ZCCList Object (level 6)
|
|
(
|
|
[ZCCId] => Zcc Array (level 7)
|
|
(
|
|
[0] => Name
|
|
[1] => Hash
|
|
[2] => Full
|
|
[3] => ZccVersion
|
|
)
|
|
[3] => AssetType (Func)
|
|
[4] => IsNew
|
|
[5] => SlotFrequencyIndex (-1 if Reward)
|
|
[6] => HowLongStowed
|
|
[7] => Refresh Thumbnail Flag
|
|
[8] => Property
|
|
[9] => CollectionId
|
|
[10]=> Display Card In a messageBox
|
|
[11]=> Display a specific Adv when this asset is equiped
|
|
[3] => NbSlotsInRole
|
|
[[3] => PromotedCollection]
|
|
[[4] => PromotedCollectionToAnnounce]
|
|
[[5] => ContestCollection]
|
|
[[6] => Url To add in Tuto list
|
|
|
|
Client's Lua table structure:
|
|
|
|
Table
|
|
{
|
|
[0] => Table
|
|
{
|
|
[Name] => Name
|
|
[Type] => "Theme"
|
|
[Id] => ThemeId
|
|
[Children] => Roles
|
|
[Updated] => bool
|
|
[[Promo] => bool]
|
|
[[NewPromo] => bool]
|
|
[[Contest] => int]
|
|
[[UrlForTuto] => Url]
|
|
{
|
|
[0] => Table
|
|
{
|
|
[Name] => Name
|
|
[Type] => "Role"
|
|
[Id] => RoleId
|
|
[Children] => Assets
|
|
[Updated] => bool
|
|
[NbSlots] => max number of cards in this role
|
|
{
|
|
[0] => Table
|
|
{
|
|
[Name] => Name
|
|
[Type] => "Asset"
|
|
[Id] => AssetId
|
|
[Func] => AssetType
|
|
[Property] => classic|green|avataranimType,diy...
|
|
[IsReward] => IsReward
|
|
[Rarity] => index (0/1/2)
|
|
[ObtainedTS] => Number of seconds in the past
|
|
[IsNew] => bool
|
|
[Updated] => bool
|
|
[ZCCList] => ZCCList
|
|
[CollectionId] => CollectionId
|
|
[[hasToDisplay] => bool]
|
|
[[AdToDisplay] => int]
|
|
(
|
|
[0] => Table
|
|
(
|
|
[Name] => Name
|
|
[Id] => ZccId
|
|
[Full] => true|false
|
|
[ZccVersion] => int
|
|
)
|
|
*/
|
|
|
|
|
|
|
|
#define attributeLoad(outputJo, name, typ) \
|
|
{ \
|
|
if (name##Idx >= json_object_array_length(jo)) \
|
|
{ \
|
|
json_object_put(outputJo); \
|
|
return onLoadError(id); \
|
|
} \
|
|
json_object *name##Jo = json_object_array_get_idx(jo, name##Idx); \
|
|
if (name##Jo) \
|
|
name = json_object_get_##typ(name##Jo); \
|
|
else \
|
|
return onLoadError(id); \
|
|
}
|
|
|
|
#define attributeLoadPublish(outputJo, name, typ) \
|
|
{ \
|
|
if (name##Idx >= json_object_array_length(jo)) \
|
|
{ \
|
|
json_object_put(outputJo); \
|
|
return onLoadError(id); \
|
|
} \
|
|
json_object *name##Jo = json_object_array_get_idx(jo, name##Idx); \
|
|
if (name##Jo) \
|
|
{ \
|
|
name = json_object_get_##typ(name##Jo); \
|
|
json_object_object_add(outputJo, #name, json_object_get(name##Jo)); \
|
|
} \
|
|
else \
|
|
return onLoadError(id); \
|
|
}
|
|
|
|
#define attributePublish(outputJo, name) \
|
|
{ \
|
|
if (name##Idx >= json_object_array_length(jo)) \
|
|
{ \
|
|
json_object_put(outputJo); \
|
|
return onLoadError(id); \
|
|
} \
|
|
json_object *name##Jo = json_object_array_get_idx(jo, name##Idx); \
|
|
if (name##Jo) \
|
|
json_object_object_add(outputJo, #name, json_object_get(name##Jo)); \
|
|
else \
|
|
return onLoadError(id); \
|
|
}
|
|
|
|
#define attributePublishOpt(outputJo, name) \
|
|
{ \
|
|
if (name##Idx < json_object_array_length(jo)) \
|
|
{ \
|
|
json_object *name##Jo = json_object_array_get_idx(jo, name##Idx); \
|
|
if (name##Jo) \
|
|
json_object_object_add(outputJo, #name, json_object_get(name##Jo)); \
|
|
else \
|
|
return onLoadError(id); \
|
|
} \
|
|
}
|
|
|
|
#define attributePublishSetType(outputJo, name, typ) \
|
|
{ \
|
|
if (name##Idx >= json_object_array_length(jo)) \
|
|
{ \
|
|
json_object_put(outputJo); \
|
|
return onLoadError(id); \
|
|
} \
|
|
json_object *name##Jo = json_object_array_get_idx(jo, name##Idx); \
|
|
if (name##Jo) \
|
|
{ \
|
|
typ val = json_object_get_##typ(name##Jo); \
|
|
json_object_object_add(outputJo, #name, json_object_new_##typ(val)); \
|
|
} \
|
|
else \
|
|
return onLoadError(id); \
|
|
}
|
|
|
|
#define attributePublishSetTypeOpt(outputJo, name, typ) \
|
|
{ \
|
|
if (name##Idx < json_object_array_length(jo)) \
|
|
{ \
|
|
json_object *name##Jo = json_object_array_get_idx(jo, name##Idx); \
|
|
if (name##Jo) \
|
|
{ \
|
|
typ val = json_object_get_##typ(name##Jo); \
|
|
json_object_object_add(outputJo, #name, json_object_new_##typ(val)); \
|
|
} \
|
|
else \
|
|
return onLoadError(id); \
|
|
} \
|
|
}
|
|
|
|
#define attributePublishRename(outputJo, inputName, outputName) \
|
|
{ \
|
|
if (inputName##Idx >= json_object_array_length(jo)) \
|
|
{ \
|
|
json_object_put(outputJo); \
|
|
return onLoadError(id); \
|
|
} \
|
|
json_object *inputName##Jo = json_object_array_get_idx(jo, inputName##Idx); \
|
|
if (inputName##Jo) \
|
|
json_object_object_add(outputJo, #outputName, json_object_get(inputName##Jo)); \
|
|
else \
|
|
return onLoadError(id); \
|
|
}
|
|
|
|
#define attributePublishValue(outputJo, name, typ, value) \
|
|
{ json_object_object_add(outputJo, #name, json_object_new_##typ(value)); } \
|
|
|
|
#define attributeGetVal(name, typ) \
|
|
if (name##Idx >= json_object_array_length(jo)) \
|
|
{ \
|
|
json_object_put(outputJo); \
|
|
return onLoadError(id); \
|
|
} \
|
|
typ name##Val = json_object_get_##typ(json_object_array_get_idx(jo, name##Idx));
|
|
|
|
|
|
const char *CardElement::TypeStr[5] = { "Theme", "Role", "Asset", "Zcc", "Invalid" };
|
|
|
|
void logStr(const char *format, ...);
|
|
|
|
bool CardElement::onLoadError(u32 id)
|
|
{
|
|
logStr("Error loading card: %s #%u", getTypeStr(), id);
|
|
return false;
|
|
}
|
|
|
|
#ifndef USER_MODE
|
|
unsigned int Card::getMemoryUsage() const
|
|
{
|
|
unsigned int size = 0;
|
|
size += Thumbnail.size() + sizeof(MustRefreshThumbnail);
|
|
return size;
|
|
}
|
|
#endif
|
|
|
|
bool Card::load(CardElemContainer *container, bool isNew, json_object *jo, json_object *& outputJo, u32 id, CardElement::Type *errorSource, bool publishThumbnailFilenames, const CardElement *parent)
|
|
{
|
|
CardElement::Type cardType = getType();
|
|
|
|
*errorSource = cardType;
|
|
_Parent = parent;
|
|
if (json_object_get_type(jo) != json_type_array)
|
|
return onLoadError(id);
|
|
|
|
// Load and publish properties
|
|
outputJo = json_object_new_object();
|
|
attributePublish(outputJo, Name);
|
|
if (publishThumbnailFilenames)
|
|
attributeLoadPublish(outputJo, Thumbnail, string)
|
|
else
|
|
attributeLoad(outputJo, Thumbnail, string);
|
|
attributePublishValue(outputJo, Type, string, const_cast<char*>(getTypeStr()));
|
|
attributePublishValue(outputJo, Id, int, id);
|
|
Id = id;
|
|
MustRefreshThumbnail = false; // can be overriden by AssetCard::load()
|
|
switch (cardType)
|
|
{
|
|
case CardElement::Theme:
|
|
attributePublishSetTypeOpt(outputJo, Promo, boolean);
|
|
attributePublishSetTypeOpt(outputJo, NewPromo, boolean);
|
|
attributePublishSetTypeOpt(outputJo, Contest, int);
|
|
attributePublishOpt(outputJo, UrlForTuto);
|
|
break;
|
|
case CardElement::Role:
|
|
attributePublishSetType(outputJo, NbSlots, int);
|
|
break;
|
|
default:;
|
|
}
|
|
|
|
// Handle an update of an existing card: for now, all cards transmitted by the server are considered new / to update
|
|
// TODO: server gives only new / updated, client adds them into the existing tree
|
|
attributePublishValue(outputJo, Updated, boolean, isNew);
|
|
|
|
// Load children
|
|
if (json_object_array_length(jo) <= ChildrenIdx)
|
|
return true;
|
|
json_object *outputChildrenJo = json_object_new_array();
|
|
json_object_object_add(outputJo, (cardType == CardElement::Asset) ? "ZCCList" : "Children", outputChildrenJo);
|
|
json_object *childrenJo = json_object_array_get_idx(jo, ChildrenIdx);
|
|
if (json_object_get_type(childrenJo) == json_type_object)
|
|
{
|
|
json_object_object_foreach(childrenJo, key, propArrayJo)
|
|
{
|
|
u32 id = atoi(key);
|
|
CardElement *card = container->addCard((CardElement::Type)(cardType+1), id, isNew); // replaces isNew
|
|
json_object *outputChildJo = NULL;
|
|
if (!card || !card->load(container, isNew, propArrayJo, outputChildJo, id, errorSource, publishThumbnailFilenames, this))
|
|
return false;
|
|
if (outputChildJo)
|
|
json_object_array_add(outputChildrenJo, outputChildJo);
|
|
}
|
|
}
|
|
else if (json_object_get_type(childrenJo) == json_type_array)
|
|
{
|
|
int len = json_object_array_length(childrenJo);
|
|
for (int i=0; i!=len; ++i)
|
|
{
|
|
CardElement *card = container->addCard((CardElement::Type)(cardType+1), i, isNew); // replaces isNew
|
|
json_object *outputChildJo = NULL;
|
|
if (!card || !card->load(container, isNew, json_object_array_get_idx(childrenJo, i), outputChildJo, i, errorSource, publishThumbnailFilenames, this))
|
|
return false;
|
|
if (outputChildJo)
|
|
json_object_array_add(outputChildrenJo, outputChildJo);
|
|
}
|
|
}
|
|
else
|
|
return onLoadError(id);
|
|
return true;
|
|
}
|
|
|
|
bool AssetCard::load(CardElemContainer *container, bool isNew, json_object *jo, json_object *& outputJo, u32 id, CardElement::Type *errorSource, bool publishThumbnailFilenames, const CardElement *parent)
|
|
{
|
|
*errorSource = getType();
|
|
outputJo = json_object_new_object();
|
|
if (!Card::load(container, isNew, jo, outputJo, id, errorSource, publishThumbnailFilenames, parent))
|
|
return onLoadError(id);
|
|
attributePublish(outputJo, Func);
|
|
attributeGetVal(IsNew, boolean);
|
|
attributePublishValue(outputJo, IsNew, boolean, (IsNewVal == 1));
|
|
attributeGetVal(SlotFrequency, int);
|
|
attributeGetVal(ObtainedTS, int);
|
|
attributePublishValue(outputJo, IsReward, boolean, (SlotFrequencyVal == -1));
|
|
if (SlotFrequencyVal != -1)
|
|
attributePublishRename(outputJo, SlotFrequency, Rarity)
|
|
else
|
|
attributePublishValue(outputJo, Ghost, boolean, (ObtainedTSVal == -1));
|
|
if (ObtainedTSVal != -1)
|
|
attributePublishSetType(outputJo, ObtainedTS, int);
|
|
attributeLoad(outputJo, MustRefreshThumbnail, int);
|
|
attributePublish(outputJo, Property);
|
|
attributePublish(outputJo, CollectionId);
|
|
attributePublishOpt(outputJo, hasToDisplay);
|
|
attributePublishOpt(outputJo, AdToDisplay);
|
|
return true;
|
|
}
|
|
|
|
#ifndef USER_MODE
|
|
unsigned int ZccCard::getMemoryUsage() const
|
|
{
|
|
unsigned int size = 0;
|
|
// step1 : get size of each string in
|
|
for (std::vector<std::string>::const_iterator it = ZCCFiles.begin();
|
|
it != ZCCFiles.end(); ++ it)
|
|
{
|
|
// size of a string
|
|
size += sizeof(std::string::value_type) * it->capacity();
|
|
}
|
|
// TOASK
|
|
// when capacity > size, how to get the capacity of unknow string ? equal to sizeof(void*)?
|
|
// = 2 pointers for string (start/end) and 1 pointer for vector
|
|
size += (ZCCFiles.capacity() - ZCCFiles.size()) * sizeof(void*) * 3;
|
|
// other strings
|
|
size += (Name.capacity() + Hash.capacity()) * sizeof(std::string::value_type);
|
|
return size;
|
|
}
|
|
#endif
|
|
|
|
bool ZccCard::load(CardElemContainer *container, bool isNew, json_object *jo, json_object *& outputJo, u32 id, CardElement::Type *errorSource, bool publishThumbnailFilenames, const CardElement *parent)
|
|
{
|
|
*errorSource = getType();
|
|
_Parent = parent;
|
|
outputJo = json_object_new_object();
|
|
attributeLoadPublish(outputJo, Name, string);
|
|
attributeLoad(outputJo, Hash, string);
|
|
attributePublishSetType(outputJo, Full, boolean);
|
|
attributeLoad(outputJo, ZccVersion, int);
|
|
Id = id;
|
|
//json_object_object_add(outputJo, "Type", json_object_new_string(const_cast<char*>(getTypeStr())));
|
|
attributePublishValue(outputJo, Id, int, id);
|
|
return true;
|
|
}
|
|
|
|
bool ZccCard::fillClearZCC(char **files, int nbFiles)
|
|
{
|
|
if (!files || !nbFiles) return false;
|
|
|
|
for (int i=0; i<nbFiles; i++)
|
|
{
|
|
this->ZCCFiles.push_back(std::string(files[i]));
|
|
CC_free(files[i]);
|
|
}
|
|
CC_free(files);
|
|
return true;
|
|
}
|
|
|
|
std::string ZccCard::getFilename(int version)
|
|
{
|
|
if (version == -1)
|
|
version = ZccVersion;
|
|
|
|
extern char CurrentDir[];
|
|
char tmpFilename [1024];
|
|
if (version > 0)
|
|
_snprintf(tmpFilename, 2047, "%s/ZCC/z%u_%d.zcc", CurrentDir, Id, version);
|
|
else
|
|
_snprintf(tmpFilename, 2047, "%s/ZCC/%s.zcc", CurrentDir, Name.c_str());
|
|
return std::string(tmpFilename);
|
|
}
|
|
|
|
CardElement* CardElemContainer::addCard(CardElement::Type type, u32 id, bool& isNew)
|
|
{
|
|
if (type >= CardElement::Invalid)
|
|
return NULL;
|
|
CardMap::iterator cardIt = _Map.find(toCardId(type, id));
|
|
if (cardIt == _Map.end())
|
|
{
|
|
isNew = true;
|
|
return _Map.insert(std::make_pair(toCardId(type, id), newCardElement(type))).first->second;
|
|
}
|
|
else
|
|
{
|
|
isNew = false;
|
|
return cardIt->second;
|
|
}
|
|
}
|
|
|
|
CardElement* CardElemContainer::newCardElement(CardElement::Type type)
|
|
{
|
|
switch (type)
|
|
{
|
|
case CardElement::Theme: return new ThemeCard();
|
|
case CardElement::Role: return new RoleCard();
|
|
case CardElement::Asset: return new AssetCard();
|
|
case CardElement::Zcc: return new ZccCard();
|
|
default: return NULL;
|
|
}
|
|
}
|
|
|
|
bool CardElemContainer::loadAndPublishCards(json_object *jo, json_object *& outputJo, CardElement::Type *errorSource, bool publishThumbnailFilenames)
|
|
{
|
|
*errorSource = (CardElement::Type)0;
|
|
outputJo = json_object_new_array();
|
|
bool isNew;
|
|
|
|
// JSON simplifies an associative array (object) to an array if the keys are incidentally enumerated, so support both possibilities
|
|
if (json_object_get_type(jo) == json_type_object)
|
|
{
|
|
json_object_object_foreach(jo, key, propArrayJo)
|
|
{
|
|
u32 id = atoi(key);
|
|
CardElement *card = addCard((CardElement::Type)0, id, isNew); // does not create a new card for the same ids (a card object's pointer must remain valid at all times for the background queue)
|
|
json_object *outputChildJo = NULL;
|
|
if (!card->load(this, isNew, propArrayJo, outputChildJo, id, errorSource, publishThumbnailFilenames, NULL))
|
|
return false;
|
|
if (outputChildJo)
|
|
json_object_array_add(outputJo, outputChildJo);
|
|
}
|
|
}
|
|
else if (json_object_get_type(jo) == json_type_array)
|
|
{
|
|
int len = json_object_array_length(jo);
|
|
for (int i=0; i!=len; ++i)
|
|
{
|
|
CardElement *card = addCard((CardElement::Type)0, i, isNew); // does not create a new card for the same ids (a card object's pointer must remain valid at all times for the background queue)
|
|
json_object *outputChildJo = NULL;
|
|
if (!card->load(this, isNew, json_object_array_get_idx(jo, i), outputChildJo, i, errorSource, publishThumbnailFilenames, NULL))
|
|
return false;
|
|
if (outputChildJo)
|
|
json_object_array_add(outputJo, outputChildJo);
|
|
}
|
|
}
|
|
else
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
std::vector<u32> CardElemContainer::getChildrenOfCard(CardElement::Type parentType, u32 parentId)
|
|
{
|
|
// We have not stored the children of a card, so we have to browse all the cards
|
|
std::vector<u32> foundChildren;
|
|
for (CardMap::iterator it=_Map.begin(); it!=_Map.end(); ++it)
|
|
{
|
|
//if (it->second &&
|
|
// it->second->getParent())
|
|
// logStr("Parent (%s): %u. Child: %u", it->second->getParent()->getTypeStr(), it->second->getParent()->Id, it->second->Id);
|
|
if (it->second &&
|
|
it->second->getParent() &&
|
|
it->second->getParent()->Id == parentId &&
|
|
it->second->getParent()->getType() == parentType)
|
|
foundChildren.push_back(it->second->Id);
|
|
}
|
|
return foundChildren;
|
|
}
|
|
|
|
void CardElemContainer::clear()
|
|
{
|
|
for (CardMap::iterator it=_Map.begin(); it!=_Map.end(); ++it)
|
|
{
|
|
if (it->second)
|
|
delete it->second;
|
|
}
|
|
_Map.clear();
|
|
}
|
|
|
|
#ifndef USER_MODE
|
|
unsigned int CardElemContainer::getMemoryUsage() const
|
|
{
|
|
unsigned int size = 0;
|
|
for (CardMap::const_iterator it=_Map.begin(); it!=_Map.end(); ++it)
|
|
{
|
|
if (it->second)
|
|
size += it->second->getMemoryUsage() + sizeof(CardId);
|
|
}
|
|
return size;
|
|
}
|
|
#endif
|
|
|
|
CardElemContainer::~CardElemContainer()
|
|
{
|
|
clear();
|
|
}
|
|
|
|
#endif // ONLINE_MODE
|