1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
use quartz_nbt::NbtTag;
use serde::{Deserialize, Serialize};
use std::path::PathBuf;
use time::OffsetDateTime;

use crate::error::CobbleError;

/// Represents a single minecraft save.
/// Includes information from the level.dat file.
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct SaveGame {
    /// Name of the world.
    pub name: String,
    /// Path of the world folder.
    pub path: PathBuf,
    /// Difficulty of the world.
    pub difficulty: Option<Difficulty>,
    /// Gametype for new players.
    pub game_type: GameType,
    /// Game version the world was saved in.
    pub version: Option<String>,
    /// The seed used for generating.
    pub seed: i64,
    /// The path to the world icon.
    pub icon_path: Option<PathBuf>,
    /// Timestamp
    #[serde(with = "time::serde::rfc3339")]
    pub last_played: OffsetDateTime,
}

/// Difficulty of a world.
#[derive(Clone, Debug, Deserialize, Serialize)]
pub enum Difficulty {
    /// Peaceful
    Peaceful,
    /// Easy
    Easy,
    /// Normal
    Normal,
    /// Hard
    Hard,
}

impl TryFrom<&NbtTag> for Difficulty {
    type Error = CobbleError;

    fn try_from(value: &NbtTag) -> Result<Self, Self::Error> {
        match value {
            NbtTag::Byte(b) => match b {
                0 => Ok(Self::Peaceful),
                1 => Ok(Self::Easy),
                2 => Ok(Self::Normal),
                3 => Ok(Self::Hard),
                _ => Err(CobbleError::InvalidNbtValue),
            },
            _ => Err(CobbleError::InvalidNbtValue),
        }
    }
}

impl TryFrom<i32> for Difficulty {
    type Error = anyhow::Error;

    fn try_from(value: i32) -> Result<Self, Self::Error> {
        match value {
            0 => Ok(Self::Peaceful),
            1 => Ok(Self::Easy),
            2 => Ok(Self::Normal),
            3 => Ok(Self::Hard),
            _ => Err(anyhow::anyhow!("Invalid difficulty value")),
        }
    }
}

impl From<Difficulty> for i32 {
    fn from(value: Difficulty) -> Self {
        match value {
            Difficulty::Peaceful => 0,
            Difficulty::Easy => 1,
            Difficulty::Normal => 2,
            Difficulty::Hard => 3,
        }
    }
}

/// Gametype of a world.
#[derive(Clone, Debug, Deserialize, Serialize)]
pub enum GameType {
    /// Survival
    Survival,
    /// Creative
    Creative,
    /// Adventure
    Adventure,
    /// Spectator
    Spectator,
}

impl TryFrom<&NbtTag> for GameType {
    type Error = CobbleError;

    fn try_from(value: &NbtTag) -> Result<Self, Self::Error> {
        match value {
            NbtTag::Int(i) => match i {
                0 => Ok(Self::Survival),
                1 => Ok(Self::Creative),
                2 => Ok(Self::Adventure),
                3 => Ok(Self::Spectator),
                _ => Err(CobbleError::InvalidNbtValue),
            },
            _ => Err(CobbleError::InvalidNbtValue),
        }
    }
}

impl TryFrom<i32> for GameType {
    type Error = anyhow::Error;

    fn try_from(value: i32) -> Result<Self, Self::Error> {
        match value {
            0 => Ok(Self::Survival),
            1 => Ok(Self::Creative),
            2 => Ok(Self::Adventure),
            3 => Ok(Self::Spectator),
            _ => Err(anyhow::anyhow!("Invalid gametype value")),
        }
    }
}

impl From<GameType> for i32 {
    fn from(value: GameType) -> Self {
        match value {
            GameType::Survival => 0,
            GameType::Creative => 1,
            GameType::Adventure => 2,
            GameType::Spectator => 3,
        }
    }
}