use common::{ terrain::BiomeKind, trade::{Good, SitePrices}, }; use rayon::ThreadPoolBuilder; use rusqlite::{Connection, ToSql}; use std::error::Error; use strum::IntoEnumIterator; use vek::Vec2; use veloren_world::{ index::Index, sim::{FileOpts, WorldOpts, DEFAULT_WORLD_MAP, DEFAULT_WORLD_SEED}, World, }; fn good_pricing_csv(world: &World, index: &Index) -> Result<(), Box> { let mut csv = csv::Writer::from_path("good_pricing.csv")?; csv.write_record([ "Site", "XCoord", "YCoord", "Flour", "Meat", "Transportation", "Food", "Wood", "Stone", "Tools", "Armor", "Ingredients", "Potions", "Coin", "RoadSecurity", ])?; for civsite in world.civs().sites() { if let Some(site_id) = civsite.site_tmp { let site = index.sites.get(site_id); if site.do_economic_simulation() { let prices = site.economy.get_site_prices(); //println!("{:?}: {:?} {:?}", site.name(), civsite.center, prices); csv.write_record([ site.name(), &format!("{}", civsite.center.x), &format!("{}", civsite.center.y), &format!("{}", prices.values.get(&Good::Flour).unwrap_or(&0.0)), &format!("{}", prices.values.get(&Good::Meat).unwrap_or(&0.0)), &format!( "{}", prices.values.get(&Good::Transportation).unwrap_or(&0.0) ), &format!("{}", prices.values.get(&Good::Food).unwrap_or(&0.0)), &format!("{}", prices.values.get(&Good::Wood).unwrap_or(&0.0)), &format!("{}", prices.values.get(&Good::Stone).unwrap_or(&0.0)), &format!("{}", prices.values.get(&Good::Tools).unwrap_or(&0.0)), &format!("{}", prices.values.get(&Good::Armor).unwrap_or(&0.0)), &format!("{}", prices.values.get(&Good::Ingredients).unwrap_or(&0.0)), &format!("{}", prices.values.get(&Good::Potions).unwrap_or(&0.0)), &format!("{}", prices.values.get(&Good::Coin).unwrap_or(&0.0)), &format!("{}", prices.values.get(&Good::RoadSecurity).unwrap_or(&0.0)), ])?; } } } Ok(()) } fn economy_sqlite(world: &World, index: &Index) -> Result<(), Box> { let conn = Connection::open("economy.sqlite")?; #[rustfmt::skip] conn.execute_batch(" DROP TABLE IF EXISTS site; CREATE TABLE site ( xcoord INTEGER NOT NULL, ycoord INTEGER NOT NULL, name TEXT NOT NULL ); CREATE UNIQUE INDEX site_position ON site(xcoord, ycoord); DROP TABLE IF EXISTS site_price; CREATE TABLE site_price ( xcoord INTEGER NOT NULL, ycoord INTEGER NOT NULL, good TEXT NOT NULL, price REAL NOT NULL ); CREATE UNIQUE INDEX site_good on site_price(xcoord, ycoord, good); ")?; let mut all_goods = Vec::new(); for good in Good::iter() { match good { Good::Territory(_) => { for biome in BiomeKind::iter() { all_goods.push(Good::Territory(biome)); } }, Good::Terrain(_) => { for biome in BiomeKind::iter() { all_goods.push(Good::Terrain(biome)); } }, _ => { all_goods.push(good); }, } } let mut good_columns = String::new(); let mut good_exprs = String::new(); for good in all_goods.iter() { good_columns += &format!(", '{:?}'", good); good_exprs += &format!( ", MAX(CASE WHEN site_price.good = '{:?}' THEN site_price.price END)", good ); } #[rustfmt::skip] let create_view = format!(" DROP VIEW IF EXISTS site_price_tr; CREATE VIEW site_price_tr (xcoord, ycoord {}) AS SELECT xcoord, ycoord {} FROM site NATURAL JOIN site_price GROUP BY xcoord, ycoord; ", good_columns, good_exprs); conn.execute_batch(&create_view)?; let mut insert_price_stmt = conn .prepare("REPLACE INTO site_price (xcoord, ycoord, good, price) VALUES (?1, ?2, ?3, ?4)")?; let mut insert_price = move |center: Vec2, good: Good, prices: &SitePrices| { let price = prices.values.get(&good).unwrap_or(&0.0); insert_price_stmt.execute([ ¢er.x as &dyn ToSql, ¢er.y, &format!("{:?}", good), &(*price as f64), ]) }; for civsite in world.civs().sites() { if let Some(site_id) = civsite.site_tmp { let site = index.sites.get(site_id); if site.do_economic_simulation() { let prices = site.economy.get_site_prices(); conn.execute( "REPLACE INTO site (xcoord, ycoord, name) VALUES (?1, ?2, ?3)", [ &civsite.center.x as &dyn ToSql, &civsite.center.y, &site.name(), ], )?; for good in all_goods.iter() { insert_price(civsite.center, *good, &prices)?; } } } } Ok(()) } fn main() { common_frontend::init_stdout(None); println!("Loading world"); let pool = ThreadPoolBuilder::new().build().unwrap(); let (world, index) = World::generate( DEFAULT_WORLD_SEED, WorldOpts { seed_elements: true, world_file: FileOpts::LoadAsset(DEFAULT_WORLD_MAP.into()), calendar: None, }, &pool, &|_| {}, ); println!("Loaded world"); if let Err(e) = good_pricing_csv(&world, &index) { println!("Error generating goodpricing csv: {:?}", e); } if let Err(e) = economy_sqlite(&world, &index) { println!("Error generating economy sqlite db: {:?}", e); } }