Используя serde, возможно ли десериализацию в структуру, которая реализует тип? - PullRequest
1 голос
/ 05 апреля 2019

Я создаю мошенническое приложение, у меня уже работает загрузчик данных и часть ECS (сборка с нуля).Данные хранятся в .yml файлах и используются для описания вещей в игре (в данном случае мобов) и особенностей этих вещей, например:

---
orc:
  feature_packs:
    - physical
    - basic_identifiers_mob
  features:
    - component: char
      initial_value: T
goblin:
  feature_packs:
    - physical
    - basic_identifiers_mob
  features:
    - component: char
      initial_value: t

Как вы можете видеть, естьописаны два моба, гоблин и орк, оба обладают двумя пакетами функций (группами функций), а также функцией char, которая используется для описания того, как они выглядят для игрока.

* initial_value поле может быть строкой, целым числом, с плавающей запятой, логическим значением, диапазоном и т. д., в зависимости от того, что требуется компоненту, это будет указывать значение или возможные значения, которые может иметь компонент, когда компонент генерируется во время построения / создания сущности.

Проблема в том, что я не знаю, как при итерации по объектам выбирать структуру на основе имени компонента, например, выбирать структуру Char для функции "char".

Чтобы лучше описать, что я имею в виду, я написал пример на языке, который я лучше понимаю, Ruby:

data_manager = function_that_loads_data('folder_path')

Entity_Manager.build(:mob, :orc, data_manager)

class Entity_Manager
  class << self
    attr_accessor :entities, :components
  end

  def self.build(entity_type, template_name, data_manager)
    template = data_manager[entity_type][template_name]
    entity_id = generate_unique_id
    entities[entity_id] = Entity.new(entity_id, components: template.components.keys)
    template.components.each do |component|
      components[component.name][entity_id] =
        Components.get(component.name).new(component.initial_value) # <= This part, how do I do the equivalent in rust, a function that will return or allow me to get or create a struct based on the value of a string variable 
    end
  end
end

Теперь serdeединственное, что я знаю, это то, что я могу читать текстовые данные и преобразовывать их в данные, поэтому

Как я могу использовать serde (или более подходящее решение, не использующее serde), чтобы взять именафункции и получить правильную структуру, все реализующие тип?

Кстати, единственное решение, которое я пытаюсь не использовать, - это гигантское выражение соответствия.

Репозиторий моей работы какстоит здесь

  • Диспетчер данных - Загружает и управляет данными, загруженными в игру
  • Диспетчер сущностей - Управляет объектами и их компонентами (не поддерживает битовые ключи)
  • Entity Builder - Где объекты будут создаваться с использованием данных из диспетчера данных (это то, где я сейчас застрял)
  • Компоненты - список простых компонентов

То, чего я пытаюсь избежать, делает что-то вроде этого:

pub fn get(comp_name: &String) -> impl Component {
    match comp_name.as_ref() {
        "kind"      => Kind,
        "location"  => Location,
        "name"      => Name,
        "position"  => Position,
        "char"      => Char,
    }
}

потому что это не совсем поддерживаемо, хотя макрос поможетт, я не очень хорош в этих банкоматах, и это даже не работает, Руст продолжает думать, что я пытаюсь инициализировать типы, когда я просто хочу вернуть один из нескольких возможных типов, которые все будут реализовывать Component

РЕДАКТИРОВАТЬ: потому что, похоже, я не достаточно ясно:

  • Я не пытаюсь загружать игровые объекты в игру, я загружаю шаблоны
  • Я использую эти шаблоны , чтобы затем генерировать сущности, которые будут существовать во время игры
  • Я уже могу загрузить нужные данные в игру в следующей структуре:
pub enum InitialValue {
    Char(char),
    String(String),
    Int(i32),
    Float(f32),
    Bool(bool),
    Range(Range<i32>),
    Point((i32,i32))
}


impl InitialValue {

    pub fn unwrap_char(&self) -> &char {
        match &self {
            InitialValue::Char(val) => val,
            _ => panic!("Stored value does not match unwrap type")
        }
    }

    pub fn unwrap_string(&self) -> &String {
        match &self {
            InitialValue::String(val) => val,
            _ => panic!("Stored value does not match unwrap type")
        }
    }

    pub fn unwrap_int(&self) -> &i32 {
        match &self {
            InitialValue::Int(val) => val,
            _ => panic!("Stored value does not match unwrap type")
        }
    }

    pub fn unwrap_float(&self) -> &f32 {
        match &self {
            InitialValue::Float(val) => val,
            _ => panic!("Stored value does not match unwrap type")
        }
    }

    pub fn unwrap_bool(&self) -> &bool {
        match &self {
            InitialValue::Bool(val) => val,
            _ => panic!("Stored value does not match unwrap type")
        }
    }

    pub fn unwrap_range(&self) -> &Range<i32> {
        match &self {
            InitialValue::Range(val) => val,
            _ => panic!("Stored value does not match unwrap type")
        }
    }

    pub fn unwrap_point(&self) -> &(i32, i32) {
        match &self {
            InitialValue::Point(val) => val,
            _ => panic!("Stored value does not match unwrap type")
        }
    }
}

#[derive(Debug, Deserialize)]
pub struct Component {
    #[serde(rename="component")]
    name: String,
    #[serde(default)]
    initial_value: Option<InitialValue>,
}

#[derive(Debug, Deserialize)]
pub struct Template {
    pub feature_packs: Vec<String>,
    pub features: Vec<Component>,
}
  • Как преобразовать шаблоны в экземпляры сущностей?

  • В частности, Как мне сделать для данногоComponent.name найти компонент и затем инициализировать его?ИЛИ мой подход неверен, и есть лучший способ.

  • И если я делаю это неправильно, как другие игры загружают данные, а затем используют их для создания игровых объектов?

1 Ответ

1 голос
/ 05 апреля 2019

Звучит так, будто вы хотите пометить объединение или тип суммы;Руст знает это как enum erations .Serde даже поддерживает использование внутренних тегов контейнера .Итак, вот мой маленький эксперимент:

#[macro_use] extern crate serde_derive;
extern crate serde_yaml;

#[derive(Debug, Serialize, Deserialize)]
#[serde(tag="component")]
enum Feature {
    Char { initial_value : char },
    Weight { kgs : u32 }
}

fn main() {
    let v = vec![
        Feature::Char{initial_value:'x'},
        Feature::Weight{kgs:12}
    ];
    println!("{}", serde_yaml::to_string(&v).unwrap());
}

Это выводит:

---
- component: Char
  initial_value: x
- component: Weight
  kgs: 12

Вероятно, следующим шагом будет создание выделенных структур для вариантов.

...