command line
more commands pls
- and show me their output
persistence
#define BLANK_SERVER_CLIENTCONNECTION_HPP_
#include "ChunkTransmitter.hpp"
+#include "NetworkCLIFeedback.hpp"
#include "Server.hpp"
#include "../app/IntervalTimer.hpp"
#include "../ui/DirectInput.hpp"
#include "../world/EntityState.hpp"
#include "../world/Player.hpp"
+#include <cstdint>
#include <deque>
#include <list>
#include <memory>
Connection &GetConnection() noexcept { return conn; }
bool Disconnected() const noexcept { return conn.Closed(); }
+ Server &GetServer() noexcept { return server; }
+
/// prepare a packet of given type
template<class Type>
Type Prepare() const noexcept {
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
Server &server;
Connection conn;
std::unique_ptr<DirectInput> input;
+ std::unique_ptr<NetworkCLIFeedback> cli_ctx;
const Model *player_model;
std::list<SpawnStatus> spawns;
unsigned int confirm_wait;
--- /dev/null
+#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
namespace blank {
class ChunkIndex;
+class CLIContext;
class Model;
class Player;
class WorldSave;
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);
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();
server.GetWorldSave().Write(input->GetPlayer());
PlayerEntity().Kill();
PlayerEntity().UnRef();
+ cli_ctx.reset();
input.reset();
transmitter.Abort();
chunk_queue.clear();
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<Packet::Message>();
+ 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,
}
}
-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);
}
}
namespace blank {
-class Player;
+class CLIContext;
class TokenStreamReader;
class World;
public:
struct Command {
virtual ~Command();
- virtual void Execute(CLI &, Player &, TokenStreamReader &) = 0;
+ virtual void Execute(CLI &, CLIContext &, TokenStreamReader &) = 0;
};
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;
--- /dev/null
+#ifndef BLANK_SHARED_CLICONTEXT_HPP_
+#define BLANK_SHARED_CLICONTEXT_HPP_
+
+#include <string>
+
+
+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
#include "CLI.hpp"
+#include "CLIContext.hpp"
#include "commands.hpp"
#include "../io/TokenStreamReader.hpp"
#include <iostream>
#include <sstream>
+#include <glm/gtx/io.hpp>
using namespace std;
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()) {
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());
}
}
class TeleportCommand
: public CLI::Command {
- void Execute(CLI &, Player &, TokenStreamReader &) override;
+ void Execute(CLI &, CLIContext &, TokenStreamReader &) override;
};
--- /dev/null
+#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
+++ /dev/null
-#include "MasterState.hpp"
-
-#include "../app/Config.hpp"
-#include "../app/Environment.hpp"
-#include "../app/init.hpp"
-#include "../geometry/distance.hpp"
-#include "../io/WorldSave.hpp"
-
-#include <SDL.h>
-
-
-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);
- }
-}
-
-}
-}
#include "../app/State.hpp"
#include "../ui/ClientController.hpp"
+#include "DirectCLIFeedback.hpp"
#include "PreloadState.hpp"
#include "UnloadState.hpp"
#include "../ai/Spawner.hpp"
SkyBox sky;
CLI cli;
+ DirectCLIFeedback cli_ctx;
PreloadState preload;
UnloadState unload;
+++ /dev/null
-#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);
- }
-}
-
-}
-}
+++ /dev/null
-#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);
- }
-}
-
-}
-}
--- /dev/null
+#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 <SDL.h>
+
+
+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);
+ }
+}
+
+}
+}