JD2022-TU1/main/extern/Camcam/TestApp/card.cpp

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