Да как бы я особых иллюзий я не испытываю. Когда-то сам писал на Java что-то похожее, второй раз не тянет. Но неужели всё до сих пор настолько плохо, что проще тянуть в проект ORM и БД, пусть и урезанно-десктопные, чем сериализовать в JSON/XML?Jesus05 писал(а): ↑08 фев 2017, 07:57По поводу JSON у меня сложилось впечатление, что "не сишники" считаю, что в Сях JSON реализуеться как-то так:Максим Кич писал(а): ↑07 фев 2017, 09:10Ни разу не сишник, но думаю, что решения как минимум на каком-то уровне примерно схожие.
...или такКод: Выделить всё
json_encode(get_object_vars($class));
Только в сях все чуточку сложнее...Код: Выделить всё
public function toJSON(){ return json_encode($this); }
Сохранение в игре.
Модераторы: Sanja, Максим Кич
- Максим Кич
- Администратор
- Сообщения: 1642
- Зарегистрирован: 03 дек 2006, 20:17
- Откуда: Витебск, Беларусь
- Контактная информация:
Re: Сохранение в игре.
Dump the screen? [y/n]
Re: Сохранение в игре.
Ручная сериализация не так страшна.
Там сущностей в рогалике - карта, монстры, игрок, итемы, эффекты, спеллы, глобальные всякие параметры, типа уровня проклятия или погода.
С ООП - интерфейс сериалайзабл, перегружаем функции сейв/лоад. Да придеться ручками указать все сохраняемые параметры. Или готовый велосипед взять, который это и делает. Циклические ссылки. Эмм, а может без них? Что там инвентарь указывает на предмет, а тот обратно на инвентарь? Перевести ссылку в айдишник из коллекции. Реализуемо.
С процедурным программированием сложнее чуть чуть. Но по сути также реализуется. Сейв функция на каждый тип сущности, которой передавать контекст сущности. Ну и понятно типовую запись данных в жсон тоже обернуть в функции, если уж совсем все с нуля.
Там сущностей в рогалике - карта, монстры, игрок, итемы, эффекты, спеллы, глобальные всякие параметры, типа уровня проклятия или погода.
С ООП - интерфейс сериалайзабл, перегружаем функции сейв/лоад. Да придеться ручками указать все сохраняемые параметры. Или готовый велосипед взять, который это и делает. Циклические ссылки. Эмм, а может без них? Что там инвентарь указывает на предмет, а тот обратно на инвентарь? Перевести ссылку в айдишник из коллекции. Реализуемо.
С процедурным программированием сложнее чуть чуть. Но по сути также реализуется. Сейв функция на каждый тип сущности, которой передавать контекст сущности. Ну и понятно типовую запись данных в жсон тоже обернуть в функции, если уж совсем все с нуля.
- Jesus05
- Сообщения: 1840
- Зарегистрирован: 02 дек 2009, 07:50
- Откуда: Норильск, сейчас Санкт-петербург.
- Контактная информация:
Re: Сохранение в игре.
Ну вон 40 перегруженных методов моих чуть выше... что там:
Массивы из структур, сами структуры, item`ы, рецпепты, хранилища рецептов, "собиратель" событий. все мелочи, все собрано в одном месте (т.е. классы не знают даже о том что их сохраняют записывают, их задача в игре дела делать, а сохраняет и записывает один God-класс, который знает все обо всех и всем друг )
- Jesus05
- Сообщения: 1840
- Зарегистрирован: 02 дек 2009, 07:50
- Откуда: Норильск, сейчас Санкт-петербург.
- Контактная информация:
Re: Сохранение в игре.
Ну я счас поискал фреймворки для рефлексии, как-то не впечатлило...Максим Кич писал(а): ↑08 фев 2017, 08:10Но неужели всё до сих пор настолько плохо, что проще тянуть в проект ORM и БД, пусть и урезанно-десктопные, чем сериализовать в JSON/XML?
ну вот https://github.com/RAttab/reflect
"Yet another reflection system for C++."
по примерам кода создалось впечатление что что-бы им пользоваться на такой класс:
Код: Выделить всё
struct Foo
{
int bar(int a, int b) const { return a + b; }
int baz;
};
Код: Выделить всё
reflectType(Foo)
{
reflectPlumbing();
reflectField(baz);
reflectFn(bar);
}
тогда уж действительно лучше Boost взять и в каждом классе реализовать описание всех параметров, НО! тогда получиться ровно тоже самое что и я делал, только без God класса, мусор по сохранению будет разбросан по всем файлам.
- Jesus05
- Сообщения: 1840
- Зарегистрирован: 02 дек 2009, 07:50
- Откуда: Норильск, сейчас Санкт-петербург.
- Контактная информация:
Re: Сохранение в игре.
Теоритически можно взять Qt-шную систему свойств.
Но опять-же в каждом классе надо будет описывать что-да как, плюс гетеры сеттеры писать.
вместо
надо будет писать что-то типа:
Потом правда становятся доступны многие Qt-ные штучки... типа
или
Но все равно это все описывать вручную всегда
Но опять-же в каждом классе надо будет описывать что-да как, плюс гетеры сеттеры писать.
вместо
Код: Выделить всё
class MyClass
{
public:
MyClass(QObject *parent = 0);
~MyClass();
enum Priority { High, Low, VeryHigh, VeryLow };
void setPriority(Priority priority);
Priority priority() const;
private:
Priority m_priority;
};
Код: Выделить всё
class MyClass : public QObject
{
Q_OBJECT
Q_PROPERTY(Priority priority READ priority WRITE setPriority)
public:
MyClass(QObject *parent = 0);
~MyClass();
enum Priority { High, Low, VeryHigh, VeryLow };
Q_ENUM(Priority)
void setPriority(Priority priority);
Priority priority() const;
private:
Priority m_priority;
};
Код: Выделить всё
QObject *object = myinstance;
object->setProperty("priority", "VeryHigh");
Код: Выделить всё
QObject *object = myinstance;
const QMetaObject *metaobject = object->metaObject();
int count = metaobject->propertyCount();
for (int i=0; i<count; ++i) {
QMetaProperty metaproperty = metaobject->property(i);
const char *name = metaproperty.name();
QVariant value = object->property(name);
}
- Максим Кич
- Администратор
- Сообщения: 1642
- Зарегистрирован: 03 дек 2006, 20:17
- Откуда: Витебск, Беларусь
- Контактная информация:
Re: Сохранение в игре.
Причём, всё ещё интереснее. Мы в любом случае все игровые объекты когда-то создаём в первый раз. И нам в любом случае надо указывать, какие параметры мы получаем, грубо говоря из ГСЧ и тут уже не удастся как-то автомагически реализовать рандомное заполнение свойства объекта просто по факту того, что мы его добавили. Если мы добавили персонажу свойство «лохматистость», то мы как минимум один раз прописываем его где-то, где эта самая лохматистость выдаётся каждому новому персу в целочисленном диапазоне 0..100 с нормальным распределением. Если у нашего персонажа есть инвентарь — то он уже где-то создаётся и наполняется какими-то стартовыми предметами на основании левой пятки ГСЧ. И там уже есть логика, которая следит за всеми ссылками. И мы можем этими же средствами создавать объекты не из случайных данных, а из сериализованных. И у нас будет один, прозрачный и предсказуемый способ, которым что-то появляется в игре. Как только у нас генерация нового объекта и десериализация сохранённого начинают ходить разными путями — начинается боль.Oreyn писал(а): ↑08 фев 2017, 08:42Ручная сериализация не так страшна.
Там сущностей в рогалике - карта, монстры, игрок, итемы, эффекты, спеллы, глобальные всякие параметры, типа уровня проклятия или погода.
С ООП - интерфейс сериалайзабл, перегружаем функции сейв/лоад. Да придеться ручками указать все сохраняемые параметры. Или готовый велосипед взять, который это и делает.
Тут вопрос даже не в том, можно ли обойтись без циклических ссылок. Вопрос в том, зачем их хранить?
Dump the screen? [y/n]
- Jesus05
- Сообщения: 1840
- Зарегистрирован: 02 дек 2009, 07:50
- Откуда: Норильск, сейчас Санкт-петербург.
- Контактная информация:
Re: Сохранение в игре.
Пример1: бутылек с лечебным зельем, который можно отпить 4(N) раз. создается фабрикой всегда полный, изменяет кол-во сколько его еще-раз можно выпить только по методу Use(). Это его внутреннее состояние. Но нам надо вытащить его на "свет божий" что-бы сериализовать и десериализовать.Максим Кич писал(а): ↑08 фев 2017, 09:35Причём, всё ещё интереснее. Мы в любом случае все игровые объекты когда-то создаём в первый раз. И нам в любом случае надо указывать, какие параметры мы получаем ...
И у нас будет один, прозрачный и предсказуемый способ, которым что-то появляется в игре. Как только у нас генерация нового объекта и десериализация сохранённого начинают ходить разными путями — начинается боль.
Пример2: монстр хранит свои текущие ХП и никому их не говорит. На запросу сколько у него ХП отвечает "много, средне, мало, почти метрв". Опять для сериализации нам надо вытащить все внутренности такого монстра наружу что-бы можно было сохранять\загружать его.
Я думаю можно придумать более сложные "части" игры которые могут хранить временное состояние внутри себя, и не предполагать создание себя в половинном состоянии. Отсюда начинают расти "дополнительные" конструкторы строго для десеарилизации, методы save\load у каждого класса, или как у меня God классы и отношения дружбы между таким год классом и всеми объектами в игре.
- Apromix
- Мастер
- Сообщения: 1236
- Зарегистрирован: 04 июл 2011, 10:44
- Откуда: Украина, Черновцы
- Контактная информация:
Re: Сохранение в игре.
Велисипед не так страшен в данном вопросе, как его малюют При решении данной проблемы даже с сериализацией приходится изобретать некий велосипед?
Re: Сохранение в игре.
А велосипед уже изобретен? Хотелось бы увидеть его. Пока сошлись на мнении, что каждый изобретает велосипед для себя.
Re: Сохранение в игре.
Не, не, не.Jesus05 писал(а): ↑08 фев 2017, 09:48Пример1: бутылек с лечебным зельем, который можно отпить 4(N) раз. создается фабрикой всегда полный, изменяет кол-во сколько его еще-раз можно выпить только по методу Use(). Это его внутреннее состояние. Но нам надо вытащить его на "свет божий" что-бы сериализовать и десериализовать.Максим Кич писал(а): ↑08 фев 2017, 09:35Причём, всё ещё интереснее. Мы в любом случае все игровые объекты когда-то создаём в первый раз. И нам в любом случае надо указывать, какие параметры мы получаем ...
И у нас будет один, прозрачный и предсказуемый способ, которым что-то появляется в игре. Как только у нас генерация нового объекта и десериализация сохранённого начинают ходить разными путями — начинается боль.
Пример2: монстр хранит свои текущие ХП и никому их не говорит. На запросу сколько у него ХП отвечает "много, средне, мало, почти метрв". Опять для сериализации нам надо вытащить все внутренности такого монстра наружу что-бы можно было сохранять\загружать его.
Я думаю можно придумать более сложные "части" игры которые могут хранить временное состояние внутри себя, и не предполагать создание себя в половинном состоянии. Отсюда начинают расти "дополнительные" конструкторы строго для десеарилизации, методы save\load у каждого класса, или как у меня God классы и отношения дружбы между таким год классом и всеми объектами в игре.
Годобжект который опрашивает кого-то на предмет инкапсулированых данных - нихт гут.
Интерфейс с перегружаемыми методами сериалайз/десериалайз, в который передается контекст слота сохранения.
Добавляешь в класс итема новое свойство - тут же лезешь в его перегруженные методы интерфейса и прописываешь что они тоже сохраняются.
Своим велосипедом, или бустом / другой ет эназер сейвилкой.
Вот это кстати кусок с таким человеческим фактором. Через полгода разработки - откуда этот чертов баг? Ааа, я забыл указать что это свойство сериализутся. А как вообще я это делал?
При сохранении дернул корень твоего графа игровых сущностей. Тот всем ниже тоже вызвал сериалайз и передал контекст куда сохранять.
Карта сохранилась - дернула предметы и существа, та в свою очередь сохранили свои инвентари, эффекты и т.д.
Загрузка в таком-же порядке.
>> Как только у нас генерация нового объекта и десериализация сохранённого начинают ходить разными путями — начинается боль.
Именно. При загрузке бутылки с зельем из инвентаря, она создается через фабрику как новый обьект такого типа, и тут же у новосозданного объекта вызывается ее перегруженный метод лоад с переданным контекстом загрузки и она восстанавливает из сейва состояние своих внутренних параметров.
UPD:
Ага, понял, кстати само первое создание объектов можно сделать этим же образом - фабрика создала, и на вход лоада подала стартовую конфигурацию объекта. Таким образом точка входа (механизм создания) один. Да еще и стартовые конфиги объектов вынес в json вместо хардкода.
- Jesus05
- Сообщения: 1840
- Зарегистрирован: 02 дек 2009, 07:50
- Откуда: Норильск, сейчас Санкт-петербург.
- Контактная информация:
Re: Сохранение в игре.
Наверное у нас немного разный подход идеалогически.
Я считаю - класс не должен заниматься своей сериализацией. У него другая задача.
Я не люблю многозадачные классы, если ты предметы ты должен быть предметом, а не сериализуемым предметом, или предметом-монстром-клеткой_карты-с сериализаций одновременно.
Я допускаю в такой ситуации, что-бы что-то вне класса умело его восстанавливать\сохранять.
Пусть даже это будет тупо внешне перегруженный оператор << или как еще более редко используемый <<= для возможности "ложить" класс в поток сохранялки (ну или просто в класс сохранялки) . Пусть даже код этого перегруженного метода будет в cpp или h\hpp файле с классом, но я не считаю, что он должен быть частью класса.
Адд: И да я согласен, что плохо когда кто-то кроме самого класса знает его внутренности, но здесь считаю это допустимым злом, потому что это снижает сложность класса, убирает из зоны видимости одну из навязанных ответственностей.
Адд2: Хотелось бы какой-нить фреймворк\прекомпилер который.
требовал минимального вмешательства в код такого класс:
требовал добавить 1 строчку:
После чего давал возможность делать что-то типа:
Но пока я ничего такого не встретил
Я считаю - класс не должен заниматься своей сериализацией. У него другая задача.
Я не люблю многозадачные классы, если ты предметы ты должен быть предметом, а не сериализуемым предметом, или предметом-монстром-клеткой_карты-с сериализаций одновременно.
Я допускаю в такой ситуации, что-бы что-то вне класса умело его восстанавливать\сохранять.
Пусть даже это будет тупо внешне перегруженный оператор << или как еще более редко используемый <<= для возможности "ложить" класс в поток сохранялки (ну или просто в класс сохранялки) . Пусть даже код этого перегруженного метода будет в cpp или h\hpp файле с классом, но я не считаю, что он должен быть частью класса.
Адд: И да я согласен, что плохо когда кто-то кроме самого класса знает его внутренности, но здесь считаю это допустимым злом, потому что это снижает сложность класса, убирает из зоны видимости одну из навязанных ответственностей.
Адд2: Хотелось бы какой-нить фреймворк\прекомпилер который.
требовал минимального вмешательства в код такого класс:
Код: Выделить всё
class SomeCLass
{
private:
int t;
protected:
int y;
public:
...
}
Код: Выделить всё
class SomeCLass
{
SERIALIZABLE();
private:
int t;
protected:
int y;
public:
...
}
Код: Выделить всё
SomeClass t;
Serializer << t;
Serializer.saveToFile(....);
Serializer.loadFromFile(...);
Serializer >> t;
- Jesus05
- Сообщения: 1840
- Зарегистрирован: 02 дек 2009, 07:50
- Откуда: Норильск, сейчас Санкт-петербург.
- Контактная информация:
Re: Сохранение в игре.
Кстати, вот, а почему создание класса никто не против что-бы было вынесено в отдельный класс, а сохранение обязательно должны быть строго внутри класса, конечно фабрика обращается к открытым членам класса, но думаю допустимо создать "дополнительный" интерфейс для класса с которым могла-бы общаться только фабрика.Oreyn писал(а): ↑08 фев 2017, 15:36Ага, понял, кстати само первое создание объектов можно сделать этим же образом - фабрика создала, и на вход лоада подала стартовую конфигурацию объекта. Таким образом точка входа (механизм создания) один. Да еще и стартовые конфиги объектов вынес в json вместо хардкода.
Код: Выделить всё
class IItem //Interface
{
virtual use() = 0;
virtual throw() = 0;
}
class IItemInnerInfo //Interface
{
virtual getCountOfUse() = 0;
virtual setCountOfUse() = 0;
virtual getThrowDamage() = 0;
virtual setThrowDamage() = 0;
}
class Item : public IItem, public IItemInnerInfo
{
use();
throw();
getCountOfUse();
setCountOfUse();
getThrowDamage();
setThrowDamage();
}
Код: Выделить всё
class IItem //Interface
{
virtual use() = 0;
virtual throw() = 0;
}
class ItemInnerInfo
{
protected:
int itemParam1;
int itemParam2;
public:
getCountOfUse();
setCountOfUse();
getThrowDamage();
setThrowDamage();
}
class Item : public IItem, public ItemInnerInfo
{
use();
throw();
}
Код: Выделить всё
class IItem //Interface
{
virtual use() = 0;
virtual throw() = 0;
}
struct ItemInnerInfo //Без поведения, только данные, хотя если очень хочется здесь можно сделать сериализацию, тогда она будет "вне зоны" видимости основного класса.
{
public:
int itemParam1;
int itemParam2;
}
class Item : public IItem, public ItemInnerInfo //Если не наследоваться от данных, то получим необходимость добавлять методы "отдай данные, возми данные"
{
use();
throw();
}
- Максим Кич
- Администратор
- Сообщения: 1642
- Зарегистрирован: 03 дек 2006, 20:17
- Откуда: Витебск, Беларусь
- Контактная информация:
Re: Сохранение в игре.
Ну как бы, мне кажется, что это выплёскивание младенца вместе с водой. Предмет будет сериализуемым, потому что его пра-прадедушка был сериализуемым базовым игровым объектом. В целом, любое поведение, которое укладывается в манипулирование свойствами объекта, может быть в него инкапсулировано.Jesus05 писал(а): ↑09 фев 2017, 07:06Наверное у нас немного разный подход идеологически.
Я считаю - класс не должен заниматься своей сериализацией. У него другая задача.
Я не люблю многозадачные классы, если ты предметы ты должен быть предметом, а не сериализуемым предметом, или предметом-монстром-клеткой_карты-с сериализаций одновременно.
Потому что конструктор должен быть откуда-то вызван. Сохранение инициировать тоже будет отдельный класс. И в файл писать/читать будет отдельный класс. Объект должен уметь себя свернуть в строку, и «пустой» экземпляр заполнить из строки.Jesus05 писал(а): ↑09 фев 2017, 09:40Кстати, вот, а почему создание класса никто не против что-бы было вынесено в отдельный класс, а сохранение обязательно должны быть строго внутри класса, конечно фабрика обращается к открытым членам класса, но думаю допустимо создать "дополнительный" интерфейс для класса с которым могла-бы общаться только фабрика.
Во-первых, никто не мешает пользоваться ими внутри методов serialize/deserialize. Во-вторых, на одну сериализацию/десериализацию будут приходиться сотни/тысячи обращений во время обычного игрового процесса (если на каждый ход autosave не делать, или если у нас обработка логики не дублируется где-то на удалённом сервере)
Именно! Но в чём я согласен с Jesus05 — разные языки имеют разный оверхед при работе с тем же JSON/XML/etc. Это тоже надо учитывать. Но в целом, у нас видение архитектуры совпадает.Oreyn писал(а): ↑08 фев 2017, 15:36Ага, понял, кстати само первое создание объектов можно сделать этим же образом - фабрика создала, и на вход лоада подала стартовую конфигурацию объекта. Таким образом точка входа (механизм создания) один. Да еще и стартовые конфиги объектов вынес в json вместо хардкода.
Dump the screen? [y/n]
- Cfyz
- Сообщения: 776
- Зарегистрирован: 30 ноя 2006, 10:03
- Откуда: Санкт-Петербург
- Контактная информация:
Re: Сохранение в игре.
Ага, то естьJesus05 писал(а):Я считаю - класс не должен заниматься своей сериализацией. У него другая задача. <...> Пусть даже это будет тупо внешне перегруженный оператор << <...> Пусть даже код этого перегруженного метода будет в cpp или h\hpp файле с классом, но я не считаю, что он должен быть частью класса.
Код: Выделить всё
struct Foo
{
Serializer& operator<<(Serializer& s) { ... }
};
Код: Выделить всё
struct Foo
{
friend Serializer& operator<<(Serializer& s, const Foo& v);
};
Serializer& operator<<(Serializer& s, const Foo& v) { ... }
Тут присутствует некоторая игра терминами. Класс как шаблон сущности представляет собой довольно общий концепт из набора данных и методов работы с ними. В этом контексте неважно где именно располагается реализация, если это метод для работы с данными сущности -- это часть класса.
С другой стороны часто (и, очевидно, в этом случае) под классом подразумевается строго интерфейс. Тут да, какой-нибудь void Seriazlizer::Write(const Foo&); уже интерфейсом Foo строго говоря не является. Но при этом реализация данного метода оказывается настолько сильно приколочена к Foo, что уже несправедливо слишком строго их разделять. При изменении Foo надо менять Write(const Foo&), и наоборот -- при некоторых изменениях сериализации (например способ сохранения ссылок) может потребоваться изменить Foo (добавить необходимую информацию). В этом свете самое рациональное место для размещения метода сериализации -- "внутри" Foo. Потому что это и группирует логически тесно связанный код в одном месте, и как правило упрощает работу с нутром Foo (private-protected, туда-сюда).
Пытается раскуклиться
- Jesus05
- Сообщения: 1840
- Зарегистрирован: 02 дек 2009, 07:50
- Откуда: Норильск, сейчас Санкт-петербург.
- Контактная информация:
Re: Сохранение в игре.
Когда мне нужно вывести какие-то данные в поток отданные мне сторонней библиотекой (структуру, или класс насколько я могу добраться до важных для меня частей) я пишу в своей части кода.Cfyz писал(а): ↑09 фев 2017, 12:15это уже не часть класса?Код: Выделить всё
struct Foo { friend Serializer& operator<<(Serializer& s, const Foo& v); }; Serializer& operator<<(Serializer& s, const Foo& v) { ... }
Тут присутствует некоторая игра терминами. Класс как шаблон сущности представляет собой довольно общий концепт из набора данных и методов работы с ними. В этом контексте неважно где именно располагается реализация, если это метод для работы с данными сущности -- это часть класса.
...
Код: Выделить всё
std::ostream& operator<<(std::ostream& s, const Foo& v) { ... }
но согласен, что это "крестопроблемы" и это касается скорее сей чем в общем программирования.
Я все больше склоняюсь к мысли что данные класса и поведение класса это 2 разные сущности и жить они должны отдельно.
Кто сейчас на конференции
Сейчас этот форум просматривают: нет зарегистрированных пользователей и 39 гостей