mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Make resource depletion an rtsim rule
This commit is contained in:
parent
0b06eaec6f
commit
9d3dadfaba
9
Cargo.lock
generated
9
Cargo.lock
generated
@ -145,6 +145,12 @@ version = "1.0.65"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "98161a4e3e2184da77bb14f02184cdd111e83bbbcc9979dfee3c44b9a85f5602"
|
checksum = "98161a4e3e2184da77bb14f02184cdd111e83bbbcc9979dfee3c44b9a85f5602"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anymap2"
|
||||||
|
version = "0.13.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d301b3b94cb4b2f23d7917810addbbaff90738e0ca2be692bd027e70d7e0330c"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "app_dirs2"
|
name = "app_dirs2"
|
||||||
version = "2.5.4"
|
version = "2.5.4"
|
||||||
@ -6939,11 +6945,14 @@ dependencies = [
|
|||||||
name = "veloren-rtsim"
|
name = "veloren-rtsim"
|
||||||
version = "0.10.0"
|
version = "0.10.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"anymap2",
|
||||||
|
"atomic_refcell",
|
||||||
"enum-map",
|
"enum-map",
|
||||||
"hashbrown 0.12.3",
|
"hashbrown 0.12.3",
|
||||||
"rmp-serde",
|
"rmp-serde",
|
||||||
"ron 0.8.0",
|
"ron 0.8.0",
|
||||||
"serde",
|
"serde",
|
||||||
|
"tracing",
|
||||||
"vek 0.15.8",
|
"vek 0.15.8",
|
||||||
"veloren-common",
|
"veloren-common",
|
||||||
"veloren-world",
|
"veloren-world",
|
||||||
|
@ -12,3 +12,6 @@ hashbrown = { version = "0.12", features = ["rayon", "serde", "nightly"] }
|
|||||||
enum-map = { version = "2.4", features = ["serde"] }
|
enum-map = { version = "2.4", features = ["serde"] }
|
||||||
vek = { version = "0.15.8", features = ["serde"] }
|
vek = { version = "0.15.8", features = ["serde"] }
|
||||||
rmp-serde = "1.1.0"
|
rmp-serde = "1.1.0"
|
||||||
|
anymap2 = "0.13"
|
||||||
|
tracing = "0.1"
|
||||||
|
atomic_refcell = "0.1"
|
||||||
|
@ -7,6 +7,8 @@ use common::{
|
|||||||
use world::World;
|
use world::World;
|
||||||
use vek::*;
|
use vek::*;
|
||||||
|
|
||||||
|
/// Represents the state of 'natural' elements of the world such as plant/animal/resource populations, weather systems,
|
||||||
|
/// etc.
|
||||||
#[derive(Clone, Serialize, Deserialize)]
|
#[derive(Clone, Serialize, Deserialize)]
|
||||||
pub struct Nature {
|
pub struct Nature {
|
||||||
chunks: Grid<Chunk>,
|
chunks: Grid<Chunk>,
|
||||||
@ -40,5 +42,6 @@ impl Nature {
|
|||||||
|
|
||||||
#[derive(Clone, Serialize, Deserialize)]
|
#[derive(Clone, Serialize, Deserialize)]
|
||||||
pub struct Chunk {
|
pub struct Chunk {
|
||||||
|
/// Represent the 'naturally occurring' resource proportion that exists in this chunk.
|
||||||
res: EnumMap<ChunkResource, f32>,
|
res: EnumMap<ChunkResource, f32>,
|
||||||
}
|
}
|
||||||
|
8
rtsim/src/event.rs
Normal file
8
rtsim/src/event.rs
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
use common::resources::Time;
|
||||||
|
|
||||||
|
pub trait Event: Clone + 'static {}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct OnTick { pub dt: f32 }
|
||||||
|
|
||||||
|
impl Event for OnTick {}
|
109
rtsim/src/lib.rs
109
rtsim/src/lib.rs
@ -1,10 +1,107 @@
|
|||||||
pub mod data;
|
#![feature(explicit_generic_args_with_impl_trait)]
|
||||||
pub mod gen;
|
|
||||||
|
|
||||||
use self::data::Data;
|
pub mod data;
|
||||||
use std::sync::Arc;
|
pub mod event;
|
||||||
use world::World;
|
pub mod gen;
|
||||||
|
pub mod rule;
|
||||||
|
|
||||||
|
pub use self::{
|
||||||
|
data::Data,
|
||||||
|
event::{Event, OnTick},
|
||||||
|
rule::{Rule, RuleError},
|
||||||
|
};
|
||||||
|
use anymap2::SendSyncAnyMap;
|
||||||
|
use tracing::{info, error};
|
||||||
|
use atomic_refcell::AtomicRefCell;
|
||||||
|
use std::{
|
||||||
|
any::type_name,
|
||||||
|
ops::{Deref, DerefMut},
|
||||||
|
};
|
||||||
|
|
||||||
pub struct RtState {
|
pub struct RtState {
|
||||||
pub data: Data,
|
resources: SendSyncAnyMap,
|
||||||
|
rules: SendSyncAnyMap,
|
||||||
|
event_handlers: SendSyncAnyMap,
|
||||||
|
}
|
||||||
|
|
||||||
|
type RuleState<R> = AtomicRefCell<R>;
|
||||||
|
type EventHandlersOf<E> = Vec<Box<dyn Fn(&RtState, E) + Send + Sync + 'static>>;
|
||||||
|
|
||||||
|
impl RtState {
|
||||||
|
pub fn new(data: Data) -> Self {
|
||||||
|
let mut this = Self {
|
||||||
|
resources: SendSyncAnyMap::new(),
|
||||||
|
rules: SendSyncAnyMap::new(),
|
||||||
|
event_handlers: SendSyncAnyMap::new(),
|
||||||
|
}
|
||||||
|
.with_resource(data);
|
||||||
|
|
||||||
|
this.start_default_rules();
|
||||||
|
|
||||||
|
this
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_resource<R: Send + Sync + 'static>(mut self, r: R) -> Self {
|
||||||
|
self.resources.insert(AtomicRefCell::new(r));
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
fn start_default_rules(&mut self) {
|
||||||
|
info!("Starting default rtsim rules...");
|
||||||
|
self.start_rule::<rule::example::RuleState>();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn start_rule<R: Rule>(&mut self) {
|
||||||
|
info!("Initiating '{}' rule...", type_name::<R>());
|
||||||
|
match R::start(self) {
|
||||||
|
Ok(rule) => { self.rules.insert::<RuleState<R>>(AtomicRefCell::new(rule)); },
|
||||||
|
Err(e) => error!("Error when initiating '{}' rule: {}", type_name::<R>(), e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn rule_mut<R: Rule>(&self) -> impl DerefMut<Target = R> + '_ {
|
||||||
|
self.rules
|
||||||
|
.get::<RuleState<R>>()
|
||||||
|
.unwrap_or_else(|| panic!("Tried to access rule '{}' but it does not exist", type_name::<R>()))
|
||||||
|
.borrow_mut()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn bind<R: Rule, E: Event>(&mut self, mut f: impl FnMut(&mut R, &RtState, E) + Send + Sync + 'static) {
|
||||||
|
let f = AtomicRefCell::new(f);
|
||||||
|
self.event_handlers
|
||||||
|
.entry::<EventHandlersOf<E>>()
|
||||||
|
.or_default()
|
||||||
|
.push(Box::new(move |rtstate, event| {
|
||||||
|
(f.borrow_mut())(&mut rtstate.rule_mut(), rtstate, event)
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn data(&self) -> impl Deref<Target = Data> + '_ { self.resource() }
|
||||||
|
pub fn data_mut(&self) -> impl DerefMut<Target = Data> + '_ { self.resource_mut() }
|
||||||
|
|
||||||
|
pub fn resource<R: Send + Sync + 'static>(&self) -> impl Deref<Target = R> + '_ {
|
||||||
|
self.resources
|
||||||
|
.get::<AtomicRefCell<R>>()
|
||||||
|
.unwrap_or_else(|| panic!("Tried to access resource '{}' but it does not exist", type_name::<R>()))
|
||||||
|
.borrow()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn resource_mut<R: Send + Sync + 'static>(&self) -> impl DerefMut<Target = R> + '_ {
|
||||||
|
self.resources
|
||||||
|
.get::<AtomicRefCell<R>>()
|
||||||
|
.unwrap_or_else(|| panic!("Tried to access resource '{}' but it does not exist", type_name::<R>()))
|
||||||
|
.borrow_mut()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn emit<E: Event>(&mut self, e: E) {
|
||||||
|
self.event_handlers
|
||||||
|
.get::<EventHandlersOf<E>>()
|
||||||
|
.map(|handlers| handlers
|
||||||
|
.iter()
|
||||||
|
.for_each(|f| f(self, e.clone())));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn tick(&mut self, dt: f32) {
|
||||||
|
self.emit(OnTick { dt });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
21
rtsim/src/rule.rs
Normal file
21
rtsim/src/rule.rs
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
pub mod example;
|
||||||
|
|
||||||
|
use std::fmt;
|
||||||
|
use super::RtState;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum RuleError {
|
||||||
|
NoSuchRule(&'static str),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for RuleError {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
Self::NoSuchRule(r) => write!(f, "tried to fetch rule state '{}' but it does not exist", r),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait Rule: Sized + Send + Sync + 'static {
|
||||||
|
fn start(rtstate: &mut RtState) -> Result<Self, RuleError>;
|
||||||
|
}
|
19
rtsim/src/rule/example.rs
Normal file
19
rtsim/src/rule/example.rs
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
use tracing::info;
|
||||||
|
use crate::{
|
||||||
|
event::OnTick,
|
||||||
|
RtState, Rule, RuleError,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct RuleState;
|
||||||
|
|
||||||
|
impl Rule for RuleState {
|
||||||
|
fn start(rtstate: &mut RtState) -> Result<Self, RuleError> {
|
||||||
|
info!("Hello from example rule!");
|
||||||
|
|
||||||
|
rtstate.bind::<Self, OnTick>(|this, rtstate, event| {
|
||||||
|
// println!("Tick!");
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(Self)
|
||||||
|
}
|
||||||
|
}
|
@ -7,7 +7,8 @@
|
|||||||
let_chains,
|
let_chains,
|
||||||
never_type,
|
never_type,
|
||||||
option_zip,
|
option_zip,
|
||||||
unwrap_infallible
|
unwrap_infallible,
|
||||||
|
explicit_generic_args_with_impl_trait
|
||||||
)]
|
)]
|
||||||
#![feature(hash_drain_filter)]
|
#![feature(hash_drain_filter)]
|
||||||
|
|
||||||
|
12
server/src/rtsim2/event.rs
Normal file
12
server/src/rtsim2/event.rs
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
use rtsim2::Event;
|
||||||
|
use common::terrain::Block;
|
||||||
|
use vek::*;
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct OnBlockChange {
|
||||||
|
pub wpos: Vec3<i32>,
|
||||||
|
pub old: Block,
|
||||||
|
pub new: Block,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Event for OnBlockChange {}
|
@ -1,3 +1,5 @@
|
|||||||
|
pub mod event;
|
||||||
|
pub mod rule;
|
||||||
pub mod tick;
|
pub mod tick;
|
||||||
|
|
||||||
use common::{
|
use common::{
|
||||||
@ -8,7 +10,11 @@ use common::{
|
|||||||
vol::RectRasterableVol,
|
vol::RectRasterableVol,
|
||||||
};
|
};
|
||||||
use common_ecs::{dispatch, System};
|
use common_ecs::{dispatch, System};
|
||||||
use rtsim2::{data::{Data, ReadError}, RtState};
|
use rtsim2::{
|
||||||
|
data::{Data, ReadError},
|
||||||
|
rule::Rule,
|
||||||
|
RtState,
|
||||||
|
};
|
||||||
use specs::{DispatcherBuilder, WorldExt};
|
use specs::{DispatcherBuilder, WorldExt};
|
||||||
use std::{
|
use std::{
|
||||||
fs::{self, File},
|
fs::{self, File},
|
||||||
@ -19,14 +25,13 @@ use std::{
|
|||||||
error::Error,
|
error::Error,
|
||||||
};
|
};
|
||||||
use enum_map::EnumMap;
|
use enum_map::EnumMap;
|
||||||
use tracing::{error, warn, info};
|
use tracing::{error, warn, info, debug};
|
||||||
use vek::*;
|
use vek::*;
|
||||||
use world::World;
|
use world::World;
|
||||||
|
|
||||||
pub struct RtSim {
|
pub struct RtSim {
|
||||||
file_path: PathBuf,
|
file_path: PathBuf,
|
||||||
last_saved: Option<Instant>,
|
last_saved: Option<Instant>,
|
||||||
chunk_states: Grid<Option<LoadedChunkState>>,
|
|
||||||
state: RtState,
|
state: RtState,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -34,20 +39,15 @@ impl RtSim {
|
|||||||
pub fn new(world: &World, data_dir: PathBuf) -> Result<Self, ron::Error> {
|
pub fn new(world: &World, data_dir: PathBuf) -> Result<Self, ron::Error> {
|
||||||
let file_path = Self::get_file_path(data_dir);
|
let file_path = Self::get_file_path(data_dir);
|
||||||
|
|
||||||
Ok(Self {
|
info!("Looking for rtsim data in {}...", file_path.display());
|
||||||
chunk_states: Grid::populate_from(world.sim().get_size().as_(), |_| None),
|
let data = 'load: {
|
||||||
last_saved: None,
|
|
||||||
state: RtState {
|
|
||||||
data: {
|
|
||||||
info!("Looking for rtsim state in {}...", file_path.display());
|
|
||||||
'load: {
|
|
||||||
match File::open(&file_path) {
|
match File::open(&file_path) {
|
||||||
Ok(file) => {
|
Ok(file) => {
|
||||||
info!("Rtsim state found. Attempting to load...");
|
info!("Rtsim data found. Attempting to load...");
|
||||||
match Data::from_reader(io::BufReader::new(file)) {
|
match Data::from_reader(io::BufReader::new(file)) {
|
||||||
Ok(data) => { info!("Rtsim state loaded."); break 'load data },
|
Ok(data) => { info!("Rtsim data loaded."); break 'load data },
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
error!("Rtsim state failed to load: {}", e);
|
error!("Rtsim data failed to load: {}", e);
|
||||||
let mut i = 0;
|
let mut i = 0;
|
||||||
loop {
|
loop {
|
||||||
let mut backup_path = file_path.clone();
|
let mut backup_path = file_path.clone();
|
||||||
@ -58,8 +58,8 @@ impl RtSim {
|
|||||||
});
|
});
|
||||||
if !backup_path.exists() {
|
if !backup_path.exists() {
|
||||||
fs::rename(&file_path, &backup_path)?;
|
fs::rename(&file_path, &backup_path)?;
|
||||||
warn!("Failed rtsim state was moved to {}", backup_path.display());
|
warn!("Failed rtsim data was moved to {}", backup_path.display());
|
||||||
info!("A fresh rtsim state will now be generated.");
|
info!("A fresh rtsim data will now be generated.");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
i += 1;
|
i += 1;
|
||||||
@ -68,18 +68,25 @@ impl RtSim {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
Err(e) if e.kind() == io::ErrorKind::NotFound =>
|
Err(e) if e.kind() == io::ErrorKind::NotFound =>
|
||||||
info!("No rtsim state found. Generating from initial world state..."),
|
info!("No rtsim data found. Generating from world..."),
|
||||||
Err(e) => return Err(e.into()),
|
Err(e) => return Err(e.into()),
|
||||||
}
|
}
|
||||||
|
|
||||||
let data = Data::generate(&world);
|
let data = Data::generate(&world);
|
||||||
info!("Rtsim state generated.");
|
info!("Rtsim data generated.");
|
||||||
data
|
data
|
||||||
}
|
};
|
||||||
},
|
|
||||||
},
|
let mut this = Self {
|
||||||
|
last_saved: None,
|
||||||
|
state: RtState::new(data)
|
||||||
|
.with_resource(ChunkStates(Grid::populate_from(world.sim().get_size().as_(), |_| None))),
|
||||||
file_path,
|
file_path,
|
||||||
})
|
};
|
||||||
|
|
||||||
|
rule::start_rules(&mut this.state);
|
||||||
|
|
||||||
|
Ok(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_file_path(mut data_dir: PathBuf) -> PathBuf {
|
fn get_file_path(mut data_dir: PathBuf) -> PathBuf {
|
||||||
@ -89,31 +96,31 @@ impl RtSim {
|
|||||||
data_dir.push("rtsim");
|
data_dir.push("rtsim");
|
||||||
data_dir
|
data_dir
|
||||||
});
|
});
|
||||||
path.push("state.dat");
|
path.push("data.dat");
|
||||||
path
|
path
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn hook_load_chunk(&mut self, key: Vec2<i32>, max_res: EnumMap<ChunkResource, usize>) {
|
pub fn hook_load_chunk(&mut self, key: Vec2<i32>, max_res: EnumMap<ChunkResource, usize>) {
|
||||||
if let Some(chunk_state) = self.chunk_states.get_mut(key) {
|
if let Some(chunk_state) = self.state.resource_mut::<ChunkStates>().0.get_mut(key) {
|
||||||
*chunk_state = Some(LoadedChunkState { max_res });
|
*chunk_state = Some(LoadedChunkState { max_res });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn hook_unload_chunk(&mut self, key: Vec2<i32>) {
|
pub fn hook_unload_chunk(&mut self, key: Vec2<i32>) {
|
||||||
if let Some(chunk_state) = self.chunk_states.get_mut(key) {
|
if let Some(chunk_state) = self.state.resource_mut::<ChunkStates>().0.get_mut(key) {
|
||||||
*chunk_state = None;
|
*chunk_state = None;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn save(&mut self, slowjob_pool: &SlowJobPool) {
|
pub fn save(&mut self, slowjob_pool: &SlowJobPool) {
|
||||||
info!("Beginning rtsim state save...");
|
info!("Saving rtsim data...");
|
||||||
let file_path = self.file_path.clone();
|
let file_path = self.file_path.clone();
|
||||||
let data = self.state.data.clone();
|
let data = self.state.data().clone();
|
||||||
info!("Starting rtsim save job...");
|
debug!("Starting rtsim data save job...");
|
||||||
// TODO: Use slow job
|
// TODO: Use slow job
|
||||||
// slowjob_pool.spawn("RTSIM_SAVE", move || {
|
// slowjob_pool.spawn("RTSIM_SAVE", move || {
|
||||||
std::thread::spawn(move || {
|
std::thread::spawn(move || {
|
||||||
let tmp_file_name = "state_tmp.dat";
|
let tmp_file_name = "data_tmp.dat";
|
||||||
if let Err(e) = file_path
|
if let Err(e) = file_path
|
||||||
.parent()
|
.parent()
|
||||||
.map(|dir| {
|
.map(|dir| {
|
||||||
@ -127,16 +134,16 @@ impl RtSim {
|
|||||||
})
|
})
|
||||||
.map_err(|e: io::Error| Box::new(e) as Box::<dyn Error>)
|
.map_err(|e: io::Error| Box::new(e) as Box::<dyn Error>)
|
||||||
.and_then(|(mut file, tmp_file_path)| {
|
.and_then(|(mut file, tmp_file_path)| {
|
||||||
info!("Writing rtsim state to file...");
|
debug!("Writing rtsim data to file...");
|
||||||
data.write_to(io::BufWriter::new(&mut file))?;
|
data.write_to(io::BufWriter::new(&mut file))?;
|
||||||
file.flush()?;
|
file.flush()?;
|
||||||
drop(file);
|
drop(file);
|
||||||
fs::rename(tmp_file_path, file_path)?;
|
fs::rename(tmp_file_path, file_path)?;
|
||||||
info!("Rtsim state saved.");
|
debug!("Rtsim data saved.");
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
{
|
{
|
||||||
error!("Saving rtsim state failed: {}", e);
|
error!("Saving rtsim data failed: {}", e);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
self.last_saved = Some(Instant::now());
|
self.last_saved = Some(Instant::now());
|
||||||
@ -144,34 +151,15 @@ impl RtSim {
|
|||||||
|
|
||||||
// TODO: Clean up this API a bit
|
// TODO: Clean up this API a bit
|
||||||
pub fn get_chunk_resources(&self, key: Vec2<i32>) -> EnumMap<ChunkResource, f32> {
|
pub fn get_chunk_resources(&self, key: Vec2<i32>) -> EnumMap<ChunkResource, f32> {
|
||||||
self.state.data.nature.get_chunk_resources(key)
|
self.state.data().nature.get_chunk_resources(key)
|
||||||
}
|
|
||||||
pub fn hook_block_update(&mut self, wpos: Vec3<i32>, old_block: Block, new_block: Block) {
|
|
||||||
let key = wpos
|
|
||||||
.xy()
|
|
||||||
.map2(TerrainChunk::RECT_SIZE, |e, sz| e.div_euclid(sz as i32));
|
|
||||||
if let Some(Some(chunk_state)) = self.chunk_states.get(key) {
|
|
||||||
let mut chunk_res = self.get_chunk_resources(key);
|
|
||||||
// Remove resources
|
|
||||||
if let Some(res) = old_block.get_rtsim_resource() {
|
|
||||||
if chunk_state.max_res[res] > 0 {
|
|
||||||
chunk_res[res] = (chunk_res[res] - 1.0 / chunk_state.max_res[res] as f32).max(0.0);
|
|
||||||
println!("Subbing {} to resources", 1.0 / chunk_state.max_res[res] as f32);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Add resources
|
|
||||||
if let Some(res) = new_block.get_rtsim_resource() {
|
|
||||||
if chunk_state.max_res[res] > 0 {
|
|
||||||
chunk_res[res] = (chunk_res[res] + 1.0 / chunk_state.max_res[res] as f32).min(1.0);
|
|
||||||
println!("Added {} to resources", 1.0 / chunk_state.max_res[res] as f32);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
println!("Chunk resources are {:?}", chunk_res);
|
|
||||||
self.state.data.nature.set_chunk_resources(key, chunk_res);
|
|
||||||
}
|
}
|
||||||
|
pub fn hook_block_update(&mut self, wpos: Vec3<i32>, old: Block, new: Block) {
|
||||||
|
self.state.emit(event::OnBlockChange { wpos, old, new });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct ChunkStates(pub Grid<Option<LoadedChunkState>>);
|
||||||
|
|
||||||
struct LoadedChunkState {
|
struct LoadedChunkState {
|
||||||
// The maximum possible number of each resource in this chunk
|
// The maximum possible number of each resource in this chunk
|
||||||
max_res: EnumMap<ChunkResource, usize>,
|
max_res: EnumMap<ChunkResource, usize>,
|
||||||
|
9
server/src/rtsim2/rule.rs
Normal file
9
server/src/rtsim2/rule.rs
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
pub mod deplete_resources;
|
||||||
|
|
||||||
|
use tracing::info;
|
||||||
|
use rtsim2::RtState;
|
||||||
|
|
||||||
|
pub fn start_rules(rtstate: &mut RtState) {
|
||||||
|
info!("Starting server rtsim rules...");
|
||||||
|
rtstate.start_rule::<deplete_resources::State>();
|
||||||
|
}
|
47
server/src/rtsim2/rule/deplete_resources.rs
Normal file
47
server/src/rtsim2/rule/deplete_resources.rs
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
use tracing::info;
|
||||||
|
use rtsim2::{RtState, Rule, RuleError};
|
||||||
|
use crate::rtsim2::{
|
||||||
|
event::OnBlockChange,
|
||||||
|
ChunkStates,
|
||||||
|
};
|
||||||
|
use common::{
|
||||||
|
terrain::TerrainChunk,
|
||||||
|
vol::RectRasterableVol,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct State;
|
||||||
|
|
||||||
|
impl Rule for State {
|
||||||
|
fn start(rtstate: &mut RtState) -> Result<Self, RuleError> {
|
||||||
|
info!("Hello from the resource depletion rule!");
|
||||||
|
|
||||||
|
rtstate.bind::<Self, OnBlockChange>(|this, rtstate, event| {
|
||||||
|
let key = event.wpos
|
||||||
|
.xy()
|
||||||
|
.map2(TerrainChunk::RECT_SIZE, |e, sz| e.div_euclid(sz as i32));
|
||||||
|
if let Some(Some(chunk_state)) = rtstate.resource_mut::<ChunkStates>().0.get(key) {
|
||||||
|
let mut chunk_res = rtstate.data().nature.get_chunk_resources(key);
|
||||||
|
// Remove resources
|
||||||
|
if let Some(res) = event.old.get_rtsim_resource() {
|
||||||
|
if chunk_state.max_res[res] > 0 {
|
||||||
|
chunk_res[res] = (chunk_res[res] * chunk_state.max_res[res] as f32 - 1.0)
|
||||||
|
.round()
|
||||||
|
.max(0.0) / chunk_state.max_res[res] as f32;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Add resources
|
||||||
|
if let Some(res) = event.new.get_rtsim_resource() {
|
||||||
|
if chunk_state.max_res[res] > 0 {
|
||||||
|
chunk_res[res] = (chunk_res[res] * chunk_state.max_res[res] as f32 + 1.0)
|
||||||
|
.round()
|
||||||
|
.max(0.0) / chunk_state.max_res[res] as f32;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
println!("Chunk resources = {:?}", chunk_res);
|
||||||
|
rtstate.data_mut().nature.set_chunk_resources(key, chunk_res);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(Self)
|
||||||
|
}
|
||||||
|
}
|
@ -36,6 +36,8 @@ impl<'a> System<'a> for Sys {
|
|||||||
) {
|
) {
|
||||||
let rtsim = &mut *rtsim;
|
let rtsim = &mut *rtsim;
|
||||||
|
|
||||||
|
rtsim.state.tick(dt.0);
|
||||||
|
|
||||||
if rtsim.last_saved.map_or(true, |ls| ls.elapsed() > Duration::from_secs(60)) {
|
if rtsim.last_saved.map_or(true, |ls| ls.elapsed() > Duration::from_secs(60)) {
|
||||||
rtsim.save(&slow_jobs);
|
rtsim.save(&slow_jobs);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user