Skip to content

Commit

Permalink
♻️ Refactor and add pin feature
Browse files Browse the repository at this point in the history
  • Loading branch information
bal7hazar committed Nov 23, 2024
1 parent a64c1d8 commit 9596534
Show file tree
Hide file tree
Showing 20 changed files with 356 additions and 141 deletions.
2 changes: 1 addition & 1 deletion Scarb.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
version = 1

[[package]]
name = "arcade_trophy"
name = "achievement"
version = "0.0.0"
dependencies = [
"dojo",
Expand Down
2 changes: 1 addition & 1 deletion Scarb.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ members = [
"packages/provider",
"packages/registry",
"packages/society",
"packages/trophy",
"packages/achievement",
]
description = "Dojo achievement library"
homepage = "https://github.com/cartridge-gg/arcade/"
Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[package]
name = "arcade_trophy"
name = "achievement"
version.workspace = true

[dependencies]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,14 @@ mod AchievableComponent {

use core::debug::PrintTrait;

// Starknet imports

use starknet::info::{get_caller_address, get_block_timestamp, get_contract_address};

// Dojo imports

use dojo::world::WorldStorage;

// Internal imports

use arcade_trophy::types::task::Task;
use arcade_trophy::store::{Store, StoreTrait};
use achievement::types::task::Task;
use achievement::store::{Store, StoreTrait};

// Errors

Expand Down Expand Up @@ -111,7 +107,7 @@ mod AchievableComponent {
let store: Store = StoreTrait::new(world);

// [Event] Emit achievement completion
let time: u64 = get_block_timestamp();
let time: u64 = starknet::get_block_timestamp();
store.progress(player_id, task_id, count, time);
}
}
Expand Down
58 changes: 58 additions & 0 deletions packages/achievement/src/components/pinnable.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
#[starknet::component]
mod PinnableComponent {
// Core imports

use core::debug::PrintTrait;

// Dojo imports

use dojo::world::WorldStorage;

// Internal imports

use achievement::store::{Store, StoreTrait};

// Errors

mod errors {}

// Storage

#[storage]
struct Storage {}

// Events

#[event]
#[derive(Drop, starknet::Event)]
enum Event {}

#[generate_trait]
impl InternalImpl<
TContractState, +HasComponent<TContractState>
> of InternalTrait<TContractState> {
fn pin(
self: @ComponentState<TContractState>,
world: WorldStorage,
player_id: felt252,
achievement_id: felt252,
) {
// [Setup] Store
let store: Store = StoreTrait::new(world);

// [Event] Emit achievement creation
let time = starknet::get_block_timestamp();
store.pin(player_id, achievement_id, time);
}

fn unpin(
self: @ComponentState<TContractState>,
world: WorldStorage,
player_id: felt252,
achievement_id: felt252,
) {
let store: Store = StoreTrait::new(world);
store.unpin(player_id, achievement_id);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,22 +1,27 @@
// Internal imports

use arcade_trophy::events::index::TrophyCreation;
use arcade_trophy::types::task::{Task, TaskTrait};
use achievement::events::index::TrophyCreation;
use achievement::types::task::{Task, TaskTrait};

// Constants

const MAX_POINTS: u16 = 100;

// Errors

pub mod errors {
pub const TROPHY_INVALID_ID: felt252 = 'Trophy: invalid id';
pub const TROPHY_INVALID_TITLE: felt252 = 'Trophy: invalid title';
pub const TROPHY_INVALID_DESCRIPTION: felt252 = 'Trophy: invalid desc.';
pub const TROPHY_INVALID_TASKS: felt252 = 'Trophy: invalid tasks.';
pub const TROPHY_INVALID_DURATION: felt252 = 'Trophy: invalid duration.';
pub const CREATION_INVALID_ID: felt252 = 'Creation: invalid id';
pub const CREATION_INVALID_TITLE: felt252 = 'Creation: invalid title';
pub const CREATION_INVALID_DESCRIPTION: felt252 = 'Creation: invalid desc.';
pub const CREATION_INVALID_TASKS: felt252 = 'Creation: invalid tasks';
pub const CREATION_INVALID_DURATION: felt252 = 'Creation: invalid duration';
pub const CREATION_INVALID_POINTS: felt252 = 'Creation: too much points';
}

// Implementations

#[generate_trait]
impl TrophyImpl of TrophyTrait {
impl CreationImpl of CreationTrait {
#[inline]
fn new(
id: felt252,
Expand All @@ -34,10 +39,11 @@ impl TrophyImpl of TrophyTrait {
) -> TrophyCreation {
// [Check] Inputs
// [Info] We don't check points here, leave free the game to decide
TrophyAssert::assert_valid_id(id);
TrophyAssert::assert_valid_title(title);
TrophyAssert::assert_valid_description(@description);
TrophyAssert::assert_valid_duration(start, end);
CreationAssert::assert_valid_id(id);
CreationAssert::assert_valid_title(title);
CreationAssert::assert_valid_description(@description);
CreationAssert::assert_valid_duration(start, end);
CreationAssert::assert_valid_points(points);
// [Return] TrophyCreation
TrophyCreation {
id, hidden, index, points, start, end, group, icon, title, description, tasks, data
Expand All @@ -46,43 +52,48 @@ impl TrophyImpl of TrophyTrait {
}

#[generate_trait]
impl TrophyAssert of AssertTrait {
impl CreationAssert of AssertTrait {
#[inline]
fn assert_valid_id(id: felt252) {
assert(id != 0, errors::TROPHY_INVALID_ID);
assert(id != 0, errors::CREATION_INVALID_ID);
}

#[inline]
fn assert_valid_title(title: felt252) {
assert(title != 0, errors::TROPHY_INVALID_TITLE);
assert(title != 0, errors::CREATION_INVALID_TITLE);
}

#[inline]
fn assert_valid_description(description: @ByteArray) {
assert(description.len() > 0, errors::TROPHY_INVALID_DESCRIPTION);
assert(description.len() > 0, errors::CREATION_INVALID_DESCRIPTION);
}

#[inline]
fn assert_valid_tasks(tasks: Span<Task>) {
assert(tasks.len() > 0, errors::TROPHY_INVALID_TASKS);
assert(tasks.len() > 0, errors::CREATION_INVALID_TASKS);
}

#[inline]
fn assert_valid_duration(start: u64, end: u64) {
assert(end >= start, errors::TROPHY_INVALID_DURATION);
assert(end >= start, errors::CREATION_INVALID_DURATION);
}

#[inline]
fn assert_valid_points(points: u16) {
assert(points <= MAX_POINTS, errors::CREATION_INVALID_POINTS);
}
}

#[cfg(test)]
mod tests {
// Local imports

use super::TrophyTrait;
use super::{Task, TaskTrait};
use super::CreationTrait;
use super::{Task, TaskTrait, MAX_POINTS};

// Constants

const ID: felt252 = 'TROPHY';
const ID: felt252 = 'CREATION';
const INDEX: u8 = 0;
const GROUP: felt252 = 'GROUP';
const HIDDEN: bool = false;
Expand All @@ -97,7 +108,7 @@ mod tests {
#[test]
fn test_achievement_creation_new() {
let tasks: Array<Task> = array![TaskTrait::new(TASK_ID, TOTAL, "TASK DESCRIPTION"),];
let achievement = TrophyTrait::new(
let achievement = CreationTrait::new(
ID,
HIDDEN,
INDEX,
Expand Down Expand Up @@ -126,10 +137,10 @@ mod tests {
}

#[test]
#[should_panic(expected: ('Trophy: invalid id',))]
#[should_panic(expected: ('Creation: invalid id',))]
fn test_achievement_creation_new_invalid_id() {
let tasks: Array<Task> = array![TaskTrait::new(TASK_ID, TOTAL, "TASK DESCRIPTION"),];
TrophyTrait::new(
CreationTrait::new(
0,
HIDDEN,
INDEX,
Expand All @@ -146,30 +157,50 @@ mod tests {
}

#[test]
#[should_panic(expected: ('Trophy: invalid title',))]
#[should_panic(expected: ('Creation: invalid title',))]
fn test_achievement_creation_new_invalid_title() {
let tasks: Array<Task> = array![TaskTrait::new(TASK_ID, TOTAL, "TASK DESCRIPTION"),];
TrophyTrait::new(
CreationTrait::new(
ID, HIDDEN, INDEX, POINTS, START, END, GROUP, ICON, 0, "DESCRIPTION", tasks.span(), ""
);
}

#[test]
#[should_panic(expected: ('Trophy: invalid desc.',))]
#[should_panic(expected: ('Creation: invalid desc.',))]
fn test_achievement_creation_new_invalid_description() {
let tasks: Array<Task> = array![TaskTrait::new(TASK_ID, TOTAL, "TASK DESCRIPTION"),];
TrophyTrait::new(
CreationTrait::new(
ID, HIDDEN, INDEX, POINTS, START, END, GROUP, ICON, TITLE, "", tasks.span(), ""
);
}

#[test]
#[should_panic(expected: ('Trophy: invalid duration.',))]
#[should_panic(expected: ('Creation: invalid duration',))]
fn test_achievement_creation_new_invalid_duration() {
let tasks: Array<Task> = array![TaskTrait::new(TASK_ID, TOTAL, "TASK DESCRIPTION"),];
TrophyTrait::new(
CreationTrait::new(
ID, HIDDEN, INDEX, POINTS, START, 0, GROUP, ICON, TITLE, "DESCRIPTION", tasks.span(), ""
);
}

#[test]
#[should_panic(expected: ('Creation: too much points',))]
fn test_achievement_creation_new_invalid_points() {
let tasks: Array<Task> = array![TaskTrait::new(TASK_ID, TOTAL, "TASK DESCRIPTION"),];
CreationTrait::new(
ID,
HIDDEN,
INDEX,
MAX_POINTS + 1,
START,
END,
GROUP,
ICON,
TITLE,
"DESCRIPTION",
tasks.span(),
""
);
}
}

Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
/// Events
//! Events

// Internal imports

use arcade_trophy::types::task::Task;
use achievement::types::task::Task;

#[derive(Clone, Drop, Serde)]
#[dojo::event(historical: true)]
#[dojo::event]
pub struct TrophyCreation {
#[key]
id: felt252,
Expand All @@ -23,7 +23,7 @@ pub struct TrophyCreation {
}

#[derive(Copy, Drop, Serde)]
#[dojo::event(historical: true)]
#[dojo::event]
pub struct TrophyProgression {
#[key]
player_id: felt252,
Expand All @@ -32,3 +32,13 @@ pub struct TrophyProgression {
count: u32,
time: u64,
}

#[derive(Copy, Drop, Serde)]
#[dojo::event]
pub struct TrophyPinning {
#[key]
player_id: felt252,
#[key]
achievement_id: felt252,
time: u64,
}
57 changes: 57 additions & 0 deletions packages/achievement/src/events/pinning.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// Internal imports

use achievement::events::index::TrophyPinning;

// Errors

pub mod errors {
pub const PINNING_INVALID_ID: felt252 = 'Pinning: invalid id';
}

// Implementations

#[generate_trait]
impl PinningImpl of PinningTrait {
#[inline]
fn new(player_id: felt252, achievement_id: felt252, time: u64) -> TrophyPinning {
// [Check] Inputs
PinningAssert::assert_valid_id(achievement_id);
// [Return] Pinning
TrophyPinning { player_id, achievement_id, time }
}
}

#[generate_trait]
impl PinningAssert of AssertTrait {
#[inline]
fn assert_valid_id(achievement_id: felt252) {
assert(achievement_id != 0, errors::PINNING_INVALID_ID);
}
}

#[cfg(test)]
mod tests {
// Local imports

use super::PinningTrait;

// Constants

const PLAYER_ID: felt252 = 'PLAYER';
const ACHIEVEMENT_ID: felt252 = 'ACHIEVEMENT';
const TIME: u64 = 1000000000;

#[test]
fn test_pinning_new() {
let pinning = PinningTrait::new(PLAYER_ID, ACHIEVEMENT_ID, TIME,);
assert_eq!(pinning.player_id, PLAYER_ID);
assert_eq!(pinning.achievement_id, ACHIEVEMENT_ID);
assert_eq!(pinning.time, TIME);
}

#[test]
#[should_panic(expected: ('Pinning: invalid id',))]
fn test_pinning_new_invalid_id() {
PinningTrait::new(PLAYER_ID, 0, TIME);
}
}
Loading

0 comments on commit 9596534

Please sign in to comment.