]> git.localhorst.tv Git - blank.git/commitdiff
client-side implementation of login packet
authorDaniel Karbach <daniel.karbach@localhorst.tv>
Wed, 2 Sep 2015 20:33:08 +0000 (22:33 +0200)
committerDaniel Karbach <daniel.karbach@localhorst.tv>
Wed, 2 Sep 2015 20:33:08 +0000 (22:33 +0200)
chunk loading has to change:
need to stop the client from generating chunks on its own and
the server must be able to load chunks for multiple bases

also, I've not written a single test for all this crap
shame on me

15 files changed:
.gitignore
Makefile
building [deleted file]
doc/building [new file with mode: 0644]
doc/protocol [new file with mode: 0644]
doc/running [new file with mode: 0644]
running [deleted file]
src/app/ClientState.cpp
src/app/ClientState.hpp
src/app/WorldState.cpp
src/app/runtime.cpp
src/net/Client.hpp
src/net/Packet.hpp
src/net/Server.hpp
src/net/net.cpp

index eeb54651b6fc17257878a9a8ce4acb0206e1f3fc..5a84607debdc0a4231955e6d2c0af4ed224b39b8 100644 (file)
@@ -7,4 +7,5 @@ blank.test
 build
 cachegrind.out.*
 callgrind.out.*
+client-saves
 saves
index e6e24d9d2845caea6a24b673c9752d48bbeeda00..c23e9ac4a8f35f40dccccdc920733c3420c0de99 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -67,6 +67,9 @@ run: $(ASSET_DEP) blank
 server: $(ASSET_DEP) blank
        ./blank --server --save-path saves/
 
+client: $(ASSET_DEP) blank
+       ./blank --client --save-path client-saves/
+
 gdb: $(ASSET_DEP) blank.debug
        gdb ./blank.debug
 
@@ -88,7 +91,7 @@ clean:
 
 distclean: clean
        rm -f $(BIN) cachegrind.out.* callgrind.out.*
-       rm -Rf build saves
+       rm -Rf build client-saves saves
 
 .PHONY: all release debug profile tests run gdb cachegrind callgrind test clean distclean
 
diff --git a/building b/building
deleted file mode 100644 (file)
index ef83775..0000000
--- a/building
+++ /dev/null
@@ -1,64 +0,0 @@
-Dependencies
-============
-
-       GLEW, GLM, SDL2, SDL2_image, SDL2_net, SDL2_ttf, OpenAL, freealut, zlib
-
-       CppUnit for tests
-
-archlinux: pacman -S glew glm sdl2 sdl2_image sdl2_net sdl2_ttf openal freealut zlib cppunit
-
-manual:
-       CppUnit http://sourceforge.net/projects/cppunit/
-       GLEW    http://glew.sourceforge.net/
-       GLM     http://glm.g-truc.net/0.9.6/index.html
-       OpenAL  http://openal.org/
-       SDL     http://www.libsdl.org/
-       zlib    http://zlib.net/
-
-
-Makefile
-========
-
-Targets
--------
-
-all:
-       build everything
-
-release (default), debug, profile:
-       build executables tuned for running, debugging, and profiling
-
-run:
-       build and execute the main binary with state path set to ./saves
-
-server:
-       same as run, only in server mode
-
-test:
-       build and run unittests
-
-gdb, cachegrind, callgrind:
-       build the binary suited for given tool and launch
-
-clean:
-       remove intermediates
-
-distclean:
-       remove intermediates and artifacts
-       (also those generated by tool invocations!)
-
-
-Variables
----------
-
-CXX, LXX:
-       compiler/linker used for C++ sources/objects
-
-LIBS:
-       names of libraries (for pkg-config)
-
-CPPFLAGS, CXXFLAGS, LDXXFLAGS:
-       flags for the preprocessor, compiler, and linker
-
-DEBUG_FLAGS, PROFILE_FLAGS, RELEASE_FLAGS:
-       flags for building binaries in debug, profile, and release mode
diff --git a/doc/building b/doc/building
new file mode 100644 (file)
index 0000000..b850668
--- /dev/null
@@ -0,0 +1,68 @@
+Dependencies
+============
+
+       GLEW, GLM, SDL2, SDL2_image, SDL2_net, SDL2_ttf, OpenAL, freealut, zlib
+
+       CppUnit for tests
+
+archlinux: pacman -S glew glm sdl2 sdl2_image sdl2_net sdl2_ttf openal freealut zlib cppunit
+
+manual:
+       CppUnit http://sourceforge.net/projects/cppunit/
+       GLEW    http://glew.sourceforge.net/
+       GLM     http://glm.g-truc.net/0.9.6/index.html
+       OpenAL  http://openal.org/
+       SDL     http://www.libsdl.org/
+       zlib    http://zlib.net/
+
+
+Makefile
+========
+
+Targets
+-------
+
+all:
+       build everything
+
+release (default), debug, profile:
+       build executables tuned for running, debugging, and profiling
+
+run:
+       build and execute the main binary with state path set to ./saves
+
+server:
+       same as run, only in server mode
+
+server:
+       same as run, only in client mode and the save path is set to
+       ./client-saved to prevent clashes with a running `make server`
+
+test:
+       build and run unittests
+
+gdb, cachegrind, callgrind:
+       build the binary suited for given tool and launch
+
+clean:
+       remove intermediates
+
+distclean:
+       remove intermediates and artifacts
+       (also those generated by tool invocations!)
+
+
+Variables
+---------
+
+CXX, LXX:
+       compiler/linker used for C++ sources/objects
+
+LIBS:
+       names of libraries (for pkg-config)
+
+CPPFLAGS, CXXFLAGS, LDXXFLAGS:
+       flags for the preprocessor, compiler, and linker
+
+DEBUG_FLAGS, PROFILE_FLAGS, RELEASE_FLAGS:
+       flags for building binaries in debug, profile, and release mode
diff --git a/doc/protocol b/doc/protocol
new file mode 100644 (file)
index 0000000..723f3cf
--- /dev/null
@@ -0,0 +1,25 @@
+Packets
+=======
+
+Ping
+----
+
+To tell the other side we're still alive.
+Both server and client will send this if they haven't sent something in
+a while.
+
+Code: 0
+Payload: none
+
+
+Login
+-----
+
+Sent from client to serveri as a request to join. The server may
+respond negatively if the player name is already taken or some cap has
+been reached.
+
+Code: 1
+Payload:
+       player name, max 32 byte UTF-8 string,
+       shorter names should be zero terminated
diff --git a/doc/running b/doc/running
new file mode 100644 (file)
index 0000000..846f27d
--- /dev/null
@@ -0,0 +1,116 @@
+Arguments
+=========
+
+Runtime
+-------
+
+-n <n>
+       terminate after <n> frames
+
+-t <t>
+       terminate after <t> milliseconds
+
+if both n and t are given, terminate after n frames and
+assume <t> milliseconds pass each frame
+
+--asset-path <path>
+       load assets from given path
+       default is application dir + "assets"
+
+--save-path <path>
+       store and load saves at given path
+       default is whatever SDL thinks is good
+       (should be ~/.local/share/localhorst/blank/)
+
+Application
+-----------
+
+-d
+       disable double buffering
+
+-m <num>
+       set sample size to <num> (samples per pixel)
+
+--no-vsync
+       disable vsync
+
+--standalone
+       run as standalone (the default)
+
+--client
+       run as client
+
+--server
+       run as server
+
+Interface
+---------
+
+--no-keyboard
+       disable keyboard input handling
+
+--no-mouse
+       disable mouse input handling
+
+--no-hud
+       disable HUD drawing (includes the selected block outline)
+
+--no-audio
+       disable audio
+       the audio device and sounds will still be allocated
+       it just stops the interface from queueing buffers
+
+Network
+-------
+
+--host <hostname>
+       hostname to connect to in client mode
+
+--port <number>
+       port number to connection to (client) or listen on (server)
+
+--player-name <name>
+       use given name to identify with the server (client mode)
+       default player name is "default"
+       the server will reject players with names that are already taken
+
+World
+-----
+
+-s <seed>
+       use <seed> (unsigned integer) as the world seed
+       only used for newly created worlds
+       default is 0
+
+--world-name <name>
+       use given name for the world save
+       no checks are being done right now, so make sure it can be
+       used as a directory name
+
+
+Controls
+========
+
+Move around with WSAD, shift, and space, look around with mouse motion.
+Mouse button 1 deletes the block you're pointing at, button 2 selects it
+as the active block, and button 3 places the active block next to the one
+you're pointing at.
+
+As an alternative to picking, you can also use the mousewheel up/down to
+flip through available blocks.
+
+Q changes the face of the active block (loops over up, down, right, left,
+front, and back) and E changes the turn (none, left, around, and right).
+
+Press N to toggle player/world collision.
+
+F1 toggles UI rendering.
+F3 toggles a display telling how long on average it takes to compute a frame.
+F4 toggles audio.
+
+Controls are interpreted by scancode, meaning you don't have to break your
+fingers when you're on an AZERTY. WSAD will be ZSQD there and the above
+description is just wrong.
+
+Also I've added a plethora of alternate keys that can be used, like arrow
+keys for movement, ins/del for placing/removing blocks, etc.
diff --git a/running b/running
deleted file mode 100644 (file)
index 846f27d..0000000
--- a/running
+++ /dev/null
@@ -1,116 +0,0 @@
-Arguments
-=========
-
-Runtime
--------
-
--n <n>
-       terminate after <n> frames
-
--t <t>
-       terminate after <t> milliseconds
-
-if both n and t are given, terminate after n frames and
-assume <t> milliseconds pass each frame
-
---asset-path <path>
-       load assets from given path
-       default is application dir + "assets"
-
---save-path <path>
-       store and load saves at given path
-       default is whatever SDL thinks is good
-       (should be ~/.local/share/localhorst/blank/)
-
-Application
------------
-
--d
-       disable double buffering
-
--m <num>
-       set sample size to <num> (samples per pixel)
-
---no-vsync
-       disable vsync
-
---standalone
-       run as standalone (the default)
-
---client
-       run as client
-
---server
-       run as server
-
-Interface
----------
-
---no-keyboard
-       disable keyboard input handling
-
---no-mouse
-       disable mouse input handling
-
---no-hud
-       disable HUD drawing (includes the selected block outline)
-
---no-audio
-       disable audio
-       the audio device and sounds will still be allocated
-       it just stops the interface from queueing buffers
-
-Network
--------
-
---host <hostname>
-       hostname to connect to in client mode
-
---port <number>
-       port number to connection to (client) or listen on (server)
-
---player-name <name>
-       use given name to identify with the server (client mode)
-       default player name is "default"
-       the server will reject players with names that are already taken
-
-World
------
-
--s <seed>
-       use <seed> (unsigned integer) as the world seed
-       only used for newly created worlds
-       default is 0
-
---world-name <name>
-       use given name for the world save
-       no checks are being done right now, so make sure it can be
-       used as a directory name
-
-
-Controls
-========
-
-Move around with WSAD, shift, and space, look around with mouse motion.
-Mouse button 1 deletes the block you're pointing at, button 2 selects it
-as the active block, and button 3 places the active block next to the one
-you're pointing at.
-
-As an alternative to picking, you can also use the mousewheel up/down to
-flip through available blocks.
-
-Q changes the face of the active block (loops over up, down, right, left,
-front, and back) and E changes the turn (none, left, around, and right).
-
-Press N to toggle player/world collision.
-
-F1 toggles UI rendering.
-F3 toggles a display telling how long on average it takes to compute a frame.
-F4 toggles audio.
-
-Controls are interpreted by scancode, meaning you don't have to break your
-fingers when you're on an AZERTY. WSAD will be ZSQD there and the above
-description is just wrong.
-
-Also I've added a plethora of alternate keys that can be used, like arrow
-keys for movement, ins/del for placing/removing blocks, etc.
index c371235761558e94c0a73a3a0ff139a490654dcb..b4ed3f8fbd31e871b6ca41e4dacf081bb9f03c7c 100644 (file)
@@ -1,6 +1,7 @@
 #include "ClientState.hpp"
 
 #include "Environment.hpp"
+#include "init.hpp"
 #include "TextureIndex.hpp"
 
 namespace blank {
@@ -9,19 +10,55 @@ ClientState::ClientState(
        Environment &env,
        const World::Config &wc,
        const WorldSave &ws,
+       const Interface::Config &ic,
        const Client::Config &cc
 )
 : env(env)
 , block_types()
 , world(block_types, wc, ws)
+, chunk_renderer(world, wc.load.load_dist)
+, interface(ic, env, world)
 , client(cc, world) {
+       TextureIndex tex_index;
+       env.loader.LoadBlockTypes("default", block_types, tex_index);
+       chunk_renderer.LoadTextures(env.loader, tex_index);
+       chunk_renderer.FogDensity(wc.fog_density);
+       // TODO: better solution for initializing HUD
+       interface.SelectNext();
+       client.SendLogin(ic.player_name);
+}
+
 
+void ClientState::OnEnter() {
+       env.window.GrabMouse();
 }
 
 
 void ClientState::Handle(const SDL_Event &event) {
-       if (event.type == SDL_QUIT) {
-               env.state.PopAll();
+       switch (event.type) {
+               case SDL_KEYDOWN:
+                       interface.HandlePress(event.key);
+                       break;
+               case SDL_KEYUP:
+                       interface.HandleRelease(event.key);
+                       break;
+               case SDL_MOUSEBUTTONDOWN:
+                       interface.HandlePress(event.button);
+                       break;
+               case SDL_MOUSEBUTTONUP:
+                       interface.HandleRelease(event.button);
+                       break;
+               case SDL_MOUSEMOTION:
+                       interface.Handle(event.motion);
+                       break;
+               case SDL_MOUSEWHEEL:
+                       interface.Handle(event.wheel);
+                       break;
+               case SDL_QUIT:
+                       env.state.Pop();
+                       break;
+               default:
+                       break;
        }
 }
 
@@ -32,11 +69,26 @@ void ClientState::Update(int dt) {
        if (client.TimedOut()) {
                env.state.Pop();
        }
+
+       interface.Update(dt);
+       world.Update(dt);
+       chunk_renderer.Rebase(interface.Player().ChunkCoords());
+       chunk_renderer.Update(dt);
+
+       glm::mat4 trans = interface.Player().Transform(interface.Player().ChunkCoords());
+       glm::vec3 dir(trans * glm::vec4(0.0f, 0.0f, -1.0f, 0.0f));
+       glm::vec3 up(trans * glm::vec4(0.0f, 1.0f, 0.0f, 0.0f));
+       env.audio.Position(interface.Player().Position());
+       env.audio.Velocity(interface.Player().Velocity());
+       env.audio.Orientation(dir, up);
 }
 
 
 void ClientState::Render(Viewport &viewport) {
-
+       viewport.WorldPosition(interface.Player().Transform(interface.Player().ChunkCoords()));
+       chunk_renderer.Render(viewport);
+       world.Render(viewport);
+       interface.Render(viewport);
 }
 
 }
index f54e56f93199222547275a38d45d517ca67d292d..8868c98a7becd4635725ad238499700b37e44566 100644 (file)
@@ -3,7 +3,9 @@
 
 #include "State.hpp"
 #include "../net/Client.hpp"
+#include "../ui/Interface.hpp"
 #include "../world/BlockTypeRegistry.hpp"
+#include "../world/ChunkRenderer.hpp"
 #include "../world/World.hpp"
 
 
@@ -19,9 +21,12 @@ public:
                Environment &,
                const World::Config &,
                const WorldSave &,
+               const Interface::Config &,
                const Client::Config &
        );
 
+       void OnEnter() override;
+
        void Handle(const SDL_Event &) override;
        void Update(int dt) override;
        void Render(Viewport &) override;
@@ -30,6 +35,8 @@ private:
        Environment &env;
        BlockTypeRegistry block_types;
        World world;
+       ChunkRenderer chunk_renderer;
+       Interface interface;
        Client client;
 
 };
index 521f5b5023235281a9c51fb2336f62155f1eb9a3..fabd332c6a725b5ea55ca9cf222b11aeaad27f7f 100644 (file)
@@ -79,7 +79,6 @@ void WorldState::Update(int dt) {
        env.audio.Position(interface.Player().Position());
        env.audio.Velocity(interface.Player().Velocity());
        env.audio.Orientation(dir, up);
-
 }
 
 void WorldState::Render(Viewport &viewport) {
index f979c653bcf8c496302d366d13a18d15c64e33c5..2d128631a2e19d8bde4af93c022cee5da248e798 100644 (file)
@@ -357,7 +357,7 @@ void Runtime::RunClient() {
        }
 
        Application app(env);
-       ClientState client_state(env, config.world, save, config.client);
+       ClientState client_state(env, config.world, save, config.interface, config.client);
        app.PushState(&client_state);
        Run(app);
 }
index 63c3fdd5e9f7d1aa7ae020fe4d22ef19b0031ce4..5b83061ae48abc917e5bc5af5130a77a89a5d01f 100644 (file)
@@ -29,6 +29,9 @@ public:
 
        bool TimedOut() { return conn.TimedOut(); }
 
+       void SendPing();
+       void SendLogin(const std::string &);
+
 private:
        void HandlePacket(const UDPpacket &);
 
index 7b482e58726ca8215df125a121ff968db696927d..d5479909bac38cbe6085e0e0ce4e95c1849d6c60 100644 (file)
@@ -2,6 +2,7 @@
 #define BLANK_NET_PACKET_HPP_
 
 #include <cstdint>
+#include <string>
 
 
 namespace blank {
@@ -11,7 +12,8 @@ struct Packet {
        static constexpr std::uint32_t TAG = 0xFB1AB1AF;
 
        enum Type {
-               PING,
+               PING = 0,
+               LOGIN = 1,
        };
 
        struct Header {
@@ -25,6 +27,7 @@ struct Packet {
        void Tag() noexcept;
 
        std::size_t Ping() noexcept;
+       std::size_t Login(const std::string &name) noexcept;
 
 };
 
index b6a72a91b8b30e327f410e3eed793d11691320dd..6a1e9969bea693cbead97fafb5b9ec73d0cb7348 100644 (file)
@@ -33,6 +33,8 @@ private:
        void OnConnect(Connection &);
        void OnDisconnect(Connection &);
 
+       void HandleLogin(Connection &client, const UDPpacket &);
+
 private:
        UDPsocket serv_sock;
        UDPpacket serv_pack;
index 73ed73c2d006b06480bdef3c53310dd8501d4698..20b6f5c22660d8c27b474d0d8e652369dd650bcc 100644 (file)
@@ -5,6 +5,7 @@
 #include "Server.hpp"
 
 #include "../app/init.hpp"
+#include "../world/World.hpp"
 
 #include <cstring>
 #include <iostream>
@@ -42,7 +43,7 @@ Client::Client(const Config &conf, World &world)
        client_pack.data = new Uint8[sizeof(Packet)];
        client_pack.maxlen = sizeof(Packet);
        // establish connection
-       conn.SendPing(client_pack, client_sock);
+       SendPing();
 }
 
 Client::~Client() {
@@ -83,10 +84,20 @@ void Client::Update(int dt) {
        if (conn.TimedOut()) {
                cout << "connection timed out :(" << endl;
        } else if (conn.ShouldPing()) {
-               conn.SendPing(client_pack, client_sock);
+               SendPing();
        }
 }
 
+void Client::SendPing() {
+       conn.SendPing(client_pack, client_sock);
+}
+
+void Client::SendLogin(const string &name) {
+       Packet &pack = *reinterpret_cast<Packet *>(client_pack.data);
+       client_pack.len = pack.Login(name);
+       conn.Send(client_pack, client_sock);
+}
+
 
 Connection::Connection(const IPaddress &addr)
 : addr(addr)
@@ -160,6 +171,20 @@ size_t Packet::Ping() noexcept {
        return sizeof(Header);
 }
 
+size_t Packet::Login(const string &name) noexcept {
+       constexpr size_t maxname = 32;
+
+       Tag();
+       header.type = LOGIN;
+       if (name.size() < maxname) {
+               memset(payload, '\0', maxname);
+               memcpy(payload, name.c_str(), name.size());
+       } else {
+               memcpy(payload, name.c_str(), maxname);
+       }
+       return sizeof(Header) + maxname;
+}
+
 
 Server::Server(const Config &conf, World &world)
 : serv_sock(nullptr)
@@ -206,6 +231,18 @@ void Server::HandlePacket(const UDPpacket &udp_pack) {
 
        Connection &client = GetClient(udp_pack.address);
        client.FlagRecv();
+
+       switch (pack.header.type) {
+               case Packet::PING:
+                       // already done all that's supposed to do
+                       break;
+               case Packet::LOGIN:
+                       HandleLogin(client, udp_pack);
+                       break;
+               default:
+                       // just drop packets of unknown type
+                       break;
+       }
 }
 
 Connection &Server::GetClient(const IPaddress &addr) {
@@ -244,4 +281,25 @@ void Server::OnDisconnect(Connection &client) {
        cout << "connection timeout from " << client.Address() << endl;
 }
 
+
+void Server::HandleLogin(Connection &client, const UDPpacket &udp_pack) {
+       const Packet &pack = *reinterpret_cast<const Packet *>(udp_pack.data);
+       size_t maxlen = min(udp_pack.len - int(sizeof(Packet::Header)), 32);
+       string name;
+       name.reserve(maxlen);
+       for (size_t i = 0; i < maxlen && pack.payload[i] != '\0'; ++i) {
+               name.push_back(pack.payload[i]);
+       }
+       cout << "got login request from player \"" << name << '"' << endl;
+
+       Entity *player = world.AddPlayer(name);
+       if (player) {
+               // success!
+               cout << "\taccepted" << endl;
+       } else {
+               // aw no :(
+               cout << "\trejected" << endl;
+       }
+}
+
 }