From: Daniel Karbach Date: Wed, 14 Oct 2015 15:25:22 +0000 (+0200) Subject: store shapes in models rather than meshes X-Git-Url: https://git.localhorst.tv/?a=commitdiff_plain;h=a26ca06878d45d3ce77cbc28b574f2553e121944;p=blank.git store shapes in models rather than meshes --- diff --git a/src/ai/Spawner.cpp b/src/ai/Spawner.cpp index b14271f..064857c 100644 --- a/src/ai/Spawner.cpp +++ b/src/ai/Spawner.cpp @@ -2,6 +2,7 @@ #include "Chaser.hpp" #include "RandomWalk.hpp" +#include "../app/TextureIndex.hpp" #include "../model/Model.hpp" #include "../model/Skeletons.hpp" #include "../rand/GaloisLFSR.hpp" @@ -29,7 +30,8 @@ Spawner::Spawner(World &world, Skeletons &skeletons, GaloisLFSR &rand) , max_entities(16) , chunk_range(4) , skeletons_offset(0) -, skeletons_length(skeletons.size()) { +, skeletons_length(skeletons.size()) +, tex_map() { timer.Start(); } @@ -49,6 +51,12 @@ void Spawner::LimitSkeletons(size_t begin, size_t end) { } } +void Spawner::LoadTextures(TextureIndex &tex_index) { + tex_map.clear(); + tex_map.push_back(tex_index.GetID("rock-1")); + tex_map.push_back(tex_index.GetID("rock-face")); +} + void Spawner::Update(int dt) { CheckDespawn(); timer.Update(dt); @@ -135,6 +143,7 @@ void Spawner::Spawn(Entity &reference, const glm::ivec3 &chunk, const glm::vec3 e.Bounds({ { -0.5f, -0.5f, -0.5f }, { 0.5f, 0.5f, 0.5f } }); e.WorldCollidable(true); RandomSkeleton().Instantiate(e.GetModel()); + e.GetModel().SetTextures(tex_map); e.AngularVelocity(rot); Controller *ctrl; if (random()) { diff --git a/src/ai/Spawner.hpp b/src/ai/Spawner.hpp index 4b20dd4..6591ef9 100644 --- a/src/ai/Spawner.hpp +++ b/src/ai/Spawner.hpp @@ -14,6 +14,7 @@ class Entity; class GaloisLFSR; class Model; class Skeletons; +class TextureIndex; class World; class Spawner { @@ -23,6 +24,7 @@ public: ~Spawner(); void LimitSkeletons(std::size_t begin, std::size_t end); + void LoadTextures(TextureIndex &); void Update(int dt); @@ -49,6 +51,8 @@ private: std::size_t skeletons_offset; std::size_t skeletons_length; + std::vector tex_map; + }; } diff --git a/src/client/InteractiveState.hpp b/src/client/InteractiveState.hpp index 86757a2..731ed0a 100644 --- a/src/client/InteractiveState.hpp +++ b/src/client/InteractiveState.hpp @@ -48,7 +48,10 @@ public: void Update(int dt) override; void Render(Viewport &) override; - void MergePlayerCorrection(std::uint16_t, const EntityState &); + void Handle(const Packet::SpawnEntity &); + void Handle(const Packet::DespawnEntity &); + void Handle(const Packet::EntityUpdate &); + void Handle(const Packet::PlayerCorrection &); void Handle(const Packet::BlockUpdate &); void SetAudio(bool) override; @@ -57,6 +60,13 @@ public: void SetDebug(bool) override; void Exit() override; +private: + /// flag entity as updated by given packet + /// returns false if the update should be ignored + bool UpdateEntity(std::uint32_t id, std::uint16_t seq); + /// drop update information or given entity + void ClearEntity(std::uint32_t id); + private: MasterState &master; ShapeRegistry shapes; @@ -75,6 +85,14 @@ private: SkyBox sky; + std::vector tex_map; + + struct UpdateStatus { + std::uint16_t last_packet; + int last_update; + }; + std::map update_status; + }; } diff --git a/src/client/MasterState.hpp b/src/client/MasterState.hpp index 1d69998..a29ff83 100644 --- a/src/client/MasterState.hpp +++ b/src/client/MasterState.hpp @@ -62,13 +62,6 @@ public: void On(const Packet::ChunkData &) override; void On(const Packet::BlockUpdate &) override; -private: - /// flag entity as updated by given packet - /// returns false if the update should be ignored - bool UpdateEntity(std::uint32_t id, std::uint16_t seq); - /// drop update information or given entity - void ClearEntity(std::uint32_t id); - private: Environment &env; Config &config; @@ -80,13 +73,6 @@ private: int login_packet; - struct UpdateStatus { - std::uint16_t last_packet; - int last_update; - }; - std::map update_status; - IntervalTimer update_timer; - }; } diff --git a/src/client/client.cpp b/src/client/client.cpp index 6a2dd32..2618d98 100644 --- a/src/client/client.cpp +++ b/src/client/client.cpp @@ -61,14 +61,18 @@ InteractiveState::InteractiveState(MasterState &master, uint32_t player_id) , chunk_renderer(player.GetChunks()) , skeletons() , loop_timer(16) -, sky(master.GetEnv().loader.LoadCubeMap("skybox")) { +, sky(master.GetEnv().loader.LoadCubeMap("skybox")) +, tex_map() +, update_status() { if (!save.Exists()) { save.Write(master.GetWorldConf()); } TextureIndex tex_index; master.GetEnv().loader.LoadShapes("default", shapes); master.GetEnv().loader.LoadBlockTypes("default", block_types, tex_index, shapes); - skeletons.Load(shapes, tex_index); + skeletons.Load(shapes); + tex_map.push_back(tex_index.GetID("rock-1")); + tex_map.push_back(tex_index.GetID("rock-face")); interface.SetInventorySlots(block_types.size() - 1); chunk_renderer.LoadTextures(master.GetEnv().loader, tex_index); chunk_renderer.FogDensity(master.GetWorldConf().fog_density); @@ -155,8 +159,91 @@ void InteractiveState::Render(Viewport &viewport) { hud.Render(viewport); } -void InteractiveState::MergePlayerCorrection(std::uint16_t pack, const EntityState &state) { - input.MergePlayerCorrection(pack, state); +void InteractiveState::Handle(const Packet::SpawnEntity &pack) { + uint32_t entity_id; + pack.ReadEntityID(entity_id); + Entity &entity = world.ForceAddEntity(entity_id); + UpdateEntity(entity_id, pack.Seq()); + pack.ReadEntity(entity); + uint32_t skel_id; + pack.ReadSkeletonID(skel_id); + Model *skel = skeletons.ByID(skel_id); + if (skel) { + skel->Instantiate(entity.GetModel()); + entity.GetModel().SetTextures(tex_map); + } + cout << "spawned entity #" << entity_id << " (" << entity.Name() + << ") at " << entity.AbsolutePosition() << endl; +} + +void InteractiveState::Handle(const Packet::DespawnEntity &pack) { + uint32_t entity_id; + pack.ReadEntityID(entity_id); + ClearEntity(entity_id); + for (Entity &entity : world.Entities()) { + if (entity.ID() == entity_id) { + entity.Kill(); + cout << "despawned entity #" << entity_id << " (" << entity.Name() << ") at " << entity.AbsolutePosition() << endl; + return; + } + } +} + +void InteractiveState::Handle(const Packet::EntityUpdate &pack) { + auto world_iter = world.Entities().begin(); + auto world_end = world.Entities().end(); + + uint32_t count = 0; + pack.ReadEntityCount(count); + + for (uint32_t i = 0; i < count; ++i) { + uint32_t entity_id = 0; + pack.ReadEntityID(entity_id, i); + + while (world_iter != world_end && world_iter->ID() < entity_id) { + ++world_iter; + } + if (world_iter == world_end) { + // nothing can be done from here + return; + } + if (world_iter->ID() == entity_id) { + if (UpdateEntity(entity_id, pack.Seq())) { + pack.ReadEntityState(world_iter->GetState(), i); + } + } + } +} + +bool InteractiveState::UpdateEntity(uint32_t entity_id, uint16_t seq) { + auto entry = update_status.find(entity_id); + if (entry == update_status.end()) { + update_status.emplace(entity_id, UpdateStatus{ seq, loop_timer.Elapsed() }); + return true; + } + + int16_t pack_diff = int16_t(seq) - int16_t(entry->second.last_packet); + int time_diff = loop_timer.Elapsed() - entry->second.last_update; + entry->second.last_update = loop_timer.Elapsed(); + + if (pack_diff > 0 || time_diff > 1500) { + entry->second.last_packet = seq; + return true; + } else { + return false; + } +} + +void InteractiveState::ClearEntity(uint32_t entity_id) { + update_status.erase(entity_id); +} + +void InteractiveState::Handle(const Packet::PlayerCorrection &pack) { + uint16_t pack_seq; + EntityState corrected_state; + pack.ReadPacketSeq(pack_seq); + pack.ReadPlayerState(corrected_state); + input.MergePlayerCorrection(pack_seq, corrected_state); } void InteractiveState::Handle(const Packet::BlockUpdate &pack) { @@ -232,11 +319,8 @@ MasterState::MasterState( , state() , client(config.net) , init_state(*this) -, login_packet(-1) -, update_status() -, update_timer(16) { +, login_packet(-1) { client.GetConnection().SetHandler(this); - update_timer.Start(); } void MasterState::Quit() { @@ -259,7 +343,6 @@ void MasterState::Handle(const SDL_Event &event) { void MasterState::Update(int dt) { - update_timer.Update(dt); client.Handle(); client.Update(dt); } @@ -322,19 +405,7 @@ void MasterState::On(const Packet::SpawnEntity &pack) { cout << "got entity spawn before world was created" << endl; return; } - uint32_t entity_id; - pack.ReadEntityID(entity_id); - Entity &entity = state->GetWorld().ForceAddEntity(entity_id); - UpdateEntity(entity_id, pack.Seq()); - pack.ReadEntity(entity); - uint32_t skel_id; - pack.ReadSkeletonID(skel_id); - Model *skel = state->GetSkeletons().ByID(skel_id); - if (skel) { - skel->Instantiate(entity.GetModel()); - } - cout << "spawned entity #" << entity_id << " (" << entity.Name() - << ") at " << entity.AbsolutePosition() << endl; + state->Handle(pack); } void MasterState::On(const Packet::DespawnEntity &pack) { @@ -342,16 +413,7 @@ void MasterState::On(const Packet::DespawnEntity &pack) { cout << "got entity despawn before world was created" << endl; return; } - uint32_t entity_id; - pack.ReadEntityID(entity_id); - ClearEntity(entity_id); - for (Entity &entity : state->GetWorld().Entities()) { - if (entity.ID() == entity_id) { - entity.Kill(); - cout << "despawned entity #" << entity_id << " (" << entity.Name() << ") at " << entity.AbsolutePosition() << endl; - return; - } - } + state->Handle(pack); } void MasterState::On(const Packet::EntityUpdate &pack) { @@ -359,53 +421,7 @@ void MasterState::On(const Packet::EntityUpdate &pack) { cout << "got entity update before world was created" << endl; return; } - - auto world_iter = state->GetWorld().Entities().begin(); - auto world_end = state->GetWorld().Entities().end(); - - uint32_t count = 0; - pack.ReadEntityCount(count); - - for (uint32_t i = 0; i < count; ++i) { - uint32_t entity_id = 0; - pack.ReadEntityID(entity_id, i); - - while (world_iter != world_end && world_iter->ID() < entity_id) { - ++world_iter; - } - if (world_iter == world_end) { - // nothing can be done from here - return; - } - if (world_iter->ID() == entity_id) { - if (UpdateEntity(entity_id, pack.Seq())) { - pack.ReadEntityState(world_iter->GetState(), i); - } - } - } -} - -bool MasterState::UpdateEntity(uint32_t entity_id, uint16_t seq) { - auto entry = update_status.find(entity_id); - if (entry == update_status.end()) { - update_status.emplace(entity_id, UpdateStatus{ seq, update_timer.Elapsed() }); - return true; - } - - int16_t pack_diff = int16_t(seq) - int16_t(entry->second.last_packet); - int time_diff = update_timer.Elapsed() - entry->second.last_update; - entry->second.last_update = update_timer.Elapsed(); - - if (pack_diff > 0 || time_diff > 1500) { - entry->second.last_packet = seq; - return true; - } else { - return false; - } -} - -void MasterState::ClearEntity(uint32_t entity_id) { - update_status.erase(entity_id); + state->Handle(pack); } void MasterState::On(const Packet::PlayerCorrection &pack) { @@ -413,11 +429,7 @@ void MasterState::On(const Packet::PlayerCorrection &pack) { cout << "got player correction without a player :S" << endl; return; } - uint16_t pack_seq; - EntityState corrected_state; - pack.ReadPacketSeq(pack_seq); - pack.ReadPlayerState(corrected_state); - state->MergePlayerCorrection(pack_seq, corrected_state); + state->Handle(pack); } void MasterState::On(const Packet::ChunkBegin &pack) { diff --git a/src/model/Instance.hpp b/src/model/Instance.hpp index 9de1a5d..60c0212 100644 --- a/src/model/Instance.hpp +++ b/src/model/Instance.hpp @@ -3,6 +3,7 @@ #include "Part.hpp" +#include #include #include #include @@ -10,24 +11,40 @@ namespace blank { -class Model; class DirectionalLighting; +class EntityMesh; +class Model; +class Part; class Instance { friend class Model; + friend class Part; public: Instance(); + ~Instance(); + + Instance(const Instance &); + Instance &operator =(const Instance &); operator bool() const noexcept { return model; } const Model &GetModel() const noexcept { return *model; } - void Render(const glm::mat4 &, DirectionalLighting &) const; + void Render(const glm::mat4 &, DirectionalLighting &); + + void SetTextures(const std::vector &t); + void SetHSLModifier(const glm::vec3 &m); + void SetRGBModifier(const glm::vec3 &m); private: const Model *model; std::vector state; + std::vector> mesh; + + std::vector tex_map; + glm::vec3 hsl_mod; + glm::vec3 rgb_mod; }; diff --git a/src/model/Part.hpp b/src/model/Part.hpp index 029442b..f3d78e2 100644 --- a/src/model/Part.hpp +++ b/src/model/Part.hpp @@ -13,8 +13,9 @@ namespace blank { class DirectionalLighting; -class EntityMesh; +class Instance; class Model; +class Shape; struct Part { @@ -24,7 +25,7 @@ struct Part { glm::vec3 position = glm::vec3(0.0f, 0.0f, 0.0f); glm::quat orientation = glm::quat(1.0f, 0.0f, 0.0f, 0.0f); } initial; - const EntityMesh *mesh; + const Shape *shape; Part(); ~Part(); @@ -35,10 +36,14 @@ struct Part { std::uint16_t Enumerate(std::uint16_t) noexcept; void Index(std::vector &) noexcept; - glm::mat4 LocalTransform(const std::vector &) const noexcept; - glm::mat4 GlobalTransform(const std::vector &) const noexcept; + glm::mat4 LocalTransform(const Instance &) const noexcept; + glm::mat4 GlobalTransform(const Instance &) const noexcept; - void Render(const glm::mat4 &, const std::vector &, DirectionalLighting &) const; + void LoadMeshes(Instance &) const; + void Render( + const glm::mat4 &, + const Instance &, + DirectionalLighting &) const; private: const Part *parent; diff --git a/src/model/Skeletons.hpp b/src/model/Skeletons.hpp index 90affd3..3c7c886 100644 --- a/src/model/Skeletons.hpp +++ b/src/model/Skeletons.hpp @@ -9,9 +9,7 @@ namespace blank { class Model; -class EntityMesh; class ShapeRegistry; -class TextureIndex; class Skeletons { @@ -24,8 +22,7 @@ public: Skeletons(); ~Skeletons(); - void LoadHeadless(); - void Load(const ShapeRegistry &, TextureIndex &); + void Load(const ShapeRegistry &); size_type size() const noexcept { return skeletons.size(); } @@ -37,7 +34,6 @@ public: private: std::vector> skeletons; - std::vector meshes; }; diff --git a/src/model/model.cpp b/src/model/model.cpp index 632a5b6..730a34a 100644 --- a/src/model/model.cpp +++ b/src/model/model.cpp @@ -8,19 +8,69 @@ #include "../graphics/DirectionalLighting.hpp" #include "../graphics/EntityMesh.hpp" +#include #include +#include namespace blank { Instance::Instance() : model(nullptr) -, state() { +, state() +, mesh() +, tex_map() +, hsl_mod(0.0f, 1.0f, 1.0f) +, rgb_mod(1.0f) { } -void Instance::Render(const glm::mat4 &M, DirectionalLighting &prog) const { - model->RootPart().Render(M, state, prog); +Instance::~Instance() { + +} + +Instance::Instance(const Instance &other) +: model(other.model) +, state(other.state) +, mesh() +, tex_map(other.tex_map) +, hsl_mod(other.hsl_mod) +, rgb_mod(other.rgb_mod) { + +} + +Instance &Instance::operator =(const Instance &other) { + model = other.model; + state = other.state; + mesh.clear(); + tex_map = other.tex_map; + hsl_mod = other.hsl_mod; + rgb_mod = other.rgb_mod; + return *this; +} + +void Instance::Render(const glm::mat4 &M, DirectionalLighting &prog) { + if (mesh.empty()) { + std::cout << "building meshes for instance" << std::endl; + mesh.resize(state.size()); + model->RootPart().LoadMeshes(*this); + } + model->RootPart().Render(M, *this, prog); +} + +void Instance::SetTextures(const std::vector &t) { + tex_map = t; + mesh.clear(); +} + +void Instance::SetHSLModifier(const glm::vec3 &m) { + hsl_mod = m; + mesh.clear(); +} + +void Instance::SetRGBModifier(const glm::vec3 &m) { + rgb_mod = m; + mesh.clear(); } @@ -40,6 +90,7 @@ void Model::Enumerate() { void Model::Instantiate(Instance &inst) const { inst.model = this; inst.state.clear(); + inst.mesh.clear(); inst.state.resize(part.size()); } @@ -48,7 +99,7 @@ Part::Part() : id(0) , bounds{ glm::vec3(0.0f), glm::vec3(0.0f) } , initial() -, mesh(nullptr) +, shape(nullptr) , parent(nullptr) , children() { @@ -79,43 +130,60 @@ void Part::Index(std::vector &index) noexcept { } } -glm::mat4 Part::LocalTransform( - const std::vector &state -) const noexcept { - glm::mat4 transform(toMat4(initial.orientation * state[id].orientation)); - transform[3] = glm::vec4(initial.position + state[id].position, 1.0f); +glm::mat4 Part::LocalTransform(const Instance &inst) const noexcept { + glm::mat4 transform(toMat4(initial.orientation * inst.state[id].orientation)); + transform[3] = glm::vec4(initial.position + inst.state[id].position, 1.0f); return transform; } -glm::mat4 Part::GlobalTransform( - const std::vector &state -) const noexcept { +glm::mat4 Part::GlobalTransform(const Instance &inst) const noexcept { if (parent) { - return parent->GlobalTransform(state) * LocalTransform(state); + return parent->GlobalTransform(inst) * LocalTransform(inst); } else { - return LocalTransform(state); + return LocalTransform(inst); + } +} + +namespace { + +EntityMesh::Buffer buf; + +} + +void Part::LoadMeshes(Instance &inst) const { + if (shape && shape->IndexCount() > 0) { + buf.Clear(); + buf.hsl_mods.resize(shape->VertexCount(), inst.hsl_mod); + buf.rgb_mods.resize(shape->VertexCount(), inst.rgb_mod); + shape->Fill(buf, inst.tex_map); + inst.mesh[id].reset(new EntityMesh()); + inst.mesh[id]->Update(buf); + } else { + inst.mesh[id].reset(); + } + for (const Part &part : children) { + part.LoadMeshes(inst); } } void Part::Render( const glm::mat4 &M, - const std::vector &state, + const Instance &inst, DirectionalLighting &prog ) const { - glm::mat4 transform = M * LocalTransform(state); - if (mesh) { + glm::mat4 transform = M * LocalTransform(inst); + if (inst.mesh[id]) { prog.SetM(transform); - mesh->Draw(); + inst.mesh[id]->Draw(); } for (const Part &part : children) { - part.Render(transform, state, prog); + part.Render(transform, inst, prog); } } Skeletons::Skeletons() -: skeletons() -, meshes() { +: skeletons() { } @@ -123,78 +191,41 @@ Skeletons::~Skeletons() { } -void Skeletons::LoadHeadless() { +void Skeletons::Load(const ShapeRegistry &shapes) { skeletons.clear(); skeletons.reserve(4); AABB bounds{{ -0.5f, -0.5f, -0.5f }, { 0.5f, 0.5f, 0.5f }}; + const Shape *shape = &shapes.Get("player_head_block"); { skeletons.emplace_back(new Model); skeletons[0]->ID(1); skeletons[0]->RootPart().bounds = bounds; + skeletons[0]->RootPart().shape = shape; skeletons[0]->Enumerate(); } { skeletons.emplace_back(new Model); skeletons[1]->ID(2); skeletons[1]->RootPart().bounds = bounds; + skeletons[1]->RootPart().shape = shape; skeletons[1]->Enumerate(); } { skeletons.emplace_back(new Model); skeletons[2]->ID(3); skeletons[2]->RootPart().bounds = bounds; + skeletons[2]->RootPart().shape = shape; skeletons[2]->Enumerate(); } { skeletons.emplace_back(new Model); skeletons[3]->ID(4); skeletons[3]->RootPart().bounds = bounds; + skeletons[3]->RootPart().shape = shape; skeletons[3]->Enumerate(); } } -void Skeletons::Load(const ShapeRegistry &shapes, TextureIndex &tex_index) { - LoadHeadless(); - meshes.resize(4); - const Shape &shape = shapes.Get("player_head_block"); - EntityMesh::Buffer buf; - std::vector tex_map; - tex_map.push_back(tex_index.GetID("rock-1")); - tex_map.push_back(tex_index.GetID("rock-face")); - buf.Reserve(shape.VertexCount(), shape.IndexCount()); - { - shape.Fill(buf, tex_map); - buf.hsl_mods.resize(shape.VertexCount(), { 0.0f, 1.0f, 1.0f }); - buf.rgb_mods.resize(shape.VertexCount(), { 1.0f, 1.0f, 0.0f }); - meshes[0].Update(buf); - skeletons[0]->RootPart().mesh = &meshes[0]; - } - { - buf.Clear(); - shape.Fill(buf, tex_map); - buf.hsl_mods.resize(shape.VertexCount(), { 0.0f, 1.0f, 1.0f }); - buf.rgb_mods.resize(shape.VertexCount(), { 0.0f, 1.0f, 1.0f }); - meshes[1].Update(buf); - skeletons[1]->RootPart().mesh = &meshes[1]; - } - { - buf.Clear(); - shape.Fill(buf, tex_map); - buf.hsl_mods.resize(shape.VertexCount(), { 0.0f, 1.0f, 1.0f }); - buf.rgb_mods.resize(shape.VertexCount(), { 1.0f, 0.0f, 1.0f }); - meshes[2].Update(buf); - skeletons[2]->RootPart().mesh = &meshes[2]; - } - { - buf.Clear(); - shape.Fill(buf, tex_map); - buf.hsl_mods.resize(shape.VertexCount(), { 0.0f, 1.0f, 1.0f }); - buf.rgb_mods.resize(shape.VertexCount(), { 1.0f, 0.25f, 0.5f }); - meshes[3].Update(buf); - skeletons[3]->RootPart().mesh = &meshes[3]; - } -} - Model *Skeletons::ByID(std::uint16_t id) noexcept { if (id == 0 || id > skeletons.size()) { return nullptr; diff --git a/src/server/ServerState.cpp b/src/server/ServerState.cpp index 7617b41..98861bf 100644 --- a/src/server/ServerState.cpp +++ b/src/server/ServerState.cpp @@ -32,8 +32,9 @@ ServerState::ServerState( env.loader.LoadShapes("default", shapes); env.loader.LoadBlockTypes("default", block_types, tex_index, shapes); generator.LoadTypes(block_types); - skeletons.LoadHeadless(); + skeletons.Load(shapes); spawner.LimitSkeletons(1, skeletons.size()); + spawner.LoadTextures(tex_index); server.SetPlayerModel(skeletons[0]); loop_timer.Start(); diff --git a/src/standalone/MasterState.cpp b/src/standalone/MasterState.cpp index d357a84..8199414 100644 --- a/src/standalone/MasterState.cpp +++ b/src/standalone/MasterState.cpp @@ -43,12 +43,13 @@ MasterState::MasterState( TextureIndex tex_index; env.loader.LoadShapes("default", shapes); env.loader.LoadBlockTypes("default", block_types, tex_index, shapes); - skeletons.Load(shapes, tex_index); + skeletons.Load(shapes); + spawner.LimitSkeletons(0, skeletons.size()); + spawner.LoadTextures(tex_index); interface.SetInventorySlots(block_types.size() - 1); generator.LoadTypes(block_types); chunk_renderer.LoadTextures(env.loader, tex_index); chunk_renderer.FogDensity(wc.fog_density); - spawner.LimitSkeletons(0, skeletons.size()); if (save.Exists(player)) { save.Read(player); } else {