Browse Source

mostly client side refactoring and core updates

Kajetan Johannes Hammerle 2 years ago
parent
commit
998b580d97

+ 54 - 0
client/Controls.cpp

@@ -0,0 +1,54 @@
+#include "GLFW/glfw3.h"
+
+#include "client/Controls.h"
+
+Window::Controls::ButtonId Controls::left = 0;
+Window::Controls::ButtonId Controls::right = 0;
+Window::Controls::ButtonId Controls::up = 0;
+Window::Controls::ButtonId Controls::down = 0;
+
+Window::Controls::ButtonId Controls::jump = 0;
+Window::Controls::ButtonId Controls::sneak = 0;
+
+Window::Controls::ButtonId Controls::escape = 0;
+Window::Controls::ButtonId Controls::chat = 0;
+Window::Controls::ButtonId Controls::enter = 0;
+
+Window::Controls::ButtonId Controls::primaryClick = 0;
+Window::Controls::ButtonId Controls::secondaryClick = 0;
+
+void Controls::init() {
+    left = Window::Controls::add("Left");
+    Window::Controls::bindKey(left, GLFW_KEY_A);
+    right = Window::Controls::add("Right");
+    Window::Controls::bindKey(right, GLFW_KEY_D);
+    up = Window::Controls::add("Up");
+    Window::Controls::bindKey(up, GLFW_KEY_W);
+    down = Window::Controls::add("Down");
+    Window::Controls::bindKey(down, GLFW_KEY_S);
+
+    jump = Window::Controls::add("Jump");
+    Window::Controls::bindKey(jump, GLFW_KEY_SPACE);
+    sneak = Window::Controls::add("Sneak");
+    Window::Controls::bindKey(sneak, GLFW_KEY_LEFT_SHIFT);
+
+    escape = Window::Controls::add("Escape");
+    Window::Controls::bindKey(escape, GLFW_KEY_ESCAPE);
+    chat = Window::Controls::add("Chat");
+    Window::Controls::bindKey(chat, GLFW_KEY_T);
+    enter = Window::Controls::add("Enter");
+    Window::Controls::bindKey(enter, GLFW_KEY_ENTER);
+
+    primaryClick = Window::Controls::add("Primary Click");
+    Window::Controls::bindMouse(primaryClick, GLFW_MOUSE_BUTTON_LEFT);
+    secondaryClick = Window::Controls::add("Secondary Click");
+    Window::Controls::bindMouse(secondaryClick, GLFW_MOUSE_BUTTON_RIGHT);
+}
+
+bool Controls::wasReleased(Window::Controls::ButtonId id) {
+    return Window::Controls::wasReleased(id);
+}
+
+bool Controls::isDown(Window::Controls::ButtonId id) {
+    return Window::Controls::isDown(id);
+}

+ 11 - 11
client/input/Controller.h → client/Controls.h

@@ -1,25 +1,25 @@
-#ifndef CONTROLLER_H
-#define CONTROLLER_H
+#ifndef CONTROLS_H
+#define CONTROLS_H
 
-#include "math/Vector.h"
 #include "rendering/Window.h"
 
-namespace Controller {
+namespace Controls {
     extern Window::Controls::ButtonId left;
     extern Window::Controls::ButtonId right;
     extern Window::Controls::ButtonId up;
     extern Window::Controls::ButtonId down;
+
     extern Window::Controls::ButtonId jump;
     extern Window::Controls::ButtonId sneak;
-    extern Window::Controls::ButtonId camLeft;
-    extern Window::Controls::ButtonId camRight;
-    extern Window::Controls::ButtonId camUp;
-    extern Window::Controls::ButtonId camDown;
-    extern Window::Controls::ButtonId leftClick;
-    extern Window::Controls::ButtonId rightClick;
 
-    void init();
+    extern Window::Controls::ButtonId escape;
+    extern Window::Controls::ButtonId chat;
+    extern Window::Controls::ButtonId enter;
 
+    extern Window::Controls::ButtonId primaryClick;
+    extern Window::Controls::ButtonId secondaryClick;
+
+    void init();
     bool wasReleased(Window::Controls::ButtonId id);
     bool isDown(Window::Controls::ButtonId id);
 }

+ 0 - 140
client/Game.cpp

@@ -1,140 +0,0 @@
-#include "client/Game.h"
-#include "client/GameClient.h"
-#include "client/World.h"
-#include "client/gui/StartGUI.h"
-#include "client/input/Controller.h"
-#include "client/rendering/Engine.h"
-#include "common/network/Packets.h"
-#include "common/network/toserver/PlayerUpdatePacket.h"
-#include "utils/Logger.h"
-#include "utils/Utils.h"
-
-Entity Game::player;
-
-typedef void (*State)();
-static State tickState;
-static State renderState;
-
-static BaseGUI baseGUI;
-static StartGUI startGUI;
-
-static void tickConnectedState() {
-    Game::player.skip = false;
-    World::tick();
-    GameClient::consumeEvents();
-
-    Quaternion q = Game::player.getRotation();
-
-    Vector3 up(0.0f, 1.0f, 0.0f);
-    Vector3 back = q * Vector3(0.0f, 0.0f, -1.0f);
-    back[1] = 0.0f;
-    back.normalize();
-    Vector3 right = back.cross(up);
-
-    constexpr float rotationSpeed = 4.0f;
-    Vector3 force;
-    if(Controller::isDown(Controller::down)) {
-        force += back;
-    }
-    if(Controller::isDown(Controller::up)) {
-        force -= back;
-    }
-    if(Controller::isDown(Controller::left)) {
-        force -= right;
-    }
-    if(Controller::isDown(Controller::right)) {
-        force += right;
-    }
-    if(force.squareLength() > 0.0f) {
-        force.normalize();
-    }
-    Game::player.addForce(force * Game::player.speed);
-
-    if(Controller::isDown(Controller::jump) && Game::player.isOnGround()) {
-        Game::player.jump();
-    }
-    if(Controller::isDown(Controller::camLeft)) {
-        Game::player.addLengthAngle(-rotationSpeed);
-    }
-    if(Controller::isDown(Controller::camRight)) {
-        Game::player.addLengthAngle(rotationSpeed);
-    }
-    if(Controller::isDown(Controller::camUp)) {
-        Game::player.addWidthAngle(-rotationSpeed * 0.5f);
-    }
-    if(Controller::isDown(Controller::camDown)) {
-        Game::player.addWidthAngle(rotationSpeed * 0.5f);
-    }
-
-    PlayerUpdatePacket p(Game::player);
-    OutPacket out = OutPacket::reliable(PlayerUpdatePacket::getSize());
-    p.write(out);
-    GameClient::send(out);
-}
-
-static void renderConnectedState() {
-}
-
-static void tickConnectState() {
-    startGUI.tick();
-    StartGUI::Address a;
-    if(startGUI.getAddress(a)) {
-        Error error = GameClient::connect(a, 11196, 3000);
-        if(error.has()) {
-            LOG_INFO(error.message);
-        } else {
-            LOG_INFO("connected");
-            tickState = tickConnectedState;
-            renderState = renderConnectedState;
-        }
-    }
-}
-
-static void renderConnectState() {
-    startGUI.render();
-}
-
-bool Game::init() {
-    Block::init();
-    if(World::init()) {
-        return true;
-    }
-    Controller::init();
-    tickState = tickConnectState;
-    renderState = renderConnectState;
-
-    Game::player.setPosition(Vector3(0.0f, 2.0f, 0.0f));
-    World::addEntity(&player);
-    return false;
-}
-
-void Game::tick() {
-    tickState();
-}
-
-void Game::renderWorld() {
-    Engine::matrix.update(player.getRenderPosition(Engine::lag),
-                          player.getRenderRotation(Engine::lag));
-    World::render();
-}
-
-void Game::renderOverlay() {
-    renderState();
-    Engine::matrix.identity().scale(2.0f).update();
-    StringBuffer<100> s;
-    s.append("FPS: &074")
-        .append(Window::getFramesPerSecond())
-        .append(" &999TPS: &722")
-        .append(Window::getTicksPerSecond());
-    Engine::renderer.renderString(Vector2(10.0f, 10.0f), s);
-    s.clear().append("a = ").append(player.acceleration);
-    Engine::renderer.renderString(Vector2(10.0f, 20.0f), s);
-    s.clear().append("v = ").append(player.velocity);
-    Engine::renderer.renderString(Vector2(10.0f, 30.0f), s);
-}
-
-void Game::onEntityUpdate(EntityUpdatePacket& p) {
-    LOG_DEBUG("set");
-    player.setPosition(p.position);
-    player.velocity = p.velocity;
-}

+ 0 - 19
client/Game.h

@@ -1,19 +0,0 @@
-#ifndef GAME_H
-#define GAME_H
-
-#include "common/entities/Entity.h"
-#include "common/network/toclient/EntityUpdatePacket.h"
-
-namespace Game {
-    extern Entity player;
-
-    bool init();
-
-    void tick();
-    void renderWorld();
-    void renderOverlay();
-
-    void onEntityUpdate(EntityUpdatePacket& p);
-}
-
-#endif

+ 0 - 67
client/GameClient.cpp

@@ -1,67 +0,0 @@
-#include "client/GameClient.h"
-#include "client/Game.h"
-#include "client/packets/WorldPackets.h"
-#include "common/network/Packets.h"
-#include "utils/Logger.h"
-
-static constexpr const char* INVALID_PACKAGE = "invalid package from server";
-
-static Client client;
-
-static void handleEntityUpdate(InPacket& in) {
-    EntityUpdatePacket p;
-    if(p.read(in)) {
-        LOG_WARNING(INVALID_PACKAGE);
-        return;
-    }
-    Game::onEntityUpdate(p);
-}
-
-struct Receiver {
-    void onConnect() {
-    }
-
-    void onDisconnect() {
-    }
-
-    void onPacket(InPacket& in) {
-        uint16 id;
-        if(in.readU16(id)) {
-            return;
-        }
-        switch(id) {
-            case Packets::S_CHAT: {
-                StringBuffer<256> s;
-                in.readString(s);
-                puts(s);
-                break;
-            }
-            case Packets::S_WORLD_SEGMENT:
-                WorldPackets::receiveChunk(in);
-                break;
-            case Packets::S_ENTITY_UPDATE: handleEntityUpdate(in); break;
-            default: LOG_WARNING(INVALID_PACKAGE);
-        }
-    }
-};
-
-bool GameClient::init() {
-    Error error = client.start();
-    if(error.has()) {
-        LOG_ERROR(error.message);
-    }
-    return error.has();
-}
-
-Error GameClient::connect(const char* address, Client::Port port, int timeout) {
-    return client.connect(address, port, timeout);
-}
-
-void GameClient::consumeEvents() {
-    Receiver r;
-    client.consumeEvents(r);
-}
-
-void GameClient::send(OutPacket& out) {
-    client.send(out);
-}

+ 0 - 13
client/GameClient.h

@@ -1,13 +0,0 @@
-#ifndef GAME_CLIENT_H
-#define GAME_CLIENT_H
-
-#include "network/Client.h"
-
-namespace GameClient {
-    bool init();
-    Error connect(const char* address, Client::Port port, int timeout);
-    void consumeEvents();
-    void send(OutPacket& out);
-}
-
-#endif

+ 87 - 466
client/Main.cpp

@@ -1,11 +1,13 @@
-#include "GLFW/glfw3.h"
-
-#include "common/Box.h"
-#include "common/Packets.h"
+#include "client/Main.h"
+#include "client/Controls.h"
+#include "client/Network.h"
+#include "client/Player.h"
+#include "client/World.h"
+#include "common/NetworkPackets.h"
 #include "data/Array.h"
-#include "data/HashMap.h"
 #include "data/RingBuffer.h"
-#include "math/Frustum.h"
+#include "math/Box.h"
+#include "math/View.h"
 #include "network/Client.h"
 #include "rendering/Shader.h"
 #include "rendering/Texture.h"
@@ -13,147 +15,24 @@
 #include "rendering/Window.h"
 #include "utils/Buffer.h"
 
-static constexpr int WORLD_SIZE = 16;
-
-static Array<bool, WORLD_SIZE * WORLD_SIZE * WORLD_SIZE> world(true);
-static Shader shader;
 static Shader fontShader;
-static VertexBuffer vertexBuffer;
 static VertexBuffer markVertexBuffer;
 static VertexBuffer fontBuffer;
 static Texture fontTexture;
-static bool dirtyVertexBuffer = true;
-static int vertices = 0;
-static Frustum frustum(80.0f, 0.1f, 1000.0f);
-
-static Vector3 lastPosition(8.0f, 30.0f, 8.0f);
-static Vector3 position(lastPosition);
-static float lastWidthAngle = 0.0f;
-static float widthAngle = lastWidthAngle;
-static float lastLengthAngle = 0.0f;
-static float lengthAngle = lastLengthAngle;
-static Vector3 velocity;
-static Vector3 acceleration;
-static bool onGround = false;
-
-static Window::Controls::ButtonId leftKey;
-static Window::Controls::ButtonId rightKey;
-static Window::Controls::ButtonId upKey;
-static Window::Controls::ButtonId downKey;
-static Window::Controls::ButtonId jumpKey;
-static Window::Controls::ButtonId sneakKey;
-static Window::Controls::ButtonId escapeKey;
-static Window::Controls::ButtonId chatKey;
-static Window::Controls::ButtonId sendChatKey;
-static Window::Controls::ButtonId primaryClick;
-
-static bool trappedMoused = false;
-
-static Vector3 up;
-static Vector3 down;
-static Vector3 left;
-static Vector3 right;
-static Vector3 front;
-static Vector3 back;
-
-static Vector3 focus;
-static bool hasFocus = false;
-
-struct Player {
-    Vector3 lastPosition;
-    Vector3 position;
-};
 
-static HashMap<int, Player> players;
+HashMap<int, OtherPlayer> players;
 
-typedef StringBuffer<50> ChatMessage;
 static Array<ChatMessage, 20> chat;
 static int chatIndex = 0;
-static bool renderInput = false;
+bool renderInput = false;
 
-static void addToChat(const ChatMessage& msg) {
+void addToChat(const ChatMessage& msg) {
     chat[chatIndex] = msg;
     chatIndex = (chatIndex + 1) % chat.getLength();
 }
 
 static bool isRunning() {
     return !Window::shouldClose();
-    // return !Window::shouldClose() &&
-    //       (Client::isConnecting() || Client::isConnected());
-}
-
-static void set(int x, int y, int z, bool b) {
-    if(x < 0 || x >= WORLD_SIZE || y < 0 || y >= WORLD_SIZE || z < 0 ||
-       z >= WORLD_SIZE) {
-        return;
-    }
-    world[x * WORLD_SIZE * WORLD_SIZE + y * WORLD_SIZE + z] = b;
-    dirtyVertexBuffer = true;
-}
-
-static void onPacket(InPacket& in) {
-    uint8 type = 0;
-    if(in.readU8(type)) {
-        puts("no data");
-        return;
-    }
-    switch(static_cast<Packet::Type>(type)) {
-        case Packet::Type::WORLD: {
-            for(bool& b : world) {
-                uint8 data = 0;
-                if(in.readU8(data)) {
-                    puts("too less data in world packet");
-                    return;
-                }
-                b = data;
-            }
-            dirtyVertexBuffer = true;
-            break;
-        }
-        case Packet::Type::SET_BLOCK: {
-            Vector3 pos;
-            in.readFloat(pos[0]);
-            in.readFloat(pos[1]);
-            in.readFloat(pos[2]);
-            uint8 type;
-            in.readU8(type);
-            set(pos[0], pos[1], pos[2], type);
-            break;
-        }
-        case Packet::Type::PLAYER: {
-            Vector3 pos;
-            in.readFloat(pos[0]);
-            in.readFloat(pos[1]);
-            in.readFloat(pos[2]);
-            int client = -1;
-            in.readS32(client);
-
-            Player* p = players.search(client);
-            if(p != nullptr) {
-                p->position = pos;
-            } else {
-                players.add(client, {pos, pos});
-            }
-            break;
-        }
-        case Packet::Type::CHAT: {
-            ChatMessage msg;
-            uint32 u;
-            int i = 0;
-            while(!in.readU32(u)) {
-                i++;
-                msg.appendUnicode(u);
-            }
-            printf("%d\n", i);
-            addToChat(msg);
-            break;
-        }
-        default: printf("invalid package type %d\n", static_cast<int>(type));
-    }
-}
-
-static void onDisconnect() {
-    puts("Disconnect");
 }
 
 static void addTriangle(Buffer& buffer, const Vector3& a, const Vector3& b,
@@ -163,14 +42,11 @@ static void addTriangle(Buffer& buffer, const Vector3& a, const Vector3& b,
 }
 
 static bool init() {
-    Error e =
-        shader.compile("resources/shader/test.vs", "resources/shader/test.fs");
-    if(e.has()) {
-        e.message.printLine();
+    if(World::init()) {
         return true;
     }
-    e = fontShader.compile("resources/shader/fontTest.vs",
-                           "resources/shader/fontTest.fs");
+    Error e = fontShader.compile("resources/shader/fontTest.vs",
+                                 "resources/shader/fontTest.fs");
     if(e.has()) {
         e.message.printLine();
         return true;
@@ -180,7 +56,6 @@ static bool init() {
         e.message.printLine();
         return true;
     }
-    vertexBuffer.init(VertexBuffer::Attributes().addFloat(3).addFloat(3));
     markVertexBuffer.init(VertexBuffer::Attributes().addFloat(3).addFloat(3));
     fontBuffer.init(VertexBuffer::Attributes().addFloat(3).addFloat(2));
 
@@ -206,299 +81,68 @@ static bool init() {
     addTriangle(buffer, v111, v011, v101);
     addTriangle(buffer, v000, v010, v100);
     addTriangle(buffer, v110, v100, v010);
-    markVertexBuffer.setStaticData(buffer.getLength(), buffer);
+    markVertexBuffer.setData(buffer, GL::STATIC_DRAW);
 
-    Client::setPacketHandler(onPacket);
-    Client::setDisconnectHandler(onDisconnect);
-
-    leftKey = Window::Controls::add("Left");
-    Window::Controls::bindKey(leftKey, GLFW_KEY_A);
-    rightKey = Window::Controls::add("Right");
-    Window::Controls::bindKey(rightKey, GLFW_KEY_D);
-    upKey = Window::Controls::add("Up");
-    Window::Controls::bindKey(upKey, GLFW_KEY_W);
-    downKey = Window::Controls::add("Right");
-    Window::Controls::bindKey(downKey, GLFW_KEY_S);
-    jumpKey = Window::Controls::add("Jump");
-    Window::Controls::bindKey(jumpKey, GLFW_KEY_SPACE);
-    sneakKey = Window::Controls::add("Sneak");
-    Window::Controls::bindKey(sneakKey, GLFW_KEY_LEFT_SHIFT);
-    escapeKey = Window::Controls::add("Escape");
-    Window::Controls::bindKey(escapeKey, GLFW_KEY_ESCAPE);
-    chatKey = Window::Controls::add("Chat");
-    Window::Controls::bindKey(chatKey, GLFW_KEY_T);
-    sendChatKey = Window::Controls::add("Send Chat");
-    Window::Controls::bindKey(sendChatKey, GLFW_KEY_ENTER);
-    primaryClick = Window::Controls::add("Primary Click");
-    Window::Controls::bindMouse(primaryClick, GLFW_MOUSE_BUTTON_LEFT);
+    Controls::init();
     return false;
 }
 
-static void updateDirections(float lag) {
-    front.setAngles(Math::interpolate(lastLengthAngle, lengthAngle, lag),
-                    Math::interpolate(lastWidthAngle, widthAngle, lag));
-    back = -front;
-    right = front.cross(Vector3(0.0f, 1.0f, 0.0f));
-    right.normalize();
-    left = -right;
-    up = front.cross(left);
-    down = -up;
-}
-
-static bool isAir(int x, int y, int z) {
-    if(x < 0 || x >= WORLD_SIZE || y < 0 || y >= WORLD_SIZE || z < 0 ||
-       z >= WORLD_SIZE) {
-        return true;
-    }
-    return !world[x * WORLD_SIZE * WORLD_SIZE + y * WORLD_SIZE + z];
-}
-
-static List<Box> getBoxes(const Box& box) {
-    int minX = floorf(box.getMin()[0]);
-    int minY = floorf(box.getMin()[1]);
-    int minZ = floorf(box.getMin()[2]);
-    int maxX = floorf(box.getMax()[0]);
-    int maxY = floorf(box.getMax()[1]);
-    int maxZ = floorf(box.getMax()[2]);
-
-    Box base(Vector3(1.0f, 1.0f, 1.0f));
-    List<Box> boxes;
-    for(int x = minX; x <= maxX; x++) {
-        for(int y = minY; y <= maxY; y++) {
-            for(int z = minZ; z <= maxZ; z++) {
-                if(!isAir(x, y, z)) {
-                    boxes.add(base.offset(Vector3(x, y, z)));
-                }
-            }
-        }
-    }
-    return boxes;
-}
-
-static Vector3 limitMove() {
-    Vector3 move = velocity;
-    Box box(Vector3(0.8f, 0.8f, 0.8f));
-    box = box.offset(position - Vector3(0.4f, 0.0f, 0.4f));
-
-    List<Box> boxes = getBoxes(box.expand(move));
-    if(boxes.getLength() == 0) {
-        return move;
-    }
-    Vector3 realMove;
-
-    constexpr float step = 0.05f;
-    while(move[0] != 0.0f || move[1] != 0.0f || move[2] != 0.0f) {
-        for(int i = 0; i < 3; i++) {
-            Vector3 old = realMove;
-            if(move[i] > step) {
-                realMove[i] += step;
-                move[i] -= step;
-            } else if(move[i] < -step) {
-                realMove[i] -= step;
-                move[i] += step;
-            } else if(move[i] != 0.0f) {
-                realMove[i] += move[i];
-                move[i] = 0.0f;
-            }
-            Box moved = box.offset(realMove);
-            for(const Box& box : boxes) {
-                if(box.collidesWith(moved)) {
-                    move[i] = 0.0f;
-                    realMove = old;
-                    break;
-                }
-            }
-        }
-    }
-    return realMove;
-}
-
 static void tick() {
     if(renderInput) {
-        if(Window::Controls::wasReleased(sendChatKey)) {
-            OutPacket out = Packet::build(Packet::Type::CHAT);
+        if(Controls::wasReleased(Controls::enter)) {
+            OutPacket out(100);
+            out.writeU8(static_cast<uint8>(ToServerPacket::CHAT));
             for(uint32 u : Window::Input::getUnicode()) {
                 out.writeU32(u);
             }
-            Client::send(out, PacketType::RELIABLE);
+            Client::send(out, PacketSendMode::RELIABLE);
             Window::Input::reset();
         }
-        if(Window::Controls::wasReleased(escapeKey)) {
+        if(Controls::wasReleased(Controls::escape)) {
             Window::Input::disable();
             Window::trapCursor();
-            trappedMoused = true;
             renderInput = false;
         }
     } else {
-        if(Window::Controls::isDown(chatKey)) {
+        if(Controls::isDown(Controls::chat)) {
             Window::Input::enable();
             Window::freeCursor();
-            trappedMoused = false;
             renderInput = true;
         }
-        if(Window::Controls::wasReleased(primaryClick)) {
+        if(Controls::wasReleased(Controls::primaryClick)) {
             Window::trapCursor();
-            trappedMoused = true;
         }
-        if(Window::Controls::wasReleased(escapeKey)) {
+        if(Controls::wasReleased(Controls::escape)) {
             Window::freeCursor();
-            trappedMoused = false;
         }
     }
 
-    lastPosition = position;
-    lastWidthAngle = widthAngle;
-    lastLengthAngle = lengthAngle;
-
-    for(Player& p : players.values()) {
-        p.lastPosition = p.position;
+    Player::tick();
+    for(OtherPlayer& p : players.values()) {
+        p.position.update();
     }
-    Client::tick();
-
-    updateDirections(0.0f);
+    Network::tick();
 
-    Vector3 b = back;
-    b[1] = 0.0f;
-    b.normalize();
+    if(Controls::wasReleased(Controls::primaryClick) && !renderInput) {
+        RayTrace rt =
+            World::rayTrace(Player::getPosition() + Vector3(0.0f, 0.8f, 0.0f),
+                            Player::getLook(), 10.0f);
+        if(rt.type == RayTrace::Type::BLOCK) {
+            World::set(rt.block[0], rt.block[1], rt.block[2], false);
 
-    Vector3 r = right;
-    r[1] = 0.0f;
-    r.normalize();
-
-    if(!renderInput) {
-        Vector3 force;
-        if(Window::Controls::isDown(downKey)) {
-            force += b;
-        }
-        if(Window::Controls::isDown(upKey)) {
-            force -= b;
-        }
-        if(Window::Controls::isDown(leftKey)) {
-            force -= r;
-        }
-        if(Window::Controls::isDown(rightKey)) {
-            force += r;
-        }
-        if(force.squareLength() > 0.0f) {
-            force.normalize();
-        }
-        acceleration += force * 0.1f;
-
-        if(Window::Controls::isDown(jumpKey) && onGround) {
-            acceleration[1] += (0.42f / 0.98f + 0.08f);
-        }
-    }
-
-    if(trappedMoused) {
-        Vector2 diff = (Window::Controls::getLastMousePosition() -
-                        Window::Controls::getMousePosition()) *
-                       0.1f;
-        widthAngle += diff[1];
-        lengthAngle += diff[0];
-        if(widthAngle > 89.0f) {
-            widthAngle = 89.0f;
-        }
-        if(widthAngle < -89.0f) {
-            widthAngle = -89.0f;
+            OutPacket out(100);
+            out.writeU8(static_cast<uint8>(ToServerPacket::SET_BLOCK));
+            out.writeVector(rt.block);
+            out.writeU8(0);
+            Client::send(out, PacketSendMode::RELIABLE);
         }
     }
 
-    hasFocus = false;
-    Vector3 p = position + Vector3(0.0f, 0.8f, 0.0f);
-    Vector3 step = front * 0.125f;
-    for(int i = 0; i < 80; i++) {
-        if(!isAir(p[0], p[1], p[2])) {
-            hasFocus = true;
-            focus = Vector3(static_cast<int>(p[0]), static_cast<int>(p[1]),
-                            static_cast<int>(p[2]));
-            break;
-        }
-        p += step;
-    }
-
-    if(hasFocus && Window::Controls::wasReleased(primaryClick) &&
-       !renderInput) {
-        set(focus[0], focus[1], focus[2], false);
-        dirtyVertexBuffer = true;
-
-        OutPacket out = Packet::build(Packet::Type::SET_BLOCK);
-        out.writeFloat(focus[0]);
-        out.writeFloat(focus[1]);
-        out.writeFloat(focus[2]);
-        out.writeU8(0);
-        Client::send(out, PacketType::RELIABLE);
-    }
-
-    OutPacket out = Packet::build(Packet::Type::PLAYER);
-    out.writeFloat(position[0]);
-    out.writeFloat(position[1]);
-    out.writeFloat(position[2]);
+    OutPacket out(100);
+    out.writeU8(static_cast<uint8>(ToServerPacket::PLAYER));
+    out.writeVector(Player::getPosition());
     out.writeS32(-1);
-    Client::send(out, PacketType::RELIABLE);
-
-    velocity += acceleration;
-    velocity *= Vector3(0.686f, 0.98f, 0.686f);
-    acceleration = Vector3(0.0f, -0.08f, 0.0f);
-
-    Vector3 move = limitMove();
-    if(move[1] + position[1] < 0.0f) {
-        move[1] = -position[1];
-    }
-    position += move;
-    onGround = move[1] == 0.0f && velocity[1] < 0.0f;
-    velocity = move;
-}
-
-static void addCube(Buffer& buffer, int x, int y, int z) {
-    if(isAir(x, y, z)) {
-        return;
-    }
-    Vector3 v000(x, y, z);
-    Vector3 v001(x, y, z + 1);
-    Vector3 v010(x, y + 1, z);
-    Vector3 v011(x, y + 1, z + 1);
-    Vector3 v100(x + 1, y, z);
-    Vector3 v101(x + 1, y, z + 1);
-    Vector3 v110(x + 1, y + 1, z);
-    Vector3 v111(x + 1, y + 1, z + 1);
-
-    if(isAir(x, y - 1, z)) {
-        addTriangle(buffer, v000, v100, v001);
-        addTriangle(buffer, v100, v101, v001);
-    }
-    if(isAir(x, y + 1, z)) {
-        addTriangle(buffer, v010, v011, v110);
-        addTriangle(buffer, v110, v011, v111);
-    }
-    if(isAir(x - 1, y, z)) {
-        addTriangle(buffer, v000, v001, v010);
-        addTriangle(buffer, v001, v011, v010);
-    }
-    if(isAir(x + 1, y, z)) {
-        addTriangle(buffer, v100, v110, v101);
-        addTriangle(buffer, v101, v110, v111);
-    }
-    if(isAir(x, y, z + 1)) {
-        addTriangle(buffer, v001, v101, v011);
-        addTriangle(buffer, v111, v011, v101);
-    }
-    if(isAir(x, y, z - 1)) {
-        addTriangle(buffer, v000, v010, v100);
-        addTriangle(buffer, v110, v100, v010);
-    }
-}
-
-static void buildRenderingBuffer() {
-    Buffer buffer(100);
-    for(int x = 0; x < WORLD_SIZE; x++) {
-        for(int y = 0; y < WORLD_SIZE; y++) {
-            for(int z = 0; z < WORLD_SIZE; z++) {
-                addCube(buffer, x, y, z);
-            }
-        }
-    }
-    vertices = buffer.getLength() / (2 * sizeof(Vector3));
-    vertexBuffer.setStaticData(buffer.getLength(), buffer);
+    Client::send(out, PacketSendMode::RELIABLE);
 }
 
 static void renderString(const char* text) {
@@ -531,86 +175,70 @@ static void renderString(const char* text) {
         vertices += 6;
         index++;
     }
-    fontBuffer.setDynamicData(buffer.getLength(), buffer);
+    fontBuffer.setData(buffer, GL::DYNAMIC_DRAW);
     fontBuffer.draw(vertices);
 }
 
 static void render(float lag) {
     GL::clear();
     GL::enableDepthTesting();
-    if(dirtyVertexBuffer) {
-        dirtyVertexBuffer = false;
-        buildRenderingBuffer();
-        puts("rebuilt buffer");
-    }
+    World::render(lag);
 
-    shader.use();
-    GL::setViewport(Window::getSize()[0], Window::getSize()[1]);
-    const Matrix& proj = frustum.updateProjection(Window::getSize());
-    shader.setMatrix("proj", proj.getValues());
-    updateDirections(lag);
-    Matrix view;
-    Vector3 center = Math::interpolate(lastPosition, position, lag) +
-                     Vector3(0.0f, 0.8f, 0.0f);
-    view.set(0, Vector4(right[0], right[1], right[2], right.dot(-center)));
-    view.set(1, Vector4(up[0], up[1], up[2], up.dot(-center)));
-    view.set(2, Vector4(back[0], back[1], back[2], back.dot(-center)));
-    view.set(3, Vector4(0.0f, 0.0f, 0.0f, 1.0f));
-    shader.setMatrix("view", view.getValues());
-    shader.setVector("color", Vector3(1.0f, 1.0f, 1.0f));
-    Matrix model;
-    shader.setMatrix("model", model.getValues());
-    vertexBuffer.draw(vertices);
+    RayTrace rt =
+        World::rayTrace(Player::getPosition(lag) + Vector3(0.0f, 0.8f, 0.0f),
+                        Player::getLook(lag), 10.0f);
 
-    if(hasFocus) {
-        shader.setVector("color", Vector3(0.8f, 0.6f, 0.6f));
+    if(rt.type == RayTrace::Type::BLOCK) {
+        World::shader.setVector("color", Vector3(0.8f, 0.6f, 0.6f));
+        Matrix model;
         model.translate(Vector3(-0.5f, -0.5f, -0.5f));
         model.scale(1.01f);
         model.translate(Vector3(0.5f, 0.5f, 0.5f));
-        model.translate(focus);
-        shader.setMatrix("model", model.getValues());
+        model.translate(rt.block.toFloat());
+        World::shader.setMatrix("model", model);
         markVertexBuffer.draw(36);
     }
 
-    shader.setVector("color", Vector3(1.0f, 0.0f, 0.0f));
-    for(const Player& p : players.values()) {
+    World::shader.setVector("color", Vector3(1.0f, 0.0f, 0.0f));
+    for(const OtherPlayer& p : players.values()) {
+        Matrix model;
         model.translateTo(Vector3(-0.5f, -0.5f, -0.5f));
         model.scale(0.8f);
-        model.translate(Math::interpolate(p.lastPosition, p.position, lag) +
-                        Vector3(0.0f, 0.4f, 0.0f));
-        shader.setMatrix("model", model.getValues());
+        model.translate(p.position.get(lag) + Vector3(0.0f, 0.4f, 0.0f));
+        World::shader.setMatrix("model", model);
         markVertexBuffer.draw(36);
     }
 
-    shader.setMatrix("proj", Matrix().getValues());
-    shader.setMatrix("view", Matrix().getValues());
-    shader.setVector("color", Vector3(1.0f, 0.0f, 1.0f));
+    World::shader.setMatrix("proj", Matrix());
+    World::shader.setMatrix("view", Matrix());
+    World::shader.setVector("color", Vector3(1.0f, 0.0f, 1.0f));
+    Matrix model;
     model.translateTo(Vector3(-0.5f, -0.5f, -0.5f));
     model.scale(0.05f);
-    shader.setMatrix("model", model.getValues());
+    World::shader.setMatrix("model", model);
     markVertexBuffer.draw(36);
 
     GL::disableDepthTesting();
     GL::enableBlending();
     fontShader.use();
-    fontShader.setMatrix("proj", Matrix().getValues());
-    view.translateTo(Vector3(0.0f, 0.0f, 0.0f));
+    fontShader.setMatrix("proj", Matrix());
+    Matrix v;
     IntVector2 size = Window::getSize();
-    view.scale(Vector3(2.0f / size[0], -2.0f / size[1], 1.0f));
-    view.translate(Vector3(-1.0f, 1.0f, 0.0f));
-    fontShader.setMatrix("view", view.getValues());
+    v.scale(Vector3(2.0f / size[0], -2.0f / size[1], 1.0f));
+    v.translate(Vector3(-1.0f, 1.0f, 0.0f));
+    fontShader.setMatrix("view", v);
     fontTexture.bindTo(0);
 
     for(int i = 0; i < chat.getLength(); i++) {
         model.translateTo(Vector3(0.0f, i * 8.0f, 0.0f)).scale(3.0f);
-        fontShader.setMatrix("model", model.getValues());
+        fontShader.setMatrix("model", model);
         renderString(chat[(i + chatIndex) % chat.getLength()]);
     }
 
     if(renderInput) {
         model.translateTo(Vector3(0.0f, chat.getLength() * 8.0f, 0.0f))
             .scale(3.0f);
-        fontShader.setMatrix("model", model.getValues());
+        fontShader.setMatrix("model", model);
         StringBuffer<256> s;
         Window::Input::toString(s);
         renderString(s);
@@ -619,42 +247,35 @@ static void render(float lag) {
             .translateTo(Vector3(Window::Input::getCursor() * 8.0f,
                                  chat.getLength() * 8.0f + 2.0f, 0.0f))
             .scale(3.0f);
-        fontShader.setMatrix("model", model.getValues());
+        fontShader.setMatrix("model", model);
         renderString("_");
     }
 
     GL::disableBlending();
 }
 
+static void startWindow() {
+    Window::Options options(4, 3, IntVector2(1024, 600), false, "test");
+    Error e = Window::open(options);
+    if(e.has()) {
+        e.message.printLine();
+        return;
+    } else if(!init()) {
+        Window::show();
+        Window::run<isRunning, tick, render>(50'000'000);
+    }
+    Window::close();
+}
+
 int main(int argAmount, const char* const* args) {
     const char* server = "127.0.0.1";
     if(argAmount >= 2) {
         server = args[1];
     }
-    Error e = Client::start();
-    if(e.has()) {
-        e.message.printLine();
-        return 0;
-    }
-    e = Client::connect(server, 11196, 40);
-    if(e.has()) {
-        e.message.printLine();
-        return 0;
-    }
-    Window::Options options(4, 3, IntVector2(1024, 600), false, "test");
-    e = Window::open(options);
-    if(e.has()) {
-        e.message.printLine();
-        Client::stop();
+    if(Network::start(server)) {
         return 0;
     }
-    if(init()) {
-        Client::stop();
-        return 0;
-    }
-    Window::show();
-    Window::run<isRunning, tick, render>(50'000'000);
-    Window::close();
-    Client::stop();
+    startWindow();
+    Network::stop();
     return 0;
 }

+ 19 - 0
client/Main.h

@@ -0,0 +1,19 @@
+#ifndef CLIENT_MAIN_H
+#define CLIENT_MAIN_H
+
+#include "data/HashMap.h"
+#include "math/BufferedValue.h"
+#include "math/Vector.h"
+
+struct OtherPlayer {
+    BufferedValue<Vector3> position;
+};
+
+typedef StringBuffer<50> ChatMessage;
+
+extern HashMap<int, OtherPlayer> players;
+extern bool renderInput;
+
+void addToChat(const ChatMessage& msg);
+
+#endif

+ 110 - 0
client/Network.cpp

@@ -0,0 +1,110 @@
+#include "client/Network.h"
+#include "client/Main.h"
+#include "client/World.h"
+#include "common/NetworkPackets.h"
+#include "network/Client.h"
+#include "utils/Logger.h"
+
+static bool onWorldPacket(InPacket& in) {
+    IntVector3 size = World::getSize();
+    for(int x = 0; x < size[0]; x++) {
+        for(int y = 0; y < size[1]; y++) {
+            for(int z = 0; z < size[2]; z++) {
+                uint8 data = 0;
+                if(in.read(data)) {
+                    return true;
+                }
+                World::set(x, y, z, data);
+            }
+        }
+    }
+    return false;
+}
+
+static bool onSetBlockPacket(InPacket& in) {
+    IntVector3 pos;
+    uint8 type;
+    if(in.read(pos) || in.read(type)) {
+        return true;
+    }
+    World::set(pos[0], pos[1], pos[2], type);
+    return false;
+}
+
+static bool onPlayerPacket(InPacket& in) {
+    Vector3 pos;
+    int client;
+    if(in.read(pos) || in.read(client)) {
+        return true;
+    }
+    OtherPlayer* p = players.search(client);
+    if(p != nullptr) {
+        p->position = pos;
+    } else {
+        players.add(client, {pos});
+    }
+    return false;
+}
+
+static bool onChatPacket(InPacket& in) {
+    ChatMessage msg;
+    uint32 u;
+    while(!in.read(u)) {
+        msg.appendUnicode(u);
+    }
+    addToChat(msg);
+    return false;
+}
+
+static bool handlePacket(ToClientPacket tcp, InPacket& in) {
+    switch(tcp) {
+        case ToClientPacket::WORLD: return onWorldPacket(in);
+        case ToClientPacket::SET_BLOCK: return onSetBlockPacket(in);
+        case ToClientPacket::PLAYER: return onPlayerPacket(in);
+        case ToClientPacket::CHAT: return onChatPacket(in);
+    }
+    LOG_WARNING(StringBuffer<100>("unknown packet of type ")
+                    .append(static_cast<uint32>(tcp)));
+    return false;
+}
+
+static void onPacket(InPacket& in) {
+    uint8 type = 0;
+    if(in.read(type)) {
+        LOG_WARNING("received packet without type");
+        return;
+    }
+    if(handlePacket(static_cast<ToClientPacket>(type), in)) {
+        LOG_WARNING(StringBuffer<100>("invalid packet of type ")
+                        .append(static_cast<uint32>(type)));
+    }
+}
+
+static void onDisconnect() {
+    LOG_INFO("Disconnect");
+}
+
+bool Network::start(const char* server) {
+    Error e = Client::start();
+    if(e.has()) {
+        LOG_ERROR(e.message);
+        return true;
+    }
+    e = Client::connect(server, 11196, 40);
+    if(e.has()) {
+        LOG_ERROR(e.message);
+        Client::stop();
+        return true;
+    }
+    Client::setPacketHandler(onPacket);
+    Client::setDisconnectHandler(onDisconnect);
+    return false;
+}
+
+void Network::stop() {
+    Client::stop();
+}
+
+void Network::tick() {
+    Client::tick();
+}

+ 11 - 0
client/Network.h

@@ -0,0 +1,11 @@
+#ifndef NETWORK_H
+#define NETWORK_H
+
+namespace Network {
+    bool start(const char* server);
+    void stop();
+
+    void tick();
+}
+
+#endif

+ 101 - 0
client/Player.cpp

@@ -0,0 +1,101 @@
+#include "client/Player.h"
+#include "client/Controls.h"
+#include "client/Main.h"
+#include "client/World.h"
+#include "math/View.h"
+
+static BufferedValue<Vector3> position = Vector3(8.0f, 30.0f, 8.0f);
+static BufferedValue<float> widthAngle = 0.0f;
+static BufferedValue<float> lengthAngle = 0.0f;
+static Vector3 velocity;
+static Vector3 acceleration;
+static bool onGround = false;
+static View view;
+
+static void tickInput() {
+    Vector3 back = view.getBack();
+    back[1] = 0.0f;
+    back.normalize();
+
+    Vector3 right = view.getRight();
+    right[1] = 0.0f;
+    right.normalize();
+
+    Vector3 force;
+    if(Controls::isDown(Controls::down)) {
+        force += back;
+    }
+    if(Controls::isDown(Controls::up)) {
+        force -= back;
+    }
+    if(Controls::isDown(Controls::left)) {
+        force -= right;
+    }
+    if(Controls::isDown(Controls::right)) {
+        force += right;
+    }
+    if(force.squareLength() > 0.0f) {
+        force.normalize();
+    }
+    acceleration += force * 0.1f;
+
+    if(Controls::isDown(Controls::jump) && onGround) {
+        acceleration[1] += (0.42f / 0.98f + 0.08f);
+    }
+
+    if(Window::isCursorTrapped()) {
+        Vector2 diff = (Window::Controls::getLastMousePosition() -
+                        Window::Controls::getMousePosition()) *
+                       0.1f;
+        widthAngle += diff[1];
+        lengthAngle += diff[0];
+        if(widthAngle > 89.0f) {
+            widthAngle = 89.0f;
+        }
+        if(widthAngle < -89.0f) {
+            widthAngle = -89.0f;
+        }
+    }
+}
+
+void Player::tick() {
+    position.update();
+    widthAngle.update();
+    lengthAngle.update();
+    view.updateDirections(lengthAngle, widthAngle);
+
+    if(!renderInput) {
+        tickInput();
+    }
+
+    velocity += acceleration;
+    velocity *= Vector3(0.686f, 0.98f, 0.686f);
+    acceleration = Vector3(0.0f, -0.08f, 0.0f);
+
+    Box box(Vector3(0.8f, 0.8f, 0.8f));
+    box = box.offset(position - Vector3(0.4f, 0.0f, 0.4f));
+    Vector3 move = World::limitMove(box, velocity);
+    if(move[1] + position[1] < 0.0f) {
+        move[1] = -position[1];
+    }
+    position += move;
+    onGround = move[1] == 0.0f && velocity[1] < 0.0f;
+    velocity = move;
+}
+
+Vector3 Player::getPosition(float lag) {
+    return position.get(lag);
+}
+
+float Player::getLengthAngle(float lag) {
+    return lengthAngle.get(lag);
+}
+
+float Player::getWidthAngle(float lag) {
+    return widthAngle.get(lag);
+}
+
+Vector3 Player::getLook(float lag) {
+    view.updateDirections(lengthAngle.get(lag), widthAngle.get(lag));
+    return view.getFront();
+}

+ 16 - 0
client/Player.h

@@ -0,0 +1,16 @@
+#ifndef PLAYER_H
+#define PLAYER_H
+
+#include "math/BufferedValue.h"
+#include "math/Vector.h"
+
+namespace Player {
+    void tick();
+
+    Vector3 getPosition(float lag = 1.0f);
+    float getLengthAngle(float lag = 1.0f);
+    float getWidthAngle(float lag = 1.0f);
+    Vector3 getLook(float lag = 1.0f);
+}
+
+#endif

+ 137 - 122
client/World.cpp

@@ -1,40 +1,131 @@
-#include <cmath>
-
 #include "client/World.h"
-#include "client/rendering/Engine.h"
-#include "client/rendering/Mesh.h"
-#include "common/BlockStorage.h"
-#include "rendering/FileTexture.h"
-#include "utils/List.h"
+#include "client/Player.h"
+#include "data/Array.h"
+#include "math/Frustum.h"
+#include "math/View.h"
+#include "rendering/VertexBuffer.h"
+#include "rendering/Window.h"
 #include "utils/Logger.h"
-#include "utils/Random.h"
 
-static BlockStorage blocks{7, 7};
-static List<Entity*> entities;
-static Mesh mesh;
-static FileTexture texture;
-static bool dirty = true;
+static constexpr int WORLD_SIZE = 16;
+static Array<bool, WORLD_SIZE * WORLD_SIZE * WORLD_SIZE> world(true);
+static bool dirtyVertexBuffer = true;
+static int vertices = 0;
+static Frustum frustum(80.0f, 0.1f, 1000.0f);
+
+Shader World::shader;
+static VertexBuffer vertexBuffer;
 
 bool World::init() {
-    mesh.init();
-    Error error = texture.load("resources/textures.png", 4);
-    if(error.has()) {
-        LOG_ERROR(error.message);
+    Error e =
+        shader.compile("resources/shader/test.vs", "resources/shader/test.fs");
+    if(e.has()) {
+        e.message.printLine();
+        return true;
     }
-    return error.has();
+    vertexBuffer.init(VertexBuffer::Attributes().addFloat(3).addFloat(3));
+    return false;
 }
 
-void World::addEntity(Entity* e) {
-    entities.add(e);
+IntVector3 World::getSize() {
+    return IntVector3(WORLD_SIZE, WORLD_SIZE, WORLD_SIZE);
+}
+
+void World::set(int x, int y, int z, bool b) {
+    if(x < 0 || x >= WORLD_SIZE || y < 0 || y >= WORLD_SIZE || z < 0 ||
+       z >= WORLD_SIZE) {
+        return;
+    }
+    world[x * WORLD_SIZE * WORLD_SIZE + y * WORLD_SIZE + z] = b;
+    dirtyVertexBuffer = true;
 }
 
-void World::removeEntity(Entity* e) {
-    for(int i = 0; i < entities.getLength(); i++) {
-        if(entities[i] == e) {
-            entities.removeBySwap(i);
-            return;
+bool World::isAir(int x, int y, int z) {
+    if(x < 0 || x >= WORLD_SIZE || y < 0 || y >= WORLD_SIZE || z < 0 ||
+       z >= WORLD_SIZE) {
+        return true;
+    }
+    return !world[x * WORLD_SIZE * WORLD_SIZE + y * WORLD_SIZE + z];
+}
+
+static void addTriangle(Buffer& buffer, const Vector3& a, const Vector3& b,
+                        const Vector3& c) {
+    Vector3 normal = (b - a).cross(c - a);
+    buffer.add(a).add(normal).add(b).add(normal).add(c).add(normal);
+}
+
+static void addCube(Buffer& buffer, int x, int y, int z) {
+    if(World::isAir(x, y, z)) {
+        return;
+    }
+    Vector3 v000(x, y, z);
+    Vector3 v001(x, y, z + 1);
+    Vector3 v010(x, y + 1, z);
+    Vector3 v011(x, y + 1, z + 1);
+    Vector3 v100(x + 1, y, z);
+    Vector3 v101(x + 1, y, z + 1);
+    Vector3 v110(x + 1, y + 1, z);
+    Vector3 v111(x + 1, y + 1, z + 1);
+
+    if(World::isAir(x, y - 1, z)) {
+        addTriangle(buffer, v000, v100, v001);
+        addTriangle(buffer, v100, v101, v001);
+    }
+    if(World::isAir(x, y + 1, z)) {
+        addTriangle(buffer, v010, v011, v110);
+        addTriangle(buffer, v110, v011, v111);
+    }
+    if(World::isAir(x - 1, y, z)) {
+        addTriangle(buffer, v000, v001, v010);
+        addTriangle(buffer, v001, v011, v010);
+    }
+    if(World::isAir(x + 1, y, z)) {
+        addTriangle(buffer, v100, v110, v101);
+        addTriangle(buffer, v101, v110, v111);
+    }
+    if(World::isAir(x, y, z + 1)) {
+        addTriangle(buffer, v001, v101, v011);
+        addTriangle(buffer, v111, v011, v101);
+    }
+    if(World::isAir(x, y, z - 1)) {
+        addTriangle(buffer, v000, v010, v100);
+        addTriangle(buffer, v110, v100, v010);
+    }
+}
+
+static void buildRenderingBuffer() {
+    Buffer buffer(100);
+    IntVector3 size = World::getSize();
+    for(int x = 0; x < size[0]; x++) {
+        for(int y = 0; y < size[1]; y++) {
+            for(int z = 0; z < size[2]; z++) {
+                addCube(buffer, x, y, z);
+            }
         }
     }
+    vertices = buffer.getLength() / (2 * sizeof(Vector3));
+    vertexBuffer.setData(buffer, GL::STATIC_DRAW);
+}
+
+void World::render(float lag) {
+    if(dirtyVertexBuffer) {
+        dirtyVertexBuffer = false;
+        buildRenderingBuffer();
+        LOG_INFO("rebuilt buffer");
+    }
+
+    View view;
+    view.updateDirections(Player::getLengthAngle(lag),
+                          Player::getWidthAngle(lag));
+    Vector3 center = Player::getPosition(lag) + Vector3(0.0f, 0.8f, 0.0f);
+
+    shader.use();
+    GL::setViewport(Window::getSize()[0], Window::getSize()[1]);
+    shader.setMatrix("proj", frustum.updateProjection(Window::getSize()));
+    shader.setMatrix("view", view.updateMatrix(center));
+    shader.setVector("color", Vector3(1.0f, 1.0f, 1.0f));
+    shader.setMatrix("model", Matrix());
+    vertexBuffer.draw(vertices);
 }
 
 static List<Box> getBoxes(const Box& box) {
@@ -45,22 +136,21 @@ static List<Box> getBoxes(const Box& box) {
     int maxY = floorf(box.getMax()[1]);
     int maxZ = floorf(box.getMax()[2]);
 
+    Box base(Vector3(1.0f, 1.0f, 1.0f));
     List<Box> boxes;
     for(int x = minX; x <= maxX; x++) {
         for(int y = minY; y <= maxY; y++) {
             for(int z = minZ; z <= maxZ; z++) {
-                Block::addBoxes(blocks.get(x, y, z), boxes,
-                                Vector3(static_cast<float>(x),
-                                        static_cast<float>(y),
-                                        static_cast<float>(z)));
+                if(!World::isAir(x, y, z)) {
+                    boxes.add(base.offset(Vector3(x, y, z)));
+                }
             }
         }
     }
     return boxes;
 }
 
-static Vector3 limitMove(const Entity& e, Vector3 move) {
-    Box box = e.getBox();
+Vector3 World::limitMove(const Box& box, Vector3 move) {
     List<Box> boxes = getBoxes(box.expand(move));
     if(boxes.getLength() == 0) {
         return move;
@@ -94,100 +184,25 @@ static Vector3 limitMove(const Entity& e, Vector3 move) {
     return realMove;
 }
 
-void World::tick() {
-    for(Entity* e : entities) {
-        e->tick();
-        if(!e->skip) {
-            Vector3 move = limitMove(*e, e->getVelocity());
-            e->move(move);
-        }
-    }
-}
-
-static bool isAir(int x, int y, int z) {
-    return blocks.get(x, y, z) == 0;
-}
-
-static void addCube(TypedBuffer<Mesh::Triangle>& buffer, float x, float y,
-                    float z) {
-    Vector3 v000(x, y, z);
-    Vector3 v001(x, y, z + 1);
-    Vector3 v010(x, y + 1, z);
-    Vector3 v011(x, y + 1, z + 1);
-    Vector3 v100(x + 1, y, z);
-    Vector3 v101(x + 1, y, z + 1);
-    Vector3 v110(x + 1, y + 1, z);
-    Vector3 v111(x + 1, y + 1, z + 1);
-
-    Vector2 t1(0.1875f, 0.0f);
-    Vector2 t2(0.25f, 0.0f);
-    Vector2 t3(0.25f, 0.0625f);
-    Vector2 t4(0.1875f, 0.0625f);
+RayTrace World::rayTrace(const Vector3& start, const Vector3& direction,
+                         float maxDistance) {
+    constexpr float stepLength = 0.125f;
+    RayTrace result{RayTrace::Type::AIR, IntVector3()};
 
-    if(isAir(x, y - 1, z)) {
-        Vector2 tb(0.125f, 0.0625f);
-        buffer.add({{v000, {0.125f, 0.0f}}, {v100, t1}, {v001, tb}});
-        buffer.add({{v100, t1}, {v101, t4}, {v001, tb}});
-    }
-    if(isAir(x, y + 1, z)) {
-        Vector2 tt(0.3125f, 0.0f);
-        buffer.add({{v010, t2}, {v011, t3}, {v110, tt}});
-        buffer.add({{v110, tt}, {v011, t3}, {v111, {0.3125f, 0.0625f}}});
-    }
-    if(isAir(x - 1, y, z)) {
-        buffer.add({{v000, t4}, {v001, t3}, {v010, t1}});
-        buffer.add({{v001, t3}, {v011, t2}, {v010, t1}});
-    }
-    if(isAir(x + 1, y, z)) {
-        buffer.add({{v100, t3}, {v110, t2}, {v101, t4}});
-        buffer.add({{v101, t4}, {v110, t2}, {v111, t1}});
-    }
-    if(isAir(x, y, z + 1)) {
-        buffer.add({{v001, t4}, {v101, t3}, {v011, t1}});
-        buffer.add({{v111, t2}, {v011, t1}, {v101, t3}});
-    }
-    if(isAir(x, y, z - 1)) {
-        buffer.add({{v000, t3}, {v010, t2}, {v100, t4}});
-        buffer.add({{v110, t1}, {v100, t4}, {v010, t2}});
-    }
-}
+    Vector3 pos = start;
+    Vector3 step = direction;
+    step.normalize();
+    step *= stepLength;
 
-static void rebuildRenderData() {
-    TypedBuffer<Mesh::Triangle> buffer(100);
-    for(int x = 0; x < blocks.getSize(); x++) {
-        for(int y = 0; y < blocks.getHeight(); y++) {
-            for(int z = 0; z < blocks.getSize(); z++) {
-                if(!isAir(x, y, z)) {
-                    addCube(buffer, x, y, z);
-                }
-            }
-        }
-    }
-    mesh.build(buffer);
-    LOG_DEBUG("world render update");
-}
+    int end = maxDistance / stepLength;
 
-void World::render() {
-    if(dirty) {
-        rebuildRenderData();
-        dirty = false;
-    }
-    texture.bindTo(0);
-    for(int x = -1; x <= 1; x++) {
-        for(int z = -1; z <= 1; z++) {
-            Engine::matrix
-                .translateTo(blocks.getSize() * x, 0.0f, blocks.getSize() * z)
-                .update();
-            mesh.draw();
+    for(int i = 0; i < end; i++) {
+        if(!World::isAir(pos[0], pos[1], pos[2])) {
+            result.block = pos.toInt();
+            result.type = RayTrace::Type::BLOCK;
+            return result;
         }
+        pos += step;
     }
-}
-
-int World::getHeight() {
-    return blocks.getHeight();
-}
-
-void World::setBlock(int x, int y, int z, Block::Id block) {
-    dirty = true;
-    blocks.set(x, y, z, block);
+    return result;
 }

+ 13 - 8
client/World.h

@@ -1,20 +1,25 @@
 #ifndef WORLD_H
 #define WORLD_H
 
-#include "common/Block.h"
-#include "common/entities/Entity.h"
+#include "common/RayTrace.h"
+#include "math/Box.h"
+#include "math/Vector.h"
+#include "rendering/Shader.h"
 
 namespace World {
     bool init();
-    void render();
+    extern Shader shader;
 
-    void addEntity(Entity* e);
-    void removeEntity(Entity* e);
+    IntVector3 getSize();
+    void set(int x, int y, int z, bool b);
+    bool isAir(int x, int y, int z);
 
-    void tick();
+    void render(float lag);
 
-    int getHeight();
-    void setBlock(int x, int y, int z, Block::Id block);
+    Vector3 limitMove(const Box& box, Vector3 move);
+
+    RayTrace rayTrace(const Vector3& start, const Vector3& direction,
+                      float maxDistance);
 }
 
 #endif

+ 0 - 53
client/input/Controller.cpp

@@ -1,53 +0,0 @@
-#include "GLFW/glfw3.h"
-
-#include "client/input/Controller.h"
-#include "client/rendering/Engine.h"
-
-Window::Controls::ButtonId Controller::left = 0;
-Window::Controls::ButtonId Controller::right = 0;
-Window::Controls::ButtonId Controller::up = 0;
-Window::Controls::ButtonId Controller::down = 0;
-Window::Controls::ButtonId Controller::jump = 0;
-Window::Controls::ButtonId Controller::sneak = 0;
-Window::Controls::ButtonId Controller::camLeft = 0;
-Window::Controls::ButtonId Controller::camRight = 0;
-Window::Controls::ButtonId Controller::camUp = 0;
-Window::Controls::ButtonId Controller::camDown = 0;
-Window::Controls::ButtonId Controller::leftClick = 0;
-Window::Controls::ButtonId Controller::rightClick = 0;
-
-void Controller::init() {
-    left = Window::Controls::add("Left");
-    Window::Controls::bindKey(left, GLFW_KEY_A);
-    right = Window::Controls::add("Right");
-    Window::Controls::bindKey(right, GLFW_KEY_D);
-    up = Window::Controls::add("Up");
-    Window::Controls::bindKey(up, GLFW_KEY_W);
-    down = Window::Controls::add("Down");
-    Window::Controls::bindKey(down, GLFW_KEY_S);
-    jump = Window::Controls::add("Jump");
-    Window::Controls::bindKey(jump, GLFW_KEY_SPACE);
-    sneak = Window::Controls::add("Sneak");
-    Window::Controls::bindKey(sneak, GLFW_KEY_LEFT_SHIFT);
-    camLeft = Window::Controls::add("Camera Left");
-    Window::Controls::bindKey(camLeft, GLFW_KEY_LEFT);
-    camRight = Window::Controls::add("Camera Right");
-    Window::Controls::bindKey(camRight, GLFW_KEY_RIGHT);
-    camUp = Window::Controls::add("Camera Up");
-    Window::Controls::bindKey(camUp, GLFW_KEY_UP);
-    camDown = Window::Controls::add("Camera Down");
-    Window::Controls::bindKey(camDown, GLFW_KEY_DOWN);
-
-    leftClick = Window::Controls::add("Left Click");
-    Window::Controls::bindMouse(leftClick, GLFW_MOUSE_BUTTON_LEFT);
-    rightClick = Window::Controls::add("Right Click");
-    Window::Controls::bindMouse(leftClick, GLFW_MOUSE_BUTTON_RIGHT);
-}
-
-bool Controller::wasReleased(Window::Controls::ButtonId id) {
-    return Window::Controls::wasReleased(id);
-}
-
-bool Controller::isDown(Window::Controls::ButtonId id) {
-    return Window::Controls::isDown(id);
-}

+ 0 - 30
client/packets/WorldPackets.cpp

@@ -1,30 +0,0 @@
-#include "client/packets/WorldPackets.h"
-#include "client/Game.h"
-#include "client/World.h"
-#include "common/BlockStorage.h"
-#include "common/network/Packets.h"
-#include "utils/Logger.h"
-
-void WorldPackets::receiveChunk(InPacket& in) {
-    LOG_DEBUG("received chunk from server");
-    int32 ox;
-    int32 oz;
-    if(in.readS32(ox) || in.readS32(oz)) {
-        LOG_WARNING("missing chunk offset in packet");
-        return;
-    }
-    uint16 counter = 0;
-    Block::Id b = 0;
-    for(int y = 0; y < World::getHeight(); y++) {
-        for(int x = 0; x < BlockStorage::SEGMENT; x++) {
-            for(int z = 0; z < BlockStorage::SEGMENT; z++) {
-                if(counter == 0 && (in.readU16(b) || in.readU16(counter))) {
-                    LOG_WARNING("missing block data in packet");
-                    return;
-                }
-                World::setBlock(ox + x, y, oz + z, b);
-                counter--;
-            }
-        }
-    }
-}

+ 0 - 10
client/packets/WorldPackets.h

@@ -1,10 +0,0 @@
-#ifndef WORLD_PACKETS_H
-#define WORLD_PACKETS_H
-
-#include "network/Packet.h"
-
-namespace WorldPackets {
-    void receiveChunk(InPacket& in);
-}
-
-#endif

+ 0 - 33
common/Box.cpp

@@ -1,33 +0,0 @@
-#include "common/Box.h"
-
-Box::Box(const Vector3& min, const Vector3& max) : min(min), max(max) {
-}
-
-Box::Box(const Vector3& size) : max(size) {
-}
-
-Box Box::offset(const Vector3& offset) const {
-    return Box(offset + min, offset + max);
-}
-
-bool Box::collidesWith(const Box& other) const {
-    return max[0] > other.min[0] && min[0] < other.max[0] &&
-           max[1] > other.min[1] && min[1] < other.max[1] &&
-           max[2] > other.min[2] && min[2] < other.max[2];
-}
-
-Box Box::expand(const Vector3& offset) const {
-    Vector3 add(offset[0] * (offset[0] >= 0.0), offset[1] * (offset[1] >= 0.0),
-                offset[2] * (offset[2] >= 0.0));
-    Vector3 sub(offset[0] * (offset[0] <= 0.0), offset[1] * (offset[1] <= 0.0),
-                offset[2] * (offset[2] <= 0.0));
-    return Box(min + sub, max + add);
-}
-
-const Vector3& Box::getMin() const {
-    return min;
-}
-
-const Vector3& Box::getMax() const {
-    return max;
-}

+ 0 - 29
common/Box.h

@@ -1,29 +0,0 @@
-#ifndef COLLISION_BOX_H
-#define COLLISION_BOX_H
-
-#include "math/Vector.h"
-#include "utils/StringBuffer.h"
-
-class Box {
-    Vector3 min;
-    Vector3 max;
-
-    Box(const Vector3& min, const Vector3& max);
-
-public:
-    Box(const Vector3& size);
-
-    Box offset(const Vector3& offset) const;
-    bool collidesWith(const Box& other) const;
-    Box expand(const Vector3& offset) const;
-
-    const Vector3& getMin() const;
-    const Vector3& getMax() const;
-
-    template<int L>
-    void toString(StringBuffer<L>& s) const {
-        s.append("Box(").append(min).append(", ").append(max).append(")");
-    }
-};
-
-#endif

+ 7 - 0
common/NetworkPackets.h

@@ -0,0 +1,7 @@
+#ifndef NETWORK_PACKETS_H
+#define NETWORK_PACKETS_H
+
+enum class ToClientPacket { CHAT, WORLD, SET_BLOCK, PLAYER };
+enum class ToServerPacket { CHAT, SET_BLOCK, PLAYER };
+
+#endif

+ 0 - 7
common/Packets.cpp

@@ -1,7 +0,0 @@
-#include "common/Packets.h"
-
-OutPacket Packet::build(Type t) {
-    OutPacket out(100);
-    out.writeU8(static_cast<uint8>(t));
-    return out;
-}

+ 0 - 12
common/Packets.h

@@ -1,12 +0,0 @@
-#ifndef PACKETS_H
-#define PACKETS_H
-
-#include "network/Packet.h"
-
-namespace Packet {
-    enum class Type { WORLD, SET_BLOCK, PLAYER, CHAT };
-
-    OutPacket build(Type t);
-}
-
-#endif

+ 12 - 0
common/RayTrace.h

@@ -0,0 +1,12 @@
+#ifndef RAYTRACE_H
+#define RAYTRACE_H
+
+#include "math/Vector.h"
+
+struct RayTrace final {
+    enum class Type { AIR, BLOCK };
+    Type type;
+    IntVector3 block;
+};
+
+#endif

+ 4 - 2
meson.build

@@ -1,8 +1,6 @@
 project('cubes plus plus', 'cpp')
 
 src_common = [
-    'common/Packets.cpp',
-    'common/Box.cpp',
 ]
 
 src_server = [
@@ -13,6 +11,10 @@ src_server = [
 
 src_client = [
     'client/Main.cpp',
+    'client/Controls.cpp',
+    'client/Network.cpp',
+    'client/World.cpp',
+    'client/Player.cpp',
 ]
 
 sources_test = ['tests/Main.cpp']

+ 3 - 1
resources/scripts/test.snuvi

@@ -1,6 +1,8 @@
+const int* text = "Hi there\n";
+
 void main() {
     for(int i = 0; i < 5; i++) {
-        print("Hi there\n");
+        print(text);
     }
     /*print("----------\n");
     while(true) {

+ 28 - 26
server/Main.cpp

@@ -1,4 +1,4 @@
-#include "common/Packets.h"
+#include "common/NetworkPackets.h"
 #include "data/Array.h"
 #include "data/HashMap.h"
 #include "math/Vector.h"
@@ -54,20 +54,22 @@ static void set(int x, int y, int z, bool b) {
     }
     world[x * WORLD_SIZE * WORLD_SIZE + y * WORLD_SIZE + z] = b;
 
-    OutPacket out = Packet::build(Packet::Type::SET_BLOCK);
-    out.writeFloat(x);
-    out.writeFloat(y);
-    out.writeFloat(z);
+    OutPacket out(100);
+    out.writeU8(static_cast<uint8>(ToClientPacket::SET_BLOCK));
+    out.writeS32(x);
+    out.writeS32(y);
+    out.writeS32(z);
     out.writeU8(b);
-    Server::send(out, PacketType::RELIABLE);
+    Server::send(out, PacketSendMode::RELIABLE);
 }
 
 static void onConnect(Server::Client client) {
-    OutPacket out = Packet::build(Packet::Type::WORLD);
+    OutPacket out(100);
+    out.writeU8(static_cast<uint8>(ToClientPacket::WORLD));
     for(bool b : world) {
         out.writeU8(b);
     }
-    Server::send(client, out, PacketType::RELIABLE);
+    Server::send(client, out, PacketSendMode::RELIABLE);
     players.add(client, 0);
 }
 
@@ -77,43 +79,42 @@ static void onDisconnect(Server::Client client) {
 
 static void onPacket(Server::Client client, InPacket& in) {
     uint8 type = 0;
-    if(in.readU8(type)) {
+    if(in.read(type)) {
         puts("no data");
         return;
     }
-    switch(static_cast<Packet::Type>(type)) {
-        case Packet::Type::SET_BLOCK: {
-            Vector3 pos;
-            in.readFloat(pos[0]);
-            in.readFloat(pos[1]);
-            in.readFloat(pos[2]);
+    switch(static_cast<ToServerPacket>(type)) {
+        case ToServerPacket::SET_BLOCK: {
+            IntVector3 pos;
+            in.read(pos);
             uint8 type;
-            in.readU8(type);
+            in.read(type);
             set(pos[0], pos[1], pos[2], type);
             break;
         }
-        case Packet::Type::PLAYER: {
+        case ToServerPacket::PLAYER: {
             Vector3 pos;
-            in.readFloat(pos[0]);
-            in.readFloat(pos[1]);
-            in.readFloat(pos[2]);
+            in.read(pos[0]);
+            in.read(pos[1]);
+            in.read(pos[2]);
 
-            OutPacket out = Packet::build(Packet::Type::PLAYER);
+            OutPacket out(100);
+            out.writeU8(static_cast<uint8>(ToClientPacket::PLAYER));
             out.writeFloat(pos[0]);
             out.writeFloat(pos[1]);
             out.writeFloat(pos[2]);
             out.writeS32(client);
             for(Server::Client c : players.keys()) {
                 if(c != client) {
-                    Server::send(c, out, PacketType::RELIABLE);
+                    Server::send(c, out, PacketSendMode::RELIABLE);
                 }
             }
             break;
         }
-        case Packet::Type::CHAT: {
+        case ToServerPacket::CHAT: {
             List<uint32> s;
             uint32 u;
-            while(!in.readU32(u)) {
+            while(!in.read(u)) {
                 s.add(u);
             }
 
@@ -122,11 +123,12 @@ static void onPacket(Server::Client client, InPacket& in) {
                 return;
             }
 
-            OutPacket out = Packet::build(Packet::Type::CHAT);
+            OutPacket out(100);
+            out.writeU8(static_cast<uint8>(ToClientPacket::CHAT));
             for(uint32 u : s) {
                 out.writeU32(u);
             }
-            Server::send(out, PacketType::RELIABLE);
+            Server::send(out, PacketSendMode::RELIABLE);
             break;
         }
         default: printf("invalid package type %d\n", static_cast<int>(type));

+ 4 - 3
server/snuviscript/Snuvi.cpp

@@ -2,7 +2,7 @@
 #include "Compiler.h"
 #include "data/HashMap.h"
 #undef MATH_H
-#include "common/Packets.h"
+#include "common/NetworkPackets.h"
 #include "libraries/Math.h"
 #include "libraries/Time.h"
 #include "network/Server.h"
@@ -38,7 +38,8 @@ static void printString(Script* sc) {
     if(!sPopPointer(sc, &p) || sGetPointerLength(sc, &p, &length)) {
         return;
     }
-    OutPacket out = Packet::build(Packet::Type::CHAT);
+    OutPacket out(100);
+    out.writeU8(static_cast<uint8>(ToClientPacket::CHAT));
     for(int i = 0; i < length; i++) {
         const void* data = sCheckAddress(sc, &p, sizeof(int));
         if(data != nullptr) {
@@ -49,7 +50,7 @@ static void printString(Script* sc) {
         }
         p.offset += sizeof(int);
     }
-    Server::send(out, PacketType::RELIABLE);
+    Server::send(out, PacketSendMode::RELIABLE);
 }
 
 static void printInt32(Script* sc) {

+ 1 - 1
subprojects/gaming-core

@@ -1 +1 @@
-Subproject commit 18790f58accfe58d37bc84eddd1b12c94f131449
+Subproject commit ab398c30bc566f56db81fdbbf2b7a40f07d1a308