From: Daniel Karbach Date: Mon, 23 Nov 2015 13:47:30 +0000 (+0100) Subject: make command output visible to player(s) X-Git-Url: https://git.localhorst.tv/?a=commitdiff_plain;h=fa3c4a14546d73ddc2671cd5cc58208839bf7173;p=blank.git make command output visible to player(s) --- diff --git a/doc/todo b/doc/todo index bbf432c..9df0ec4 100644 --- a/doc/todo +++ b/doc/todo @@ -20,7 +20,6 @@ font rendering command line more commands pls - and show me their output persistence diff --git a/src/server/ClientConnection.hpp b/src/server/ClientConnection.hpp index 57a34e4..3ab0453 100644 --- a/src/server/ClientConnection.hpp +++ b/src/server/ClientConnection.hpp @@ -2,6 +2,7 @@ #define BLANK_SERVER_CLIENTCONNECTION_HPP_ #include "ChunkTransmitter.hpp" +#include "NetworkCLIFeedback.hpp" #include "Server.hpp" #include "../app/IntervalTimer.hpp" #include "../ui/DirectInput.hpp" @@ -10,6 +11,7 @@ #include "../world/EntityState.hpp" #include "../world/Player.hpp" +#include #include #include #include @@ -39,6 +41,8 @@ public: Connection &GetConnection() noexcept { return conn; } bool Disconnected() const noexcept { return conn.Closed(); } + Server &GetServer() noexcept { return server; } + /// prepare a packet of given type template Type Prepare() const noexcept { @@ -63,6 +67,8 @@ public: bool ChunkInRange(const glm::ivec3 &) const noexcept; + std::uint16_t SendMessage(std::uint8_t type, std::uint32_t from, const std::string &msg); + private: struct SpawnStatus { // the entity in question @@ -105,6 +111,7 @@ private: Server &server; Connection conn; std::unique_ptr input; + std::unique_ptr cli_ctx; const Model *player_model; std::list spawns; unsigned int confirm_wait; diff --git a/src/server/NetworkCLIFeedback.hpp b/src/server/NetworkCLIFeedback.hpp new file mode 100644 index 0000000..25a8781 --- /dev/null +++ b/src/server/NetworkCLIFeedback.hpp @@ -0,0 +1,31 @@ +#ifndef BLANK_SERVER_NETWORKCLIFEEDBACK_HPP_ +#define BLANK_SERVER_NETWORKCLIFEEDBACK_HPP_ + +#include "../shared/CLIContext.hpp" + + +namespace blank { + +namespace server { + +class ClientConnection; + +class NetworkCLIFeedback +: public CLIContext { + +public: + NetworkCLIFeedback(Player &, ClientConnection &); + + void Error(const std::string &) override; + void Message(const std::string &) override; + void Broadcast(const std::string &) override; + +private: + ClientConnection &conn; + +}; + +} +} + +#endif diff --git a/src/server/Server.hpp b/src/server/Server.hpp index 40905c8..9b89ab1 100644 --- a/src/server/Server.hpp +++ b/src/server/Server.hpp @@ -14,6 +14,7 @@ namespace blank { class ChunkIndex; +class CLIContext; class Model; class Player; class WorldSave; @@ -52,7 +53,7 @@ public: void SetBlock(Chunk &, int, const Block &) override; /// for use by client connections when they receive a line from the player - void DispatchMessage(Player &, const std::string &); + void DispatchMessage(CLIContext &, const std::string &); /// send message to all connected clients void DistributeMessage(std::uint8_t type, std::uint32_t ref, const std::string &msg); diff --git a/src/server/net.cpp b/src/server/net.cpp index d160889..e8325da 100644 --- a/src/server/net.cpp +++ b/src/server/net.cpp @@ -439,6 +439,8 @@ void ClientConnection::AttachPlayer(Player &player) { input.reset(new DirectInput(server.GetWorld(), player, server)); PlayerEntity().Ref(); + cli_ctx.reset(new NetworkCLIFeedback(player, *this)); + old_base = PlayerChunks().Base(); ExactLocation::Coarse begin = PlayerChunks().CoordsBegin(); ExactLocation::Coarse end = PlayerChunks().CoordsEnd(); @@ -469,6 +471,7 @@ void ClientConnection::DetachPlayer() { server.GetWorldSave().Write(input->GetPlayer()); PlayerEntity().Kill(); PlayerEntity().UnRef(); + cli_ctx.reset(); input.reset(); transmitter.Abort(); chunk_queue.clear(); @@ -629,11 +632,38 @@ void ClientConnection::On(const Packet::Message &pack) { pack.ReadReferral(ref); pack.ReadMessage(msg); - if (type == 1 && HasPlayer()) { - server.DispatchMessage(input->GetPlayer(), msg); + if (type == 1 && cli_ctx) { + server.DispatchMessage(*cli_ctx, msg); } } +uint16_t ClientConnection::SendMessage(uint8_t type, uint32_t from, const string &msg) { + auto pack = Prepare(); + pack.WriteType(type); + pack.WriteReferral(from); + pack.WriteMessage(msg); + return Send(Packet::Message::GetSize(msg)); +} + + +NetworkCLIFeedback::NetworkCLIFeedback(Player &p, ClientConnection &c) +: CLIContext(p) +, conn(c) { + +} + +void NetworkCLIFeedback::Error(const string &msg) { + conn.SendMessage(0, 0, msg); +} + +void NetworkCLIFeedback::Message(const string &msg) { + conn.SendMessage(0, 0, msg); +} + +void NetworkCLIFeedback::Broadcast(const string &msg) { + conn.GetServer().DistributeMessage(0, GetPlayer().GetEntity().ID(), msg); +} + Server::Server( const Config::Network &conf, @@ -788,14 +818,14 @@ void Server::SetBlock(Chunk &chunk, int index, const Block &block) { } } -void Server::DispatchMessage(Player &player, const string &msg) { +void Server::DispatchMessage(CLIContext &ctx, const string &msg) { if (msg.empty()) { return; } if (msg[0] == '/' && msg.size() > 1 && msg[1] != '/') { - cli.Execute(player, msg.substr(1)); + cli.Execute(ctx, msg.substr(1)); } else { - DistributeMessage(1, player.GetEntity().ID(), msg); + DistributeMessage(1, ctx.GetPlayer().GetEntity().ID(), msg); } } diff --git a/src/shared/CLI.hpp b/src/shared/CLI.hpp index b601e57..ab1e3ee 100644 --- a/src/shared/CLI.hpp +++ b/src/shared/CLI.hpp @@ -7,7 +7,7 @@ namespace blank { -class Player; +class CLIContext; class TokenStreamReader; class World; @@ -16,7 +16,7 @@ class CLI { public: struct Command { virtual ~Command(); - virtual void Execute(CLI &, Player &, TokenStreamReader &) = 0; + virtual void Execute(CLI &, CLIContext &, TokenStreamReader &) = 0; }; public: @@ -25,10 +25,7 @@ public: void AddCommand(const std::string &name, Command *); - void Execute(Player &, const std::string &); - - void Message(const std::string &msg); - void Error(const std::string &msg); + void Execute(CLIContext &, const std::string &); private: World &world; diff --git a/src/shared/CLIContext.hpp b/src/shared/CLIContext.hpp new file mode 100644 index 0000000..8de432b --- /dev/null +++ b/src/shared/CLIContext.hpp @@ -0,0 +1,38 @@ +#ifndef BLANK_SHARED_CLICONTEXT_HPP_ +#define BLANK_SHARED_CLICONTEXT_HPP_ + +#include + + +namespace blank { + +class Player; + +class CLIContext { + +public: + explicit CLIContext(Player &p) + : player(p) { } + + /// get the player responsible for all this + Player &GetPlayer() { return player; } + + /// an error has happened and the player should be notified + virtual void Error(const std::string &) = 0; + + /// return to sender + /// use this for output concerning the originator of a command + virtual void Message(const std::string &) = 0; + + /// send a status message to all players + /// use this to announce stuff which may be interesting to anyone + virtual void Broadcast(const std::string &) = 0; + +private: + Player &player; + +}; + +} + +#endif diff --git a/src/shared/cli.cpp b/src/shared/cli.cpp index 6942f78..d4cf525 100644 --- a/src/shared/cli.cpp +++ b/src/shared/cli.cpp @@ -1,4 +1,5 @@ #include "CLI.hpp" +#include "CLIContext.hpp" #include "commands.hpp" #include "../io/TokenStreamReader.hpp" @@ -7,6 +8,7 @@ #include #include +#include using namespace std; @@ -29,7 +31,7 @@ void CLI::AddCommand(const string &name, Command *cmd) { commands[name] = cmd; } -void CLI::Execute(Player &player, const string &line) { +void CLI::Execute(CLIContext &ctx, const string &line) { stringstream s(line); TokenStreamReader args(s); if (!args.HasMore()) { @@ -37,44 +39,39 @@ void CLI::Execute(Player &player, const string &line) { return; } if (args.Peek().type != Token::IDENTIFIER) { - Error("I don't understand"); + ctx.Error("I don't understand"); return; } string name; args.ReadIdentifier(name); auto entry = commands.find(name); if (entry == commands.end()) { - Error(name + ": command not found"); + ctx.Error(name + ": command not found"); return; } try { - entry->second->Execute(*this, player, args); + entry->second->Execute(*this, ctx, args); } catch (exception &e) { - Error(name + ": " + e.what()); + ctx.Error(name + ": " + e.what()); } catch (...) { - Error(name + ": unknown execution error"); + ctx.Error(name + ": unknown execution error"); } } -void CLI::Message(const string &msg) { - // TODO: display message to player - cout << msg << endl; -} - -void CLI::Error(const string &msg) { - Message("CLI error: " + msg); -} - CLI::Command::~Command() { } -void TeleportCommand::Execute(CLI &cli, Player &player, TokenStreamReader &args) { +void TeleportCommand::Execute(CLI &cli, CLIContext &ctx, TokenStreamReader &args) { glm::vec3 pos(args.GetFloat(), args.GetFloat(), args.GetFloat()); - EntityState state = player.GetEntity().GetState(); + EntityState state = ctx.GetPlayer().GetEntity().GetState(); state.pos = ExactLocation(pos).Sanitize(); - player.GetEntity().SetState(state); + ctx.GetPlayer().GetEntity().SetState(state); + + stringstream msg; + msg << ctx.GetPlayer().Name() << " teleported to " << pos; + ctx.Broadcast(msg.str()); } } diff --git a/src/shared/commands.hpp b/src/shared/commands.hpp index 53ab697..1c00241 100644 --- a/src/shared/commands.hpp +++ b/src/shared/commands.hpp @@ -9,7 +9,7 @@ namespace blank { class TeleportCommand : public CLI::Command { - void Execute(CLI &, Player &, TokenStreamReader &) override; + void Execute(CLI &, CLIContext &, TokenStreamReader &) override; }; diff --git a/src/standalone/DirectCLIFeedback.hpp b/src/standalone/DirectCLIFeedback.hpp new file mode 100644 index 0000000..06f2038 --- /dev/null +++ b/src/standalone/DirectCLIFeedback.hpp @@ -0,0 +1,31 @@ +#ifndef BLANK_STANDALONE_DIRECTCLIFEEDBACK_HPP_ +#define BLANK_STANDALONE_DIRECTCLIFEEDBACK_HPP_ + +#include "../shared/CLIContext.hpp" + + +namespace blank { + +class HUD; + +namespace standalone { + +class DirectCLIFeedback +: public CLIContext { + +public: + DirectCLIFeedback(Player &, HUD &); + + void Error(const std::string &) override; + void Message(const std::string &) override; + void Broadcast(const std::string &) override; + +private: + HUD &hud; + +}; + +} +} + +#endif diff --git a/src/standalone/MasterState.cpp b/src/standalone/MasterState.cpp deleted file mode 100644 index 6d45738..0000000 --- a/src/standalone/MasterState.cpp +++ /dev/null @@ -1,234 +0,0 @@ -#include "MasterState.hpp" - -#include "../app/Config.hpp" -#include "../app/Environment.hpp" -#include "../app/init.hpp" -#include "../geometry/distance.hpp" -#include "../io/WorldSave.hpp" - -#include - - -namespace blank { -namespace standalone { - -MasterState::MasterState( - Environment &env, - Config &config, - const Generator::Config &gc, - const World::Config &wc, - const WorldSave &save -) -: config(config) -, env(env) -, res() -, sounds() -, save(save) -, world(res.block_types, wc) -, spawn_index(world.Chunks().MakeIndex(wc.spawn, 3)) -, player(*world.AddPlayer(config.player.name)) -, spawn_player(false) -, hud(env, config, player) -, manip(env.audio, sounds, player.GetEntity()) -, input(world, player, manip) -, interface(config, env.keymap, input, *this) -, generator(gc) -, chunk_loader(world.Chunks(), generator, save) -, chunk_renderer(player.GetChunks()) -, spawner(world, res.models, env.rng) -, sky(env.loader.LoadCubeMap("skybox")) -, cli(world) -, preload(env, chunk_loader, chunk_renderer) -, unload(env, world.Chunks(), save) -, chat(env, *this, *this) { - res.Load(env.loader, "default"); - if (res.models.size() < 2) { - throw std::runtime_error("need at least two models to run"); - } - res.models[0].Instantiate(player.GetEntity().GetModel()); - sounds.Load(env.loader, res.snd_index); - spawner.LimitModels(1, res.models.size()); - interface.SetInventorySlots(res.block_types.size() - 1); - generator.LoadTypes(res.block_types); - chunk_renderer.LoadTextures(env.loader, res.tex_index); - chunk_renderer.FogDensity(wc.fog_density); - if (save.Exists(player)) { - save.Read(player); - } else { - spawn_player = true; - } -} - -MasterState::~MasterState() { - world.Chunks().UnregisterIndex(spawn_index); -} - - -void MasterState::OnResume() { - if (spawn_index.MissingChunks() > 0) { - env.state.Push(&preload); - return; - } - if (spawn_player) { - // TODO: spawn - spawn_player = false; - } - hud.KeepMessages(false); - OnFocus(); -} - -void MasterState::OnPause() { - OnBlur(); -} - -void MasterState::OnFocus() { - if (config.input.mouse) { - env.window.GrabMouse(); - } - interface.Unlock(); -} - -void MasterState::OnBlur() { - env.window.ReleaseMouse(); - interface.Lock(); -} - - -void MasterState::Handle(const SDL_Event &event) { - switch (event.type) { - case SDL_KEYDOWN: - // TODO: move to interface - if (event.key.keysym.sym == SDLK_RETURN) { - chat.Clear(); - env.state.Push(&chat); - hud.KeepMessages(true); - } else if (event.key.keysym.sym == SDLK_SLASH) { - chat.Preset("/"); - env.state.Push(&chat); - hud.KeepMessages(true); - } else { - 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: - Exit(); - break; - default: - break; - } -} - -void MasterState::Update(int dt) { - spawner.Update(dt); - world.Update(dt); - if (input.BlockFocus()) { - hud.FocusBlock(input.BlockFocus().GetChunk(), input.BlockFocus().block); - } else if (input.EntityFocus()) { - hud.FocusEntity(*input.EntityFocus().entity); - } else { - hud.FocusNone(); - } - hud.Display(res.block_types[player.GetInventorySlot() + 1]); - hud.Update(dt); - chunk_loader.Update(dt); - chunk_renderer.Update(dt); - - glm::mat4 trans = player.GetEntity().Transform(player.GetEntity().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(player.GetEntity().Position()); - env.audio.Velocity(player.GetEntity().Velocity()); - env.audio.Orientation(dir, up); -} - -void MasterState::Render(Viewport &viewport) { - viewport.WorldPosition(player.GetEntity().ViewTransform(player.GetEntity().ChunkCoords())); - if (config.video.world) { - chunk_renderer.Render(viewport); - world.Render(viewport); - if (config.video.debug) { - world.RenderDebug(viewport); - } - sky.Render(viewport); - } - hud.Render(viewport); -} - - -void MasterState::SetAudio(bool b) { - config.audio.enabled = b; - if (b) { - hud.PostMessage("Audio enabled"); - } else { - hud.PostMessage("Audio disabled"); - } -} - -void MasterState::SetVideo(bool b) { - config.video.world = b; - if (b) { - hud.PostMessage("World rendering enabled"); - } else { - hud.PostMessage("World rendering disabled"); - } -} - -void MasterState::SetHUD(bool b) { - config.video.hud = b; - if (b) { - hud.PostMessage("HUD rendering enabled"); - } else { - hud.PostMessage("HUD rendering disabled"); - } -} - -void MasterState::SetDebug(bool b) { - config.video.debug = b; - if (b) { - hud.PostMessage("Debug rendering enabled"); - } else { - hud.PostMessage("Debug rendering disabled"); - } -} - -void MasterState::NextCamera() { - if (iszero(env.viewport.CameraOffset())) { - env.viewport.OffsetCamera(glm::vec3(0.0f, 0.0f, -5.0f)); - } else { - env.viewport.OffsetCamera(glm::vec3(0.0f, 0.0f, 0.0f)); - } -} - -void MasterState::Exit() { - save.Write(player); - env.state.Switch(&unload); -} - -void MasterState::OnLineSubmit(const std::string &line) { - if (line.empty()) { - return; - } - if (line[0] == '/' && line.size() > 1 && line[1] != '/') { - cli.Execute(player, line.substr(1)); - } else { - hud.PostMessage(line); - } -} - -} -} diff --git a/src/standalone/MasterState.hpp b/src/standalone/MasterState.hpp index 610f13a..04234e5 100644 --- a/src/standalone/MasterState.hpp +++ b/src/standalone/MasterState.hpp @@ -4,6 +4,7 @@ #include "../app/State.hpp" #include "../ui/ClientController.hpp" +#include "DirectCLIFeedback.hpp" #include "PreloadState.hpp" #include "UnloadState.hpp" #include "../ai/Spawner.hpp" @@ -90,6 +91,7 @@ private: SkyBox sky; CLI cli; + DirectCLIFeedback cli_ctx; PreloadState preload; UnloadState unload; diff --git a/src/standalone/PreloadState.cpp b/src/standalone/PreloadState.cpp deleted file mode 100644 index 554a458..0000000 --- a/src/standalone/PreloadState.cpp +++ /dev/null @@ -1,32 +0,0 @@ -#include "PreloadState.hpp" - -#include "../app/Environment.hpp" -#include "../world/ChunkLoader.hpp" -#include "../world/ChunkRenderer.hpp" - - -namespace blank { -namespace standalone { - -PreloadState::PreloadState(Environment &env, ChunkLoader &loader, ChunkRenderer &render) -: ProgressState(env, "Preloading chunks: %d/%d (%d%%)") -, env(env) -, loader(loader) -, render(render) -, total(loader.ToLoad()) -, per_update(64) { - -} - -void PreloadState::Update(int dt) { - loader.LoadN(per_update); - if (loader.ToLoad() <= 0) { - env.state.Pop(); - render.Update(render.MissingChunks()); - } else { - SetProgress(total - loader.ToLoad(), total); - } -} - -} -} diff --git a/src/standalone/UnloadState.cpp b/src/standalone/UnloadState.cpp deleted file mode 100644 index 0b48904..0000000 --- a/src/standalone/UnloadState.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include "UnloadState.hpp" - -#include "../app/Environment.hpp" -#include "../io/WorldSave.hpp" -#include "../world/ChunkLoader.hpp" - - -namespace blank { -namespace standalone { - -UnloadState::UnloadState( - Environment &env, - ChunkStore &chunks, - const WorldSave &save) -: ProgressState(env, "Unloading chunks: %d/%d (%d%%)") -, env(env) -, chunks(chunks) -, save(save) -, cur(chunks.begin()) -, end(chunks.end()) -, done(0) -, total(chunks.NumLoaded()) -, per_update(64) { - -} - - -void UnloadState::OnResume() { - cur = chunks.begin(); - end = chunks.end(); - done = 0; - total = chunks.NumLoaded(); -} - - -void UnloadState::Handle(const SDL_Event &) { - // ignore everything -} - -void UnloadState::Update(int dt) { - for (std::size_t i = 0; i < per_update && cur != end; ++i, ++cur, ++done) { - if (cur->ShouldUpdateSave()) { - save.Write(*cur); - } - } - if (cur == end) { - env.state.Pop(); - } else { - SetProgress(done, total); - } -} - -} -} diff --git a/src/standalone/standalone.cpp b/src/standalone/standalone.cpp new file mode 100644 index 0000000..ad6450d --- /dev/null +++ b/src/standalone/standalone.cpp @@ -0,0 +1,323 @@ +#include "DirectCLIFeedback.hpp" +#include "MasterState.hpp" +#include "PreloadState.hpp" +#include "UnloadState.hpp" + +#include "../app/Config.hpp" +#include "../app/Environment.hpp" +#include "../app/init.hpp" +#include "../geometry/distance.hpp" +#include "../io/WorldSave.hpp" +#include "../world/ChunkLoader.hpp" +#include "../world/ChunkRenderer.hpp" + +#include + + +namespace blank { +namespace standalone { + +DirectCLIFeedback::DirectCLIFeedback(Player &p, HUD &h) +: CLIContext(p) +, hud(h) { + +} + +void DirectCLIFeedback::Error(const std::string &msg) { + hud.PostMessage(msg); +} + +void DirectCLIFeedback::Message(const std::string &msg) { + hud.PostMessage(msg); +} + +void DirectCLIFeedback::Broadcast(const std::string &msg) { + hud.PostMessage(msg); +} + + +MasterState::MasterState( + Environment &env, + Config &config, + const Generator::Config &gc, + const World::Config &wc, + const WorldSave &save +) +: config(config) +, env(env) +, res() +, sounds() +, save(save) +, world(res.block_types, wc) +, spawn_index(world.Chunks().MakeIndex(wc.spawn, 3)) +, player(*world.AddPlayer(config.player.name)) +, spawn_player(false) +, hud(env, config, player) +, manip(env.audio, sounds, player.GetEntity()) +, input(world, player, manip) +, interface(config, env.keymap, input, *this) +, generator(gc) +, chunk_loader(world.Chunks(), generator, save) +, chunk_renderer(player.GetChunks()) +, spawner(world, res.models, env.rng) +, sky(env.loader.LoadCubeMap("skybox")) +, cli(world) +, cli_ctx(player, hud) +, preload(env, chunk_loader, chunk_renderer) +, unload(env, world.Chunks(), save) +, chat(env, *this, *this) { + res.Load(env.loader, "default"); + if (res.models.size() < 2) { + throw std::runtime_error("need at least two models to run"); + } + res.models[0].Instantiate(player.GetEntity().GetModel()); + sounds.Load(env.loader, res.snd_index); + spawner.LimitModels(1, res.models.size()); + interface.SetInventorySlots(res.block_types.size() - 1); + generator.LoadTypes(res.block_types); + chunk_renderer.LoadTextures(env.loader, res.tex_index); + chunk_renderer.FogDensity(wc.fog_density); + if (save.Exists(player)) { + save.Read(player); + } else { + spawn_player = true; + } +} + +MasterState::~MasterState() { + world.Chunks().UnregisterIndex(spawn_index); +} + + +void MasterState::OnResume() { + if (spawn_index.MissingChunks() > 0) { + env.state.Push(&preload); + return; + } + if (spawn_player) { + // TODO: spawn + spawn_player = false; + } + hud.KeepMessages(false); + OnFocus(); +} + +void MasterState::OnPause() { + OnBlur(); +} + +void MasterState::OnFocus() { + if (config.input.mouse) { + env.window.GrabMouse(); + } + interface.Unlock(); +} + +void MasterState::OnBlur() { + env.window.ReleaseMouse(); + interface.Lock(); +} + + +void MasterState::Handle(const SDL_Event &event) { + switch (event.type) { + case SDL_KEYDOWN: + // TODO: move to interface + if (event.key.keysym.sym == SDLK_RETURN) { + chat.Clear(); + env.state.Push(&chat); + hud.KeepMessages(true); + } else if (event.key.keysym.sym == SDLK_SLASH) { + chat.Preset("/"); + env.state.Push(&chat); + hud.KeepMessages(true); + } else { + 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: + Exit(); + break; + default: + break; + } +} + +void MasterState::Update(int dt) { + spawner.Update(dt); + world.Update(dt); + if (input.BlockFocus()) { + hud.FocusBlock(input.BlockFocus().GetChunk(), input.BlockFocus().block); + } else if (input.EntityFocus()) { + hud.FocusEntity(*input.EntityFocus().entity); + } else { + hud.FocusNone(); + } + hud.Display(res.block_types[player.GetInventorySlot() + 1]); + hud.Update(dt); + chunk_loader.Update(dt); + chunk_renderer.Update(dt); + + glm::mat4 trans = player.GetEntity().Transform(player.GetEntity().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(player.GetEntity().Position()); + env.audio.Velocity(player.GetEntity().Velocity()); + env.audio.Orientation(dir, up); +} + +void MasterState::Render(Viewport &viewport) { + viewport.WorldPosition(player.GetEntity().ViewTransform(player.GetEntity().ChunkCoords())); + if (config.video.world) { + chunk_renderer.Render(viewport); + world.Render(viewport); + if (config.video.debug) { + world.RenderDebug(viewport); + } + sky.Render(viewport); + } + hud.Render(viewport); +} + + +void MasterState::SetAudio(bool b) { + config.audio.enabled = b; + if (b) { + hud.PostMessage("Audio enabled"); + } else { + hud.PostMessage("Audio disabled"); + } +} + +void MasterState::SetVideo(bool b) { + config.video.world = b; + if (b) { + hud.PostMessage("World rendering enabled"); + } else { + hud.PostMessage("World rendering disabled"); + } +} + +void MasterState::SetHUD(bool b) { + config.video.hud = b; + if (b) { + hud.PostMessage("HUD rendering enabled"); + } else { + hud.PostMessage("HUD rendering disabled"); + } +} + +void MasterState::SetDebug(bool b) { + config.video.debug = b; + if (b) { + hud.PostMessage("Debug rendering enabled"); + } else { + hud.PostMessage("Debug rendering disabled"); + } +} + +void MasterState::NextCamera() { + if (iszero(env.viewport.CameraOffset())) { + env.viewport.OffsetCamera(glm::vec3(0.0f, 0.0f, -5.0f)); + } else { + env.viewport.OffsetCamera(glm::vec3(0.0f, 0.0f, 0.0f)); + } +} + +void MasterState::Exit() { + save.Write(player); + env.state.Switch(&unload); +} + +void MasterState::OnLineSubmit(const std::string &line) { + if (line.empty()) { + return; + } + if (line[0] == '/' && line.size() > 1 && line[1] != '/') { + cli.Execute(cli_ctx, line.substr(1)); + } else { + hud.PostMessage(line); + } +} + + +PreloadState::PreloadState(Environment &env, ChunkLoader &loader, ChunkRenderer &render) +: ProgressState(env, "Preloading chunks: %d/%d (%d%%)") +, env(env) +, loader(loader) +, render(render) +, total(loader.ToLoad()) +, per_update(64) { + +} + +void PreloadState::Update(int dt) { + loader.LoadN(per_update); + if (loader.ToLoad() <= 0) { + env.state.Pop(); + render.Update(render.MissingChunks()); + } else { + SetProgress(total - loader.ToLoad(), total); + } +} + + +UnloadState::UnloadState( + Environment &env, + ChunkStore &chunks, + const WorldSave &save) +: ProgressState(env, "Unloading chunks: %d/%d (%d%%)") +, env(env) +, chunks(chunks) +, save(save) +, cur(chunks.begin()) +, end(chunks.end()) +, done(0) +, total(chunks.NumLoaded()) +, per_update(64) { + +} + + +void UnloadState::OnResume() { + cur = chunks.begin(); + end = chunks.end(); + done = 0; + total = chunks.NumLoaded(); +} + + +void UnloadState::Handle(const SDL_Event &) { + // ignore everything +} + +void UnloadState::Update(int dt) { + for (std::size_t i = 0; i < per_update && cur != end; ++i, ++cur, ++done) { + if (cur->ShouldUpdateSave()) { + save.Write(*cur); + } + } + if (cur == end) { + env.state.Pop(); + } else { + SetProgress(done, total); + } +} + +} +}