void ReadNumber(int &);
void ReadNumber(unsigned long &);
void ReadString(std::string &);
+ // like ReadString, but does not require the value to be
+ // written as a string literal in source
+ void ReadRelaxedString(std::string &);
void ReadVec(glm::vec2 &);
void ReadVec(glm::vec3 &);
// the Get* functions advance to the next token
// the As* functions try to cast the current token
// if the value could not be converted, a std::runtime_error is thrown
+ // conversion to string is always possible
bool GetBool();
bool AsBool() const;
int AsInt() const;
unsigned long GetULong();
unsigned long AsULong() const;
+ const std::string &GetString();
+ const std::string &AsString() const;
private:
void SkipComments();
out = GetValue();
}
+void TokenStreamReader::ReadRelaxedString(string &out) {
+ out = GetString();
+}
+
void TokenStreamReader::ReadVec(glm::vec2 &v) {
Skip(Token::BRACKET_OPEN);
return stoul(GetValue());
}
+const string &TokenStreamReader::GetString() {
+ Next();
+ return AsString();
+}
+
+const string &TokenStreamReader::AsString() const {
+ return GetValue();
+}
+
}
void Execute(CLIContext &, const std::string &);
+ World &GetWorld() noexcept { return world; }
+
private:
World &world;
std::map<std::string, Command *> commands;
/// values when reset.
explicit CLIContext(Player *p = nullptr, Entity *e = nullptr);
+ /// get a best name for this context
+ std::string Name() const;
+
/// check if this context associates a player
- bool HasPlayer() { return effective_player; }
+ bool HasPlayer() const noexcept { return effective_player; }
/// get the player responsible for all this
/// only valid if HasPlayer() returns true
- Player &GetPlayer() { return *effective_player; }
+ Player &GetPlayer() noexcept { return *effective_player; }
+ const Player &GetPlayer() const noexcept { return *effective_player; }
/// change the effective player of this context
/// note that this will *not* change the effective entity
- void SetPlayer(Player &p) { effective_player = &p; }
+ void SetPlayer(Player &p) noexcept { effective_player = &p; }
/// check if this context associates an entity
- bool HasEntity() { return effective_entity; }
+ bool HasEntity() const noexcept { return effective_entity; }
/// get the entity on which operations should be performed
/// only valid if HasPlayer() returns true
- Entity &GetEntity() { return *effective_entity; }
+ Entity &GetEntity() noexcept { return *effective_entity; }
+ const Entity &GetEntity() const noexcept { return *effective_entity; }
/// change the effective player of this context
- void SetEntity(Entity &e) { effective_entity = &e; }
+ void SetEntity(Entity &e) noexcept { effective_entity = &e; }
/// reset effective player and entity to their original values
- void Reset() {
+ void Reset() noexcept {
effective_player = original_player;
effective_player = original_player;
}
#include "../io/TokenStreamReader.hpp"
#include "../world/Entity.hpp"
#include "../world/Player.hpp"
+#include "../world/World.hpp"
#include <iostream>
#include <sstream>
CLI::CLI(World &world)
: world(world)
, commands() {
+ AddCommand("as", new ImpersonateCommand);
AddCommand("tp", new TeleportCommand);
}
}
}
+std::string CLIContext::Name() const {
+ if (HasPlayer()) return GetPlayer().Name();
+ if (HasEntity()) return GetEntity().Name();
+ return "anonymous";
+}
+
+
+void ImpersonateCommand::Execute(CLI &cli, CLIContext &ctx, TokenStreamReader &args) {
+ if (!args.HasMore()) {
+ // no argument => reset
+ ctx.Reset();
+ ctx.Broadcast(ctx.Name() + " returned to their own self");
+ return;
+ }
+ // TODO: broadcast who (real player name) impersonates who
+ string old_name = ctx.Name();
+ string name(args.GetString());
+
+ Player *p = cli.GetWorld().FindPlayer(name);
+ if (p) {
+ ctx.SetPlayer(*p);
+ ctx.SetEntity(p->GetEntity());
+ ctx.Broadcast(old_name + " now impersonating " + p->Name());
+ return;
+ }
+
+ // not a player, try an entity
+ Entity *e = cli.GetWorld().FindEntity(name);
+ if (e) {
+ ctx.SetEntity(*e);
+ ctx.Broadcast(old_name + " now impersonating " + e->Name());
+ return;
+ }
+
+ ctx.Error("no player or entity with name " + name);
+}
+
void TeleportCommand::Execute(CLI &, CLIContext &ctx, TokenStreamReader &args) {
if (!ctx.HasEntity()) {
namespace blank {
+class ImpersonateCommand
+: public CLI::Command {
+
+ void Execute(CLI &, CLIContext &, TokenStreamReader &) override;
+
+};
+
class TeleportCommand
: public CLI::Command {
/// returs an existing entity if ID is already taken
Entity &ForceAddEntity(std::uint32_t id);
+ /// get the player with given name
+ /// returns nullptr if no player bears this name
+ Player *FindPlayer(const std::string &name);
+ /// get an entity with given name
+ /// returns nullptr if name doesn't refer to any entity
+ /// note that unlike players, entity names are not unique
+ Entity *FindEntity(const std::string &name);
+
std::list<Player> &Players() noexcept { return players; }
const std::list<Player> &Players() const noexcept { return players; }
std::list<Entity> &Entities() noexcept { return entities; }
}
+Player *World::FindPlayer(const std::string &name) {
+ for (Player &p : players) {
+ if (p.Name() == name) {
+ return &p;
+ }
+ }
+ return nullptr;
+}
+
+Entity *World::FindEntity(const std::string &name) {
+ // TODO: this may get inefficient
+ for (Entity &e : entities) {
+ if (e.Name() == name) {
+ return &e;
+ }
+ }
+ return nullptr;
+}
+
+
namespace {
struct Candidate {
"0 1 -1 2.5\n"
// strings
"\"hello\" \"\" \"\\r\\n\\t\\\"\"\n"
+ "\"world\" foo 12\n"
// vectors
"[1,0] [ 0.707, 0.707 ] // vec2\n"
"[.577,.577 ,0.577] [ 1,-2,3] // vec3\n"
"reading string \"\\r\\n\\t\\\"\"",
"\r\n\t\"", value_string, in);
+ in.ReadRelaxedString(value_string);
+ assert_read<string>(
+ "reading relaxed string \"world\"",
+ "world", value_string, in);
+
+ in.ReadRelaxedString(value_string);
+ assert_read<string>(
+ "reading relaxed string foo",
+ "foo", value_string, in);
+
+ in.ReadRelaxedString(value_string);
+ assert_read<string>(
+ "reading relaxed string 12",
+ "12", value_string, in);
+
// vectors
glm::vec2 value_vec2;