|  | @@ -1,11 +1,660 @@
 | 
	
		
			
				|  |  | -#include "client/Game.h"
 | 
	
		
			
				|  |  | -#include "client/GameClient.h"
 | 
	
		
			
				|  |  | -#include "client/rendering/Engine.h"
 | 
	
		
			
				|  |  | +#include "GLFW/glfw3.h"
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -int main() {
 | 
	
		
			
				|  |  | -    if(GameClient::init() || Engine::init() || Game::init()) {
 | 
	
		
			
				|  |  | +#include "common/Box.h"
 | 
	
		
			
				|  |  | +#include "common/Packets.h"
 | 
	
		
			
				|  |  | +#include "data/Array.h"
 | 
	
		
			
				|  |  | +#include "data/HashMap.h"
 | 
	
		
			
				|  |  | +#include "data/RingBuffer.h"
 | 
	
		
			
				|  |  | +#include "math/Frustum.h"
 | 
	
		
			
				|  |  | +#include "network/Client.h"
 | 
	
		
			
				|  |  | +#include "rendering/Shader.h"
 | 
	
		
			
				|  |  | +#include "rendering/Texture.h"
 | 
	
		
			
				|  |  | +#include "rendering/VertexBuffer.h"
 | 
	
		
			
				|  |  | +#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;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +typedef StringBuffer<50> ChatMessage;
 | 
	
		
			
				|  |  | +static Array<ChatMessage, 20> chat;
 | 
	
		
			
				|  |  | +static int chatIndex = 0;
 | 
	
		
			
				|  |  | +static bool renderInput = false;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static 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,
 | 
	
		
			
				|  |  | +                        const Vector3& c) {
 | 
	
		
			
				|  |  | +    Vector3 normal = (b - a).cross(c - a);
 | 
	
		
			
				|  |  | +    buffer.add(a).add(normal).add(b).add(normal).add(c).add(normal);
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static bool init() {
 | 
	
		
			
				|  |  | +    Error e =
 | 
	
		
			
				|  |  | +        shader.compile("resources/shader/test.vs", "resources/shader/test.fs");
 | 
	
		
			
				|  |  | +    if(e.has()) {
 | 
	
		
			
				|  |  | +        e.message.printLine();
 | 
	
		
			
				|  |  | +        return true;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    e = fontShader.compile("resources/shader/fontTest.vs",
 | 
	
		
			
				|  |  | +                           "resources/shader/fontTest.fs");
 | 
	
		
			
				|  |  | +    if(e.has()) {
 | 
	
		
			
				|  |  | +        e.message.printLine();
 | 
	
		
			
				|  |  | +        return true;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    e = fontTexture.load("resources/font8x8.png", 0);
 | 
	
		
			
				|  |  | +    if(e.has()) {
 | 
	
		
			
				|  |  | +        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));
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    Vector3 v000(0, 0, 0);
 | 
	
		
			
				|  |  | +    Vector3 v001(0, 0, 1);
 | 
	
		
			
				|  |  | +    Vector3 v010(0, 1, 0);
 | 
	
		
			
				|  |  | +    Vector3 v011(0, 1, 1);
 | 
	
		
			
				|  |  | +    Vector3 v100(1, 0, 0);
 | 
	
		
			
				|  |  | +    Vector3 v101(1, 0, 1);
 | 
	
		
			
				|  |  | +    Vector3 v110(1, 1, 0);
 | 
	
		
			
				|  |  | +    Vector3 v111(1, 1, 1);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    Buffer buffer(100);
 | 
	
		
			
				|  |  | +    addTriangle(buffer, v000, v100, v001);
 | 
	
		
			
				|  |  | +    addTriangle(buffer, v100, v101, v001);
 | 
	
		
			
				|  |  | +    addTriangle(buffer, v010, v011, v110);
 | 
	
		
			
				|  |  | +    addTriangle(buffer, v110, v011, v111);
 | 
	
		
			
				|  |  | +    addTriangle(buffer, v000, v001, v010);
 | 
	
		
			
				|  |  | +    addTriangle(buffer, v001, v011, v010);
 | 
	
		
			
				|  |  | +    addTriangle(buffer, v100, v110, v101);
 | 
	
		
			
				|  |  | +    addTriangle(buffer, v101, v110, v111);
 | 
	
		
			
				|  |  | +    addTriangle(buffer, v001, v101, v011);
 | 
	
		
			
				|  |  | +    addTriangle(buffer, v111, v011, v101);
 | 
	
		
			
				|  |  | +    addTriangle(buffer, v000, v010, v100);
 | 
	
		
			
				|  |  | +    addTriangle(buffer, v110, v100, v010);
 | 
	
		
			
				|  |  | +    markVertexBuffer.setStaticData(buffer.getLength(), buffer);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    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);
 | 
	
		
			
				|  |  | +    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);
 | 
	
		
			
				|  |  | +            for(uint32 u : Window::Input::getUnicode()) {
 | 
	
		
			
				|  |  | +                out.writeU32(u);
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +            Client::send(out, PacketType::RELIABLE);
 | 
	
		
			
				|  |  | +            Window::Input::reset();
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +        if(Window::Controls::wasReleased(escapeKey)) {
 | 
	
		
			
				|  |  | +            Window::Input::disable();
 | 
	
		
			
				|  |  | +            Window::trapCursor();
 | 
	
		
			
				|  |  | +            trappedMoused = true;
 | 
	
		
			
				|  |  | +            renderInput = false;
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +    } else {
 | 
	
		
			
				|  |  | +        if(Window::Controls::isDown(chatKey)) {
 | 
	
		
			
				|  |  | +            Window::Input::enable();
 | 
	
		
			
				|  |  | +            Window::freeCursor();
 | 
	
		
			
				|  |  | +            trappedMoused = false;
 | 
	
		
			
				|  |  | +            renderInput = true;
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +        if(Window::Controls::wasReleased(primaryClick)) {
 | 
	
		
			
				|  |  | +            Window::trapCursor();
 | 
	
		
			
				|  |  | +            trappedMoused = true;
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +        if(Window::Controls::wasReleased(escapeKey)) {
 | 
	
		
			
				|  |  | +            Window::freeCursor();
 | 
	
		
			
				|  |  | +            trappedMoused = false;
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    lastPosition = position;
 | 
	
		
			
				|  |  | +    lastWidthAngle = widthAngle;
 | 
	
		
			
				|  |  | +    lastLengthAngle = lengthAngle;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    for(Player& p : players.values()) {
 | 
	
		
			
				|  |  | +        p.lastPosition = p.position;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    Client::tick();
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    updateDirections(0.0f);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    Vector3 b = back;
 | 
	
		
			
				|  |  | +    b[1] = 0.0f;
 | 
	
		
			
				|  |  | +    b.normalize();
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    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;
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    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]);
 | 
	
		
			
				|  |  | +    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);
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static void renderString(const char* text) {
 | 
	
		
			
				|  |  | +    static Buffer buffer(50);
 | 
	
		
			
				|  |  | +    buffer.clear();
 | 
	
		
			
				|  |  | +    constexpr float fontSize = 8.0f;
 | 
	
		
			
				|  |  | +    constexpr float fontStep = 8.0f / 128.0f;
 | 
	
		
			
				|  |  | +    int index = 0;
 | 
	
		
			
				|  |  | +    Vector3 pos;
 | 
	
		
			
				|  |  | +    int vertices = 0;
 | 
	
		
			
				|  |  | +    while(text[index] != '\0') {
 | 
	
		
			
				|  |  | +        Vector3 right = pos + Vector3(fontSize, 0.0f, 0.0f);
 | 
	
		
			
				|  |  | +        Vector3 down = pos + Vector3(0.0f, fontSize, 0.0f);
 | 
	
		
			
				|  |  | +        Vector3 downRight = pos + Vector3(fontSize, fontSize, 0.0f);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        int i = text[index];
 | 
	
		
			
				|  |  | +        if(i < 0 && text[index + 1] != '\0') {
 | 
	
		
			
				|  |  | +            index++;
 | 
	
		
			
				|  |  | +            i = ((i & 0x1F) << 6) | (text[index] & 0x3F);
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +        Vector2 tPos(fontStep * (i % 16), fontStep * (i / 16));
 | 
	
		
			
				|  |  | +        Vector2 tRight = tPos + Vector2(fontStep, 0.0f);
 | 
	
		
			
				|  |  | +        Vector2 tDown = tPos + Vector2(0.0f, fontStep);
 | 
	
		
			
				|  |  | +        Vector2 tDownRight = tPos + Vector2(fontStep, fontStep);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        buffer.add(pos).add(tPos).add(down).add(tDown).add(right).add(tRight);
 | 
	
		
			
				|  |  | +        buffer.add(down).add(tDown).add(right).add(tRight).add(downRight).add(
 | 
	
		
			
				|  |  | +            tDownRight);
 | 
	
		
			
				|  |  | +        pos += Vector3(fontSize, 0.0f, 0.0f);
 | 
	
		
			
				|  |  | +        vertices += 6;
 | 
	
		
			
				|  |  | +        index++;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    fontBuffer.setDynamicData(buffer.getLength(), buffer);
 | 
	
		
			
				|  |  | +    fontBuffer.draw(vertices);
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static void render(float lag) {
 | 
	
		
			
				|  |  | +    GL::clear();
 | 
	
		
			
				|  |  | +    GL::enableDepthTesting();
 | 
	
		
			
				|  |  | +    if(dirtyVertexBuffer) {
 | 
	
		
			
				|  |  | +        dirtyVertexBuffer = false;
 | 
	
		
			
				|  |  | +        buildRenderingBuffer();
 | 
	
		
			
				|  |  | +        puts("rebuilt buffer");
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    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);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    if(hasFocus) {
 | 
	
		
			
				|  |  | +        shader.setVector("color", Vector3(0.8f, 0.6f, 0.6f));
 | 
	
		
			
				|  |  | +        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());
 | 
	
		
			
				|  |  | +        markVertexBuffer.draw(36);
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    shader.setVector("color", Vector3(1.0f, 0.0f, 0.0f));
 | 
	
		
			
				|  |  | +    for(const Player& p : players.values()) {
 | 
	
		
			
				|  |  | +        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());
 | 
	
		
			
				|  |  | +        markVertexBuffer.draw(36);
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    shader.setMatrix("proj", Matrix().getValues());
 | 
	
		
			
				|  |  | +    shader.setMatrix("view", Matrix().getValues());
 | 
	
		
			
				|  |  | +    shader.setVector("color", Vector3(1.0f, 0.0f, 1.0f));
 | 
	
		
			
				|  |  | +    model.translateTo(Vector3(-0.5f, -0.5f, -0.5f));
 | 
	
		
			
				|  |  | +    model.scale(0.05f);
 | 
	
		
			
				|  |  | +    shader.setMatrix("model", model.getValues());
 | 
	
		
			
				|  |  | +    markVertexBuffer.draw(36);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    GL::disableDepthTesting();
 | 
	
		
			
				|  |  | +    GL::enableBlending();
 | 
	
		
			
				|  |  | +    fontShader.use();
 | 
	
		
			
				|  |  | +    fontShader.setMatrix("proj", Matrix().getValues());
 | 
	
		
			
				|  |  | +    view.translateTo(Vector3(0.0f, 0.0f, 0.0f));
 | 
	
		
			
				|  |  | +    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());
 | 
	
		
			
				|  |  | +    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());
 | 
	
		
			
				|  |  | +        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());
 | 
	
		
			
				|  |  | +        StringBuffer<256> s;
 | 
	
		
			
				|  |  | +        Window::Input::toString(s);
 | 
	
		
			
				|  |  | +        renderString(s);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        model
 | 
	
		
			
				|  |  | +            .translateTo(Vector3(Window::Input::getCursor() * 8.0f,
 | 
	
		
			
				|  |  | +                                 chat.getLength() * 8.0f + 2.0f, 0.0f))
 | 
	
		
			
				|  |  | +            .scale(3.0f);
 | 
	
		
			
				|  |  | +        fontShader.setMatrix("model", model.getValues());
 | 
	
		
			
				|  |  | +        renderString("_");
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    GL::disableBlending();
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +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();
 | 
	
		
			
				|  |  | +        return 0;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    if(init()) {
 | 
	
		
			
				|  |  | +        Client::stop();
 | 
	
		
			
				|  |  |          return 0;
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  | -    Engine::run();
 | 
	
		
			
				|  |  | +    Window::show();
 | 
	
		
			
				|  |  | +    Window::run<isRunning, tick, render>(50'000'000);
 | 
	
		
			
				|  |  | +    Window::close();
 | 
	
		
			
				|  |  | +    Client::stop();
 | 
	
		
			
				|  |  |      return 0;
 | 
	
		
			
				|  |  |  }
 |