Browse Source

console input with GNU readline, basic chunk / world

Kajetan Johannes Hammerle 4 years ago
parent
commit
d84b11d6d0

+ 0 - 1
common/block/BlockRegistry.cpp

@@ -29,7 +29,6 @@ namespace BlockRegistry
                 break;
             }
             u16 id = idMap.size();
-            std::cout << id << std::endl;
             idMap.emplace_back(id, line);
             registry[line] = id;
         }

+ 11 - 83
common/world/Chunk.cpp

@@ -1,98 +1,26 @@
-#include <cstdlib>
-#include <cmath>
-#include <iostream>
-
 #include "common/world/Chunk.h"
-#include "common/block/Blocks.h"
-
-using namespace std;
+#include "common/block/BlockRegistry.h"
 
-Chunk::Chunk(int chunkX, int chunkZ) : chunkX(chunkX), chunkZ(chunkZ)
+Chunk::Chunk()
 {
-    for(int i = 0; i < HEIGHT_PARTIONS; i++)
+    for(u32 y = 0; y < HEIGHT; y++)
     {
-        dirty[i] = true;
-    }
-    for(int z = 0; z < DEPTH; z++)
-    {
-        for(int x = 0; x < WIDTH; x++)
+        for(u32 x = 0; x < CHUNK_PARTION_SIZE; x++)
         {
-            int maxY = (int) (sinf((x + chunkX * WIDTH) * 0.3) * 20 + 22) + (sinf((z + chunkZ * DEPTH) * 0.3) * 20 + 22);
-            //maxY = 10;
-            if(maxY >= HEIGHT)
-            {
-                maxY = HEIGHT - 1;
-            }
-            
-            for(int y = 0; y < maxY - 3; y++)
-            {
-                setBlock(x, y, z, Blocks::STONE);
-            }
-            
-            for(int y = maxY - 3; y < maxY - 1; y++)
+            for(u32 z = 0; z < CHUNK_PARTION_SIZE; z++)
             {
-                setBlock(x, y, z, Blocks::DIRT);
+                blocks[y][x][z] = 0;
             }
-            
-            setBlock(x, maxY - 1, z, Blocks::GRASS);
-
-            for(int y = maxY; y < HEIGHT; y++)
-            {
-                setBlock(x, y, z, Blocks::AIR);
-            }
-        }
-    }
-}
-
-Chunk::~Chunk()
-{
-}
-
-void Chunk::setBlock(int x, int y, int z, const Block& block)
-{
-    blocks[y & BITMASK_HEIGHT][x & BITMASK_WIDTH][z & BITMASK_DEPTH] = block.getId();
-}
-
-const Block& Chunk::getBlock(int x, int y, int z)
-{
-    return BlockRegistry::getBlockFromId(blocks[y & BITMASK_HEIGHT][x & BITMASK_WIDTH][z & BITMASK_DEPTH]);
-}
-
-int Chunk::getChunkX() const
-{
-    return chunkX;
-}
-
-int Chunk::getChunkZ() const
-{
-    return chunkZ;
-}
-
-bool Chunk::isDirty() const
-{
-    for(int i = 0; i < HEIGHT_PARTIONS; i++)
-    {
-        if(dirty[i])
-        {
-            return true;
         }
     }
-    return false;
 }
 
-bool Chunk::isDirty(int index) const
+const Block& Chunk::getBlock(u32 x, u32 y, u32 z) const
 {
-    if(index >= 0 && index < HEIGHT_PARTIONS)
-    {
-        return dirty[index];
-    }
-    return false;
+    return BlockRegistry::getBlock(blocks[y & BITMASK_HEIGHT][x & CHUNK_PARTION_SIZE][z & CHUNK_PARTION_SIZE]);
 }
 
-void Chunk::clearDirtyFlag(int index)
+void Chunk::setBlock(u32 x, u32 y, u32 z, const Block& block)
 {
-    if(index >= 0 && index < HEIGHT_PARTIONS)
-    {
-        dirty[index] = false;
-    }
-}
+    blocks[y & BITMASK_HEIGHT][x & CHUNK_PARTION_SIZE][z & CHUNK_PARTION_SIZE] = block.getId();
+}

+ 15 - 28
common/world/Chunk.h

@@ -2,40 +2,27 @@
 #define CHUNK_H
 
 #include "common/block/Block.h"
+#include "common/utils/Types.h"
 
-class Chunk
+class Chunk final
 {
 public:
-    Chunk(int chunkX, int chunkZ);
-    virtual ~Chunk();
-    
-    void setBlock(int x, int y, int z, const Block& block);
-    const Block& getBlock(int x, int y, int z);
-    
-    int getChunkX() const;
-    int getChunkZ() const;
-    
-    bool isDirty() const;
-    bool isDirty(int index) const;
-    void clearDirtyFlag(int index);
-    
-    // chunk constants
-    static const int PARTION_HEIGHT = 16;
-    static const int HEIGHT_PARTIONS = 16;
-    
-    static const int WIDTH = 16;
-    static const int HEIGHT = PARTION_HEIGHT * HEIGHT_PARTIONS;
-    static const int DEPTH = 16;
+    Chunk();
+
+    void setBlock(u32 x, u32 y, u32 z, const Block& block);
+    const Block& getBlock(u32 x, u32 y, u32 z) const;
+
+    static const u32 CHUNK_BIT_SIZE = 4;
     
-    static const int BITMASK_WIDTH = WIDTH - 1;
-    static const int BITMASK_HEIGHT = HEIGHT - 1;
-    static const int BITMASK_DEPTH = DEPTH - 1;
 private:
-    int chunkX;
-    int chunkZ;
+    static const u32 CHUNK_PARTION_SIZE = 1 << CHUNK_BIT_SIZE;
+    static const u32 HEIGHT_PARTIONS = 16;
+    static const u32 HEIGHT = CHUNK_PARTION_SIZE * HEIGHT_PARTIONS;
+    
+    static const u32 BITMASK = CHUNK_PARTION_SIZE - 1;
+    static const u32 BITMASK_HEIGHT = HEIGHT - 1;
     
-    BlockId blocks[HEIGHT][WIDTH][DEPTH];
-    bool dirty[HEIGHT_PARTIONS];
+    u16 blocks[HEIGHT][CHUNK_PARTION_SIZE][CHUNK_PARTION_SIZE];
 };
 
 #endif

+ 0 - 14
common/world/IChunkListener.h

@@ -1,14 +0,0 @@
-#ifndef ICHUNKLISTENER_H
-#define ICHUNKLISTENER_H
-
-#include "common/world/Chunk.h"
-
-class IChunkListener
-{
-public:
-    virtual ~IChunkListener() = default;
-    virtual void updateChunk(Chunk& c, Chunk* north, Chunk* east, Chunk* south, Chunk* west) = 0;
-};
-
-#endif
-

+ 0 - 15
common/world/IChunkProvider.h

@@ -1,15 +0,0 @@
-#ifndef ICHUNKPROVIDER_H
-#define ICHUNKPROVIDER_H
-
-#include "common/world/Chunk.h"
-
-class IChunkProvider
-{
-public:
-    virtual ~IChunkProvider() = default;
-    virtual Chunk* getChunk(int x, int z) = 0;
-    virtual bool isChunkLoaded(int x, int z) const = 0;
-    virtual void forEachLoadedChunk(void* data, void (*fun) (Chunk&, void*)) const = 0;
-};
-
-#endif

+ 9 - 38
common/world/World.cpp

@@ -1,48 +1,19 @@
 #include "common/world/World.h"
 
-World::World(IChunkProvider* chunks) : chunks(chunks)
+World::World()
 {
 }
 
-World::~World()
+void World::setBlock(u32 x, u32 y, u32 z, const Block& block)
 {
-    
+    u32 cx = (x >> Chunk::CHUNK_BIT_SIZE) & BITMASK;
+    u32 cz = (z >> Chunk::CHUNK_BIT_SIZE) & BITMASK;
+    chunks[cx][cz].setBlock(x, y, z, block);
 }
 
-void World::registerChunkListener(IChunkListener* listener)
+const Block& World::getBlock(u32 x, u32 y, u32 z) const
 {
-    chunkListener.push_back(listener);
+    u32 cx = (x >> Chunk::CHUNK_BIT_SIZE) & BITMASK;
+    u32 cz = (z >> Chunk::CHUNK_BIT_SIZE) & BITMASK;
+    return chunks[cx][cz].getBlock(x, y, z);
 }
-
-void World::removeChunkListener(IChunkListener* listener)
-{
-    //chunkListener.remove(listener);
-}
-
-void World::updateDirtyChunks()
-{
-    chunks->forEachLoadedChunk(this, [](Chunk& c, void* world)
-    {
-        if(c.isDirty())
-        {
-            ((World*) world)->updateChunk(c);
-        }
-    });
-}
-
-void World::updateChunk(Chunk& c)
-{
-    int x = c.getChunkX();
-    int z = c.getChunkZ();
-    
-    Chunk* north = chunks->getChunk(x + 1, z);
-    Chunk* east = chunks->getChunk(x, z + 1);
-    Chunk* south = chunks->getChunk(x - 1, z);
-    Chunk* west = chunks->getChunk(x, z - 1);
-    
-    for(unsigned long i = 0; i < chunkListener.size(); i++)
-    {
-        chunkListener[i]->updateChunk(c, north, east, south, west);
-    }
-}
-

+ 9 - 16
common/world/World.h

@@ -1,30 +1,23 @@
 #ifndef WORLD_H
 #define WORLD_H
 
-#include <iostream>
-#include <vector>
-
 #include "common/world/Chunk.h"
-#include "common/world/IChunkProvider.h"
-#include "common/world/IChunkListener.h"
-
-using namespace std;
 
-class World
+class World final
 {
 public:
-    World(IChunkProvider* chunks);
-    virtual ~World();
+    World();
     
-    void registerChunkListener(IChunkListener* listener);
-    void removeChunkListener(IChunkListener* listener);
+    void setBlock(u32 x, u32 y, u32 z, const Block& block);
+    const Block& getBlock(u32 x, u32 y, u32 z) const;
+    
+    static const u32 WORLD_SIZE = 256;
     
-    void updateDirtyChunks();
 private:
-    void updateChunk(Chunk& c);
+    static const u32 BITMASK = WORLD_SIZE - 1;
+    
+    Chunk chunks[WORLD_SIZE >> Chunk::CHUNK_BIT_SIZE][WORLD_SIZE >> Chunk::CHUNK_BIT_SIZE];
     
-    vector<IChunkListener*> chunkListener;
-    IChunkProvider* chunks;
 };
 
 #endif

+ 5 - 2
meson.build

@@ -2,7 +2,7 @@ project('cubes plus plus', 'cpp')
 
 #sourcesCommon = ['common/stream/Stream.cpp', 'common/utils/Face.cpp', 'common/block/Block.cpp', 'common/block/Blocks.cpp', 'common/block/BlockAir.cpp', 'common/world/Chunk.cpp', 'common/world/World.cpp']
 
-sourcesCommon = ['common/stream/Stream.cpp', 'common/block/BlockRegistry.cpp','common/block/Block.cpp', 'common/utils/DataVector.cpp']
+sourcesCommon = ['common/stream/Stream.cpp', 'common/block/BlockRegistry.cpp','common/block/Block.cpp', 'common/utils/DataVector.cpp', 'common/world/Chunk.cpp', 'common/world/World.cpp']
 
 sourcesServer = ['server/Main.cpp', 'server/GameServer.cpp', 'server/network/Server.cpp', 'server/commands/CommandManager.cpp', 'server/commands/CommandUtils.cpp', 'server/commands/GeneralCommands.cpp', 'server/commands/ServerCommands.cpp']
 
@@ -10,6 +10,9 @@ sourcesServer = ['server/Main.cpp', 'server/GameServer.cpp', 'server/network/Ser
 
 sourcesTest = ['tests/Main.cpp', 'server/commands/CommandUtils.cpp']
 
+c_compiler = meson.get_compiler('cpp')
+readline = c_compiler.find_library('readline', required: true)
+
 threadDep = dependency('threads')
 glewDep = dependency('glew')
 glfwDep = dependency('glfw3')
@@ -17,7 +20,7 @@ pngDep = dependency('libpng')
 
 executable('game_server', 
     sources: sourcesCommon + sourcesServer,
-    dependencies : threadDep,
+    dependencies : [threadDep, readline],
     cpp_args: ['-Wall', '-Wextra', '-pedantic', '-Werror'])
     
 #executable('game_tests', 

+ 78 - 5
server/GameServer.cpp

@@ -1,8 +1,29 @@
 #include <iostream>
+#include <cstring>
+
+#include <poll.h>
+#include <readline/readline.h>
+#include <readline/history.h>
+#include <unistd.h>
 
 #include "server/GameServer.h"
 #include "server/network/Server.h"
 
+GameServer::GameServer()
+{
+    rl_bind_key ('\t', rl_insert);
+    
+    static GameServer* pointer = this;
+    rl_event_hook = []()
+    {
+        if(!pointer->serverCommands.isRunning())
+        {
+            rl_stuff_char('\n');
+        }
+        return 0;
+    };
+}
+
 void GameServer::start(uint16_t port, uint16_t maxClients)
 {
     std::cout << port << std::endl;
@@ -12,13 +33,30 @@ void GameServer::start(uint16_t port, uint16_t maxClients)
         return;
     }
     
+    auto lastTime = std::chrono::steady_clock::now().time_since_epoch();
+    auto lag = lastTime.zero();
+    
+    std::thread commandThread = std::thread(&GameServer::readFromConsole, this);
+    
     while(serverCommands.isRunning())
     {
-        std::cout << "> ";
-        std::string command;
-        std::getline(std::cin, command, '\n');
-        commandManager.execute(serverCommands, command);
+        auto time = std::chrono::steady_clock::now().time_since_epoch();
+        lag += time - lastTime;
+        lastTime = time;
+        while(lag >= NANOS_PER_TICK)
+        {
+            lag -= NANOS_PER_TICK;
+            tick();
+        }
+        
+        auto waitTime = NANOS_PER_TICK - lag - MIN_NANO_SLEEP;
+        if(waitTime > MIN_NANO_SLEEP)
+        {
+            std::this_thread::sleep_for(waitTime);
+        }
     }
+    
+    commandThread.join();
 }
 
 void GameServer::onFullServerClientConnect(int socket) const
@@ -59,4 +97,39 @@ void GameServer::onClientDisconnect(int socket) const
     Stream answer;
     answer.write("Bye.");
     answer.sendToSocket(socket);
-}
+}
+
+void GameServer::tick()
+{
+    rl_clear_visible_line();
+    handleCommandQueue();
+    std::cout << "tick\n";
+    rl_forced_update_display();
+}
+
+void GameServer::readFromConsole()
+{
+    while(serverCommands.isRunning())
+    {
+        char* test = readline("> ");
+        std::string command(test);
+        if(command.size() != 0)
+        {
+            add_history(test);
+            std::lock_guard<std::mutex> lg(comandQueueMutex);
+            commandQueue.push_back(command);
+        }
+        free(test);
+    }
+}
+
+void GameServer::handleCommandQueue()
+{
+    std::lock_guard<std::mutex> lg(comandQueueMutex);
+    for(std::string& s : commandQueue)
+    {
+        commandManager.execute(serverCommands, s);
+    }
+    commandQueue.clear();
+}
+

+ 16 - 0
server/GameServer.h

@@ -1,6 +1,11 @@
 #ifndef GAMESERVER_H
 #define GAMESERVER_H
 
+#include <chrono>
+#include <vector>
+#include <mutex>
+
+#include "common/utils/Types.h"
 #include "server/network/IServerListener.h"
 #include "server/commands/CommandManager.h"
 #include "commands/ServerCommands.h"
@@ -8,6 +13,8 @@
 class GameServer : public IServerListener
 {
 public:
+    GameServer();
+    
     void start(uint16_t port, uint16_t maxClients);
     
     void onFullServerClientConnect(int socket) const override;
@@ -16,8 +23,17 @@ public:
     void onClientDisconnect(int socket) const override;
     
 private:
+    void readFromConsole();
+    void handleCommandQueue();
+    const std::chrono::nanoseconds NANOS_PER_TICK = std::chrono::nanoseconds(50000000);
+    const std::chrono::nanoseconds MIN_NANO_SLEEP = std::chrono::nanoseconds(300000);
+    void tick();
+    
     CommandManager commandManager;
     ServerCommands serverCommands;
+    
+    std::vector<std::string> commandQueue;
+    std::mutex comandQueueMutex; 
 };
 
 #endif

+ 3 - 6
server/Main.cpp

@@ -1,5 +1,3 @@
-#include <iostream>
-
 #include "server/GameServer.h"
 #include "common/block/BlockRegistry.h"
 
@@ -7,8 +5,7 @@ int main(int /*argc*/, char** /*argv*/)
 {
     BlockRegistry::loadFromFile("resources/blocks");
 
-    //GameServer server;
-    //server.start(25565, 2);
+    GameServer server;
+    server.start(25565, 2);
     return 0;
-}
-
+}

+ 3 - 1
server/commands/ServerCommands.h

@@ -1,6 +1,8 @@
 #ifndef SERVERCOMMANDS_H
 #define SERVERCOMMANDS_H
 
+#include <atomic>
+
 class ServerCommands
 {
 public:
@@ -10,7 +12,7 @@ public:
     void stop();
     
 private:
-    bool running;
+    std::atomic_bool running;
 };
 
 #endif