Создание прототипа Capy на Sui
--
Sui Capys, новый прототип Mysten Labs, служит предварительным просмотром для разработчиков и демонстрирует ключевые возможности экосистемы Sui. В этой децентрализованной игре игроки покупают, обменивают, разводят и украшают капибар, милых южноамериканских полуводных грызунов. Разработчики Sui могут адаптировать многие принципы и примеры кода, изложенные здесь, для своих собственных проектов.
Как программируемые объекты в блокчейне Sui, Capys демонстрируют такие принципы, как владение активами, возможность передачи и динамические поля объектов. Они появляются в кошельке игрока вместе с аксессуарами, включая шапки, велосипеды и шарфы. Чтобы просмотреть Capys сейчас, зайдите на Capy.art.
Разработка Sui Capys с использованием Sui Move требовала определения основных модулей, создания типов и, что наиболее важно, создания реестра для записи и проверки Capys. Одной из уникальных особенностей прототипа является возможность скрещивания двух существующих капи, создавая совершенно новый на основе характеристик размножающейся пары.
Повторим еще раз: это предварительная версия для разработчиков, которая демонстрирует уникальные аспекты Sui и предназначена для разработчиков, на которые можно ссылаться при создании собственных проектов. Capys не продаются.
Отказ от ответственности
- Это децентрализованное приложение в настоящее время считается ранней альфа-версией, поэтому оно может быть немного грубоватым.
- Децентрализованное приложение работает на Devnet Суи, который не обладает зрелостью и стабильностью основной сети.
- Существует известная ошибка Sui Wallet в истории транзакций при использовании Capys, и мы выпустим исправление Wallet на следующей неделе, чтобы решить эту проблему.
- Мы планируем обновить Capy.art до стандарта адаптера кошелька в ближайшем будущем. Sui Capys — это демонстрация, созданная специально для того, чтобы вдохновить наше сообщество разработчиков.
- Это не аирдроп. Пожалуйста, используйте кран ответственно — не спамьте наш сборщик Devnet.
Capy архитектура
Приложение Capy (Github) состоит из трех модулей: capy, capy_items и capy_market. Эти модули определяют Capys, аксессуары и торговый механизм.
Мы начали процесс разработки с определения конкретных принципов, чтобы сделать прототип приятным и эффективным. Следующие принципы помогли определить архитектуру и варианты реализации:
- Capys должны свободно передаваться и использоваться в любом приложении в сети.
- Типы должны содержать минимальный объем данных для поддержания производительности.
- События можно использовать для создания статических данных, которые будут извлечены индексатором.
- Прототип должен быть расширяемым, чтобы позже можно было добавить новые свойства.
Разработчики, желающие создавать игры и приложения на Sui, должны начать с определения таких основных концепций, которые будут направлять процесс разработки.
Capy Core
Модуль capy определяет основные функции Sui Capys: он определяет тип Capy, а также предоставляет издателю CapyManagerCap, открывая функции администратора для носителя. Он определяет CapyRegistry, централизованное состояние прототипа и способы его развития.
Тип: Сapy
Capy, основной тип приложения, представляет собой принадлежащий объект с определенным набором атрибутов: 32 гена и дополнительная служебная информация, необходимая для функций прототипа. У Капи есть две способности: ключ и магазин. Первый делает его доступным активом, а второй допускает бесплатную передачу и упаковку.
- Свойство gen отмечает поколение Capy.
- Первые Capy имеют поколение 0; новые породы имеют поколение своих родителей плюс один, то есть поколение 1, поколение 2 и т. д. Свойство src позволяет отображать изображение в проводнике.
- Сами Capys не хранят свое полное изображение, так как оно динамическое и может быть изменено при добавлении новых элементов.
- Свойство гены хранит последовательность генов, 32-байтовый вектор, который используется для расчета признаков и выбора генов для новорожденного во время размножения. item_count — это служебное свойство, отслеживающее количество объектов, прикрепленных к каждому Capy.
- Свойство атрибутов хранит удобочитаемые атрибуты, созданные во время размножения. Например, { «имя»: «шаблон», «значение»: «панда» }.
Этот набор полей является минимальным требованием для функций Sui Capys, включая размножение или добавление/удаление элементов.
/// The Capy itself. Every Capy has its unique set of genes,
/// as well as generation and utility information. Ownable, tradeable.
struct Capy has key, store {
id: UID,
gen: u64,
url: Url,
genes: Genes,
item_count: u8,
attributes: vector<Attribute>,
}
Тип: Capy Registry
CapyRegistry, общий объект, необходимый для разведения, хранит общее количество когда-либо родившихся Capys и содержит псевдослучайное семя, описанное ниже в разделе «Наука о генах», используемое для отбора генов во время разведения. Он содержит все определения атрибутов, присвоенные новорожденным на стадии размножения.
В прототип можно добавить новые атрибуты, как описано ниже в разделе функций администратора.
/// Every capybara is registered here. CapyRegistry acts as a source of randomness
/// as well as the storage for the main information about the game state.
struct CapyRegistry has key {
id: UID,
capy_born: u64,
capy_hash: vector<u8>,
genes: vector<GeneDefinition>
}
Тип: CapyManagerCap
CapyManagerCap — это возможность, отправляемая издателю модуля (отправителю транзакции публикации) при публикации модуля. Он разрешает действия администратора во всех модулях, включая capy_items и capy_market.
Инициализатор Capys
Это отдельное (не универсальное) приложение, поэтому их основная логика может быть запущена в инициализаторе модуля. Функция init делает две вещи:
- Создает CapyManagerCap и отправляет его издателю модуля.
- Создает и публикует CapyRegistry.
Функции администратора
Чтобы приложение стало играбельным и имело какой-то смысл, админ должен выполнить ряд действий:
- Функция add_gene регистрирует новое GeneDefinition в CapyRegistry. При разведении все существующие в реестре атрибуты присваиваются новому Capy. Если к прототипу было добавлено новое GeneDefinition (Атрибут), Capys, родившиеся до этого добавления, не получат его, но получат его дети. Каждое определение гена имеет имя и набор селекторов, которые используются для выбора значения каждого атрибута.
- Пакетная функция позволяет создавать партии Capys с предопределенными генами. Он используется для инициализации и на более поздних этапах для заполнения рынка дополнительными Capys для новых пользователей.
Разведение
Основной частью логики, создающей непредсказуемость и помогающей эволюции прототипа, является функция capy::breed. Эту функцию может выполнять любой игрок с двумя Капи. Логика этой функции следующая:
- На основе CapyRegistry.capy_hash выберите родительские гены для нового Capy.
- Получите список текущих GeneDefinitions из CapyRegistry и установите атрибуты.
- Создайте событие с новыми данными Capy. Верните новый Capy (используйте Breed_and_keep для отправки отправителю).
public fun breed(
reg: &mut CapyRegistry, c1: &mut Capy, c2: &mut Capy, ctx: &mut TxContext
): Capy {
let id = object::new(ctx);
// Update capy hash in the registry
vec::append(&mut reg.capy_hash, object::uid_to_bytes(&id));
reg.capy_hash = hash(reg.capy_hash);
// compute genes
let genes = compute_genes(®.capy_hash, &c1.genes, &c2.genes, GENES);
let gen = math::max(c1.gen, c2.gen) + 1;
let attributes = get_attributes(®.genes, &genes);
let sender = tx_context::sender(ctx);
emit(CapyBorn { /* ... */ });
// Send newborn to parents.
Capy {
url: img_url(&id),
id,
gen,
genes,
attributes,
item_count: 0,
}
}
Генная наука
Прежде чем мы перейдем к самой интересной части приложения, мы должны отметить, что это решение не обеспечивает абсолютной непредсказуемости, и поэтому не должно использоваться для приложений с высокими ставками. Но он создает некоторую степень случайности, превращая пользовательский ввод в псевдослучайный модификатор.
Действительно забавная и уникальная особенность этого прототипа — возможность скрестить двух существующих Capys, чтобы получить третьего. Новый Capy наследует характеристики своих родителей.
Функция породы берет двух родительских капи и вычисляет гены новорожденного. Чтобы это действие было честным и случайным, нам нужен алгоритм выбора и начальное число. CapyRegistry предоставляет начальное значение (хранится как capy_hash) и обновляется после каждого размножения. Алгоритм следующий:
- Используйте хеш-функцию (sha3_256) три раза с солью, чтобы сгенерировать три вектора по 32 байта (отмеченных как A, B и C), полученных из capy_hash. Используйте первый вектор (A) для выбора родительского гена. Если значение N-го байта больше 126, выберите ген первого родителя. В противном случае выберите ген второго родителя. Как показано на диаграмме выше, первый ген будет P2, второй P1, третий P2 и снова четвертый P1 (до N=32). Второй вектор (В) определяет вероятность мутации.
- Если значение в позиции N больше 250, используйте ту же позицию в третьем векторе ©, чтобы выбрать значение для мутации. В этом примере третий ген будет мутировать и его значение будет равно 42.
/// Computes genes for the newborn based on the 'random' seed r0, and parents' genes.
///
/// The `max` parameter affects how many genes should be changed.
/// For example, if the number of genes is 32 but only 4 Attributes defined in the
/// registry, the `max` should be set to 4. Remainder genes should not mutate.
fun compute_genes(r0: &vector<u8>, g1: &Genes, g2: &Genes, max: u64): Genes {
let i = 0;
let s1 = &g1.sequence;
let s2 = &g2.sequence;
let s3 = vec::empty();
let r1 = derive(r0, 1); // for parent gene selection
let r2 = derive(r0, 2); // chance of random mutation
let r3 = derive(r0, 3); // value selector for random mutation
while (i < max) {
let rng = *vec::borrow(&r1, i);
let gene = if (lor(rng, 127)) {
*vec::borrow(s1, i)
} else {
*vec::borrow(s2, i)
};
// There's a tiny chance that a mutation will happen.
if (lor(*vec::borrow(&r2, i), MUTATION_CHANCE)) {
gene = *vec::borrow(&r3, i);
};
vec::push_back(&mut s3, gene);
i = i + 1;
};
Genes { sequence: s3 }
}
Capy предметы
Этот чрезвычайно простой модуль определяет носимые предметы, которые можно добавить к каждому Capy, и то, как мы реализуем внешний интерфейс. Элементы могут быть добавлены только администратором Capy, так как для этого требуется авторизация с помощью CapyManagerCap.
/// Wearable item. Has special display in capy.art application
struct CapyItem has key, store {
id: UID,
url: Url,
type: String,
name: String,
}
Управление элементами
При добавлении и удалении аксессуаров в Capys используются поля динамических объектов, более эффективная и удобная замена родительско-дочерним объектам в Sui. Динамические поля допускают произвольные имена и могут добавляться и удаляться на лету.
Следующий код добавляет элемент в Capy:
entry fun add_item<T: key + store>(capy: &mut Capy, item: T) {
emit(ItemAdded<T> {
capy_id: object::id(capy),
item_id: object::id(&item)
});
dof::add(&mut capy.id, object::id(&item), item);
}
Capy Маркет
Чтобы приобретать и продавать Capys и CapyItems, мы создали Capy Market. Этот модуль использует поля динамических объектов и блокирует предметы, чтобы их можно было приобрести после оплаты их цены. В этой архитектуре торговой площадки один объект торговой площадки существует для каждого типа элементов (CapyMarket<Capy> продает Capys, а другой объект, CapyMarket<CapyItem>, продает аксессуары Capy), списки прикрепляются к торговой площадке в виде полей динамических объектов, а перечисленные объекты прикреплены к спискам.
- → Листинг → T CapyMarket<T> + → Листинг → T + → Листинг → T
Торговая площадка и функция списка
/// A generic marketplace for anything. T marks the type for Listings.
struct CapyMarket<phantom T: key + store> has key { id: UID }
/// A listing for the marketplace. Contains the price and owner of the Listing.
struct Listing<phantom T: key + store> has key, store {
id: UID,
price: u64,
owner: address
}Каждый экземпляр Marketplace обслуживает только один тип. В этом приложении один экземпляр Marketplace существует для типа Capy, а другой — для типа CapyItem.
Функция списка использует динамические поля. Он делает перечисленный элемент полем листинга, а затем делает листинг полем CapyMarket.
/// List a new item on the `CapyMarket`.
public entry fun list<T: key + store>(
market: &mut CapyMarket<T>,
item: T,
price: u64,
ctx: &mut TxContext
) {
let id = object::new(ctx);
let item_id = object::id(&item);
let owner = tx_context::sender(ctx);
emit(ItemListed<T> { /* ... */ });
// First attach Item to the Listing with a boolean `true` value;
// Then attach listing to the marketplace through `item.id`;
dynamic_object_field::add(&mut id, true, item);
dynamic_object_field::add(&mut market.id, item_id, Listing<T> {
id, price, owner
});
}
Capy Вдохновение
Мы создали прототип Capy, чтобы продемонстрировать некоторые ключевые функции Sui и вдохновить разработчиков на их собственные проекты. Capys использует объектно-ориентированный характер Sui, позволяя игрокам использовать переносные аксессуары, которые игроки могут продавать и покупать, а также создавать новые Capys на основе родительских атрибутов. Мы разработали прототип с возможностью бесконечного расширения, чтобы он порадовал как пользователей, так и разработчиков.
Мы надеемся, что приведенные здесь примеры и код окажутся полезными для разработчиков Sui. Несколько реализаций, которые должны выделиться, включают Capy Market, Accessories и Breeding. Capy Market служит моделью для любого механизма торговли или хранения. Аксессуары показывают хорошее использование динамических полей. Разведение предлагает уникальные средства автоматического создания новых объектов с бесконечными приложениями.
Удачи и удачного развития!
Created by wonder.jpiggy#7100