mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Added rtsim saving, chunk resources, chunk resource depletion
This commit is contained in:
parent
d5e324bded
commit
c168ff2f9b
27
Cargo.lock
generated
27
Cargo.lock
generated
@ -1839,6 +1839,27 @@ dependencies = [
|
|||||||
"syn 1.0.100",
|
"syn 1.0.100",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "enum-map"
|
||||||
|
version = "2.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f5a56d54c8dd9b3ad34752ed197a4eb2a6601bc010808eb097a04a58ae4c43e1"
|
||||||
|
dependencies = [
|
||||||
|
"enum-map-derive",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "enum-map-derive"
|
||||||
|
version = "0.10.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a9045e2676cd5af83c3b167d917b0a5c90a4d8e266e2683d6631b235c457fc27"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2 1.0.43",
|
||||||
|
"quote 1.0.21",
|
||||||
|
"syn 1.0.100",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "enumset"
|
name = "enumset"
|
||||||
version = "1.0.11"
|
version = "1.0.11"
|
||||||
@ -6655,6 +6676,8 @@ dependencies = [
|
|||||||
"crossbeam-utils 0.8.11",
|
"crossbeam-utils 0.8.11",
|
||||||
"csv",
|
"csv",
|
||||||
"dot_vox",
|
"dot_vox",
|
||||||
|
"enum-iterator 1.1.3",
|
||||||
|
"enum-map",
|
||||||
"fxhash",
|
"fxhash",
|
||||||
"hashbrown 0.12.3",
|
"hashbrown 0.12.3",
|
||||||
"indexmap",
|
"indexmap",
|
||||||
@ -6888,9 +6911,11 @@ dependencies = [
|
|||||||
name = "veloren-rtsim"
|
name = "veloren-rtsim"
|
||||||
version = "0.10.0"
|
version = "0.10.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"enum-map",
|
||||||
"hashbrown 0.12.3",
|
"hashbrown 0.12.3",
|
||||||
"ron 0.8.0",
|
"ron 0.8.0",
|
||||||
"serde",
|
"serde",
|
||||||
|
"vek 0.15.8",
|
||||||
"veloren-common",
|
"veloren-common",
|
||||||
"veloren-world",
|
"veloren-world",
|
||||||
]
|
]
|
||||||
@ -6907,6 +6932,7 @@ dependencies = [
|
|||||||
"chrono-tz",
|
"chrono-tz",
|
||||||
"crossbeam-channel",
|
"crossbeam-channel",
|
||||||
"drop_guard",
|
"drop_guard",
|
||||||
|
"enum-map",
|
||||||
"enumset",
|
"enumset",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"hashbrown 0.12.3",
|
"hashbrown 0.12.3",
|
||||||
@ -7120,6 +7146,7 @@ dependencies = [
|
|||||||
"csv",
|
"csv",
|
||||||
"deflate",
|
"deflate",
|
||||||
"enum-iterator 1.1.3",
|
"enum-iterator 1.1.3",
|
||||||
|
"enum-map",
|
||||||
"fallible-iterator",
|
"fallible-iterator",
|
||||||
"flate2",
|
"flate2",
|
||||||
"fxhash",
|
"fxhash",
|
||||||
|
@ -1855,6 +1855,7 @@ impl Client {
|
|||||||
true,
|
true,
|
||||||
None,
|
None,
|
||||||
&self.connected_server_constants,
|
&self.connected_server_constants,
|
||||||
|
|_, _, _, _| {},
|
||||||
);
|
);
|
||||||
// TODO: avoid emitting these in the first place
|
// TODO: avoid emitting these in the first place
|
||||||
let _ = self
|
let _ = self
|
||||||
|
@ -26,6 +26,8 @@ common-base = { package = "veloren-common-base", path = "base" }
|
|||||||
serde = { version = "1.0.110", features = ["derive", "rc"] }
|
serde = { version = "1.0.110", features = ["derive", "rc"] }
|
||||||
|
|
||||||
# Util
|
# Util
|
||||||
|
enum-iterator = "1.1.3"
|
||||||
|
enum-map = "2.4"
|
||||||
vek = { version = "0.15.8", features = ["serde"] }
|
vek = { version = "0.15.8", features = ["serde"] }
|
||||||
cfg-if = "1.0.0"
|
cfg-if = "1.0.0"
|
||||||
chrono = "0.4.22"
|
chrono = "0.4.22"
|
||||||
|
@ -8,7 +8,9 @@ use crate::{
|
|||||||
lottery::LootSpec,
|
lottery::LootSpec,
|
||||||
npc::{self, NPC_NAMES},
|
npc::{self, NPC_NAMES},
|
||||||
trade::SiteInformation,
|
trade::SiteInformation,
|
||||||
|
rtsim,
|
||||||
};
|
};
|
||||||
|
use enum_map::EnumMap;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use vek::*;
|
use vek::*;
|
||||||
|
|
||||||
@ -449,6 +451,7 @@ impl EntityInfo {
|
|||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct ChunkSupplement {
|
pub struct ChunkSupplement {
|
||||||
pub entities: Vec<EntityInfo>,
|
pub entities: Vec<EntityInfo>,
|
||||||
|
pub rtsim_max_resources: EnumMap<rtsim::ChunkResource, usize>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ChunkSupplement {
|
impl ChunkSupplement {
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
// module in `server`.
|
// module in `server`.
|
||||||
|
|
||||||
use specs::Component;
|
use specs::Component;
|
||||||
|
use serde::{Serialize, Deserialize};
|
||||||
use vek::*;
|
use vek::*;
|
||||||
|
|
||||||
use crate::comp::dialogue::MoodState;
|
use crate::comp::dialogue::MoodState;
|
||||||
@ -82,3 +83,10 @@ impl RtSimController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, Serialize, Deserialize, enum_map::Enum)]
|
||||||
|
pub enum ChunkResource {
|
||||||
|
Grass,
|
||||||
|
Flax,
|
||||||
|
Cotton,
|
||||||
|
}
|
||||||
|
@ -4,6 +4,7 @@ use crate::{
|
|||||||
consts::FRIC_GROUND,
|
consts::FRIC_GROUND,
|
||||||
lottery::LootSpec,
|
lottery::LootSpec,
|
||||||
make_case_elim,
|
make_case_elim,
|
||||||
|
rtsim,
|
||||||
};
|
};
|
||||||
use num_derive::FromPrimitive;
|
use num_derive::FromPrimitive;
|
||||||
use num_traits::FromPrimitive;
|
use num_traits::FromPrimitive;
|
||||||
@ -195,6 +196,26 @@ impl Block {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the rtsim resource, if any, that this block corresponds to. If you want the scarcity of a block to change with rtsim's resource depletion tracking, you can do so by editing this function.
|
||||||
|
#[inline]
|
||||||
|
pub fn get_rtsim_resource(&self) -> Option<rtsim::ChunkResource> {
|
||||||
|
match self.get_sprite()? {
|
||||||
|
SpriteKind::LongGrass
|
||||||
|
| SpriteKind::MediumGrass
|
||||||
|
| SpriteKind::ShortGrass
|
||||||
|
| SpriteKind::LargeGrass
|
||||||
|
| SpriteKind::GrassSnow
|
||||||
|
| SpriteKind::GrassBlue
|
||||||
|
| SpriteKind::SavannaGrass
|
||||||
|
| SpriteKind::TallSavannaGrass
|
||||||
|
| SpriteKind::RedSavannaGrass
|
||||||
|
| SpriteKind::JungleRedGrass => Some(rtsim::ChunkResource::Grass),
|
||||||
|
SpriteKind::WildFlax => Some(rtsim::ChunkResource::Flax),
|
||||||
|
SpriteKind::Cotton => Some(rtsim::ChunkResource::Cotton),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_glow(&self) -> Option<u8> {
|
pub fn get_glow(&self) -> Option<u8> {
|
||||||
match self.kind() {
|
match self.kind() {
|
||||||
|
@ -524,7 +524,12 @@ impl State {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Apply terrain changes
|
// Apply terrain changes
|
||||||
pub fn apply_terrain_changes(&self) { self.apply_terrain_changes_internal(false); }
|
pub fn apply_terrain_changes(
|
||||||
|
&self,
|
||||||
|
mut block_update: impl FnMut(&specs::World, Vec3<i32>, Block, Block),
|
||||||
|
) {
|
||||||
|
self.apply_terrain_changes_internal(false, block_update);
|
||||||
|
}
|
||||||
|
|
||||||
/// `during_tick` is true if and only if this is called from within
|
/// `during_tick` is true if and only if this is called from within
|
||||||
/// [State::tick].
|
/// [State::tick].
|
||||||
@ -534,7 +539,11 @@ impl State {
|
|||||||
/// from within both the client and the server ticks, right after
|
/// from within both the client and the server ticks, right after
|
||||||
/// handling terrain messages; currently, client sets it to true and
|
/// handling terrain messages; currently, client sets it to true and
|
||||||
/// server to false.
|
/// server to false.
|
||||||
fn apply_terrain_changes_internal(&self, during_tick: bool) {
|
fn apply_terrain_changes_internal(
|
||||||
|
&self,
|
||||||
|
during_tick: bool,
|
||||||
|
mut block_update: impl FnMut(&specs::World, Vec3<i32>, Block, Block),
|
||||||
|
) {
|
||||||
span!(
|
span!(
|
||||||
_guard,
|
_guard,
|
||||||
"apply_terrain_changes",
|
"apply_terrain_changes",
|
||||||
@ -575,14 +584,17 @@ impl State {
|
|||||||
}
|
}
|
||||||
// Apply block modifications
|
// Apply block modifications
|
||||||
// Only include in `TerrainChanges` if successful
|
// Only include in `TerrainChanges` if successful
|
||||||
modified_blocks.retain(|pos, block| {
|
modified_blocks.retain(|pos, new_block| {
|
||||||
let res = terrain.set(*pos, *block);
|
let res = terrain.map(*pos, |old_block| {
|
||||||
|
block_update(&self.ecs, *pos, old_block, *new_block);
|
||||||
|
*new_block
|
||||||
|
});
|
||||||
if let (&Ok(old_block), true) = (&res, during_tick) {
|
if let (&Ok(old_block), true) = (&res, during_tick) {
|
||||||
// NOTE: If the changes are applied during the tick, we push the *old* value as
|
// NOTE: If the changes are applied during the tick, we push the *old* value as
|
||||||
// the modified block (since it otherwise can't be recovered after the tick).
|
// the modified block (since it otherwise can't be recovered after the tick).
|
||||||
// Otherwise, the changes will be applied after the tick, so we push the *new*
|
// Otherwise, the changes will be applied after the tick, so we push the *new*
|
||||||
// value.
|
// value.
|
||||||
*block = old_block;
|
*new_block = old_block;
|
||||||
}
|
}
|
||||||
res.is_ok()
|
res.is_ok()
|
||||||
});
|
});
|
||||||
@ -597,6 +609,7 @@ impl State {
|
|||||||
update_terrain_and_regions: bool,
|
update_terrain_and_regions: bool,
|
||||||
mut metrics: Option<&mut StateTickMetrics>,
|
mut metrics: Option<&mut StateTickMetrics>,
|
||||||
server_constants: &ServerConstants,
|
server_constants: &ServerConstants,
|
||||||
|
block_update: impl FnMut(&specs::World, Vec3<i32>, Block, Block),
|
||||||
) {
|
) {
|
||||||
span!(_guard, "tick", "State::tick");
|
span!(_guard, "tick", "State::tick");
|
||||||
|
|
||||||
@ -643,7 +656,7 @@ impl State {
|
|||||||
drop(guard);
|
drop(guard);
|
||||||
|
|
||||||
if update_terrain_and_regions {
|
if update_terrain_and_regions {
|
||||||
self.apply_terrain_changes_internal(true);
|
self.apply_terrain_changes_internal(true, block_update);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Process local events
|
// Process local events
|
||||||
|
@ -9,3 +9,5 @@ world = { package = "veloren-world", path = "../world" }
|
|||||||
ron = "0.8"
|
ron = "0.8"
|
||||||
serde = { version = "1.0.110", features = ["derive"] }
|
serde = { version = "1.0.110", features = ["derive"] }
|
||||||
hashbrown = { version = "0.12", features = ["rayon", "serde", "nightly"] }
|
hashbrown = { version = "0.12", features = ["rayon", "serde", "nightly"] }
|
||||||
|
enum-map = { version = "2.4", features = ["serde"] }
|
||||||
|
vek = { version = "0.15.8", features = ["serde"] }
|
||||||
|
@ -1,13 +1,16 @@
|
|||||||
use hashbrown::HashMap;
|
use hashbrown::HashMap;
|
||||||
|
use serde::{Serialize, Deserialize};
|
||||||
|
|
||||||
#[derive(Copy, Clone, Hash, PartialEq, Eq)]
|
#[derive(Copy, Clone, Hash, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
pub struct ActorId {
|
pub struct ActorId {
|
||||||
pub idx: u32,
|
pub idx: u32,
|
||||||
pub gen: u32,
|
pub gen: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Serialize, Deserialize)]
|
||||||
pub struct Actor {}
|
pub struct Actor {}
|
||||||
|
|
||||||
|
#[derive(Clone, Serialize, Deserialize)]
|
||||||
pub struct Actors {
|
pub struct Actors {
|
||||||
pub actors: HashMap<ActorId, Actor>,
|
pub actors: HashMap<ActorId, Actor>,
|
||||||
}
|
}
|
||||||
|
@ -1,55 +0,0 @@
|
|||||||
use serde::{
|
|
||||||
de::{DeserializeOwned, Error},
|
|
||||||
Deserialize, Deserializer, Serialize, Serializer,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, Default, PartialEq, Eq, Hash)]
|
|
||||||
pub struct V<T>(pub T);
|
|
||||||
|
|
||||||
impl<T: Serialize> Serialize for V<T> {
|
|
||||||
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
|
||||||
self.0.serialize(serializer)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'de, T: Version> Deserialize<'de> for V<T> {
|
|
||||||
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
|
|
||||||
T::try_from_value_compat(ron::Value::deserialize(deserializer)?)
|
|
||||||
.map(Self)
|
|
||||||
.map_err(|e| D::Error::custom(e))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<U, T: Latest<U>> Latest<U> for V<T> {
|
|
||||||
fn to_unversioned(self) -> U { self.0.to_unversioned() }
|
|
||||||
|
|
||||||
fn from_unversioned(x: &U) -> Self { Self(T::from_unversioned(x)) }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait Latest<T> {
|
|
||||||
fn to_unversioned(self) -> T;
|
|
||||||
fn from_unversioned(x: &T) -> Self;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait Version: Sized + DeserializeOwned {
|
|
||||||
type Prev: Version;
|
|
||||||
|
|
||||||
fn migrate(prev: Self::Prev) -> Self;
|
|
||||||
|
|
||||||
fn try_from_value_compat(value: ron::Value) -> Result<Self, ron::Error> {
|
|
||||||
value.clone().into_rust().or_else(|e| {
|
|
||||||
Ok(Self::migrate(
|
|
||||||
<Self as Version>::Prev::try_from_value_compat(value).map_err(|_| e)?,
|
|
||||||
))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
|
||||||
pub enum Bottom {}
|
|
||||||
|
|
||||||
impl Version for Bottom {
|
|
||||||
type Prev = Self;
|
|
||||||
|
|
||||||
fn migrate(prev: Self::Prev) -> Self { prev }
|
|
||||||
}
|
|
@ -1,6 +1,3 @@
|
|||||||
pub mod helper;
|
|
||||||
pub mod version;
|
|
||||||
|
|
||||||
pub mod actor;
|
pub mod actor;
|
||||||
pub mod nature;
|
pub mod nature;
|
||||||
|
|
||||||
@ -11,9 +8,10 @@ pub use self::{
|
|||||||
|
|
||||||
use self::helper::Latest;
|
use self::helper::Latest;
|
||||||
use ron::error::SpannedResult;
|
use ron::error::SpannedResult;
|
||||||
use serde::Deserialize;
|
use serde::{Serialize, Deserialize};
|
||||||
use std::io::{Read, Write};
|
use std::io::{Read, Write};
|
||||||
|
|
||||||
|
#[derive(Clone, Serialize, Deserialize)]
|
||||||
pub struct Data {
|
pub struct Data {
|
||||||
pub nature: Nature,
|
pub nature: Nature,
|
||||||
pub actors: Actors,
|
pub actors: Actors,
|
||||||
@ -21,10 +19,10 @@ pub struct Data {
|
|||||||
|
|
||||||
impl Data {
|
impl Data {
|
||||||
pub fn from_reader<R: Read>(reader: R) -> SpannedResult<Self> {
|
pub fn from_reader<R: Read>(reader: R) -> SpannedResult<Self> {
|
||||||
ron::de::from_reader(reader).map(version::LatestData::to_unversioned)
|
ron::de::from_reader(reader)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn write_to<W: Write>(&self, writer: W) -> Result<(), ron::Error> {
|
pub fn write_to<W: Write>(&self, writer: W) -> Result<(), ron::Error> {
|
||||||
ron::ser::to_writer(writer, &version::LatestData::from_unversioned(self))
|
ron::ser::to_writer(writer, self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1 +1,44 @@
|
|||||||
pub struct Nature {}
|
use serde::{Serialize, Deserialize};
|
||||||
|
use enum_map::EnumMap;
|
||||||
|
use common::{
|
||||||
|
grid::Grid,
|
||||||
|
rtsim::ChunkResource,
|
||||||
|
};
|
||||||
|
use world::World;
|
||||||
|
use vek::*;
|
||||||
|
|
||||||
|
#[derive(Clone, Serialize, Deserialize)]
|
||||||
|
pub struct Nature {
|
||||||
|
chunks: Grid<Chunk>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Nature {
|
||||||
|
pub fn generate(world: &World) -> Self {
|
||||||
|
Self {
|
||||||
|
chunks: Grid::populate_from(
|
||||||
|
world.sim().get_size().map(|e| e as i32),
|
||||||
|
|pos| Chunk {
|
||||||
|
res: EnumMap::<_, f32>::default().map(|_, _| 1.0),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Clean up this API a bit
|
||||||
|
pub fn get_chunk_resources(&self, key: Vec2<i32>) -> EnumMap<ChunkResource, f32> {
|
||||||
|
self.chunks
|
||||||
|
.get(key)
|
||||||
|
.map(|c| c.res)
|
||||||
|
.unwrap_or_default()
|
||||||
|
}
|
||||||
|
pub fn set_chunk_resources(&mut self, key: Vec2<i32>, res: EnumMap<ChunkResource, f32>) {
|
||||||
|
if let Some(chunk) = self.chunks.get_mut(key) {
|
||||||
|
chunk.res = res;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Serialize, Deserialize)]
|
||||||
|
pub struct Chunk {
|
||||||
|
res: EnumMap<ChunkResource, f32>,
|
||||||
|
}
|
||||||
|
@ -1,85 +0,0 @@
|
|||||||
use super::*;
|
|
||||||
use crate::data::{Actor, ActorId, Actors};
|
|
||||||
use hashbrown::HashMap;
|
|
||||||
|
|
||||||
// ActorId
|
|
||||||
|
|
||||||
impl Latest<ActorId> for ActorIdV0 {
|
|
||||||
fn to_unversioned(self) -> ActorId {
|
|
||||||
ActorId {
|
|
||||||
idx: self.idx,
|
|
||||||
gen: self.gen,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn from_unversioned(id: &ActorId) -> Self {
|
|
||||||
Self {
|
|
||||||
idx: id.idx,
|
|
||||||
gen: id.gen,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Copy, Clone, Hash, PartialEq, Eq)]
|
|
||||||
pub struct ActorIdV0 {
|
|
||||||
pub idx: u32,
|
|
||||||
pub gen: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Version for ActorIdV0 {
|
|
||||||
type Prev = Bottom;
|
|
||||||
|
|
||||||
fn migrate(x: Self::Prev) -> Self { match x {} }
|
|
||||||
}
|
|
||||||
|
|
||||||
// Actor
|
|
||||||
|
|
||||||
impl Latest<Actor> for ActorV0 {
|
|
||||||
fn to_unversioned(self) -> Actor { Actor {} }
|
|
||||||
|
|
||||||
fn from_unversioned(actor: &Actor) -> Self { Self {} }
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Copy, Clone, Hash, PartialEq, Eq)]
|
|
||||||
pub struct ActorV0 {}
|
|
||||||
|
|
||||||
impl Version for ActorV0 {
|
|
||||||
type Prev = Bottom;
|
|
||||||
|
|
||||||
fn migrate(x: Self::Prev) -> Self { match x {} }
|
|
||||||
}
|
|
||||||
|
|
||||||
// Actors
|
|
||||||
|
|
||||||
impl Latest<Actors> for ActorsV0 {
|
|
||||||
fn to_unversioned(self) -> Actors {
|
|
||||||
Actors {
|
|
||||||
actors: self
|
|
||||||
.actors
|
|
||||||
.into_iter()
|
|
||||||
.map(|(k, v)| (k.to_unversioned(), v.to_unversioned()))
|
|
||||||
.collect(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn from_unversioned(actors: &Actors) -> Self {
|
|
||||||
Self {
|
|
||||||
actors: actors
|
|
||||||
.actors
|
|
||||||
.iter()
|
|
||||||
.map(|(k, v)| (Latest::from_unversioned(k), Latest::from_unversioned(v)))
|
|
||||||
.collect(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
|
||||||
pub struct ActorsV0 {
|
|
||||||
actors: HashMap<V<ActorIdV0>, V<ActorV0>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Version for ActorsV0 {
|
|
||||||
type Prev = Bottom;
|
|
||||||
|
|
||||||
fn migrate(x: Self::Prev) -> Self { match x {} }
|
|
||||||
}
|
|
@ -1,84 +0,0 @@
|
|||||||
// # Hey, you! Yes, you!
|
|
||||||
//
|
|
||||||
// Don't touch anything in this module, or any sub-modules. No, really. Bad
|
|
||||||
// stuff will happen.
|
|
||||||
//
|
|
||||||
// You're only an exception to this rule if you fulfil the following criteria:
|
|
||||||
//
|
|
||||||
// - You *really* understand exactly how the versioning system in `helper.rs`
|
|
||||||
// works, what assumptions it makes, and how all of this can go badly wrong.
|
|
||||||
//
|
|
||||||
// - You are creating a new version of a data structure, and *not* modifying an
|
|
||||||
// existing one.
|
|
||||||
//
|
|
||||||
// - You've thought really carefully about things and you've come to the
|
|
||||||
// conclusion that there's just no way to add the feature you want to add
|
|
||||||
// without creating a new version of the data structure in question.
|
|
||||||
//
|
|
||||||
// Please note that in *very specific* cases, it is possible to make a change to
|
|
||||||
// an existing data structure that is backward-compatible. For example, adding a
|
|
||||||
// new variant to an enum or a new field to a struct (where said field is
|
|
||||||
// annotated with `#[serde(default)]`) is generally considered to be a
|
|
||||||
// backward-compatible change.
|
|
||||||
//
|
|
||||||
// That said, here's how to make a breaking change to one of the structures in
|
|
||||||
// this module, or submodules.
|
|
||||||
//
|
|
||||||
// 1) Duplicate the latest version of the data structure and the `Version` impl
|
|
||||||
// for it (later versions should be kept at the top of each file).
|
|
||||||
//
|
|
||||||
// 2) Rename the duplicated version, incrementing the version number (i.e: V0
|
|
||||||
// becomes V1).
|
|
||||||
//
|
|
||||||
// 3) Change the `type Prev =` associated type in the new `Version` impl to the
|
|
||||||
// previous versions' type. You will need to write an implementation of
|
|
||||||
// `migrate` that migrates from the old version to the new version.
|
|
||||||
//
|
|
||||||
// 4) *Change* the existing `Latest` impl so that it uses the new version you
|
|
||||||
// have created.
|
|
||||||
//
|
|
||||||
// 5) If your data structure is contained within another data structure, you
|
|
||||||
// will need to similarly update the parent data structure too, also
|
|
||||||
// following these instructions.
|
|
||||||
//
|
|
||||||
// The *golden rule* is that, once merged to master, an old version's type must
|
|
||||||
// not be changed!
|
|
||||||
|
|
||||||
pub mod actor;
|
|
||||||
pub mod nature;
|
|
||||||
|
|
||||||
use super::{
|
|
||||||
helper::{Bottom, Latest, Version, V},
|
|
||||||
Data,
|
|
||||||
};
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
pub type LatestData = DataV0;
|
|
||||||
|
|
||||||
impl Latest<Data> for LatestData {
|
|
||||||
fn to_unversioned(self) -> Data {
|
|
||||||
Data {
|
|
||||||
nature: self.nature.to_unversioned(),
|
|
||||||
actors: self.actors.to_unversioned(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn from_unversioned(data: &Data) -> Self {
|
|
||||||
Self {
|
|
||||||
nature: Latest::from_unversioned(&data.nature),
|
|
||||||
actors: Latest::from_unversioned(&data.actors),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
|
||||||
pub struct DataV0 {
|
|
||||||
nature: V<nature::NatureV0>,
|
|
||||||
actors: V<actor::ActorsV0>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Version for DataV0 {
|
|
||||||
type Prev = Bottom;
|
|
||||||
|
|
||||||
fn migrate(x: Self::Prev) -> Self { match x {} }
|
|
||||||
}
|
|
@ -1,17 +0,0 @@
|
|||||||
use super::*;
|
|
||||||
use crate::data::Nature;
|
|
||||||
|
|
||||||
impl Latest<crate::data::Nature> for NatureV0 {
|
|
||||||
fn to_unversioned(self) -> Nature { Nature {} }
|
|
||||||
|
|
||||||
fn from_unversioned(nature: &Nature) -> Self { Self {} }
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
|
||||||
pub struct NatureV0 {}
|
|
||||||
|
|
||||||
impl Version for NatureV0 {
|
|
||||||
type Prev = Bottom;
|
|
||||||
|
|
||||||
fn migrate(x: Self::Prev) -> Self { match x {} }
|
|
||||||
}
|
|
@ -5,7 +5,7 @@ use world::World;
|
|||||||
impl Data {
|
impl Data {
|
||||||
pub fn generate(world: &World) -> Self {
|
pub fn generate(world: &World) -> Self {
|
||||||
Self {
|
Self {
|
||||||
nature: Nature {},
|
nature: Nature::generate(world),
|
||||||
actors: Actors {
|
actors: Actors {
|
||||||
actors: HashMap::default(),
|
actors: HashMap::default(),
|
||||||
},
|
},
|
||||||
|
@ -64,6 +64,7 @@ authc = { git = "https://gitlab.com/veloren/auth.git", rev = "fb3dcbc4962b367253
|
|||||||
slab = "0.4"
|
slab = "0.4"
|
||||||
rand_distr = "0.4.0"
|
rand_distr = "0.4.0"
|
||||||
enumset = "1.0.8"
|
enumset = "1.0.8"
|
||||||
|
enum-map = "2.4"
|
||||||
noise = { version = "0.7", default-features = false }
|
noise = { version = "0.7", default-features = false }
|
||||||
censor = "0.2"
|
censor = "0.2"
|
||||||
|
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
use crate::metrics::ChunkGenMetrics;
|
use crate::{
|
||||||
|
metrics::ChunkGenMetrics,
|
||||||
|
rtsim2::RtSim,
|
||||||
|
};
|
||||||
#[cfg(not(feature = "worldgen"))]
|
#[cfg(not(feature = "worldgen"))]
|
||||||
use crate::test_world::{IndexOwned, World};
|
use crate::test_world::{IndexOwned, World};
|
||||||
use common::{
|
use common::{
|
||||||
@ -44,6 +47,10 @@ impl ChunkGenerator {
|
|||||||
key: Vec2<i32>,
|
key: Vec2<i32>,
|
||||||
slowjob_pool: &SlowJobPool,
|
slowjob_pool: &SlowJobPool,
|
||||||
world: Arc<World>,
|
world: Arc<World>,
|
||||||
|
#[cfg(feature = "worldgen")]
|
||||||
|
rtsim: &RtSim,
|
||||||
|
#[cfg(not(feature = "worldgen"))]
|
||||||
|
rtsim: &(),
|
||||||
index: IndexOwned,
|
index: IndexOwned,
|
||||||
time: (TimeOfDay, Calendar),
|
time: (TimeOfDay, Calendar),
|
||||||
) {
|
) {
|
||||||
@ -56,10 +63,17 @@ impl ChunkGenerator {
|
|||||||
v.insert(Arc::clone(&cancel));
|
v.insert(Arc::clone(&cancel));
|
||||||
let chunk_tx = self.chunk_tx.clone();
|
let chunk_tx = self.chunk_tx.clone();
|
||||||
self.metrics.chunks_requested.inc();
|
self.metrics.chunks_requested.inc();
|
||||||
|
|
||||||
|
// Get state for this chunk from rtsim
|
||||||
|
#[cfg(feature = "worldgen")]
|
||||||
|
let rtsim_resources = Some(rtsim.get_chunk_resources(key));
|
||||||
|
#[cfg(not(feature = "worldgen"))]
|
||||||
|
let rtsim_resources = None;
|
||||||
|
|
||||||
slowjob_pool.spawn("CHUNK_GENERATOR", move || {
|
slowjob_pool.spawn("CHUNK_GENERATOR", move || {
|
||||||
let index = index.as_index_ref();
|
let index = index.as_index_ref();
|
||||||
let payload = world
|
let payload = world
|
||||||
.generate_chunk(index, key, || cancel.load(Ordering::Relaxed), Some(time))
|
.generate_chunk(index, key, rtsim_resources, || cancel.load(Ordering::Relaxed), Some(time))
|
||||||
// FIXME: Since only the first entity who cancels a chunk is notified, we end up
|
// FIXME: Since only the first entity who cancels a chunk is notified, we end up
|
||||||
// delaying chunk re-requests for up to 3 seconds for other clients, which isn't
|
// delaying chunk re-requests for up to 3 seconds for other clients, which isn't
|
||||||
// great. We *could* store all the other requesting clients here, but it could
|
// great. We *could* store all the other requesting clients here, but it could
|
||||||
|
@ -82,7 +82,7 @@ use common::{
|
|||||||
rtsim::RtSimEntity,
|
rtsim::RtSimEntity,
|
||||||
shared_server_config::ServerConstants,
|
shared_server_config::ServerConstants,
|
||||||
slowjob::SlowJobPool,
|
slowjob::SlowJobPool,
|
||||||
terrain::{TerrainChunk, TerrainChunkSize},
|
terrain::{TerrainChunk, TerrainChunkSize, Block},
|
||||||
vol::RectRasterableVol,
|
vol::RectRasterableVol,
|
||||||
};
|
};
|
||||||
use common_ecs::run_now;
|
use common_ecs::run_now;
|
||||||
@ -342,6 +342,7 @@ impl Server {
|
|||||||
pool.configure("CHUNK_DROP", |_n| 1);
|
pool.configure("CHUNK_DROP", |_n| 1);
|
||||||
pool.configure("CHUNK_GENERATOR", |n| n / 2 + n / 4);
|
pool.configure("CHUNK_GENERATOR", |n| n / 2 + n / 4);
|
||||||
pool.configure("CHUNK_SERIALIZER", |n| n / 2);
|
pool.configure("CHUNK_SERIALIZER", |n| n / 2);
|
||||||
|
pool.configure("RTSIM_SAVE", |_| 1);
|
||||||
}
|
}
|
||||||
state
|
state
|
||||||
.ecs_mut()
|
.ecs_mut()
|
||||||
@ -700,6 +701,13 @@ impl Server {
|
|||||||
|
|
||||||
let before_state_tick = Instant::now();
|
let before_state_tick = Instant::now();
|
||||||
|
|
||||||
|
fn on_block_update(ecs: &specs::World, wpos: Vec3<i32>, old_block: Block, new_block: Block) {
|
||||||
|
// When a resource block updates, inform rtsim
|
||||||
|
if old_block.get_rtsim_resource().is_some() || new_block.get_rtsim_resource().is_some() {
|
||||||
|
ecs.write_resource::<rtsim2::RtSim>().hook_block_update(wpos, old_block, new_block);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 4) Tick the server's LocalState.
|
// 4) Tick the server's LocalState.
|
||||||
// 5) Fetch any generated `TerrainChunk`s and insert them into the terrain.
|
// 5) Fetch any generated `TerrainChunk`s and insert them into the terrain.
|
||||||
// in sys/terrain.rs
|
// in sys/terrain.rs
|
||||||
@ -726,6 +734,7 @@ impl Server {
|
|||||||
false,
|
false,
|
||||||
Some(&mut state_tick_metrics),
|
Some(&mut state_tick_metrics),
|
||||||
&self.server_constants,
|
&self.server_constants,
|
||||||
|
on_block_update,
|
||||||
);
|
);
|
||||||
|
|
||||||
let before_handle_events = Instant::now();
|
let before_handle_events = Instant::now();
|
||||||
@ -749,7 +758,7 @@ impl Server {
|
|||||||
self.state.update_region_map();
|
self.state.update_region_map();
|
||||||
// NOTE: apply_terrain_changes sends the *new* value since it is not being
|
// NOTE: apply_terrain_changes sends the *new* value since it is not being
|
||||||
// synchronized during the tick.
|
// synchronized during the tick.
|
||||||
self.state.apply_terrain_changes();
|
self.state.apply_terrain_changes(on_block_update);
|
||||||
|
|
||||||
let before_sync = Instant::now();
|
let before_sync = Instant::now();
|
||||||
|
|
||||||
@ -994,6 +1003,10 @@ impl Server {
|
|||||||
let mut chunk_generator = ecs.write_resource::<ChunkGenerator>();
|
let mut chunk_generator = ecs.write_resource::<ChunkGenerator>();
|
||||||
let client = ecs.read_storage::<Client>();
|
let client = ecs.read_storage::<Client>();
|
||||||
let mut terrain = ecs.write_resource::<common::terrain::TerrainGrid>();
|
let mut terrain = ecs.write_resource::<common::terrain::TerrainGrid>();
|
||||||
|
#[cfg(feature = "worldgen")]
|
||||||
|
let rtsim = ecs.read_resource::<rtsim2::RtSim>();
|
||||||
|
#[cfg(not(feature = "worldgen"))]
|
||||||
|
let rtsim = ();
|
||||||
|
|
||||||
// Cancel all pending chunks.
|
// Cancel all pending chunks.
|
||||||
chunk_generator.cancel_all();
|
chunk_generator.cancel_all();
|
||||||
@ -1009,6 +1022,7 @@ impl Server {
|
|||||||
pos,
|
pos,
|
||||||
&slow_jobs,
|
&slow_jobs,
|
||||||
Arc::clone(world),
|
Arc::clone(world),
|
||||||
|
&rtsim,
|
||||||
index.clone(),
|
index.clone(),
|
||||||
(
|
(
|
||||||
*ecs.read_resource::<TimeOfDay>(),
|
*ecs.read_resource::<TimeOfDay>(),
|
||||||
@ -1172,11 +1186,16 @@ impl Server {
|
|||||||
pub fn generate_chunk(&mut self, entity: EcsEntity, key: Vec2<i32>) {
|
pub fn generate_chunk(&mut self, entity: EcsEntity, key: Vec2<i32>) {
|
||||||
let ecs = self.state.ecs();
|
let ecs = self.state.ecs();
|
||||||
let slow_jobs = ecs.read_resource::<SlowJobPool>();
|
let slow_jobs = ecs.read_resource::<SlowJobPool>();
|
||||||
|
#[cfg(feature = "worldgen")]
|
||||||
|
let rtsim = ecs.read_resource::<rtsim2::RtSim>();
|
||||||
|
#[cfg(not(feature = "worldgen"))]
|
||||||
|
let rtsim = ();
|
||||||
ecs.write_resource::<ChunkGenerator>().generate_chunk(
|
ecs.write_resource::<ChunkGenerator>().generate_chunk(
|
||||||
Some(entity),
|
Some(entity),
|
||||||
key,
|
key,
|
||||||
&slow_jobs,
|
&slow_jobs,
|
||||||
Arc::clone(&self.world),
|
Arc::clone(&self.world),
|
||||||
|
&rtsim,
|
||||||
self.index.clone(),
|
self.index.clone(),
|
||||||
(
|
(
|
||||||
*ecs.read_resource::<TimeOfDay>(),
|
*ecs.read_resource::<TimeOfDay>(),
|
||||||
|
@ -1,17 +1,31 @@
|
|||||||
pub mod tick;
|
pub mod tick;
|
||||||
|
|
||||||
use common::grid::Grid;
|
use common::{
|
||||||
|
grid::Grid,
|
||||||
|
slowjob::SlowJobPool,
|
||||||
|
rtsim::ChunkResource,
|
||||||
|
terrain::{TerrainChunk, Block},
|
||||||
|
vol::RectRasterableVol,
|
||||||
|
};
|
||||||
use common_ecs::{dispatch, System};
|
use common_ecs::{dispatch, System};
|
||||||
use rtsim2::{data::Data, RtState};
|
use rtsim2::{data::Data, RtState};
|
||||||
use specs::{DispatcherBuilder, WorldExt};
|
use specs::{DispatcherBuilder, WorldExt};
|
||||||
use std::{fs::File, io, path::PathBuf, sync::Arc};
|
use std::{
|
||||||
use tracing::info;
|
fs::{self, File},
|
||||||
|
path::PathBuf,
|
||||||
|
sync::Arc,
|
||||||
|
time::Instant,
|
||||||
|
io::{self, Write},
|
||||||
|
};
|
||||||
|
use enum_map::EnumMap;
|
||||||
|
use tracing::{error, warn, info};
|
||||||
use vek::*;
|
use vek::*;
|
||||||
use world::World;
|
use world::World;
|
||||||
|
|
||||||
pub struct RtSim {
|
pub struct RtSim {
|
||||||
file_path: PathBuf,
|
file_path: PathBuf,
|
||||||
chunk_states: Grid<bool>, // true = loaded
|
last_saved: Option<Instant>,
|
||||||
|
chunk_states: Grid<Option<LoadedChunkState>>,
|
||||||
state: RtState,
|
state: RtState,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -20,21 +34,47 @@ impl RtSim {
|
|||||||
let file_path = Self::get_file_path(data_dir);
|
let file_path = Self::get_file_path(data_dir);
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
chunk_states: Grid::populate_from(world.sim().get_size().as_(), |_| false),
|
chunk_states: Grid::populate_from(world.sim().get_size().as_(), |_| None),
|
||||||
|
last_saved: None,
|
||||||
state: RtState {
|
state: RtState {
|
||||||
data: {
|
data: {
|
||||||
info!("Looking for rtsim state in {}...", file_path.display());
|
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. Attending to load...");
|
info!("Rtsim state found. Attempting to load...");
|
||||||
Data::from_reader(file)?
|
match Data::from_reader(file) {
|
||||||
|
Ok(data) => { info!("Rtsim state loaded."); break 'load data },
|
||||||
|
Err(e) => {
|
||||||
|
error!("Rtsim state failed to load: {}", e);
|
||||||
|
let mut i = 0;
|
||||||
|
loop {
|
||||||
|
let mut backup_path = file_path.clone();
|
||||||
|
backup_path.set_extension(if i == 0 {
|
||||||
|
format!("ron_backup_{}", i)
|
||||||
|
} else {
|
||||||
|
"ron_backup".to_string()
|
||||||
|
});
|
||||||
|
if !backup_path.exists() {
|
||||||
|
fs::rename(&file_path, &backup_path)?;
|
||||||
|
warn!("Failed rtsim state was moved to {}", backup_path.display());
|
||||||
|
info!("A fresh rtsim state will now be generated.");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
Err(e) if e.kind() == io::ErrorKind::NotFound => {
|
}
|
||||||
info!("No rtsim state found. Generating from initial world state...");
|
|
||||||
Data::generate(&world)
|
|
||||||
},
|
},
|
||||||
|
Err(e) if e.kind() == io::ErrorKind::NotFound =>
|
||||||
|
info!("No rtsim state found. Generating from initial world state..."),
|
||||||
Err(e) => return Err(e.into()),
|
Err(e) => return Err(e.into()),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let data = Data::generate(&world);
|
||||||
|
info!("Rtsim state generated.");
|
||||||
|
data
|
||||||
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
file_path,
|
file_path,
|
||||||
@ -52,17 +92,88 @@ impl RtSim {
|
|||||||
path
|
path
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn hook_load_chunk(&mut self, key: Vec2<i32>) {
|
pub fn hook_load_chunk(&mut self, key: Vec2<i32>, max_res: EnumMap<ChunkResource, usize>) {
|
||||||
if let Some(is_loaded) = self.chunk_states.get_mut(key) {
|
if let Some(chunk_state) = self.chunk_states.get_mut(key) {
|
||||||
*is_loaded = true;
|
*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(is_loaded) = self.chunk_states.get_mut(key) {
|
if let Some(chunk_state) = self.chunk_states.get_mut(key) {
|
||||||
*is_loaded = false;
|
*chunk_state = None;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn save(&mut self, slowjob_pool: &SlowJobPool) {
|
||||||
|
info!("Beginning rtsim state save...");
|
||||||
|
let file_path = self.file_path.clone();
|
||||||
|
let data = self.state.data.clone();
|
||||||
|
info!("Starting rtsim save job...");
|
||||||
|
// TODO: Use slow job
|
||||||
|
// slowjob_pool.spawn("RTSIM_SAVE", move || {
|
||||||
|
std::thread::spawn(move || {
|
||||||
|
let tmp_file_name = "state_tmp.ron";
|
||||||
|
if let Err(e) = file_path
|
||||||
|
.parent()
|
||||||
|
.map(|dir| {
|
||||||
|
fs::create_dir_all(dir)?;
|
||||||
|
// We write to a temporary file and then rename to avoid corruption.
|
||||||
|
Ok(dir.join(tmp_file_name))
|
||||||
|
})
|
||||||
|
.unwrap_or_else(|| Ok(tmp_file_name.into()))
|
||||||
|
.and_then(|tmp_file_path| {
|
||||||
|
Ok((File::create(&tmp_file_path)?, tmp_file_path))
|
||||||
|
})
|
||||||
|
.map_err(|e: io::Error| ron::Error::from(e))
|
||||||
|
.and_then(|(mut file, tmp_file_path)| {
|
||||||
|
info!("Writing rtsim state to file...");
|
||||||
|
data.write_to(&mut file)?;
|
||||||
|
file.flush()?;
|
||||||
|
drop(file);
|
||||||
|
fs::rename(tmp_file_path, file_path)?;
|
||||||
|
info!("Rtsim state saved.");
|
||||||
|
Ok(())
|
||||||
|
})
|
||||||
|
{
|
||||||
|
error!("Saving rtsim state failed: {}", e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
self.last_saved = Some(Instant::now());
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Clean up this API a bit
|
||||||
|
pub fn get_chunk_resources(&self, key: Vec2<i32>) -> EnumMap<ChunkResource, f32> {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct LoadedChunkState {
|
||||||
|
// The maximum possible number of each resource in this chunk
|
||||||
|
max_res: EnumMap<ChunkResource, usize>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_server_systems(dispatch_builder: &mut DispatcherBuilder) {
|
pub fn add_server_systems(dispatch_builder: &mut DispatcherBuilder) {
|
||||||
|
@ -7,10 +7,11 @@ use common::{
|
|||||||
event::{EventBus, ServerEvent},
|
event::{EventBus, ServerEvent},
|
||||||
generation::{BodyBuilder, EntityConfig, EntityInfo},
|
generation::{BodyBuilder, EntityConfig, EntityInfo},
|
||||||
resources::{DeltaTime, Time},
|
resources::{DeltaTime, Time},
|
||||||
|
slowjob::SlowJobPool,
|
||||||
};
|
};
|
||||||
use common_ecs::{Job, Origin, Phase, System};
|
use common_ecs::{Job, Origin, Phase, System};
|
||||||
use specs::{Join, Read, ReadExpect, ReadStorage, WriteExpect, WriteStorage};
|
use specs::{Join, Read, ReadExpect, ReadStorage, WriteExpect, WriteStorage};
|
||||||
use std::sync::Arc;
|
use std::{sync::Arc, time::Duration};
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct Sys;
|
pub struct Sys;
|
||||||
@ -22,6 +23,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
WriteExpect<'a, RtSim>,
|
WriteExpect<'a, RtSim>,
|
||||||
ReadExpect<'a, Arc<world::World>>,
|
ReadExpect<'a, Arc<world::World>>,
|
||||||
ReadExpect<'a, world::IndexOwned>,
|
ReadExpect<'a, world::IndexOwned>,
|
||||||
|
ReadExpect<'a, SlowJobPool>,
|
||||||
);
|
);
|
||||||
|
|
||||||
const NAME: &'static str = "rtsim::tick";
|
const NAME: &'static str = "rtsim::tick";
|
||||||
@ -30,9 +32,14 @@ impl<'a> System<'a> for Sys {
|
|||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
_job: &mut Job<Self>,
|
_job: &mut Job<Self>,
|
||||||
(dt, time, server_event_bus, mut rtsim, world, index): Self::SystemData,
|
(dt, time, server_event_bus, mut rtsim, world, index, slow_jobs): Self::SystemData,
|
||||||
) {
|
) {
|
||||||
let rtsim = &mut *rtsim;
|
let rtsim = &mut *rtsim;
|
||||||
|
|
||||||
|
if rtsim.last_saved.map_or(true, |ls| ls.elapsed() > Duration::from_secs(60)) {
|
||||||
|
rtsim.save(&slow_jobs);
|
||||||
|
}
|
||||||
|
|
||||||
// rtsim.tick += 1;
|
// rtsim.tick += 1;
|
||||||
|
|
||||||
// Update unloaded rtsim entities, in groups at a time
|
// Update unloaded rtsim entities, in groups at a time
|
||||||
|
@ -7,6 +7,7 @@ use crate::{
|
|||||||
presence::{Presence, RepositionOnChunkLoad},
|
presence::{Presence, RepositionOnChunkLoad},
|
||||||
settings::Settings,
|
settings::Settings,
|
||||||
sys::sentinel::DeletedEntities,
|
sys::sentinel::DeletedEntities,
|
||||||
|
rtsim2::RtSim,
|
||||||
wiring, BattleModeBuffer, SpawnPoint,
|
wiring, BattleModeBuffer, SpawnPoint,
|
||||||
};
|
};
|
||||||
use common::{
|
use common::{
|
||||||
@ -497,6 +498,10 @@ impl StateExt for State {
|
|||||||
{
|
{
|
||||||
let ecs = self.ecs();
|
let ecs = self.ecs();
|
||||||
let slow_jobs = ecs.write_resource::<SlowJobPool>();
|
let slow_jobs = ecs.write_resource::<SlowJobPool>();
|
||||||
|
#[cfg(feature = "worldgen")]
|
||||||
|
let rtsim = ecs.read_resource::<RtSim>();
|
||||||
|
#[cfg(not(feature = "worldgen"))]
|
||||||
|
let rtsim = ();
|
||||||
let mut chunk_generator =
|
let mut chunk_generator =
|
||||||
ecs.write_resource::<crate::chunk_generator::ChunkGenerator>();
|
ecs.write_resource::<crate::chunk_generator::ChunkGenerator>();
|
||||||
let chunk_pos = self.terrain().pos_key(pos.0.map(|e| e as i32));
|
let chunk_pos = self.terrain().pos_key(pos.0.map(|e| e as i32));
|
||||||
@ -517,7 +522,7 @@ impl StateExt for State {
|
|||||||
#[cfg(feature = "worldgen")]
|
#[cfg(feature = "worldgen")]
|
||||||
{
|
{
|
||||||
let time = (*ecs.read_resource::<TimeOfDay>(), (*ecs.read_resource::<Calendar>()).clone());
|
let time = (*ecs.read_resource::<TimeOfDay>(), (*ecs.read_resource::<Calendar>()).clone());
|
||||||
chunk_generator.generate_chunk(None, chunk_key, &slow_jobs, Arc::clone(world), index.clone(), time);
|
chunk_generator.generate_chunk(None, chunk_key, &slow_jobs, Arc::clone(world), &rtsim, index.clone(), time);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -112,7 +112,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
mut terrain_changes,
|
mut terrain_changes,
|
||||||
mut chunk_requests,
|
mut chunk_requests,
|
||||||
//mut rtsim,
|
//mut rtsim,
|
||||||
mut rtsim2,
|
mut rtsim,
|
||||||
mut _terrain_persistence,
|
mut _terrain_persistence,
|
||||||
mut positions,
|
mut positions,
|
||||||
presences,
|
presences,
|
||||||
@ -137,6 +137,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
request.key,
|
request.key,
|
||||||
&slow_jobs,
|
&slow_jobs,
|
||||||
Arc::clone(&world),
|
Arc::clone(&world),
|
||||||
|
&rtsim,
|
||||||
index.clone(),
|
index.clone(),
|
||||||
(*time_of_day, calendar.clone()),
|
(*time_of_day, calendar.clone()),
|
||||||
)
|
)
|
||||||
@ -182,7 +183,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
} else {
|
} else {
|
||||||
terrain_changes.new_chunks.insert(key);
|
terrain_changes.new_chunks.insert(key);
|
||||||
#[cfg(feature = "worldgen")]
|
#[cfg(feature = "worldgen")]
|
||||||
rtsim2.hook_load_chunk(key);
|
rtsim.hook_load_chunk(key, supplement.rtsim_max_resources);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle chunk supplement
|
// Handle chunk supplement
|
||||||
@ -387,7 +388,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
terrain.remove(key).map(|chunk| {
|
terrain.remove(key).map(|chunk| {
|
||||||
terrain_changes.removed_chunks.insert(key);
|
terrain_changes.removed_chunks.insert(key);
|
||||||
#[cfg(feature = "worldgen")]
|
#[cfg(feature = "worldgen")]
|
||||||
rtsim2.hook_unload_chunk(key);
|
rtsim.hook_unload_chunk(key);
|
||||||
chunk
|
chunk
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -21,6 +21,7 @@ common-dynlib = {package = "veloren-common-dynlib", path = "../common/dynlib", o
|
|||||||
bincode = "1.3.1"
|
bincode = "1.3.1"
|
||||||
bitvec = "1.0.1"
|
bitvec = "1.0.1"
|
||||||
enum-iterator = "1.1.3"
|
enum-iterator = "1.1.3"
|
||||||
|
enum-map = "2.4"
|
||||||
fxhash = "0.2.1"
|
fxhash = "0.2.1"
|
||||||
image = { version = "0.24", default-features = false, features = ["png"] }
|
image = { version = "0.24", default-features = false, features = ["png"] }
|
||||||
itertools = "0.10"
|
itertools = "0.10"
|
||||||
|
@ -141,6 +141,7 @@ pub struct Canvas<'a> {
|
|||||||
pub(crate) info: CanvasInfo<'a>,
|
pub(crate) info: CanvasInfo<'a>,
|
||||||
pub(crate) chunk: &'a mut TerrainChunk,
|
pub(crate) chunk: &'a mut TerrainChunk,
|
||||||
pub(crate) entities: Vec<EntityInfo>,
|
pub(crate) entities: Vec<EntityInfo>,
|
||||||
|
pub(crate) rtsim_resource_blocks: Vec<Vec3<i32>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Canvas<'a> {
|
impl<'a> Canvas<'a> {
|
||||||
@ -159,11 +160,20 @@ impl<'a> Canvas<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn set(&mut self, pos: Vec3<i32>, block: Block) {
|
pub fn set(&mut self, pos: Vec3<i32>, block: Block) {
|
||||||
|
if block.get_rtsim_resource().is_some() {
|
||||||
|
self.rtsim_resource_blocks.push(pos);
|
||||||
|
}
|
||||||
let _ = self.chunk.set(pos - self.wpos(), block);
|
let _ = self.chunk.set(pos - self.wpos(), block);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn map(&mut self, pos: Vec3<i32>, f: impl FnOnce(Block) -> Block) {
|
pub fn map(&mut self, pos: Vec3<i32>, f: impl FnOnce(Block) -> Block) {
|
||||||
let _ = self.chunk.map(pos - self.wpos(), f);
|
let _ = self.chunk.map(pos - self.wpos(), |b| {
|
||||||
|
let new_block = f(b);
|
||||||
|
if new_block.get_rtsim_resource().is_some() {
|
||||||
|
self.rtsim_resource_blocks.push(pos);
|
||||||
|
}
|
||||||
|
new_block
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_sprite_cfg(&mut self, pos: Vec3<i32>, sprite_cfg: SpriteCfg) {
|
pub fn set_sprite_cfg(&mut self, pos: Vec3<i32>, sprite_cfg: SpriteCfg) {
|
||||||
|
@ -53,8 +53,10 @@ use common::{
|
|||||||
Block, BlockKind, SpriteKind, TerrainChunk, TerrainChunkMeta, TerrainChunkSize, TerrainGrid,
|
Block, BlockKind, SpriteKind, TerrainChunk, TerrainChunkMeta, TerrainChunkSize, TerrainGrid,
|
||||||
},
|
},
|
||||||
vol::{ReadVol, RectVolSize, WriteVol},
|
vol::{ReadVol, RectVolSize, WriteVol},
|
||||||
|
rtsim::ChunkResource,
|
||||||
};
|
};
|
||||||
use common_net::msg::{world_msg, WorldMapMsg};
|
use common_net::msg::{world_msg, WorldMapMsg};
|
||||||
|
use enum_map::EnumMap;
|
||||||
use rand::{prelude::*, Rng};
|
use rand::{prelude::*, Rng};
|
||||||
use rand_chacha::ChaCha8Rng;
|
use rand_chacha::ChaCha8Rng;
|
||||||
use rayon::iter::ParallelIterator;
|
use rayon::iter::ParallelIterator;
|
||||||
@ -235,7 +237,7 @@ impl World {
|
|||||||
// Unwrapping because generate_chunk only returns err when should_continue evals
|
// Unwrapping because generate_chunk only returns err when should_continue evals
|
||||||
// to true
|
// to true
|
||||||
let (tc, _cs) = self
|
let (tc, _cs) = self
|
||||||
.generate_chunk(index, chunk_pos, || false, None)
|
.generate_chunk(index, chunk_pos, None, || false, None)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
tc.find_accessible_pos(spawn_wpos, ascending)
|
tc.find_accessible_pos(spawn_wpos, ascending)
|
||||||
@ -246,6 +248,7 @@ impl World {
|
|||||||
&self,
|
&self,
|
||||||
index: IndexRef,
|
index: IndexRef,
|
||||||
chunk_pos: Vec2<i32>,
|
chunk_pos: Vec2<i32>,
|
||||||
|
rtsim_resources: Option<EnumMap<ChunkResource, f32>>,
|
||||||
// TODO: misleading name
|
// TODO: misleading name
|
||||||
mut should_continue: impl FnMut() -> bool,
|
mut should_continue: impl FnMut() -> bool,
|
||||||
time: Option<(TimeOfDay, Calendar)>,
|
time: Option<(TimeOfDay, Calendar)>,
|
||||||
@ -377,6 +380,7 @@ impl World {
|
|||||||
},
|
},
|
||||||
chunk: &mut chunk,
|
chunk: &mut chunk,
|
||||||
entities: Vec::new(),
|
entities: Vec::new(),
|
||||||
|
rtsim_resource_blocks: Vec::new(),
|
||||||
};
|
};
|
||||||
|
|
||||||
if index.features.train_tracks {
|
if index.features.train_tracks {
|
||||||
@ -416,9 +420,12 @@ impl World {
|
|||||||
.iter()
|
.iter()
|
||||||
.for_each(|site| index.sites[*site].apply_to(&mut canvas, &mut dynamic_rng));
|
.for_each(|site| index.sites[*site].apply_to(&mut canvas, &mut dynamic_rng));
|
||||||
|
|
||||||
|
let mut rtsim_resource_blocks = std::mem::take(&mut canvas.rtsim_resource_blocks);
|
||||||
let mut supplement = ChunkSupplement {
|
let mut supplement = ChunkSupplement {
|
||||||
entities: canvas.entities,
|
entities: std::mem::take(&mut canvas.entities),
|
||||||
|
rtsim_max_resources: Default::default(),
|
||||||
};
|
};
|
||||||
|
drop(canvas);
|
||||||
|
|
||||||
let gen_entity_pos = |dynamic_rng: &mut ChaCha8Rng| {
|
let gen_entity_pos = |dynamic_rng: &mut ChaCha8Rng| {
|
||||||
let lpos2d = TerrainChunkSize::RECT_SIZE
|
let lpos2d = TerrainChunkSize::RECT_SIZE
|
||||||
@ -485,6 +492,33 @@ impl World {
|
|||||||
// Finally, defragment to minimize space consumption.
|
// Finally, defragment to minimize space consumption.
|
||||||
chunk.defragment();
|
chunk.defragment();
|
||||||
|
|
||||||
|
// Before we finish, we check candidate rtsim resource blocks, deduplicating positions and only keeping those
|
||||||
|
// that actually do have resources. Although this looks potentially very expensive, only blocks that are rtsim
|
||||||
|
// resources (i.e: a relatively small number of sprites) are processed here.
|
||||||
|
if let Some(rtsim_resources) = rtsim_resources {
|
||||||
|
rtsim_resource_blocks.sort_unstable_by_key(|pos| pos.into_array());
|
||||||
|
rtsim_resource_blocks.dedup();
|
||||||
|
for wpos in rtsim_resource_blocks {
|
||||||
|
chunk.map(
|
||||||
|
wpos - chunk_wpos2d.with_z(0),
|
||||||
|
|block| if let Some(res) = block.get_rtsim_resource() {
|
||||||
|
// Note: this represents the upper limit, not the actual number spanwed, so we increment this before deciding whether we're going to spawn the resource.
|
||||||
|
supplement.rtsim_max_resources[res] += 1;
|
||||||
|
// Throw a dice to determine whether this resource should actually spawn
|
||||||
|
// TODO: Don't throw a dice, try to generate the *exact* correct number
|
||||||
|
if dynamic_rng.gen_bool(rtsim_resources[res] as f64) {
|
||||||
|
block
|
||||||
|
} else {
|
||||||
|
block.into_vacant()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
block
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
Ok((chunk, supplement))
|
Ok((chunk, supplement))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user