Browse Source

add info line on console to show average tick time and loaded chunks

Kolja Strohm 1 year ago
parent
commit
911ed1c183

+ 4 - 0
FactoryCraft/Dimension.cpp

@@ -909,4 +909,8 @@ void Dimension::updateMap(int x, int y, int height)
             (char)x, (char)y, (unsigned char)(height / 2), color1, color2);
     }
     chunkCs.unlock();
+}
+
+int Dimension::getChunkCount() {
+    return chunkList.getEintragAnzahl();
 }

+ 1 - 0
FactoryCraft/Dimension.h

@@ -84,4 +84,5 @@ public:
     MultiblockStructure* zStructureById(__int64 id);
     void requestStopAndWait();
     void updateMap(int x, int y, int height);
+    int getChunkCount();
 };

+ 3 - 3
FactoryCraft/FactoryCraft.vcxproj

@@ -254,7 +254,7 @@
   <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
     <Link>
       <AdditionalLibraryDirectories>$(RemoteRootDir)/sql/debug;$(RemoteRootDir)/Network/debug;$(RemoteRootDir)/Framework/debug;/usr/lib/;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
-      <LibraryDependencies>dbgFramework;dbgNetwork;dbgSQL;pq;pthread;ssl</LibraryDependencies>
+      <LibraryDependencies>dbgFramework;dbgNetwork;dbgSQL;pq;pthread;ssl;ncurses</LibraryDependencies>
       <AdditionalOptions>-Wl,-rpath,../lib %(AdditionalOptions)</AdditionalOptions>
       <OutputFile>$(RemoteProjectDir)/$(TargetName)$(TargetExt)</OutputFile>
     </Link>
@@ -266,8 +266,8 @@
   </ItemDefinitionGroup>
   <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
     <Link>
-      <AdditionalLibraryDirectories>$(RemoteRootDir)/sql/release;$(RemoteRootDir)/Network/release;$(RemoteRootDir)/Framework/release;/usr/lib/;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
-      <LibraryDependencies>Framework;Network;pthread;ssl</LibraryDependencies>
+      <AdditionalLibraryDirectories>$(RemoteRootDir)/sql/release;$(RemoteRootDir)/Network/release;$(RemoteRootDir)/Framework/release;/usr/lib/;/usr/lib/x86_64-linux-gnu;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+      <LibraryDependencies>Framework;Network;pthread;ssl;ncurses</LibraryDependencies>
       <AdditionalOptions>-Wl,-rpath,../lib %(AdditionalOptions)</AdditionalOptions>
     </Link>
     <ClCompile>

+ 47 - 14
FactoryCraft/Game.cpp

@@ -292,7 +292,10 @@ Game::Game(Framework::Text name, Framework::Text worldsDir)
       chat(0),
       playerRegister(new PlayerRegister(path)),
       totalTickTime(0),
-      tickCounter(0)
+      tickCounter(0),
+      averageTickTime(0),
+      ticksPerSecond(0),
+      totalTime(0)
 {
     if (!DateiExistiert(worldsDir + "/" + name))
         DateiPfadErstellen(worldsDir + "/" + name + "/");
@@ -339,6 +342,10 @@ void Game::thread()
     ZeitMesser clientReply;
     ZeitMesser removeOldChunks;
     ZeitMesser m;
+    ZeitMesser total;
+    total.messungStart();
+    double tickTime = 0;
+    double sleepTime = 0;
     while (!stop)
     {
         m.messungStart();
@@ -430,23 +437,24 @@ void Game::thread()
         double sec = m.getSekunden();
         tickCounter++;
         totalTickTime += sec;
-        if (tickCounter >= 1000)
+        sleepTime += 1.0 / 20 - tickTime;
+        if (sleepTime > 0)
         {
-            std::cout << "Average Tick time: " << (totalTickTime / 1000)
-                      << "\n";
-            if ((totalTickTime / 1000) * 20 > 1)
-            {
-                std::cout << "The game runns slower than normal.\n";
-            }
-            else
-            {
-                std::cout << "No performance issues detected.\n";
-            }
+            Sleep((int)(sleepTime * 1000));
+        }
+        total.messungEnde();
+        total.messungStart();
+        tickTime = total.getSekunden();
+        totalTime += tickTime;
+        if (totalTime >= 1)
+        {
+            averageTickTime = totalTickTime / tickCounter;
+            ticksPerSecond = tickCounter;
             totalTickTime = 0;
             tickCounter = 0;
+            totalTime = 0;
+            std::cout << std::flush; // update info in console
         }
-        if (sec < 0.05)
-            Sleep((int)((0.05 - sec) * 1000));
         else if (sec > 1)
         {
             std::cout << "WARNING: tick needed " << sec
@@ -939,4 +947,29 @@ Player* Game::zPlayerByName(const char* name) const
 int Game::getPlayerId(const char* name) const
 {
     return playerRegister->getPlayerId(name);
+}
+
+double Game::getAverageTickTime() const
+{
+    return averageTickTime;
+}
+
+int Game::getTicksPerSecond() const
+{
+    return ticksPerSecond;
+}
+
+int Game::getPlayerCount() const
+{
+    return clients->getEintragAnzahl();
+}
+
+int Game::getChunkCount() const
+{
+    int result = 0;
+    for (Dimension* dim : *dimensions)
+    {
+        result += dim->getChunkCount();
+    }
+    return result;
 }

+ 8 - 0
FactoryCraft/Game.h

@@ -92,6 +92,9 @@ private:
     PlayerRegister* playerRegister;
     double totalTickTime;
     int tickCounter;
+    double averageTickTime;
+    int ticksPerSecond;
+    double totalTime;
 
     void thread() override;
 
@@ -138,6 +141,11 @@ public:
     Chat* zChat() const;
     Player* zPlayerByName(const char* name) const;
     int getPlayerId(const char* name) const;
+    
+    double getAverageTickTime() const;
+    int getTicksPerSecond() const;
+    int getPlayerCount() const;
+    int getChunkCount() const;
 
     static Game* INSTANCE;
     static Critical INSTANCE_CS;

+ 1 - 1
FactoryCraft/Inventory.cpp

@@ -169,7 +169,7 @@ MultipleInventoryLock::MultipleInventoryLock(Inventory** inventories, int count)
 {
     // sort given inventories in locking order
     bool* used = new bool[count];
-    memset(&used, 0, count);
+    memset(used, 0, count);
     // TODO: use a performant sorting algorithm
     for (int i = 0; i < count; i++)
     {

+ 111 - 23
FactoryCraft/Start.cpp

@@ -6,15 +6,15 @@
 #include <iostream>
 #include <Klient.h>
 #ifndef _WINDOWS
-#include <sys/resource.h>
+#    include <curses.h>
+#    include <sys/resource.h>
 #endif
+#include <AsynchronCall.h>
 #include <Text.h>
 #include <Zeit.h>
-#include <AsynchronCall.h>
-
-#include "Server.h"
 
 #include "ChunkMap.h"
+#include "Server.h"
 
 FactoryCraftServer* mserver = 0;
 
@@ -22,7 +22,8 @@ void exit()
 {
     if (mserver)
     {
-        std::cout << "The server terminated unexpectedly. Trying to save game progress.\n";
+        std::cout << "The server terminated unexpectedly. Trying to save game "
+                     "progress.\n";
         mserver->close();
     }
 }
@@ -30,31 +31,106 @@ void exit()
 class DuplicatingStreamBuf : public std::stringbuf
 {
 private:
-    std::ostream& m_os1;
-    std::ostream& m_os2;
+    std::ostream& console;
+    std::ostream& file;
+    bool hasFile = 0;
     Critical cs;
+    int infoLength;
+
+    int getCursorColumn()
+    {
+#ifdef _WINDOWS
+        CONSOLE_SCREEN_BUFFER_INFO csbi;
+        int columns, rows;
+
+        GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi);
+        return csbi.dwCursorPosition.X;
+#else
+        int x = getcurx(curscr);
+        return x;
+#endif
+    }
 
 public:
-    DuplicatingStreamBuf(std::ostream& os1, std::ostream& os2)
+    DuplicatingStreamBuf(std::ostream& console, std::ostream& file)
+        : std::stringbuf(),
+          console(console),
+          file(file),
+          hasFile(1),
+          infoLength(0)
+    {}
+
+    DuplicatingStreamBuf(std::ostream& console)
         : std::stringbuf(),
-          m_os1(os1),
-          m_os2(os2)
+          console(console),
+          file(console),
+          hasFile(0),
+          infoLength(0)
     {}
 
     int sync() override
     {
         cs.lock();
-        m_os1 << str();
-        m_os2 << str();
+        if (str().length() == 0 && !Game::INSTANCE)
+        {
+            cs.unlock();
+            return 0;
+        }
+        int column = getCursorColumn();
+        if (infoLength > 0)
+        {
+            console << /* move cursor a line up*/ "\033[1A"
+                    << /* clear the line */ "\x1b[2K"
+                    << /* return to beginning of line */ "\r";
+        }
+        if (str().length() > 0)
+        {
+            console << str();
+            if (str().c_str()[str().length() - 1] != '\n')
+            {
+                console << "\n";
+            }
+        }
+        if (Game::INSTANCE)
+        {
+            int tps = Game::INSTANCE->getTicksPerSecond();
+            Framework::Text infoLine = "";
+            infoLine.append()
+                << "Players: " << Game::INSTANCE->getPlayerCount()
+                << "\tChunks: " << Game::INSTANCE->getChunkCount() << "\ttps: ";
+            if (tps < 15)
+            { // red
+                infoLine += "\033[1;31m";
+            }
+            else if (tps < 20)
+            { // yellow
+                infoLine += "\033[1;33m";
+            }
+            else
+            { // green
+                infoLine += "\033[1;32m";
+            }
+            infoLine.append()
+                << tps << /* reset color */ "\033[0m"
+                << "\tAverage Tick Time: "
+                << Game::INSTANCE->getAverageTickTime()
+                << /* move cursor a line down */ "\033[1B"
+                // << /* move cursor to beginning of line */ "\r"
+                << /* set cursor position back */ "\033[" << column + 1 << "G";
+            infoLength = infoLine.getLength();
+            console << infoLine << std::flush;
+        }
+        if (hasFile) file << str() << std::flush;
         str("");
         cs.unlock();
+        return 0;
     }
 };
 
 int main()
 {
     Framework::initFramework();
-    
+
     initializeBlockTypes();
     initializeItemTypes();
     initializeEntityTypes();
@@ -62,6 +138,7 @@ int main()
     initializeMultiblockTypes();
 
 #ifndef _WINDOWS
+    initscr();
     struct rlimit core_limits;
     core_limits.rlim_cur = core_limits.rlim_max = RLIM_INFINITY;
     setrlimit(RLIMIT_CORE, &core_limits);
@@ -73,13 +150,17 @@ int main()
     z->release();
     DateiPfadErstellen(pfad->getText());
     std::ofstream file;
-    file.open(pfad->getText());
     std::streambuf* sbuf = std::cout.rdbuf();
-    #ifndef _DEBUG
+#ifndef _DEBUG
+    file.open(pfad->getText());
+    std::ostream newCout(sbuf);
+    DuplicatingStreamBuf duplicator(newCout, file);
+    std::cout.rdbuf(&duplicator);
+#else
     std::ostream newCout(sbuf);
-    DuplicatingStreamBuf duplicator(file, newCout);
+    DuplicatingStreamBuf duplicator(newCout);
     std::cout.rdbuf(&duplicator);
-    #endif
+#endif
     pfad->release();
 
     std::cout << "Starting...\n";
@@ -91,7 +172,9 @@ int main()
         std::cout << "error: Datei konnte nicht gelesen werden. Das Programm "
                      "wird geschlossen.\n";
         dat->release();
+#ifndef _DEBUG
         file.close();
+#endif
         std::cout.rdbuf(sbuf);
         exit(1);
     }
@@ -101,9 +184,8 @@ int main()
     {
         if (!dat->wertExistiert(w))
         {
-            std::cout
-                << "error: The value '" << w
-                << "' was not specified. The Server can not start.\n";
+            std::cout << "error: The value '" << w
+                      << "' was not specified. The Server can not start.\n";
             dat->release();
             file.close();
             std::cout.rdbuf(sbuf);
@@ -120,15 +202,19 @@ int main()
     signal(SIGFPE, exit);
     signal(SIGINT, exit);
 
-    new Framework::AsynchronCall("Commander", []() { 
+    new Framework::AsynchronCall("Commander", []() {
         while (mserver)
         {
             std::string line;
             std::getline(std::cin, line);
             if (!mserver) return;
+            std::cout << "\033[1A"
+                      << "\x1b[2K"
+                      << "\r" << line << std::flush;
             if (Text(line.c_str()) == Text("exit"))
             {
-                std::cout << "The server will be terminated and the game progress will be saved.\n";
+                std::cout << "The server will be terminated and the game "
+                             "progress will be saved.\n";
                 mserver->close();
                 return;
             }
@@ -151,8 +237,10 @@ int main()
         Game::INSTANCE = 0;
     }
     dat->release();
-    std::cout << "The server was shut down successfully.\n";
+    std::cout << "The server was shut down successfully.\n" << std::flush;
+#ifndef _DEBUG
     file.close();
+#endif
     std::cout.rdbuf(sbuf);
     Framework::releaseFramework();
     return 0;

+ 17 - 0
build.bat

@@ -0,0 +1,17 @@
+@echo off
+set /a timerstart=((1%time:~0,2%-100)*60*60)+((1%time:~3,2%-100)*60)+(1%time:~6,2%-100)
+
+call init.bat
+
+cd "Allgemein"
+call build.bat
+cd "../Apps"
+call build.bat
+cd "../Spiele Platform"
+call build.bat
+version_update.exe "Spiele Platform\Klient\Fertig\x64\data\version" "Spiele Platform\Klient\Fertig\x32\data\version" "Spiele Platform\Klient\Fertig\Debug\x64\data\version"
+
+set /a timerstop=((1%time:~0,2%-100)*60*60)+((1%time:~3,2%-100)*60)+(1%time:~6,2%-100)
+set /a timeseks=(%timerstop%-%timerstart%)
+set /a timemins=(%timerstop%-%timerstart%)/60
+echo Sekunden: %timeseks% -- Minuten: %timemins%

+ 0 - 912
enc_temp_folder/d5635fc3ae2892ff9df5aec8c871e899/Dimension.cpp

@@ -1,912 +0,0 @@
-#include "Dimension.h"
-
-#include "Constants.h"
-#include "Datei.h"
-#include "Game.h"
-#include "NoBlock.h"
-
-using namespace Framework;
-
-Dimension::Dimension(int id)
-    : Thread(),
-      nextStructureId(0),
-      dimensionId(id),
-      gravity(9.8f),
-      chunks(new Trie<Chunk>()),
-      entities(new RCArray<Entity>()),
-      map(new DimensionMap(id)),
-      stop(0)
-{
-    Datei d;
-    d.setDatei(
-        Game::INSTANCE->getWorldDirectory() + "/dim/" + Text(id) + "/nst.id");
-    if (d.existiert())
-    {
-        d.open(Datei::Style::lesen);
-        d.lese((char*)&nextStructureId, 8);
-        d.close();
-    }
-    start();
-}
-
-Dimension::~Dimension()
-{
-    entities->release();
-    chunks->release();
-    map->release();
-}
-
-void Dimension::api(Framework::InMemoryBuffer* zRequest,
-    NetworkMessage* zResponse,
-    Entity* zSource)
-{
-    DoLaterHandler laterHandler;
-    char type;
-    zRequest->lese(&type, 1);
-    switch (type)
-    {
-    case 0: // chunk message
-        {
-            Punkt center;
-            zRequest->lese((char*)&center.x, 4);
-            zRequest->lese((char*)&center.y, 4);
-            cs.lock();
-            Chunk* cC = zChunk(Game::getChunkCenter(center.x, center.y));
-            if (!cC)
-            {
-                // TODO: have a max amount of waiting requests per player
-                waitingRequests.add(
-                    {dynamic_cast<InMemoryBuffer*>(zRequest->getThis()),
-                        center,
-                        zSource->getId()});
-                Game::INSTANCE->requestArea({center.x - CHUNK_SIZE / 2,
-                    center.y - CHUNK_SIZE / 2,
-                    center.x + CHUNK_SIZE / 2 - 1,
-                    center.y + CHUNK_SIZE / 2 - 1,
-                    dimensionId});
-            }
-            else
-            {
-                cC->api(zRequest, zSource, laterHandler);
-            }
-            cs.unlock();
-            break;
-        }
-    case 1: // block message
-        {
-            Vec3<int> location;
-            zRequest->lese((char*)&location.x, 4);
-            zRequest->lese((char*)&location.y, 4);
-            zRequest->lese((char*)&location.z, 4);
-            Framework::Either<Block*, int> block = zBlock(location);
-            if (block.isA())
-            {
-                block.getA()->api(zRequest, zResponse);
-            }
-            break;
-        }
-    case 2: // map request
-        {
-            Framework::Punkt location;
-            zRequest->lese((char*)&location.x, 4);
-            zRequest->lese((char*)&location.y, 4);
-            location = Game::getChunkCenter(location.x, location.y);
-            char addr[8];
-            getAddrOfWorld(location, addr);
-            ChunkMap* res = map->getMap(addr, 8, location);
-            // create an empty map for a chunk that does not yet exist
-            if (!res) res = new ChunkMap(location);
-            zResponse->sendMap(res);
-            zResponse->setUseBackground();
-            res->release();
-            break;
-        }
-    }
-}
-
-void Dimension::tickEntities()
-{
-    for (auto entity : *entities)
-    {
-        if (!entity->isRemoved()
-            && (entity->isMoving()
-                || zChunk(Game::getChunkCenter((int)entity->getPosition().x,
-                    (int)entity->getPosition().y))))
-            entity->prepareTick(this);
-    }
-    int index = 0;
-    for (auto entity : *entities)
-    {
-        if (!entity->isRemoved()
-            && (entity->isMoving()
-                || zChunk(Game::getChunkCenter((int)entity->getPosition().x,
-                    (int)entity->getPosition().y))))
-            entity->tick(this);
-        index++;
-    }
-}
-
-void Dimension::thread()
-{
-    // light calculation
-    int index = 0;
-    ZeitMesser messer;
-    messer.messungStart();
-    double time = 0;
-    bool isForeground = 0;
-    Framework::Array<Framework::Vec3<int>> internalLightUpdateQueue;
-    while (!stop)
-    {
-        Vec3<int> position;
-        if (internalLightUpdateQueue.getEintragAnzahl())
-        {
-            position = internalLightUpdateQueue.get(0);
-            internalLightUpdateQueue.remove(0);
-        }
-        else
-        {
-            removedChunksCs.lock();
-            if (removedChunks.getEintragAnzahl() > 0)
-            {
-                Chunk* removedChunk = removedChunks.z(0);
-                removedChunksCs.unlock();
-                Text filePath = Game::INSTANCE->getWorldDirectory() + "/dim/"
-                              + getDimensionId() + "/";
-                filePath.appendHex(removedChunk->getCenter().x);
-                filePath += "_";
-                filePath.appendHex(removedChunk->getCenter().y);
-                filePath += ".chunk";
-                Datei d;
-                d.setDatei(filePath);
-                d.erstellen();
-                d.open(Datei::Style::schreiben);
-                removedChunk->save(&d);
-                char addr[8];
-                getAddrOfWorld(removedChunk->getCenter(), addr);
-                map->removeMap(addr, 8);
-                d.close();
-                removedChunksCs.lock();
-                removedChunks.remove(0);
-            }
-            removedChunksCs.unlock();
-            if (priorizedLightUpdateQueue.getEintragAnzahl())
-            {
-                prioLightCs.lock();
-                position = priorizedLightUpdateQueue.get(0);
-                priorizedLightUpdateQueue.remove(0);
-                prioLightCs.unlock();
-                isForeground = 1;
-            }
-            else
-            {
-                if (!lightUpdateQueue.getEintragAnzahl())
-                {
-                    messer.messungEnde();
-                    time += messer.getSekunden();
-                    Sleep(500);
-                    messer.messungStart();
-                    continue;
-                }
-                lightCs.lock();
-                position = lightUpdateQueue.get(0);
-                lightUpdateQueue.remove(0);
-                lightCs.unlock();
-                isForeground = 0;
-            }
-        }
-        Chunk* chunk
-            = zChunk(Game::INSTANCE->getChunkCenter(position.x, position.y));
-        if (position.z >= 0 && position.z < WORLD_HEIGHT)
-        {
-            if (chunk)
-            {
-                int x = position.x % CHUNK_SIZE;
-                int y = position.y % CHUNK_SIZE;
-                if (x < 0) x += CHUNK_SIZE;
-                if (y < 0) y += CHUNK_SIZE;
-                unsigned char* light
-                    = chunk->getLightData(Vec3<int>(x, y, position.z));
-                unsigned char dayLight[6] = {255, 255, 255, 0, 0, 0};
-                unsigned char noLight[6] = {0, 0, 0, 0, 0, 0};
-                unsigned char newLight[6] = {0, 0, 0, 0, 0, 0};
-                // add neighbor light emission
-                for (int i = 0; i < 6; i++)
-                {
-                    unsigned char* neighborLeight;
-                    Vec3<int> neighborPos
-                        = position + getDirection(getDirectionFromIndex(i));
-                    if (neighborPos.z < 0)
-                    {
-                        neighborLeight = noLight;
-                    }
-                    else if (neighborPos.z >= WORLD_HEIGHT)
-                    {
-                        neighborLeight = dayLight;
-                    }
-                    else
-                    {
-                        Chunk* neighborChunk
-                            = zChunk(Game::INSTANCE->getChunkCenter(
-                                neighborPos.x, neighborPos.y));
-                        int x = neighborPos.x % CHUNK_SIZE;
-                        int y = neighborPos.y % CHUNK_SIZE;
-                        if (x < 0) x += CHUNK_SIZE;
-                        if (y < 0) y += CHUNK_SIZE;
-                        if (neighborChunk)
-                            neighborLeight = neighborChunk->getLightData(
-                                Vec3<int>(x, y, neighborPos.z));
-                        else
-                            neighborLeight = noLight;
-                    }
-                    for (int j = 0; j < 3; j++)
-                        newLight[j] = (unsigned char)MAX(newLight[j],
-                            i == getDirectionIndex(TOP)
-                                ? neighborLeight[j]
-                                : (unsigned char)((float)neighborLeight[j]
-                                                  * 0.8f));
-                    for (int j = 3; j < 6; j++)
-                        newLight[j] = (unsigned char)MAX(newLight[j],
-                            (unsigned char)((float)neighborLeight[j] * 0.85f));
-                }
-                const Block* current = zBlockOrDefault(position);
-                // add own light emission
-                for (int j = 3; j < 6; j++)
-                    newLight[j] = (unsigned char)MAX(
-                        newLight[j], current->getLightEmisionColor()[j - 3]);
-                current->filterPassingLight(newLight);
-                current->filterPassingLight(newLight + 3);
-                for (int i = 0; i < 6; i++)
-                {
-                    if (newLight[i] != light[i])
-                    {
-                        chunk->setLightData(Vec3<int>(x, y, position.z),
-                            newLight,
-                            isForeground);
-                        for (int j = 0; j < 6; j++)
-                            internalLightUpdateQueue.add(
-                                position
-                                    + getDirection(getDirectionFromIndex(j)),
-                                0);
-                        break;
-                    }
-                }
-            }
-        }
-        index++;
-        if (index > 100000)
-        {
-            messer.messungEnde();
-            time += messer.getSekunden();
-            std::cout << "100000 light updates needed " << time << " seconds\n";
-            time = 0;
-            index = 0;
-            Sleep(250);
-            messer.messungStart();
-        }
-    }
-    std::cout << Text("Dimension ") + this->getDimensionId()
-                     + " update Thread exited.\n";
-}
-
-void Dimension::getAddrOf(Punkt cPos, char* addr) const
-{
-    *(int*)addr = cPos.x;
-    *((int*)addr + 1) = cPos.y;
-}
-
-void Dimension::getAddrOfWorld(Punkt wPos, char* addr) const
-{
-    if (wPos.x < 0) wPos.x -= CHUNK_SIZE;
-    if (wPos.y < 0) // needed because otherwise would (-8, -8) have the same
-                    // adress as (8, 8)
-        wPos.y -= CHUNK_SIZE;
-    wPos /= CHUNK_SIZE;
-    getAddrOf(wPos, addr);
-}
-
-void Dimension::saveStructure(MultiblockStructure* zStructure) const
-{
-    Datei d;
-    Text path = Game::INSTANCE->getWorldDirectory() + "/dim/"
-              + Text(dimensionId) + "/structures/";
-    path.appendHex(zStructure->getStructureId());
-    path += ".str";
-    d.setDatei(path);
-    d.erstellen();
-    d.open(Datei::Style::schreiben);
-    auto uPos = zStructure->getUniquePosition();
-    d.schreibe((char*)&uPos.x, 4);
-    d.schreibe((char*)&uPos.y, 4);
-    d.schreibe((char*)&uPos.z, 4);
-    int typeId = zStructure->getStructureTypeId();
-    d.schreibe((char*)&typeId, 4);
-    __int64 strId = zStructure->getStructureId();
-    d.schreibe((char*)&strId, 8);
-    StaticRegistry<MultiblockStructureType>::INSTANCE
-        .zElement(zStructure->getStructureTypeId())
-        ->saveStructure(zStructure, &d);
-    d.close();
-}
-
-Chunk* Dimension::zChunk(Punkt wPos) const
-{
-    char addr[8];
-    getAddrOfWorld(wPos, addr);
-    return chunks->z(addr, 8);
-}
-
-Framework::Either<Block*, int> Dimension::zBlock(Vec3<int> location)
-{
-    Chunk* c = zChunk(Game::INSTANCE->getChunkCenter(location.x, location.y));
-    if (c)
-    {
-        int x = location.x % CHUNK_SIZE;
-        int y = location.y % CHUNK_SIZE;
-        if (x < 0) x += CHUNK_SIZE;
-        if (y < 0) y += CHUNK_SIZE;
-        return c->zBlockAt(Vec3<int>(x, y, location.z));
-    }
-    return 0;
-}
-
-Block* Dimension::zRealBlockInstance(Framework::Vec3<int> location)
-{
-    Chunk* c = zChunk(Game::INSTANCE->getChunkCenter(location.x, location.y));
-    if (c)
-    {
-        int x = location.x % CHUNK_SIZE;
-        int y = location.y % CHUNK_SIZE;
-        if (x < 0) x += CHUNK_SIZE;
-        if (y < 0) y += CHUNK_SIZE;
-        c->instantiateBlock(Vec3<int>(x, y, location.z));
-        auto result = c->zBlockAt(Vec3<int>(x, y, location.z));
-        return result.isA() ? result.getA() : 0;
-    }
-    return 0;
-}
-
-const Block* Dimension::zBlockOrDefault(Framework::Vec3<int> location)
-{
-    Chunk* c = zChunk(Game::INSTANCE->getChunkCenter(location.x, location.y));
-    if (c)
-    {
-        int x = location.x % CHUNK_SIZE;
-        int y = location.y % CHUNK_SIZE;
-        if (x < 0) x += CHUNK_SIZE;
-        if (y < 0) y += CHUNK_SIZE;
-        return c->zBlockConst(Vec3<int>(x, y, location.z));
-    }
-    return &NoBlock::INSTANCE;
-}
-
-void Dimension::placeBlock(
-    Framework::Vec3<int> location, Framework::Either<Block*, int> block)
-{
-    Chunk* c = zChunk(Game::getChunkCenter(location.x, location.y));
-    if (c)
-    {
-        int x = location.x % CHUNK_SIZE;
-        int y = location.y % CHUNK_SIZE;
-        if (x < 0) x += CHUNK_SIZE;
-        if (y < 0) y += CHUNK_SIZE;
-        if (block.isA())
-            c->putBlockAt(Vec3<int>(x, y, location.z), block);
-        else
-        {
-            c->putBlockAt(Vec3<int>(x, y, location.z), 0);
-            c->putBlockTypeAt(Vec3<int>(x, y, location.z), block);
-        }
-    }
-    else if (block.isA())
-        block.getA()->release();
-}
-
-void Dimension::sendBlockInfo(Framework::Vec3<int> location)
-{
-    Chunk* c = zChunk(Game::getChunkCenter(location.x, location.y));
-    if (c)
-    {
-        int x = location.x % CHUNK_SIZE;
-        int y = location.y % CHUNK_SIZE;
-        if (x < 0) x += CHUNK_SIZE;
-        if (y < 0) y += CHUNK_SIZE;
-        c->sendBlockInfo(Vec3<int>(x, y, location.z));
-    }
-}
-
-void Dimension::addEntity(Entity* entity)
-{
-    entities->add(entity);
-}
-
-void Dimension::setChunk(Chunk* chunk, Punkt center)
-{
-    char addr[8];
-    getAddrOfWorld(center, addr);
-    if (chunk) map->loadMap(addr, 8, chunk);
-    chunkCs.lock();
-    Chunk* old = chunks->get(addr, 8);
-    if (old)
-    {
-        Game::INSTANCE->zTickOrganizer()->removeTickSource(old);
-        old->prepareRemove();
-        for (int i = 0; i < chunkList.getEintragAnzahl(); i++)
-        {
-            if (chunkList.get(i) == old)
-            {
-                chunkList.remove(i);
-                break;
-            }
-        }
-    }
-    chunks->set(addr, 8, chunk);
-    if (chunk)
-    {
-        chunkList.add(chunk);
-        chunk->setAdded();
-    }
-    getAddrOfWorld(center + Punkt(CHUNK_SIZE, 0), addr);
-    Chunk* zChunk = chunks->z(addr, 8);
-    if (zChunk)
-    {
-        zChunk->setNeighbor(WEST, chunk);
-        if (chunk)
-        {
-            chunk->setNeighbor(EAST, zChunk);
-        }
-    }
-    getAddrOfWorld(center + Punkt(-CHUNK_SIZE, 0), addr);
-    zChunk = chunks->z(addr, 8);
-    if (zChunk)
-    {
-        zChunk->setNeighbor(EAST, chunk);
-        if (chunk) chunk->setNeighbor(WEST, zChunk);
-    }
-    getAddrOfWorld(center + Punkt(0, CHUNK_SIZE), addr);
-    zChunk = chunks->z(addr, 8);
-    if (zChunk)
-    {
-        zChunk->setNeighbor(NORTH, chunk);
-        if (chunk) chunk->setNeighbor(SOUTH, zChunk);
-    }
-    getAddrOfWorld(center + Punkt(0, -CHUNK_SIZE), addr);
-    zChunk = chunks->z(addr, 8);
-    if (zChunk)
-    {
-        zChunk->setNeighbor(SOUTH, chunk);
-        if (chunk) chunk->setNeighbor(NORTH, zChunk);
-    }
-    DoLaterHandler laterHandler;
-    if (chunk)
-    {
-        cs.lock();
-        int index = 0;
-        for (Iterator<RequestQueue> iterator = waitingRequests.begin();
-             iterator;)
-        {
-            Entity* zE = Game::INSTANCE->zEntity(iterator.val().sourceId);
-            if (zE)
-            {
-                if (iterator.val().chunkCenter == chunk->getCenter())
-                {
-                    chunk->api(iterator.val().request, zE, laterHandler);
-                    iterator.val().request->release();
-                    iterator.remove();
-                    continue;
-                }
-            }
-            else
-            {
-                iterator.val().request->release();
-                iterator.remove();
-                continue;
-            }
-            iterator++;
-            index++;
-        }
-        cs.unlock();
-        Game::INSTANCE->zTickOrganizer()->addTickSource(chunk);
-    }
-    chunkCs.unlock();
-    if (old)
-    {
-        old->onUnloaded();
-        removedChunksCs.lock();
-        removedChunks.add(old);
-        removedChunksCs.unlock();
-    }
-    if (chunk) chunk->onLoaded();
-    updateLightAtChunkBorders(center);
-}
-
-void Dimension::save(Text worldDir) const
-{
-    Datei d;
-    d.setDatei(Game::INSTANCE->getWorldDirectory() + "/dim/" + Text(dimensionId)
-               + "/nst.id");
-    d.erstellen();
-    d.open(Datei::Style::schreiben);
-    d.schreibe((char*)&nextStructureId, 8);
-    d.close();
-    for (auto chunk = chunkList.begin(); chunk; chunk++)
-    {
-        if (!chunk._) continue;
-        Datei* file = new Datei();
-        Text filePath = worldDir + "/dim/" + dimensionId + "/";
-        filePath.appendHex(chunk->getCenter().x);
-        filePath += "_";
-        filePath.appendHex(chunk->getCenter().y);
-        filePath += ".chunk";
-        file->setDatei(filePath);
-        if (file->open(Datei::Style::schreiben)) chunk->save(file);
-        file->close();
-        file->release();
-        char addr[8];
-        getAddrOfWorld(chunk->getCenter(), addr);
-        map->saveMap(addr, 8);
-    }
-    Text filePath = worldDir + "/dim/" + dimensionId + "/entities";
-    Datei* file = new Datei();
-    file->setDatei(filePath);
-    if (file->open(Datei::Style::schreiben))
-    {
-        for (Entity* entity : *entities)
-        {
-            if (entity->zType()->getId() != EntityTypeEnum::PLAYER)
-            {
-                if (!entity->isRemoved())
-                {
-                    int type = entity->zType()->getId();
-                    file->schreibe((char*)&type, 4);
-                    StaticRegistry<EntityType>::INSTANCE.zElement(type)
-                        ->saveEntity(entity, file);
-                }
-            }
-            else
-            {
-                Datei pFile;
-                pFile.setDatei(worldDir + "/player/"
-                               + Game::INSTANCE->getPlayerId(
-                                   ((Player*)entity)->getName()));
-                if (pFile.open(Datei::Style::schreiben))
-                    StaticRegistry<EntityType>::INSTANCE
-                        .zElement(EntityTypeEnum::PLAYER)
-                        ->saveEntity(entity, &pFile);
-            }
-        }
-        file->close();
-    }
-    for (MultiblockStructure* structure : structures)
-    {
-        saveStructure(structure);
-    }
-}
-
-int Dimension::getDimensionId() const
-{
-    return dimensionId;
-}
-
-bool Dimension::hasChunck(int x, int y)
-{
-    if (zChunk(Punkt(x, y))) return 1;
-    removedChunksCs.lock();
-    for (Chunk* c : removedChunks)
-    {
-        if (c->getCenter().x == x && c->getCenter().y == y)
-        {
-            removedChunksCs.unlock();
-            return 1;
-        }
-    }
-    removedChunksCs.unlock();
-    return 0;
-}
-
-bool Dimension::reviveChunk(int x, int y)
-{
-    chunkCs.lock();
-    if (zChunk(Punkt(x, y)))
-    {
-        chunkCs.unlock();
-        return 1;
-    }
-    removedChunksCs.lock();
-    int index = 0;
-    for (Iterator<Chunk*> i = removedChunks.begin(); i; i++)
-    {
-        if (i->getCenter().x == x && i->getCenter().y == y)
-        {
-            setChunk(dynamic_cast<Chunk*>(i->getThis()), Punkt(x, y));
-            if (index > 0) i.remove();
-            removedChunksCs.unlock();
-            chunkCs.unlock();
-            return 1;
-        }
-        index++;
-    }
-    removedChunksCs.unlock();
-    chunkCs.unlock();
-    return 0;
-}
-
-float Dimension::getGravity() const
-{
-    return gravity;
-}
-
-void Dimension::removeOldChunks()
-{
-    chunkCs.lock();
-    int index = 0;
-    for (Chunk* chunk : chunkList)
-    {
-        if (!chunk->hasObservers()) setChunk(0, chunk->getCenter());
-        index++;
-    }
-    chunkCs.unlock();
-    structurCs.lock();
-    Iterator<MultiblockStructure*> i = structures.begin();
-    while (i)
-    {
-        if (i->isEmpty())
-        {
-            i.remove();
-            continue;
-        }
-        else if (i->isFullyUnloaded())
-        {
-            saveStructure(i);
-            i.remove();
-            continue;
-        }
-        i++;
-    }
-    structurCs.unlock();
-}
-
-Entity* Dimension::zEntity(int id)
-{
-    for (auto entity : *entities)
-    {
-        if (!entity->isRemoved() && entity->getId() == id) return entity;
-    }
-    return 0;
-}
-
-Entity* Dimension::zNearestEntity(
-    Framework::Vec3<float> pos, std::function<bool(Entity*)> filter)
-{
-    Entity* result = 0;
-    float sqDist = 0;
-    for (auto entity : *entities)
-    {
-        if (!entity->isRemoved() && filter(entity))
-        {
-            float d = pos.abstandSq(entity->getPosition());
-            if (!result || d < sqDist)
-            {
-                result = entity;
-                sqDist = d;
-            }
-        }
-    }
-    return result;
-}
-
-void Dimension::removeEntity(int id)
-{
-    int index = 0;
-    for (auto entity : *entities)
-    {
-        if (entity->getId() == id)
-        {
-            entities->remove(index);
-            return;
-        }
-        index++;
-    }
-}
-
-void Dimension::removeSubscriptions(Entity* zEntity)
-{
-    for (Chunk* chunk : chunkList)
-        chunk->removeObserver(zEntity);
-}
-
-void Dimension::updateLightning(Vec3<int> location)
-{
-    lightCs.lock();
-    lightUpdateQueue.add(location, 0);
-    lightCs.unlock();
-}
-
-void Dimension::updateLightningWithoutWait(Framework::Vec3<int> location)
-{
-    prioLightCs.lock();
-    priorizedLightUpdateQueue.add(location, 0);
-    prioLightCs.unlock();
-}
-
-void Dimension::updateLightAtChunkBorders(Punkt chunkCenter)
-{
-    if (lightUpdateQueue.getEintragAnzahl() > 300000)
-    {
-        std::cout
-            << "warning: light calculation queue is over 300000 blocks long";
-    }
-    for (int i = WORLD_HEIGHT - 1; i >= 0; i--)
-    {
-        for (int j = 0; j < CHUNK_SIZE; j++)
-        {
-            updateLightning(Vec3<int>(chunkCenter.x - CHUNK_SIZE / 2 - 1,
-                chunkCenter.y - CHUNK_SIZE / 2 + j,
-                i));
-            updateLightning(Vec3<int>(chunkCenter.x - CHUNK_SIZE / 2,
-                chunkCenter.y - CHUNK_SIZE / 2 + j,
-                i));
-            updateLightning(Vec3<int>(chunkCenter.x + CHUNK_SIZE / 2 - 1,
-                chunkCenter.y - CHUNK_SIZE / 2 + j,
-                i));
-            updateLightning(Vec3<int>(chunkCenter.x + CHUNK_SIZE / 2,
-                chunkCenter.y - CHUNK_SIZE / 2 + j,
-                i));
-            updateLightning(Vec3<int>(chunkCenter.x - CHUNK_SIZE / 2 + j,
-                chunkCenter.y - CHUNK_SIZE / 2 - 1,
-                i));
-            updateLightning(Vec3<int>(chunkCenter.x - CHUNK_SIZE / 2 + j,
-                chunkCenter.y - CHUNK_SIZE / 2,
-                i));
-            updateLightning(Vec3<int>(chunkCenter.x - CHUNK_SIZE / 2 + j,
-                chunkCenter.y + CHUNK_SIZE / 2 - 1,
-                i));
-            updateLightning(Vec3<int>(chunkCenter.x - CHUNK_SIZE / 2 + j,
-                chunkCenter.y + CHUNK_SIZE / 2,
-                i));
-        }
-    }
-}
-
-__int64 Dimension::getNextStructureId()
-{
-    return nextStructureId++;
-}
-
-void Dimension::addStructure(MultiblockStructure* structure)
-{
-    structurCs.lock();
-    structures.add(structure);
-    structurCs.unlock();
-}
-
-MultiblockStructure* Dimension::zStructureByPosition(
-    Framework::Vec3<int> uniquePosition)
-{
-    structurCs.lock();
-    for (MultiblockStructure* str : structures)
-    {
-        if (str->getUniquePosition() == uniquePosition)
-        {
-            structurCs.unlock();
-            return str;
-        }
-    }
-    // search for structure file
-    Datei dir(Game::INSTANCE->getWorldDirectory() + "/dim/" + Text(dimensionId)
-              + "/structures");
-    RCArray<Text>* names = dir.getDateiListe();
-    if (names)
-    {
-        Vec3<int> uPos;
-        for (Text* name : *names)
-        {
-            Datei d(Text(dir.zPfad()->getText()) + "/" + name->getText());
-            if (d.open(Datei::Style::lesen))
-            {
-                d.lese((char*)&uPos.x, 4);
-                d.lese((char*)&uPos.y, 4);
-                d.lese((char*)&uPos.z, 4);
-                if (uPos == uniquePosition)
-                {
-                    int type;
-                    d.lese((char*)&type, 4);
-                    __int64 strId;
-                    d.lese((char*)&strId, 8);
-                    MultiblockStructure* str
-                        = StaticRegistry<MultiblockStructureType>::INSTANCE
-                              .zElement(type)
-                              ->loadStructure(
-                                  dimensionId, strId, uniquePosition, &d);
-                    d.close();
-                    structures.add(str);
-                    names->release();
-                    structurCs.unlock();
-                    return str;
-                }
-                d.close();
-            }
-        }
-        names->release();
-    }
-    structurCs.unlock();
-    return 0;
-}
-
-MultiblockStructure* Dimension::zStructureById(__int64 id)
-{
-    structurCs.lock();
-    for (MultiblockStructure* str : structures)
-    {
-        if (str->getStructureId() == id)
-        {
-            structurCs.unlock();
-            return str;
-        }
-    }
-    // search for structure file
-    Text path = Game::INSTANCE->getWorldDirectory() + "/dim/"
-              + Text(dimensionId) + "/structures/";
-    path.appendHex(id);
-    path += ".str";
-    Datei d(path);
-    Vec3<int> uPos;
-    if (d.open(Datei::Style::lesen))
-    {
-        d.lese((char*)&uPos.x, 4);
-        d.lese((char*)&uPos.y, 4);
-        d.lese((char*)&uPos.z, 4);
-        int type;
-        d.lese((char*)&type, 4);
-        __int64 strId;
-        d.lese((char*)&strId, 8);
-        MultiblockStructure* str
-            = StaticRegistry<MultiblockStructureType>::INSTANCE.zElement(type)
-                  ->loadStructure(dimensionId, strId, uPos, &d);
-        d.close();
-        structures.add(str);
-        structurCs.unlock();
-        return str;
-    }
-    std::cout << "WARNING: did not find Structure information file '" << path
-              << "'.\n";
-    structurCs.unlock();
-    return 0;
-}
-
-void Dimension::requestStopAndWait()
-{
-    stop = 1;
-    warteAufThread(1000000);
-}
-
-void Dimension::updateMap(int x, int y, int height)
-{
-    chunkCs.lock();
-    int h1 = height % 2 == 0 ? height : height - 1;
-    int h2 = h1 + 1;
-    const Block* b1 = zBlockOrDefault({x, y, h1});
-    const Block* b2 = zBlockOrDefault({x, y, h2});
-    bool visible = 1;
-    if (h2 != WORLD_HEIGHT - 1)
-    {
-        const Block* b3 = zBlockOrDefault({x, y, h2 + 1});
-        visible = b3->isPassable() || b3->isTransparent();
-    }
-    int color1
-        = (b2->isPassable() || b2->isTransparent()) ? b1->getMapColor() : 0;
-    int color2 = visible ? b2->getMapColor() : 0;
-    char addr[8];
-    Punkt center = Game::INSTANCE->getChunkCenter(x, y);
-    getAddrOfWorld(center, addr);
-    ChunkMap* cMap = map->getMap(addr, 8, center);
-    if (cMap)
-    {
-        x = x % CHUNK_SIZE;
-        y = y % CHUNK_SIZE;
-        if (x < 0) x += CHUNK_SIZE;
-        if (y < 0) y += CHUNK_SIZE;
-        cMap->update(
-            (char)x, (char)y, (unsigned char)(height / 2), color1, color2);
-    }
-    chunkCs.unlock();
-}

+ 24 - 0
init.bat

@@ -0,0 +1,24 @@
+@echo off
+type build.logo
+echo finding Visual Studio...
+rem VS2017U2 contains vswhere.exe
+if "%VSWHERE%"=="" set "VSWHERE=%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe"
+
+for /f "usebackq tokens=*" %%i in (`"%VSWHERE%" -latest -prerelease -products * -requires Microsoft.Component.MSBuild -property installationPath`) do (
+  set InstallDir=%%i
+)
+
+set MSBuildExe=MSBuild\Current\Bin\MSBuild.exe
+
+if exist "%InstallDir%\%MSBuildExe%" (
+  echo ...
+) else (
+  set MSBuildExe=MSBuild\15.0\Bin\MSBuild.exe
+)
+  
+if exist "%InstallDir%\%MSBuildExe%" (
+  echo Studio found at "%InstallDir%"
+  echo MSBuild found at "%InstallDir%\%MSBuildExe%"
+) else (
+  echo Error: No Visual Studio or MSBuild found.
+)