do some manual testing
        some more testing
        a little optimization
-       and give players an appearance as well ^^
 
 launcher ui
 
 
 #include "../world/Entity.hpp"
 #include "../world/World.hpp"
 
+#include <iostream>
+
+using namespace std;
+
 
 namespace blank {
 
-Spawner::Spawner(World &world, Skeletons &skeletons, std::uint64_t seed)
+Spawner::Spawner(World &world, Skeletons &skeletons, uint64_t seed)
 : world(world)
 , skeletons(skeletons)
 , controllers()
 , despawn_range(128 * 128)
 , spawn_distance(16 * 16)
 , max_entities(16)
-, chunk_range(4) {
+, chunk_range(4)
+, skeletons_offset(0)
+, skeletons_length(skeletons.Size()) {
        timer.Start();
 }
 
 }
 
 
+void Spawner::LimitSkeletons(size_t begin, size_t end) {
+       if (begin >= skeletons.Size() || end > skeletons.Size() || begin >= end) {
+               cout << "warning, skeleton limit out of bounds or invalid range given" << endl;
+       } else {
+               skeletons_offset = begin;
+               skeletons_length = end - begin;
+       }
+}
+
 void Spawner::Update(int dt) {
        CheckDespawn();
        timer.Update(dt);
 }
 
 void Spawner::TrySpawn() {
-       if (controllers.size() >= max_entities) return;
+       if (controllers.size() >= max_entities || skeletons_length == 0) return;
 
        // select random player to punish
        auto &players = world.Players();
        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>() % skeletons.Size()].Instantiate(e.GetModel());
+       RandomSkeleton().Instantiate(e.GetModel());
        e.AngularVelocity(rot);
        Controller *ctrl;
        if (random()) {
                ctrl = new RandomWalk(e, random.Next<std::uint64_t>());
-               e.Name("spawned walker");
+               e.Name("walker");
        } else {
                ctrl = new Chaser(world, e, reference);
-               e.Name("spawned chaser");
+               e.Name("chaser");
        }
        controllers.emplace_back(ctrl);
 }
 
+CompositeModel &Spawner::RandomSkeleton() noexcept {
+       std::size_t offset = (random.Next<std::size_t>() % skeletons_length) + skeletons_offset;
+       return skeletons[offset];
+}
+
 }
 
 
 namespace blank {
 
+class CompositeModel;
 class Controller;
 class Entity;
 class Skeletons;
        Spawner(World &, Skeletons &, std::uint64_t seed);
        ~Spawner();
 
+       void LimitSkeletons(std::size_t begin, std::size_t end);
+
        void Update(int dt);
 
 private:
        void TrySpawn();
        void Spawn(Entity &reference, const glm::ivec3 &, const glm::vec3 &);
 
+       CompositeModel &RandomSkeleton() noexcept;
+
 private:
        World &world;
        Skeletons &skeletons;
        unsigned int max_entities;
        int chunk_range;
 
+       std::size_t skeletons_offset;
+       std::size_t skeletons_length;
+
 };
 
 }
 
        chunk_renderer.LoadTextures(env.loader, tex_index);
        chunk_renderer.FogDensity(wc.fog_density);
        skeletons.Load();
+       spawner.LimitSkeletons(0, skeletons.Size());
        // TODO: better solution for initializing HUD
        interface.SelectNext();
 }
 
 
 void Skeletons::LoadHeadless() {
        skeletons.clear();
-       skeletons.reserve(3);
+       skeletons.reserve(4);
        {
-               AABB bounds{{ -0.25f, -0.5f, -0.25f }, { 0.25f, 0.5f, 0.25f }};
+               AABB bounds{{ -0.5f, -0.5f, -0.5f }, { 0.5f, 0.5f, 0.5f }};
                skeletons.emplace_back(new CompositeModel);
                skeletons[0]->ID(1);
                skeletons[0]->Bounds(bounds);
                skeletons[1]->Bounds(bounds);
        }
        {
-               AABB bounds{{ -0.5f, -0.5f, -0.5f }, { 0.5f, 0.5f, 0.5f }};
+               AABB bounds{{ -0.25f, -0.5f, -0.25f }, { 0.25f, 0.5f, 0.25f }};
                skeletons.emplace_back(new CompositeModel);
                skeletons[2]->ID(3);
                skeletons[2]->Bounds(bounds);
        }
+       {
+               AABB bounds{{ -0.25f, -0.5f, -0.35f }, { 0.25f, 0.5f, 0.35f }};
+               skeletons.emplace_back(new CompositeModel);
+               skeletons[3]->ID(4);
+               skeletons[3]->Bounds(bounds);
+       }
 }
 
 void Skeletons::Load() {
        LoadHeadless();
-       models.resize(3);
+       models.resize(4);
        EntityModel::Buffer buf;
        {
                CuboidShape shape(skeletons[0]->Bounds());
-               shape.Vertices(buf, 1.0f);
+               shape.Vertices(buf, 3.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);
+               shape.Vertices(buf, 0.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);
+               shape.Vertices(buf, 1.0f);
                buf.colors.resize(shape.VertexCount(), { 1.0f, 0.0f, 1.0f });
                models[2].Update(buf);
                skeletons[2]->SetNodeModel(&models[2]);
        }
+       {
+               CuboidShape shape(skeletons[3]->Bounds());
+               buf.Clear();
+               shape.Vertices(buf, 2.0f);
+               buf.colors.resize(shape.VertexCount(), { 1.0f, 0.25f, 0.5f });
+               models[3].Update(buf);
+               skeletons[3]->SetNodeModel(&models[3]);
+       }
 }
 
 CompositeModel *Skeletons::ByID(std::uint16_t id) noexcept {
 
 
 
 namespace blank {
+
+class CompositeModel;
+
 namespace server {
 
 class Server;
        ChunkIndex &PlayerChunks() noexcept { return *player.chunks; }
        const ChunkIndex &PlayerChunks() const noexcept { return *player.chunks; }
 
+       void SetPlayerModel(const CompositeModel &) noexcept;
+       bool HasPlayerModel() const noexcept;
+       const CompositeModel &GetPlayerModel() const noexcept;
+
 private:
        struct SpawnStatus {
                // the entity in question
        Server &server;
        Connection conn;
        Player player;
+       const CompositeModel *player_model;
        std::list<SpawnStatus> spawns;
        unsigned int confirm_wait;
 
 
 
 namespace blank {
 
+class CompositeModel;
 class World;
 
 namespace server {
 
        World &GetWorld() noexcept { return world; }
 
+       void SetPlayerModel(const CompositeModel &) noexcept;
+       bool HasPlayerModel() const noexcept;
+       const CompositeModel &GetPlayerModel() const noexcept;
+
 private:
        void HandlePacket(const UDPpacket &);
 
        std::list<ClientConnection> clients;
 
        World &world;
+       const CompositeModel *player_model;
 
 };
 
 
        TextureIndex tex_index;
        env.loader.LoadBlockTypes("default", block_types, tex_index);
        skeletons.LoadHeadless();
+       spawner.LimitSkeletons(1, skeletons.Size());
+       server.SetPlayerModel(skeletons[0]);
 
        loop_timer.Start();
 
 
 #include "Server.hpp"
 
 #include "../app/init.hpp"
+#include "../model/CompositeModel.hpp"
 #include "../world/ChunkIndex.hpp"
 #include "../world/Entity.hpp"
 #include "../world/World.hpp"
 : server(server)
 , conn(addr)
 , player(nullptr, nullptr)
+, player_model(nullptr)
 , spawns()
 , confirm_wait(0)
 , entity_updates()
                        }
                }
        }
+       if (HasPlayerModel()) {
+               GetPlayerModel().Instantiate(player.entity->GetModel());
+       }
 
        cout << "player \"" << player.entity->Name() << "\" joined" << endl;
 }
        chunk_queue.clear();
 }
 
+void ClientConnection::SetPlayerModel(const CompositeModel &m) noexcept {
+       player_model = &m;
+       if (HasPlayer()) {
+               m.Instantiate(PlayerEntity().GetModel());
+       }
+}
+
+bool ClientConnection::HasPlayerModel() const noexcept {
+       return player_model;
+}
+
+const CompositeModel &ClientConnection::GetPlayerModel() const noexcept {
+       return *player_model;
+}
+
 void ClientConnection::OnPacketReceived(uint16_t seq) {
        if (transmitter.Waiting()) {
                transmitter.Ack(seq);
 : serv_sock(nullptr)
 , serv_pack{ -1, nullptr, 0 }
 , clients()
-, world(world) {
+, world(world)
+, player_model(nullptr) {
        serv_sock = SDLNet_UDP_Open(conf.port);
        if (!serv_sock) {
                throw NetError("SDLNet_UDP_Open");
                }
        }
        clients.emplace_back(*this, addr);
+       if (HasPlayerModel()) {
+               clients.back().SetPlayerModel(GetPlayerModel());
+       }
        return clients.back();
 }
 
 void Server::Update(int dt) {
        for (list<ClientConnection>::iterator client(clients.begin()), end(clients.end()); client != end;) {
-               client->Update(dt);
                if (client->Disconnected()) {
                        client = clients.erase(client);
                } else {
        }
 }
 
+void Server::SetPlayerModel(const CompositeModel &m) noexcept {
+       player_model = &m;
+       for (ClientConnection &client : clients) {
+               client.SetPlayerModel(m);
+       }
+}
+
+bool Server::HasPlayerModel() const noexcept {
+       return player_model;
+}
+
+const CompositeModel &Server::GetPlayerModel() const noexcept {
+       return *player_model;
+}
+
 }
 }