From: Daniel Karbach Date: Wed, 2 Sep 2015 20:33:08 +0000 (+0200) Subject: client-side implementation of login packet X-Git-Url: https://git.localhorst.tv/?a=commitdiff_plain;h=104592aabdc70b21065c35fe4d092fc6cdaa1f49;p=blank.git client-side implementation of login packet 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 --- diff --git a/.gitignore b/.gitignore index eeb5465..5a84607 100644 --- a/.gitignore +++ b/.gitignore @@ -7,4 +7,5 @@ blank.test build cachegrind.out.* callgrind.out.* +client-saves saves diff --git a/Makefile b/Makefile index e6e24d9..c23e9ac 100644 --- 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 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 index 0000000..b850668 --- /dev/null +++ b/doc/building @@ -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 index 0000000..723f3cf --- /dev/null +++ b/doc/protocol @@ -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 index 0000000..846f27d --- /dev/null +++ b/doc/running @@ -0,0 +1,116 @@ +Arguments +========= + +Runtime +------- + +-n + terminate after frames + +-t + terminate after milliseconds + +if both n and t are given, terminate after n frames and +assume milliseconds pass each frame + +--asset-path + load assets from given path + default is application dir + "assets" + +--save-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 + set sample size to (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 to connect to in client mode + +--port + port number to connection to (client) or listen on (server) + +--player-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 + use (unsigned integer) as the world seed + only used for newly created worlds + default is 0 + +--world-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 index 846f27d..0000000 --- a/running +++ /dev/null @@ -1,116 +0,0 @@ -Arguments -========= - -Runtime -------- - --n - terminate after frames - --t - terminate after milliseconds - -if both n and t are given, terminate after n frames and -assume milliseconds pass each frame - ---asset-path - load assets from given path - default is application dir + "assets" - ---save-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 - set sample size to (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 to connect to in client mode - ---port - port number to connection to (client) or listen on (server) - ---player-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 - use (unsigned integer) as the world seed - only used for newly created worlds - default is 0 - ---world-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/src/app/ClientState.cpp b/src/app/ClientState.cpp index c371235..b4ed3f8 100644 --- a/src/app/ClientState.cpp +++ b/src/app/ClientState.cpp @@ -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); } } diff --git a/src/app/ClientState.hpp b/src/app/ClientState.hpp index f54e56f..8868c98 100644 --- a/src/app/ClientState.hpp +++ b/src/app/ClientState.hpp @@ -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; }; diff --git a/src/app/WorldState.cpp b/src/app/WorldState.cpp index 521f5b5..fabd332 100644 --- a/src/app/WorldState.cpp +++ b/src/app/WorldState.cpp @@ -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) { diff --git a/src/app/runtime.cpp b/src/app/runtime.cpp index f979c65..2d12863 100644 --- a/src/app/runtime.cpp +++ b/src/app/runtime.cpp @@ -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); } diff --git a/src/net/Client.hpp b/src/net/Client.hpp index 63c3fdd..5b83061 100644 --- a/src/net/Client.hpp +++ b/src/net/Client.hpp @@ -29,6 +29,9 @@ public: bool TimedOut() { return conn.TimedOut(); } + void SendPing(); + void SendLogin(const std::string &); + private: void HandlePacket(const UDPpacket &); diff --git a/src/net/Packet.hpp b/src/net/Packet.hpp index 7b482e5..d547990 100644 --- a/src/net/Packet.hpp +++ b/src/net/Packet.hpp @@ -2,6 +2,7 @@ #define BLANK_NET_PACKET_HPP_ #include +#include 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; }; diff --git a/src/net/Server.hpp b/src/net/Server.hpp index b6a72a9..6a1e996 100644 --- a/src/net/Server.hpp +++ b/src/net/Server.hpp @@ -33,6 +33,8 @@ private: void OnConnect(Connection &); void OnDisconnect(Connection &); + void HandleLogin(Connection &client, const UDPpacket &); + private: UDPsocket serv_sock; UDPpacket serv_pack; diff --git a/src/net/net.cpp b/src/net/net.cpp index 73ed73c..20b6f5c 100644 --- a/src/net/net.cpp +++ b/src/net/net.cpp @@ -5,6 +5,7 @@ #include "Server.hpp" #include "../app/init.hpp" +#include "../world/World.hpp" #include #include @@ -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(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(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; + } +} + }