~saiko/game

bbcb60bc7215045ee4059e83a21c3feae63153dc — 2xsaiko 1 year, 14 days ago ca2c1b4
modifications to init process
M boot.cfg => boot.cfg +9 -7
@@ 1,8 1,8 @@
// Development environment boot config

// Loose files, found in ./data/ directory
// Multiple data directories can be provided, seperated by spaces, in which
// case the first directory will have priority when loading files.
// Multiple data directories can be provided, seperated by spaces, in
// descending order of priority.
// It will also load from files in ZIP format with the extension '.res',
// but this is impractical for development so done for release versions only.
//data_dir ./data ./data/objects.res ./data/ui.res ./data/strings.res ./data/levels.res


@@ 13,10 13,9 @@ data_dir ./data/
// This definition will be read from top to bottom, and the first match will
// be used to determine the merge behavior. If nothing matches, the default
// is 'overwrite'.
// Configuration files and string files should be concatenated
data_merge /cfg     concat
data_merge /strs    concat
data_merge /        overwrite
data_merge_mode /cfg/sys/init.cfg concat
data_merge_mode /strs             concat
data_merge_mode /                 overwrite

// Save data (user configuration, savegames, logging) directory
save_dir ./save/


@@ 28,4 27,7 @@ v_sync 0
// For layering localizations, use e.g. 'language english german' to
// prioritize german over english localization.
// Localization data will be loaded from strs/[language].str
language english
\ No newline at end of file
language english

// What configuration file to run on startup
exec_init sys/init
\ No newline at end of file

A data/cfg/sys/fonts.cfg => data/cfg/sys/fonts.cfg +21 -0
@@ 0,0 1,21 @@
// What fonts should we use?

use_font system 12 normal /font/terminus/ter-u12n.bdf
use_font system 14 normal /font/terminus/ter-u14n.bdf
use_font system 16 normal /font/terminus/ter-u16n.bdf
use_font system 18 normal /font/terminus/ter-u18n.bdf
use_font system 20 normal /font/terminus/ter-u20n.bdf
use_font system 22 normal /font/terminus/ter-u22n.bdf
use_font system 24 normal /font/terminus/ter-u24n.bdf
use_font system 28 normal /font/terminus/ter-u28n.bdf
use_font system 32 normal /font/terminus/ter-u32n.bdf

use_font system 12 bold   /font/terminus/ter-u12n.bdf
use_font system 14 bold   /font/terminus/ter-u14n.bdf
use_font system 16 bold   /font/terminus/ter-u16n.bdf
use_font system 18 bold   /font/terminus/ter-u18n.bdf
use_font system 20 bold   /font/terminus/ter-u20n.bdf
use_font system 22 bold   /font/terminus/ter-u22n.bdf
use_font system 24 bold   /font/terminus/ter-u24n.bdf
use_font system 28 bold   /font/terminus/ter-u28n.bdf
use_font system 32 bold   /font/terminus/ter-u32n.bdf
\ No newline at end of file

A data/cfg/sys/init.cfg => data/cfg/sys/init.cfg +4 -0
@@ 0,0 1,4 @@
// Set sensible engine defaults.

exec fonts
exec autoexec
\ No newline at end of file

M src/font/bdf/draw.rs => src/font/bdf/draw.rs +2 -2
@@ 117,7 117,7 @@ struct Vertex {

implement_vertex!(Vertex, xyz, uv, color, size, uv_size);

impl<'a> FontRenderer for BakedFont<'a> {
impl FontRenderer for BakedFont<'_> {
  fn draw_strings(&self, surface: &mut AnySurface, mvp: &Mat4, strs: &[(&str, f32, f32, Color)]) {
    self.create_data(strs).draw(surface, mvp);
  }


@@ 146,7 146,7 @@ pub struct BakedString<'a> {
  data: Vec<(Rc<Texture2d>, Vec<Vertex>, Option<VertexBuffer<Vertex>>)>,
}

impl<'a> BakedString<'a> {
impl BakedString<'_> {
  fn insert(&mut self, tex: Rc<Texture2d>, data: Vec<Vertex>) {
    match self.data.iter_mut().find(|(a, ..)| a.get_id() == tex.get_id()) {
      None => { self.data.push((tex, data, None)); }

M src/main.rs => src/main.rs +12 -3
@@ 48,6 48,8 @@ use crate::ui::layout::{BorderLayout, LayoutManager, StackLayout};
use crate::ui::panel::Panel;
use crate::ui::UiController;
use crate::util::{AnySurface, Color, LogPipe};
use crate::res::merge::MergeModeTable;
use std::convert::TryInto;

mod cam;
mod cmd;


@@ 69,6 71,8 @@ fn main() {
  let mut save_dir = None;
  let mut v_sync = false;
  let mut language = None;
  let mut init_script = None;
  let mut data_merge = MergeModeTable::new();

  {
    let mut cd = CommandDispatcher::new(SimpleExecutor::new(|cmd, args| {


@@ 77,6 81,8 @@ fn main() {
        "save_dir" => save_dir = Some(PathBuf::from(args[0])),
        "v_sync" => v_sync = args[0].parse::<i32>().unwrap_or(0) != 0,
        "language" => language = Some(args.iter().map(|&s| s.to_owned()).collect::<Vec<_>>()),
        "data_merge_mode" => data_merge.add_entry(args[0], args[1].try_into().unwrap()),
        "exec_init" => init_script = Some(PathBuf::from(args[0])),
        _ => eprintln!("Ignoring invalid bootstrap command '{}'", cmd),
      }
    }));


@@ 87,6 93,7 @@ fn main() {
  let data_dir = data_dir.expect("data_dir was not set!");
  let save_dir = save_dir.expect("save_dir was not set!");
  let language = language.expect("language was not set!");
  let init_script = init_script.expect("exec_init was not set!");

  create_dir_all(&save_dir).expect("Could not create save directory.");



@@ 105,12 112,12 @@ fn main() {
    if is_res_file { DataSource::Archive(el.clone()) } else { DataSource::Filesystem(el.clone()) }
  }).collect();

  let rl = ResourceLoader::new(sources);
  let rl = ResourceLoader::new(data_merge, sources);
  let i18n = language.into_iter()
    .map(|p| I18n::parse_from_res(&rl, &format!("strs/{}.str", p)).unwrap())
    .fold(I18n::new(), |acc, a| acc.merge(a));

  start_game(Rc::new(Environment { rl, i18n, data_dir, save_dir, v_sync }));
  start_game(Rc::new(Environment { rl, i18n, data_dir, save_dir, v_sync, init_script }));
}

fn start_game(env: Rc<Environment>) {


@@ 164,7 171,8 @@ fn start_game(env: Rc<Environment>) {
      .build()
  };

  ce.scheduler().exec("exec autoexec", ExecSource::Event);
  ce.scheduler().exec(&format!("exec \"{}\"", env.init_script.to_string_lossy()), ExecSource::Event);
  ce.dispatcher_mut().resume_until_empty();

  let mut sm = ShaderManager::new(&display, &env.rl);



@@ 498,4 506,5 @@ struct Environment {
  data_dir: Vec<PathBuf>,
  save_dir: PathBuf,
  v_sync: bool,
  init_script: PathBuf,
}
\ No newline at end of file

A src/res/merge.rs => src/res/merge.rs +47 -0
@@ 0,0 1,47 @@
use std::convert::TryFrom;
use std::path::{Path, PathBuf};

use crate::res::normalize_path;

pub struct MergeModeTable {
  entries: Vec<(PathBuf, MergeMode)>,
}

impl MergeModeTable {
  pub fn new() -> Self {
    MergeModeTable { entries: vec![] }
  }

  pub fn add_entry(&mut self, path: impl AsRef<Path>, mode: MergeMode) {
    self.entries.insert(0, (normalize_path(path).unwrap(), mode));
  }

  pub fn get_mode(&self, path: impl AsRef<Path>) -> MergeMode {
    let normalized = normalize_path(path).unwrap();
    self.entries.iter()
      .find(|(p, _)| matches(p, &normalized))
      .map_or(MergeMode::Overwrite, |(_, mode)| *mode)
  }
}

fn matches(entry: impl AsRef<Path>, to_test: impl AsRef<Path>) -> bool {
  to_test.as_ref().starts_with(entry.as_ref())
}

#[derive(Copy, Clone, Eq, PartialEq, Debug)]
pub enum MergeMode {
  Overwrite,
  Concat,
}

impl TryFrom<&str> for MergeMode {
  type Error = String;

  fn try_from(value: &str) -> Result<Self, Self::Error> {
    match value {
      "overwrite" => Ok(MergeMode::Overwrite),
      "concat" => Ok(MergeMode::Concat),
      _ => Err(value.to_owned())
    }
  }
}
\ No newline at end of file

M src/res/mod.rs => src/res/mod.rs +23 -13
@@ 10,15 10,18 @@ use zip::read::ZipFile;
use zip::result::ZipError;
use zip::ZipArchive;

use crate::res::merge::MergeModeTable;

pub mod merge;

pub struct ResourceLoader {
  mmt: MergeModeTable,
  sources: Vec<DataSource>,
}

impl ResourceLoader {
  pub fn new(sources: Vec<DataSource>) -> Self {
    ResourceLoader {
      sources,
    }
  pub fn new(mmt: MergeModeTable, sources: Vec<DataSource>) -> Self {
    ResourceLoader { mmt, sources }
  }

  pub fn open(&self, path: impl AsRef<Path>) -> Result<DataSourceRead> {


@@ 72,18 75,25 @@ impl DataSource {
  }

  fn resolve_path_for_archive(path: impl AsRef<Path>) -> Option<String> {
    let mut vec = vec![];
    for c in path.as_ref().components() {
      match c {
        Component::Prefix(_) => return None,
        Component::RootDir => {}
        Component::CurDir => {}
        Component::ParentDir => { vec.pop(); }
        Component::Normal(s) => vec.push(s.to_string_lossy()),
    let pb = normalize_path(path)?;
    Some(pb.strip_prefix("/").unwrap().to_string_lossy().into_owned())
  }
}

pub fn normalize_path(path: impl AsRef<Path>) -> Option<PathBuf> {
  let mut pb = PathBuf::from("/");
  for c in path.as_ref().components() {
    match c {
      Component::Prefix(_) => return None,
      Component::RootDir => {}
      Component::CurDir => {}
      Component::ParentDir => {
        pb.pop();
      }
      Component::Normal(s) => pb.push(s),
    }
    Some(vec.into_iter().join("/"))
  }
  Some(pb)
}

pub enum DataSourceRead {