From: Daniel Karbach Date: Thu, 17 Nov 2016 08:41:32 +0000 (+0100) Subject: impersonate command X-Git-Url: http://git.localhorst.tv/?p=blank.git;a=commitdiff_plain;h=cdd865c1934eccbb1f1d0ffaf041e53f0fdd524b impersonate command --- diff --git a/src/io/TokenStreamReader.hpp b/src/io/TokenStreamReader.hpp index f1b7349..ea3a6cc 100644 --- a/src/io/TokenStreamReader.hpp +++ b/src/io/TokenStreamReader.hpp @@ -28,6 +28,9 @@ public: 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 &); @@ -42,6 +45,7 @@ public: // 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; @@ -51,6 +55,8 @@ public: int AsInt() const; unsigned long GetULong(); unsigned long AsULong() const; + const std::string &GetString(); + const std::string &AsString() const; private: void SkipComments(); diff --git a/src/io/token.cpp b/src/io/token.cpp index a2ad44c..99b25b6 100644 --- a/src/io/token.cpp +++ b/src/io/token.cpp @@ -336,6 +336,10 @@ void TokenStreamReader::ReadString(string &out) { out = GetValue(); } +void TokenStreamReader::ReadRelaxedString(string &out) { + out = GetString(); +} + void TokenStreamReader::ReadVec(glm::vec2 &v) { Skip(Token::BRACKET_OPEN); @@ -467,4 +471,13 @@ unsigned long TokenStreamReader::AsULong() const { return stoul(GetValue()); } +const string &TokenStreamReader::GetString() { + Next(); + return AsString(); +} + +const string &TokenStreamReader::AsString() const { + return GetValue(); +} + } diff --git a/src/shared/CLI.hpp b/src/shared/CLI.hpp index ab1e3ee..b0731e8 100644 --- a/src/shared/CLI.hpp +++ b/src/shared/CLI.hpp @@ -27,6 +27,8 @@ public: void Execute(CLIContext &, const std::string &); + World &GetWorld() noexcept { return world; } + private: World &world; std::map commands; diff --git a/src/shared/CLIContext.hpp b/src/shared/CLIContext.hpp index e07571f..1556b8e 100644 --- a/src/shared/CLIContext.hpp +++ b/src/shared/CLIContext.hpp @@ -19,25 +19,30 @@ public: /// 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; } diff --git a/src/shared/cli.cpp b/src/shared/cli.cpp index bb4aa6d..c144d86 100644 --- a/src/shared/cli.cpp +++ b/src/shared/cli.cpp @@ -5,6 +5,7 @@ #include "../io/TokenStreamReader.hpp" #include "../world/Entity.hpp" #include "../world/Player.hpp" +#include "../world/World.hpp" #include #include @@ -18,6 +19,7 @@ namespace blank { CLI::CLI(World &world) : world(world) , commands() { + AddCommand("as", new ImpersonateCommand); AddCommand("tp", new TeleportCommand); } @@ -73,6 +75,43 @@ CLIContext::CLIContext(Player *p, Entity *e) } } +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()) { diff --git a/src/shared/commands.hpp b/src/shared/commands.hpp index 1c00241..c151da3 100644 --- a/src/shared/commands.hpp +++ b/src/shared/commands.hpp @@ -6,6 +6,13 @@ namespace blank { +class ImpersonateCommand +: public CLI::Command { + + void Execute(CLI &, CLIContext &, TokenStreamReader &) override; + +}; + class TeleportCommand : public CLI::Command { diff --git a/src/world/World.hpp b/src/world/World.hpp index 7785ed6..771bc0c 100644 --- a/src/world/World.hpp +++ b/src/world/World.hpp @@ -96,6 +96,14 @@ public: /// 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 &Players() noexcept { return players; } const std::list &Players() const noexcept { return players; } std::list &Entities() noexcept { return entities; } diff --git a/src/world/world.cpp b/src/world/world.cpp index 2292d15..033967a 100644 --- a/src/world/world.cpp +++ b/src/world/world.cpp @@ -764,6 +764,26 @@ Entity &World::ForceAddEntity(std::uint32_t id) { } +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 { diff --git a/tst/io/TokenTest.cpp b/tst/io/TokenTest.cpp index e794a7b..16b4f4d 100644 --- a/tst/io/TokenTest.cpp +++ b/tst/io/TokenTest.cpp @@ -202,6 +202,7 @@ void TokenTest::testReader() { "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" @@ -308,6 +309,21 @@ void TokenTest::testReader() { "reading string \"\\r\\n\\t\\\"\"", "\r\n\t\"", value_string, in); + in.ReadRelaxedString(value_string); + assert_read( + "reading relaxed string \"world\"", + "world", value_string, in); + + in.ReadRelaxedString(value_string); + assert_read( + "reading relaxed string foo", + "foo", value_string, in); + + in.ReadRelaxedString(value_string); + assert_read( + "reading relaxed string 12", + "12", value_string, in); + // vectors glm::vec2 value_vec2;