#include "Chaser.hpp"
 #include "RandomWalk.hpp"
-#include "../model/shapes.hpp"
+#include "../model/CompositeModel.hpp"
+#include "../model/Skeletons.hpp"
 #include "../world/BlockLookup.hpp"
 #include "../world/BlockType.hpp"
 #include "../world/Entity.hpp"
 
 namespace blank {
 
-Spawner::Spawner(World &world, std::uint64_t seed)
+Spawner::Spawner(World &world, Skeletons &skeletons, std::uint64_t seed)
 : world(world)
+, skeletons(skeletons)
 , controllers()
 , random(seed)
 , timer(64)
 , spawn_distance(16 * 16)
 , max_entities(16)
 , chunk_range(4) {
-       EntityModel::Buffer buf;
-       {
-               AABB bounds{{ -0.25f, -0.5f, -0.25f }, { 0.25f, 0.5f, 0.25f }};
-               CuboidShape shape(bounds);
-               shape.Vertices(buf, 1.0f);
-               buf.colors.resize(shape.VertexCount(), { 1.0f, 1.0f, 0.0f });
-               models[0].Update(buf);
-               skeletons[0].Bounds(bounds);
-               skeletons[0].SetNodeModel(&models[0]);
-       }
-       {
-               AABB bounds{{ -0.5f, -0.25f, -0.5f }, { 0.5f, 0.25f, 0.5f }};
-               CuboidShape shape(bounds);
-               buf.Clear();
-               shape.Vertices(buf, 2.0f);
-               buf.colors.resize(shape.VertexCount(), { 0.0f, 1.0f, 1.0f });
-               models[1].Update(buf);
-               skeletons[1].Bounds(bounds);
-               skeletons[1].SetNodeModel(&models[1]);
-       }
-       {
-               AABB bounds{{ -0.5f, -0.5f, -0.5f }, { 0.5f, 0.5f, 0.5f }};
-               StairShape shape(bounds, { 0.4f, 0.4f });
-               buf.Clear();
-               shape.Vertices(buf, 3.0f);
-               buf.colors.resize(shape.VertexCount(), { 1.0f, 0.0f, 1.0f });
-               models[2].Update(buf);
-               skeletons[2].Bounds(bounds);
-               skeletons[2].SetNodeModel(&models[2]);
-       }
-
        timer.Start();
 }
 
                random.Next<unsigned char>() % Chunk::depth
        );
 
-
        // distance check
        glm::vec3 diff(glm::vec3(chunk * Chunk::Extent() - pos) + player.Position());
        float dist = dot(diff, diff);
        e.Position(chunk, pos);
        e.Bounds({ { -0.5f, -0.5f, -0.5f }, { 0.5f, 0.5f, 0.5f } });
        e.WorldCollidable(true);
-       skeletons[random.Next<unsigned char>() % 3].Instantiate(e.GetModel());
+       skeletons[random.Next<unsigned char>() % skeletons.Size()].Instantiate(e.GetModel());
        e.AngularVelocity(rot);
        Controller *ctrl;
        if (random()) {
 
 #define BLANK_AI_SPAWNER_HPP_
 
 #include "../app/IntervalTimer.hpp"
-#include "../model/CompositeModel.hpp"
-#include "../model/EntityModel.hpp"
 #include "../rand/GaloisLFSR.hpp"
 
 #include <vector>
 
 class Controller;
 class Entity;
+class Skeletons;
 class World;
 
 class Spawner {
 
 public:
-       Spawner(World &, std::uint64_t seed);
+       Spawner(World &, Skeletons &, std::uint64_t seed);
        ~Spawner();
 
        void Update(int dt);
 
 private:
        World &world;
+       Skeletons &skeletons;
        std::vector<Controller *> controllers;
 
-       EntityModel models[3];
-       CompositeModel skeletons[3];
-
        GaloisLFSR random;
 
        IntervalTimer timer;
 
 : env(env)
 , block_types()
 , world(block_types, wc, ws)
+, skeletons()
+, spawner(world, skeletons, wc.gen.seed)
 , server(sc, world)
 , push_timer(16) {
        TextureIndex tex_index;
        env.loader.LoadBlockTypes("default", block_types, tex_index);
+       skeletons.LoadHeadless();
 
        push_timer.Start();
 
 
 void ServerState::Update(int dt) {
        push_timer.Update(dt);
-
        server.Handle();
+       spawner.Update(dt);
+       if (!world.Players().empty()) {
+               world.Update(dt);
+       }
        if (push_timer.Hit()) {
                server.Update(dt);
        }
 
 
 #include "IntervalTimer.hpp"
 #include "State.hpp"
+#include "../ai/Spawner.hpp"
+#include "../model/Skeletons.hpp"
 #include "../net/Server.hpp"
 #include "../world/BlockTypeRegistry.hpp"
 #include "../world/World.hpp"
        HeadlessEnvironment &env;
        BlockTypeRegistry block_types;
        World world;
+       Skeletons skeletons;
+       Spawner spawner;
        Server server;
        IntervalTimer push_timer;
 
 
 , block_types()
 , world(block_types, wc, save)
 , chunk_renderer(world, wc.load.load_dist)
-, spawner(world, wc.gen.seed)
+, skeletons()
+, spawner(world, skeletons, wc.gen.seed)
 , interface(ic, env, world, *world.AddPlayer(ic.player_name))
 , preload(env, world.Loader(), chunk_renderer)
 , unload(env, world.Loader()) {
        env.loader.LoadBlockTypes("default", block_types, tex_index);
        chunk_renderer.LoadTextures(env.loader, tex_index);
        chunk_renderer.FogDensity(wc.fog_density);
+       skeletons.Load();
        // TODO: better solution for initializing HUD
        interface.SelectNext();
 }
 
 #include "State.hpp"
 #include "UnloadState.hpp"
 #include "../ai/Spawner.hpp"
+#include "../model/Skeletons.hpp"
 #include "../ui/Interface.hpp"
 #include "../world/BlockTypeRegistry.hpp"
 #include "../world/ChunkRenderer.hpp"
        BlockTypeRegistry block_types;
        World world;
        ChunkRenderer chunk_renderer;
+       Skeletons skeletons;
        Spawner spawner;
        Interface interface;
 
 
--- /dev/null
+#ifndef BLANK_MODEL_SKELETONS_HPP_
+#define BLANK_MODEL_SKELETONS_HPP_
+
+#include <memory>
+#include <vector>
+
+
+namespace blank {
+
+class CompositeModel;
+class EntityModel;
+
+class Skeletons {
+
+public:
+       Skeletons();
+       ~Skeletons();
+
+       void LoadHeadless();
+       void Load();
+
+       std::size_t Size() const noexcept { return skeletons.size(); }
+
+       CompositeModel &operator[](std::size_t i) noexcept { return *skeletons[i]; }
+       const CompositeModel &operator[](std::size_t i) const noexcept { return *skeletons[i]; }
+
+private:
+       std::vector<std::unique_ptr<CompositeModel>> skeletons;
+       std::vector<EntityModel> models;
+
+};
+
+}
+
+#endif
 
 #include "CompositeModel.hpp"
 #include "CompositeInstance.hpp"
+#include "Skeletons.hpp"
 
 #include "EntityModel.hpp"
+#include "shapes.hpp"
 #include "../graphics/DirectionalLighting.hpp"
 
 #include <glm/gtx/quaternion.hpp>
        }
 }
 
+
+Skeletons::Skeletons()
+: skeletons()
+, models() {
+
+}
+
+Skeletons::~Skeletons() {
+
+}
+
+void Skeletons::LoadHeadless() {
+       skeletons.clear();
+       skeletons.reserve(3);
+       {
+               AABB bounds{{ -0.25f, -0.5f, -0.25f }, { 0.25f, 0.5f, 0.25f }};
+               skeletons.emplace_back(new CompositeModel);
+               skeletons[0]->Bounds(bounds);
+       }
+       {
+               AABB bounds{{ -0.5f, -0.25f, -0.5f }, { 0.5f, 0.25f, 0.5f }};
+               skeletons.emplace_back(new CompositeModel);
+               skeletons[1]->Bounds(bounds);
+       }
+       {
+               AABB bounds{{ -0.5f, -0.5f, -0.5f }, { 0.5f, 0.5f, 0.5f }};
+               skeletons.emplace_back(new CompositeModel);
+               skeletons[2]->Bounds(bounds);
+       }
+}
+
+void Skeletons::Load() {
+       LoadHeadless();
+       models.resize(3);
+       EntityModel::Buffer buf;
+       {
+               CuboidShape shape(skeletons[0]->Bounds());
+               shape.Vertices(buf, 1.0f);
+               buf.colors.resize(shape.VertexCount(), { 1.0f, 1.0f, 0.0f });
+               models[0].Update(buf);
+               skeletons[0]->SetNodeModel(&models[0]);
+       }
+       {
+               CuboidShape shape(skeletons[1]->Bounds());
+               buf.Clear();
+               shape.Vertices(buf, 2.0f);
+               buf.colors.resize(shape.VertexCount(), { 0.0f, 1.0f, 1.0f });
+               models[1].Update(buf);
+               skeletons[1]->SetNodeModel(&models[1]);
+       }
+       {
+               StairShape shape(skeletons[2]->Bounds(), { 0.4f, 0.4f });
+               buf.Clear();
+               shape.Vertices(buf, 3.0f);
+               buf.colors.resize(shape.VertexCount(), { 1.0f, 0.0f, 1.0f });
+               models[2].Update(buf);
+               skeletons[2]->SetNodeModel(&models[2]);
+       }
+}
+
 }