├── .gitignore ├── .gitmodules ├── LICENSE ├── Makefile ├── README.md ├── mcbot.sln └── mcbot ├── Actor.cpp ├── Actor.h ├── BotUpdate.cpp ├── BotUpdate.h ├── Collision.cpp ├── Collision.h ├── Component.h ├── Decision.h ├── GameClient.cpp ├── GameClient.h ├── Math.h ├── Pathfinder.cpp ├── Pathfinder.h ├── Pathing.cpp ├── Pathing.h ├── PlayerList.cpp ├── PlayerList.h ├── Steering.cpp ├── Steering.h ├── Utility.cpp ├── Utility.h ├── WorldGraph.cpp ├── WorldGraph.h ├── actions └── WanderAction.h ├── components ├── EffectComponent.cpp ├── EffectComponent.h ├── JumpComponent.cpp ├── JumpComponent.h ├── PhysicsComponent.cpp ├── PhysicsComponent.h ├── SpeedComponent.cpp ├── SpeedComponent.h ├── SynchronizationComponent.cpp ├── SynchronizationComponent.h ├── TargetingComponent.cpp └── TargetingComponent.h ├── main.cpp ├── mcbot.vcxproj ├── mcbot.vcxproj.filters └── mcbot.vcxproj.user /.gitignore: -------------------------------------------------------------------------------- 1 | Debug 2 | Release 3 | Debug-Fast 4 | *.opensdf 5 | *.sdf 6 | *.suo 7 | *.vsp 8 | *.psess 9 | bin 10 | *.so 11 | *.o 12 | .vs 13 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "lib/mclib"] 2 | path = lib/mclib 3 | url = https://github.com/plushmonkey/mclib.git 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2017 plushmonkey (plushmonkey.ss@gmail.com) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CXXFLAGS=-std=c++14 -Wall -fPIC -O2 -I/usr/include/jsoncpp -Ilib/mclib/mclib/include 2 | LIBS=-L. -ljsoncpp -lmc 3 | CXX=clang++ 4 | BIN=bin 5 | 6 | SRC=$(shell find mcbot -type f -name *.cpp) 7 | 8 | bot: $(SRC:.cpp=.o) libmc.so 9 | $(CXX) -o $(BIN)/$@ $(CXXFLAGS) $^ -Wl,-rpath,. $(LIBS) 10 | -mv -f libmc.so bin/libmc.so 11 | 12 | libmc.so: directory 13 | $(MAKE) -C lib/mclib 14 | cp lib/mclib/libmc.so libmc.so 15 | 16 | directory: 17 | -mkdir $(BIN) 18 | 19 | clean: 20 | -rm -f mcbot/*.o 21 | -rm -f mcbot/*/*.o 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # mcbot 2 | A basic network bot for Minecraft 1.11.0. 3 | 4 | # Examples 5 | ![example](https://thumbs.gfycat.com/AnguishedTautKid-size_restricted.gif "Basic pathfinding") 6 | -------------------------------------------------------------------------------- /mcbot.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.26403.0 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mcbot", "mcbot\mcbot.vcxproj", "{70A1A00B-BE7F-4EF2-8D5F-90A07D3F192A}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Win32 = Debug|Win32 11 | Debug-Fast|Win32 = Debug-Fast|Win32 12 | Release|Win32 = Release|Win32 13 | EndGlobalSection 14 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 15 | {70A1A00B-BE7F-4EF2-8D5F-90A07D3F192A}.Debug|Win32.ActiveCfg = Debug|Win32 16 | {70A1A00B-BE7F-4EF2-8D5F-90A07D3F192A}.Debug|Win32.Build.0 = Debug|Win32 17 | {70A1A00B-BE7F-4EF2-8D5F-90A07D3F192A}.Debug-Fast|Win32.ActiveCfg = Debug-Fast|Win32 18 | {70A1A00B-BE7F-4EF2-8D5F-90A07D3F192A}.Debug-Fast|Win32.Build.0 = Debug-Fast|Win32 19 | {70A1A00B-BE7F-4EF2-8D5F-90A07D3F192A}.Release|Win32.ActiveCfg = Release|Win32 20 | {70A1A00B-BE7F-4EF2-8D5F-90A07D3F192A}.Release|Win32.Build.0 = Release|Win32 21 | EndGlobalSection 22 | GlobalSection(SolutionProperties) = preSolution 23 | HideSolutionNode = FALSE 24 | EndGlobalSection 25 | EndGlobal 26 | -------------------------------------------------------------------------------- /mcbot/Actor.cpp: -------------------------------------------------------------------------------- 1 | #include "Actor.h" 2 | 3 | void Actor::Update(double dt) { 4 | for (auto& entry : m_Components) 5 | entry.second->Update(dt); 6 | } 7 | 8 | void Actor::AddComponent(std::unique_ptr component) { 9 | m_Components.insert(std::make_pair(component->GetId(), std::move(component))); 10 | } 11 | 12 | void Actor::RemoveComponent(ComponentId id) { 13 | auto iter = m_Components.find(id); 14 | m_Components.erase(iter); 15 | } 16 | -------------------------------------------------------------------------------- /mcbot/Actor.h: -------------------------------------------------------------------------------- 1 | #ifndef MCBOT_ACTOR_H_ 2 | #define MCBOT_ACTOR_H_ 3 | 4 | #include "Component.h" 5 | 6 | #include 7 | 8 | class Actor { 9 | public: 10 | typedef std::map> ActorComponents; 11 | 12 | private: 13 | ActorComponents m_Components; 14 | 15 | public: 16 | Actor() = default; 17 | virtual ~Actor() { } 18 | 19 | Actor(const Actor& rhs) = delete; 20 | Actor& operator=(const Actor& rhs) = delete; 21 | Actor(Actor&& rhs) = delete; 22 | Actor& operator=(Actor&& rhs) = delete; 23 | 24 | void Update(double dt); 25 | 26 | template 27 | ComponentType* GetComponent(ComponentId id) 28 | { 29 | ActorComponents::iterator find = m_Components.find(id); 30 | 31 | if (find == m_Components.end()) return nullptr; 32 | 33 | return dynamic_cast(find->second.get()); 34 | } 35 | 36 | template 37 | ComponentType* GetComponent(const char* name) { 38 | ComponentId id = Component::GetIdFromName(name); 39 | return GetComponent(id); 40 | } 41 | 42 | const ActorComponents& GetComponents() { return m_Components; } 43 | void AddComponent(std::unique_ptr component); 44 | void RemoveComponent(ComponentId id); 45 | }; 46 | 47 | #endif 48 | -------------------------------------------------------------------------------- /mcbot/BotUpdate.cpp: -------------------------------------------------------------------------------- 1 | #include "BotUpdate.h" 2 | #include "Utility.h" 3 | #include "components/SynchronizationComponent.h" 4 | #include "components/SpeedComponent.h" 5 | #include "components/PhysicsComponent.h" 6 | 7 | using mc::Vector3d; 8 | 9 | BotUpdate::BotUpdate(GameClient* client) 10 | : m_Client(client), 11 | m_Players(client) 12 | { 13 | client->RegisterListener(this); 14 | 15 | auto sync = std::make_unique(client->GetDispatcher(), client->GetConnection(), client->GetPlayerManager()); 16 | AddComponent(std::move(sync)); 17 | 18 | auto physics = std::make_unique(client->GetWorld(), mc::AABB(Vector3d(-0.3, 0.0, -0.3), Vector3d(0.3, 1.8, 0.3))); 19 | physics->SetMaxAcceleration(100.0f); 20 | physics->SetMaxRotation(3.14159 * 8); 21 | AddComponent(std::move(physics)); 22 | 23 | auto speed = std::make_unique(client->GetConnection(), client->GetWorld()); 24 | speed->SetMovementType(SpeedComponent::Movement::Normal); 25 | AddComponent(std::move(speed)); 26 | 27 | m_StartupTime = util::GetTime(); 28 | m_Pathfinder = std::make_unique(m_Client); 29 | } 30 | 31 | BotUpdate::~BotUpdate() { 32 | m_Client->RemoveComponent(Component::GetIdFromName(SynchronizationComponent::name)); 33 | m_Client->RemoveComponent(Component::GetIdFromName(SpeedComponent::name)); 34 | m_Client->RemoveComponent(Component::GetIdFromName(PhysicsComponent::name)); 35 | m_Client->UnregisterListener(this); 36 | } 37 | 38 | void BotUpdate::AddComponent(std::unique_ptr component) { 39 | component->SetOwner(m_Client); 40 | m_Client->AddComponent(std::move(component)); 41 | } 42 | 43 | void BotUpdate::SetDecisionTree(DecisionTreeNodePtr tree) noexcept { 44 | m_DecisionTree = tree; 45 | } 46 | 47 | void BotUpdate::OnTick() { 48 | auto sync = GetActorComponent(m_Client, SynchronizationComponent); 49 | if (!sync || !sync->HasSpawned()) return; 50 | 51 | if (!m_DecisionTree) { 52 | std::cerr << "No decision tree" << std::endl; 53 | return; 54 | } 55 | 56 | DecisionAction* action = m_DecisionTree->Decide(); 57 | if (action) 58 | action->Act(); 59 | 60 | auto physics = GetActorComponent(m_Client, PhysicsComponent); 61 | if (!physics) return; 62 | 63 | m_Pathfinder->Update(); 64 | physics->Integrate(50.0 / 1000.0); 65 | } 66 | -------------------------------------------------------------------------------- /mcbot/BotUpdate.h: -------------------------------------------------------------------------------- 1 | #ifndef MCBOT_BOTUPDATE_H_ 2 | #define MCBOT_BOTUPDATE_H_ 3 | 4 | #include "GameClient.h" 5 | #include "PlayerList.h" 6 | #include "Pathfinder.h" 7 | #include "Decision.h" 8 | #include "Component.h" 9 | 10 | class BotUpdate : public mc::core::ClientListener { 11 | private: 12 | GameClient* m_Client; 13 | PlayerList m_Players; 14 | s64 m_StartupTime; 15 | 16 | std::unique_ptr m_Pathfinder; 17 | 18 | DecisionTreeNodePtr m_DecisionTree; 19 | 20 | public: 21 | BotUpdate(GameClient* client); 22 | ~BotUpdate(); 23 | 24 | BotUpdate(const BotUpdate& rhs) = delete; 25 | BotUpdate& operator=(const BotUpdate& rhs) = delete; 26 | BotUpdate(BotUpdate&& rhs) = delete; 27 | BotUpdate& operator=(BotUpdate&& rhs) = delete; 28 | 29 | GameClient* GetClient() noexcept { return m_Client; } 30 | 31 | void OnTick() override; 32 | 33 | void AddComponent(std::unique_ptr component); 34 | void SetDecisionTree(DecisionTreeNodePtr tree) noexcept; 35 | }; 36 | 37 | #endif 38 | -------------------------------------------------------------------------------- /mcbot/Collision.cpp: -------------------------------------------------------------------------------- 1 | #include "Collision.h" 2 | #include "components/PhysicsComponent.h" 3 | #include "Math.h" 4 | 5 | #include 6 | 7 | using mc::Vector3d; 8 | using mc::Vector3i; 9 | using mc::AABB; 10 | using mc::Ray; 11 | 12 | template 13 | inline T Sign(T val) { 14 | return std::signbit(val) ? static_cast(-1) : static_cast(1); 15 | } 16 | 17 | inline Vector3d BasisAxis(int basisIndex) { 18 | static const Vector3d axes[3] = { Vector3d(1, 0, 0), Vector3d(0, 1, 0), Vector3d(0, 0, 1) }; 19 | return axes[basisIndex]; 20 | } 21 | 22 | Vector3d GetClosestFaceNormal(const Vector3d& pos, AABB bounds) { 23 | Vector3d center = bounds.min + (bounds.max - bounds.min) / 2; 24 | Vector3d dim = bounds.max - bounds.min; 25 | Vector3d offset = pos - center; 26 | 27 | double minDist = std::numeric_limits::max(); 28 | Vector3d normal; 29 | 30 | for (int i = 0; i < 3; ++i) { 31 | double dist = dim[i] - std::abs(offset[i]); 32 | if (dist < minDist) { 33 | minDist = dist; 34 | normal = BasisAxis(i) * Sign(offset[i]); 35 | } 36 | } 37 | 38 | return normal; 39 | } 40 | 41 | 42 | bool CollisionDetector::DetectCollision(Vector3d from, Vector3d rayVector, Collision* collision) const { 43 | static const std::vector directions = { 44 | Vector3d(0, 0, 0), Vector3d(1, 0, 0), Vector3d(-1, 0, 0), Vector3d(0, 1, 0), Vector3d(0, -1, 0), Vector3d(0, 0, 1), Vector3d(0, 0, -1) 45 | }; 46 | 47 | Vector3d direction = Vector3Normalize(rayVector); 48 | double length = rayVector.Length(); 49 | 50 | Ray ray(from, direction); 51 | 52 | if (collision) 53 | *collision = Collision(); 54 | 55 | for (double i = 0; i < length; ++i) { 56 | Vector3d position = from + direction * i; 57 | 58 | // Look for collisions in any blocks surrounding the ray 59 | for (Vector3d checkDirection : directions) { 60 | Vector3d checkPos = position + checkDirection; 61 | mc::block::BlockPtr block = m_World->GetBlock(checkPos).GetBlock(); 62 | 63 | if (block && block->IsSolid()) { 64 | AABB bounds = block->GetBoundingBox(checkPos); 65 | double distance; 66 | 67 | if (bounds.Intersects(ray, &distance)) { 68 | if (distance < 0 || distance > length) continue; 69 | 70 | Vector3d collisionHit = from + direction * distance; 71 | Vector3d normal = GetClosestFaceNormal(collisionHit, bounds); 72 | 73 | if (collision) 74 | *collision = Collision(collisionHit, normal); 75 | 76 | return true; 77 | } 78 | } 79 | } 80 | } 81 | 82 | return false; 83 | } 84 | 85 | std::vector CollisionDetector::GetSurroundingLocations(AABB bounds) { 86 | std::vector locs; 87 | 88 | s32 radius = 2; 89 | for (s32 y = (s32)bounds.min.y - radius; y < (s32)bounds.max.y + radius; ++y) { 90 | for (s32 z = (s32)bounds.min.z - radius; z < (s32)bounds.max.z + radius; ++z) { 91 | for (s32 x = (s32)bounds.min.x - radius; x < (s32)bounds.max.x + radius; ++x) { 92 | locs.emplace_back(x, y, z); 93 | } 94 | } 95 | } 96 | 97 | return locs; 98 | } 99 | 100 | void CollisionDetector::ResolveCollisions(PhysicsComponent* physics, double dt) { 101 | const s32 MaxIterations = 10; 102 | bool collisions = true; 103 | 104 | Vector3d velocity = physics->GetVelocity(); 105 | 106 | for (s32 iteration = 0; iteration < MaxIterations && collisions; ++iteration) { 107 | Vector3d position = physics->GetPosition(); 108 | 109 | collisions = false; 110 | 111 | for (std::size_t i = 0; i < 3; ++i) { 112 | AABB playerBounds = physics->GetBoundingBox(); 113 | 114 | if (iteration == 0) 115 | position[i] += velocity[i] * dt; 116 | 117 | playerBounds.min += position; 118 | playerBounds.max += position; 119 | 120 | std::vector surrounding = GetSurroundingLocations(playerBounds); 121 | 122 | for (Vector3i checkPos : surrounding) { 123 | mc::block::BlockPtr block = m_World->GetBlock(checkPos).GetBlock(); 124 | 125 | if (block && block->IsSolid()) { 126 | AABB blockBounds = block->GetBoundingBox(checkPos); 127 | 128 | if (playerBounds.Intersects(blockBounds)) { 129 | velocity[i] = 0; 130 | 131 | double penetrationDepth; 132 | 133 | if (playerBounds.min[i] < blockBounds.min[i]) { 134 | penetrationDepth = playerBounds.max[i] - blockBounds.min[i]; 135 | } else { 136 | penetrationDepth = playerBounds.min[i] - blockBounds.max[i]; 137 | } 138 | 139 | position[i] -= penetrationDepth; 140 | collisions = true; 141 | break; 142 | } 143 | } 144 | } 145 | } 146 | 147 | physics->SetPosition(position); 148 | } 149 | physics->SetVelocity(velocity); 150 | } 151 | -------------------------------------------------------------------------------- /mcbot/Collision.h: -------------------------------------------------------------------------------- 1 | #ifndef MCBOT_COLLISION_H_ 2 | #define MCBOT_COLLISION_H_ 3 | 4 | #include 5 | 6 | class PhysicsComponent; 7 | 8 | class Collision { 9 | private: 10 | mc::Vector3d m_Position; 11 | mc::Vector3d m_Normal; 12 | 13 | public: 14 | Collision() noexcept { } 15 | Collision(mc::Vector3d position, mc::Vector3d normal) noexcept : m_Position(position), m_Normal(normal) { } 16 | 17 | mc::Vector3d GetPosition() const noexcept { return m_Position; } 18 | mc::Vector3d GetNormal() const noexcept { return m_Normal; } 19 | }; 20 | 21 | class CollisionDetector { 22 | private: 23 | mc::world::World* m_World; 24 | 25 | std::vector GetSurroundingLocations(mc::AABB bounds); 26 | 27 | public: 28 | CollisionDetector(mc::world::World* world) noexcept : m_World(world) { } 29 | 30 | bool DetectCollision(mc::Vector3d from, mc::Vector3d rayVector, Collision* collision) const; 31 | 32 | void ResolveCollisions(PhysicsComponent* physics, double dt); 33 | }; 34 | 35 | #endif 36 | -------------------------------------------------------------------------------- /mcbot/Component.h: -------------------------------------------------------------------------------- 1 | #ifndef MCBOT_COMPONENT_H_ 2 | #define MCBOT_COMPONENT_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | typedef std::size_t ComponentId; 9 | 10 | class Actor; 11 | 12 | #define GetActorComponent(actor, type) actor->GetComponent(type::name) 13 | 14 | class Component { 15 | protected: 16 | Actor* m_Owner; 17 | 18 | public: 19 | Component() = default; 20 | virtual ~Component() { } 21 | 22 | Component(const Component& rhs) = delete; 23 | Component& operator=(const Component& rhs) = delete; 24 | Component(Component&& rhs) = delete; 25 | Component& operator=(Component&& rhs) = delete; 26 | 27 | void SetOwner(Actor* owner) noexcept { m_Owner = owner; } 28 | 29 | virtual void Update(double dt) { } 30 | ComponentId GetId() const { return GetIdFromName(GetName()); } 31 | virtual const char* GetName() const = 0; 32 | 33 | static ComponentId GetIdFromName(const char* name) { 34 | return std::hash()(name); 35 | } 36 | }; 37 | 38 | #endif 39 | -------------------------------------------------------------------------------- /mcbot/Decision.h: -------------------------------------------------------------------------------- 1 | #ifndef MCBOT_DECISION_H_ 2 | #define MCBOT_DECISION_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | class DecisionAction; 9 | 10 | class DecisionTreeNode { 11 | public: 12 | virtual DecisionAction* Decide() = 0; 13 | }; 14 | typedef std::shared_ptr DecisionTreeNodePtr; 15 | 16 | class DecisionAction : public DecisionTreeNode { 17 | public: 18 | DecisionAction* Decide() { 19 | return this; 20 | } 21 | 22 | virtual void Act() = 0; 23 | }; 24 | 25 | class Decision : public DecisionTreeNode { 26 | protected: 27 | DecisionTreeNodePtr m_TrueNode; 28 | DecisionTreeNodePtr m_FalseNode; 29 | 30 | public: 31 | Decision(DecisionTreeNodePtr trueNode, DecisionTreeNodePtr falseNode) 32 | : m_TrueNode(trueNode), m_FalseNode(falseNode) 33 | { 34 | 35 | } 36 | 37 | virtual DecisionTreeNodePtr GetBranch() = 0; 38 | 39 | DecisionAction* Decide() { 40 | return GetBranch()->Decide(); 41 | } 42 | }; 43 | 44 | template 45 | class RangeDecision : public Decision { 46 | public: 47 | typedef std::function TestFunction; 48 | private: 49 | TestFunction m_TestFunction; 50 | T m_MinValue; 51 | T m_MaxValue; 52 | 53 | public: 54 | RangeDecision(DecisionTreeNodePtr trueNode, DecisionTreeNodePtr falseNode, TestFunction testFunction, T minValue, T maxValue) 55 | : Decision(trueNode, falseNode), m_TestFunction(testFunction), m_MinValue(minValue), m_MaxValue(maxValue) 56 | { 57 | 58 | } 59 | 60 | DecisionTreeNodePtr GetBranch() override { 61 | T testValue = m_TestFunction(); 62 | 63 | if (m_MinValue <= testValue && testValue <= m_MaxValue) 64 | return m_TrueNode; 65 | return m_FalseNode; 66 | } 67 | 68 | DecisionAction* Decide() override { 69 | return GetBranch()->Decide(); 70 | } 71 | }; 72 | 73 | class BooleanDecision : public Decision { 74 | public: 75 | typedef std::function TestFunction; 76 | private: 77 | TestFunction m_TestFunction; 78 | 79 | public: 80 | BooleanDecision(DecisionTreeNodePtr trueNode, DecisionTreeNodePtr falseNode, TestFunction testFunction) 81 | : Decision(trueNode, falseNode), m_TestFunction(testFunction) 82 | { 83 | 84 | } 85 | 86 | DecisionTreeNodePtr GetBranch() override { 87 | if (m_TestFunction()) return m_TrueNode; 88 | return m_FalseNode; 89 | } 90 | 91 | DecisionAction* Decide() override { 92 | return GetBranch()->Decide(); 93 | } 94 | }; 95 | 96 | #endif 97 | -------------------------------------------------------------------------------- /mcbot/GameClient.cpp: -------------------------------------------------------------------------------- 1 | #include "GameClient.h" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "Utility.h" 8 | #include "WorldGraph.h" 9 | 10 | GameClient::GameClient(mc::protocol::Version version) 11 | : m_Dispatcher(), 12 | m_Connection(&m_Dispatcher, version), 13 | m_EntityManager(&m_Dispatcher), 14 | m_PlayerManager(&m_Dispatcher, &m_EntityManager), 15 | m_World(&m_Dispatcher), 16 | m_Inventories(&m_Dispatcher, &m_Connection), 17 | m_Graph(new WorldGraph(this)), 18 | m_Connected(false), 19 | m_Hotbar(&m_Dispatcher, &m_Connection, &m_Inventories) 20 | { 21 | m_Connection.RegisterListener(this); 22 | } 23 | 24 | GameClient::~GameClient() { 25 | m_Connection.UnregisterListener(this); 26 | } 27 | 28 | void GameClient::OnSocketStateChange(mc::network::Socket::Status newState) { 29 | m_Connected = (newState == mc::network::Socket::Status::Connected); 30 | std::cout << "Connected: " << std::boolalpha << m_Connected << std::endl; 31 | } 32 | 33 | bool GameClient::login(std::string host, unsigned short port, std::string name, std::string password) { 34 | if (!m_Connection.Connect(host, port)) { 35 | return false; 36 | } 37 | 38 | m_Connection.Login(name, password); 39 | return true; 40 | } 41 | 42 | void GameClient::run() { 43 | const s64 TickDelay = 1000/20; 44 | const s64 MaximumUpdates = 3; 45 | s64 lastUpdate = util::GetTime(); 46 | s64 startupTime = util::GetTime(); 47 | 48 | while (m_Connected) { 49 | try { 50 | m_Connection.CreatePacket(); 51 | } catch (std::exception&) { 52 | 53 | } 54 | 55 | s64 time = util::GetTime(); 56 | 57 | if (time < lastUpdate + TickDelay) { 58 | std::this_thread::sleep_for(std::chrono::milliseconds(1)); 59 | continue; 60 | } 61 | 62 | s64 updateCount = (time - lastUpdate) / TickDelay; 63 | 64 | lastUpdate += TickDelay * updateCount; 65 | 66 | for (s64 i = 0; i < std::min(updateCount, MaximumUpdates); ++i) { 67 | Update(50.0 / 1000.0); 68 | NotifyListeners(&mc::core::ClientListener::OnTick); 69 | } 70 | 71 | #ifdef _DEBUG 72 | if (util::GetTime() < startupTime + 10000) continue; 73 | #endif 74 | 75 | m_Graph->ProcessQueue(); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /mcbot/GameClient.h: -------------------------------------------------------------------------------- 1 | #ifndef GAME_CLIENT_H_ 2 | #define GAME_CLIENT_H_ 3 | 4 | #include 5 | #include 6 | #include "Steering.h" 7 | #include "Actor.h" 8 | 9 | #include 10 | 11 | class WorldGraph; 12 | 13 | class GameClient : public mc::util::ObserverSubject, public mc::core::ConnectionListener, public Actor { 14 | private: 15 | mc::protocol::packets::PacketDispatcher m_Dispatcher; 16 | mc::core::Connection m_Connection; 17 | mc::entity::EntityManager m_EntityManager; 18 | mc::core::PlayerManager m_PlayerManager; 19 | mc::world::World m_World; 20 | mc::inventory::InventoryManager m_Inventories; 21 | mc::inventory::Hotbar m_Hotbar; 22 | std::unique_ptr m_Graph; 23 | 24 | bool m_Connected; 25 | 26 | public: 27 | GameClient(mc::protocol::Version version = mc::protocol::Version::Minecraft_1_11_2); 28 | ~GameClient(); 29 | 30 | void OnSocketStateChange(mc::network::Socket::Status newState); 31 | bool login(std::string host, unsigned short port, std::string name, std::string password); 32 | 33 | void run(); 34 | 35 | mc::protocol::packets::PacketDispatcher* GetDispatcher() { return &m_Dispatcher; } 36 | mc::core::Connection* GetConnection() { return &m_Connection; } 37 | mc::entity::EntityManager* GetEntityManager() { return &m_EntityManager; } 38 | mc::core::PlayerManager* GetPlayerManager() { return &m_PlayerManager; } 39 | mc::world::World* GetWorld() { return &m_World; } 40 | mc::inventory::InventoryManager* GetInventories() { return &m_Inventories; } 41 | 42 | mc::inventory::Inventory* GetInventory() { return m_Inventories.GetInventory(0); } 43 | mc::inventory::Hotbar& GetHotbar() { return m_Hotbar; } 44 | WorldGraph* GetGraph() { return m_Graph.get(); } 45 | }; 46 | 47 | #endif 48 | -------------------------------------------------------------------------------- /mcbot/Math.h: -------------------------------------------------------------------------------- 1 | #ifndef MCBOT_MATH_H_ 2 | #define MCBOT_MATH_H_ 3 | 4 | #include 5 | #include 6 | 7 | #ifndef M_PI 8 | #define M_PI 3.14159265358979323846 9 | #endif 10 | 11 | #ifndef M_TAU 12 | #define M_TAU M_PI * 2 13 | #endif 14 | 15 | inline mc::Vector3d Hadamard(mc::Vector3d first, mc::Vector3d second) { 16 | return mc::Vector3d(first.x * second.x, first.y * second.y, first.z * second.z); 17 | } 18 | 19 | inline double WrapMax(double x, double max) { 20 | return fmod(max + fmod(x, max), max); 21 | } 22 | 23 | inline double WrapMinMax(double x, double min, double max) { 24 | return min + WrapMax(x - min, max - min); 25 | } 26 | 27 | template 28 | double RandomBinomial(Engine engine) { 29 | std::uniform_real_distribution dist(0, 1); 30 | 31 | return dist(engine) - dist(engine); 32 | } 33 | 34 | inline double WrapToPi(double rads) { 35 | return WrapMinMax(rads, -M_PI, M_PI); 36 | } 37 | 38 | #endif 39 | -------------------------------------------------------------------------------- /mcbot/Pathfinder.cpp: -------------------------------------------------------------------------------- 1 | #include "Pathfinder.h" 2 | #include "WorldGraph.h" 3 | #include "components/PhysicsComponent.h" 4 | #include "components/SpeedComponent.h" 5 | #include "components/TargetingComponent.h" 6 | #include "Utility.h" 7 | 8 | #include 9 | 10 | using mc::Vector3i; 11 | using mc::Vector3d; 12 | 13 | Vector3i GetGroundLevel(mc::world::World* world, Vector3i pos) { 14 | s32 y; 15 | for (y = (s32)pos.y; y >= 0; --y) { 16 | mc::block::BlockPtr block = world->GetBlock(Vector3i(pos.x, y, pos.z)).GetBlock(); 17 | 18 | if (block && block->IsSolid()) break; 19 | } 20 | 21 | return Vector3i(pos.x, y + 1, pos.z); 22 | } 23 | 24 | CastResult RayCast(mc::world::World* world, WorldGraph* graph, const Vector3d& from, Vector3d direction, std::size_t length) { 25 | CastResult result; 26 | 27 | std::vector hit(length); 28 | 29 | direction.Normalize(); 30 | result.length = length; 31 | result.full = true; 32 | 33 | for (std::size_t i = 0; i < length; ++i) { 34 | Vector3i check = ToVector3i(from + (direction * i)); 35 | mc::block::BlockPtr block = world->GetBlock(check).GetBlock(); 36 | bool walkable = graph->IsWalkable(check); 37 | if (walkable && block && !block->IsSolid()) { 38 | hit.push_back(check); 39 | } else { 40 | result.full = false; 41 | result.length = i; 42 | break; 43 | } 44 | } 45 | 46 | result.hit = hit; 47 | 48 | return result; 49 | } 50 | 51 | 52 | bool Pathfinder::IsNearBlocks(Vector3d pos) { 53 | mc::world::World* world = m_Client->GetWorld(); 54 | static const std::vector directions = { 55 | Vector3d(-1, 0, 0), Vector3d(1, 0, 0), Vector3d(0, 0, -1), Vector3d(0, 0, 1) 56 | }; 57 | 58 | for (Vector3d dir : directions) { 59 | mc::block::BlockPtr block = world->GetBlock(pos + dir).GetBlock(); 60 | 61 | if (!block || block->IsSolid()) return true; 62 | } 63 | return false; 64 | } 65 | 66 | void Pathfinder::SmoothPath() { 67 | if (!m_Plan) return; 68 | 69 | std::vector& nodes = m_Plan->GetNodes(); 70 | if (nodes.size() <= 2) return; 71 | 72 | std::vector output; 73 | output.push_back(nodes[0]); 74 | std::size_t index = 2; 75 | 76 | while (index < nodes.size() - 1) { 77 | Vector3d from = ToVector3d(nodes[index]->GetPosition()) + Vector3d(0.5, 0, 0.5); 78 | Vector3d to = ToVector3d(output.back()->GetPosition()) + Vector3d(0.5, 0, 0.5); 79 | Vector3d direction = to - from; 80 | std::size_t length = (std::size_t)direction.Length() + 1; 81 | direction.Normalize(); 82 | 83 | bool hasNext = index < nodes.size() - 2; 84 | 85 | if (hasNext) { 86 | Vector3d next = ToVector3d(nodes[index + 1]->GetPosition()) + Vector3d(0.5, 0, 0.5); 87 | if (from == next + Vector3d(0, 1, 0)) { 88 | // Skip this one entirely because the block directly below is in the path and the bot will fall on it. 89 | // This fixes the bug where the bot has to jump in the air to reach the block after touching ground. 90 | index += 2; 91 | continue; 92 | } 93 | } 94 | 95 | if (from.y != to.y || IsNearBlocks(from) || IsNearBlocks(to)) { 96 | output.push_back(nodes[index - 1]); 97 | } else { 98 | CastResult result = RayCast(m_Client->GetWorld(), m_Client->GetGraph(), from, direction, length); 99 | if (!result.full) { 100 | output.push_back(nodes[index - 1]); 101 | } else if (hasNext) { 102 | Vector3d nextPos = ToVector3d(nodes[index + 1]->GetPosition()) + Vector3d(0.5, 0, 0.5); 103 | Vector3d nextDir = Vector3Normalize(nextPos - from); 104 | 105 | // Check to see if there's any falls in the path to the next node. 106 | // Add both of them to the output list if there is. 107 | // This makes it so it doesn't smooth around corners that are possible to fall off of. 108 | for (std::size_t i = 0; i < length * 10; ++i) { 109 | Vector3d current = from + (nextDir / 10) * i; 110 | Vector3d below = current - Vector3d(0, 1, 0); 111 | 112 | auto blockState = m_Client->GetWorld()->GetBlock(below); 113 | auto block = blockState.GetBlock(); 114 | 115 | if (block == nullptr || !block->IsSolid()) { 116 | output.push_back(nodes[index - 1]); 117 | output.push_back(nodes[index]); 118 | break; 119 | } 120 | } 121 | } 122 | } 123 | 124 | ++index; 125 | } 126 | 127 | output.push_back(nodes.back()); 128 | 129 | nodes.clear(); 130 | nodes.insert(nodes.end(), output.begin(), output.end()); 131 | } 132 | 133 | Pathfinder::Pathfinder(GameClient* client) 134 | : m_Client(client), m_Plan(nullptr), m_CollisionDetector(m_Client->GetWorld()) 135 | { 136 | 137 | } 138 | 139 | void Pathfinder::Update() { 140 | auto physics = GetActorComponent(m_Client, PhysicsComponent); 141 | if (!physics) return; 142 | 143 | auto targeting = GetActorComponent(m_Client, TargetingComponent); 144 | if (!targeting) return; 145 | 146 | Vector3i target = targeting->GetTarget(); 147 | Vector3i toTarget = target - ToVector3i(physics->GetPosition()); 148 | if (toTarget.LengthSq() <= 2.0 * 2.0) { 149 | physics->ClearHorizontalVelocity(); 150 | return; 151 | } 152 | 153 | Vector3i targetGroundPos = GetGroundLevel(m_Client->GetWorld(), target); 154 | Vector3i currentGroundPos = GetGroundLevel(m_Client->GetWorld(), ToVector3i(physics->GetPosition())); 155 | 156 | if (m_Plan == nullptr || !m_Plan->HasNext() || m_Plan->GetGoal()->GetPosition() != targetGroundPos) { 157 | s64 startTime = util::GetTime(); 158 | 159 | m_Plan = m_Client->GetGraph()->FindPath(currentGroundPos, targetGroundPos); 160 | 161 | SmoothPath(); 162 | 163 | /* 164 | if (m_Plan) { 165 | for (std::size_t i = 0; i < m_Plan->GetSize(); ++i) { 166 | auto pos = (*m_Plan)[i]->GetPosition(); 167 | 168 | std::cout << pos << std::endl; 169 | } 170 | } 171 | 172 | std::cout << "Plan built in " << (util::GetTime() - startTime) << "ms.\n"; 173 | */ 174 | } 175 | 176 | if (!m_Plan || m_Plan->GetSize() == 0) { 177 | std::cout << "No plan to " << targetGroundPos << std::endl; 178 | 179 | physics->ClearHorizontalVelocity(); 180 | } 181 | 182 | Vector3d position = physics->GetPosition(); 183 | Vector3d alignTarget = ToVector3d(target); 184 | 185 | if (m_Plan && m_Plan->GetCurrent() && ToVector3d(m_Plan->GetCurrent()->GetPosition()).DistanceSq(position) >= 5 * 5) { 186 | auto speed = GetActorComponent(m_Client, SpeedComponent); 187 | if (speed) { 188 | if (speed->GetMovementType() != SpeedComponent::Movement::Sprinting) 189 | speed->SetMovementType(SpeedComponent::Movement::Sprinting); 190 | } 191 | } else { 192 | auto speed = GetActorComponent(m_Client, SpeedComponent); 193 | if (speed) { 194 | if (speed->GetMovementType() != SpeedComponent::Movement::Normal) 195 | speed->SetMovementType(SpeedComponent::Movement::Normal); 196 | } 197 | } 198 | 199 | ai::PathFollowSteering path(m_Client, m_Plan.get(), 0.25); 200 | ai::SteeringAcceleration pathSteering = path.GetSteering(); 201 | physics->ApplyAcceleration(pathSteering.movement); 202 | physics->ApplyRotation(pathSteering.rotation); 203 | 204 | ai::ObstacleAvoidSteering avoidance(m_Client, physics->GetVelocity(), &m_CollisionDetector, 0.5, 2.0); 205 | ai::SteeringAcceleration avoidSteering = avoidance.GetSteering(); 206 | physics->ApplyAcceleration(avoidSteering.movement); 207 | physics->ApplyRotation(avoidSteering.rotation); 208 | 209 | if (m_Plan && m_Plan->GetCurrent()) 210 | alignTarget = (alignTarget + ToVector3d(m_Plan->GetCurrent()->GetPosition())) / 2.0; 211 | 212 | ai::FaceSteering align(m_Client, alignTarget, 0.1, 1, 1); 213 | physics->ApplyRotation(align.GetSteering().rotation); 214 | } 215 | -------------------------------------------------------------------------------- /mcbot/Pathfinder.h: -------------------------------------------------------------------------------- 1 | #ifndef MCBOT_PATHFINDER_H_ 2 | #define MCBOT_PATHFINDER_H_ 3 | 4 | #include "GameClient.h" 5 | #include "Collision.h" 6 | #include "Pathing.h" 7 | 8 | #include 9 | 10 | mc::Vector3i GetGroundLevel(mc::world::World* world, mc::Vector3i pos); 11 | 12 | struct CastResult { 13 | std::vector hit; 14 | std::size_t length; 15 | bool full; 16 | }; 17 | 18 | CastResult RayCast(mc::world::World* world, WorldGraph* graph, const mc::Vector3d& from, mc::Vector3d direction, std::size_t length); 19 | 20 | class Pathfinder { 21 | private: 22 | GameClient* m_Client; 23 | std::shared_ptr m_Plan; 24 | CollisionDetector m_CollisionDetector; 25 | 26 | bool IsNearBlocks(mc::Vector3d pos); 27 | void SmoothPath(); 28 | 29 | public: 30 | Pathfinder(GameClient* client); 31 | 32 | void Update(); 33 | }; 34 | 35 | #endif 36 | -------------------------------------------------------------------------------- /mcbot/Pathing.cpp: -------------------------------------------------------------------------------- 1 | #include "Pathing.h" 2 | 3 | #include "Utility.h" 4 | #include 5 | 6 | using mc::Vector3d; 7 | using mc::Vector3i; 8 | 9 | namespace ai { 10 | namespace path { 11 | 12 | Node::Node(Vector3i position) 13 | : m_Position(position) 14 | { 15 | 16 | } 17 | 18 | // Follow all of the edges to grab any immediately connected nodes 19 | std::vector Node::GetNeighbors() const { 20 | std::vector neighbors; 21 | neighbors.reserve(16); 22 | for (Edge* edge : m_Edges) { 23 | Node* to = edge->GetNode(1); 24 | if (to == nullptr || to == this) 25 | continue; 26 | neighbors.push_back(to); 27 | } 28 | return neighbors; 29 | } 30 | 31 | void Node::AddEdge(Edge* edge) { 32 | m_Edges.push_back(edge); 33 | } 34 | 35 | void Node::RemoveEdge(Edge* edge) { 36 | ai::path::Node* other = edge->GetConnected(this); 37 | 38 | auto iter = std::find(m_Edges.begin(), m_Edges.end(), edge); 39 | if (iter != m_Edges.end()) 40 | m_Edges.erase(iter); 41 | 42 | iter = std::find(other->m_Edges.begin(), other->m_Edges.end(), edge); 43 | if (iter != other->m_Edges.end()) 44 | other->m_Edges.erase(iter); 45 | } 46 | 47 | Edge* Node::FindNodeEdge(Node* other) const { 48 | for (Edge* edge : m_Edges) { 49 | if (edge->GetConnected(this) == other) 50 | return edge; 51 | } 52 | return nullptr; 53 | } 54 | 55 | float Node::GetCostFrom(Node* node) const { 56 | Edge* edge = FindNodeEdge(node); 57 | double dist = (node->GetPosition() - GetPosition()).Length(); 58 | return (float)(dist * edge->GetWeight()); 59 | } 60 | 61 | Node* Edge::GetConnected(const Node* from) const { 62 | if (from == m_Nodes[0]) 63 | return m_Nodes[1]; 64 | return m_Nodes[0]; 65 | } 66 | 67 | void Edge::LinkNodes(Node* first, Node* second) { 68 | m_Nodes[0] = first; 69 | m_Nodes[1] = second; 70 | } 71 | 72 | PlanningNode::PlanningNode(PlanningNode* prev, Node* node, Node* goal) 73 | : m_Prev(prev), 74 | m_Node(node), 75 | m_Goal(goal), 76 | m_Closed(false) 77 | { 78 | SetPrevious(prev); 79 | } 80 | 81 | AStar::~AStar() { 82 | 83 | } 84 | 85 | std::shared_ptr AStar::operator()(Node* start, Node* goal) { 86 | if (start == goal) return nullptr; 87 | 88 | m_Goal = goal; 89 | 90 | AddToOpenSet(start, nullptr); 91 | 92 | s64 startTime = util::GetTime(); 93 | static const s64 Timeout = 200; 94 | 95 | while (!m_OpenSet.Empty()) { 96 | PlanningNode* node = m_OpenSet.Pop(); 97 | 98 | // Give up after a few seconds 99 | if (util::GetTime() > startTime + Timeout) { 100 | break; 101 | } 102 | 103 | if (node->GetNode() == goal) 104 | return BuildPath(node); 105 | 106 | node->SetClosed(true); 107 | 108 | std::vector neighbors = node->GetNode()->GetNeighbors(); 109 | for (Node* neighbor : neighbors) { 110 | auto find = m_NodeMap.find(neighbor); 111 | 112 | if (find != m_NodeMap.end() && find->second.IsClosed()) 113 | continue; 114 | 115 | float cost = node->GetGoalCost() + neighbor->GetCostFrom(node->GetNode()); 116 | 117 | if (find != m_NodeMap.end()) { 118 | PlanningNode planNode = find->second; 119 | 120 | if (cost < planNode.GetGoalCost()) { 121 | planNode.SetPrevious(node); 122 | m_OpenSet.Update(); 123 | } 124 | } else { 125 | AddToOpenSet(neighbor, node); 126 | } 127 | } 128 | } 129 | 130 | return nullptr; 131 | } 132 | 133 | std::shared_ptr AStar::BuildPath(PlanningNode* goal) { 134 | std::shared_ptr plan = std::make_shared(); 135 | std::vector path; 136 | path.reserve(64); 137 | PlanningNode* node = goal; 138 | while (node) { 139 | path.push_back(node); 140 | node = node->GetPrevious(); 141 | } 142 | 143 | for (auto it = path.rbegin(); it != path.rend(); ++it) 144 | plan->AddNode((*it)->GetNode()); 145 | 146 | return plan; 147 | } 148 | 149 | void AStar::AddToOpenSet(Node* node, PlanningNode* prev) { 150 | auto iter = m_NodeMap.find(node); 151 | if (iter == m_NodeMap.end()) { 152 | m_NodeMap.insert(std::make_pair(node, PlanningNode(prev, node, m_Goal))); 153 | 154 | m_OpenSet.Push(&m_NodeMap.at(node)); 155 | } else { 156 | iter->second.SetClosed(false); 157 | m_OpenSet.Update(); 158 | } 159 | } 160 | 161 | Graph::~Graph() { 162 | Destroy(); 163 | } 164 | 165 | void Graph::Destroy() { 166 | for (auto entry : m_Nodes) { 167 | delete entry.second; 168 | } 169 | 170 | m_Nodes.clear(); 171 | 172 | for (auto kv : m_Edges) { 173 | for (Edge* edge : kv.second) 174 | delete edge; 175 | kv.second.clear(); 176 | } 177 | 178 | m_Edges.clear(); 179 | } 180 | 181 | Node* Graph::FindClosest(const Vector3i& pos) const { 182 | if (m_Nodes.empty()) return nullptr; 183 | 184 | auto find = m_Nodes.find(pos); 185 | if (find != m_Nodes.end()) 186 | return find->second; 187 | 188 | /* 189 | // Slow lookup. Disable and assume no reasonable path if pos is not a traversable node. 190 | // Maybe do spatial lookups to find an acceptable nearby node. 191 | Node* closest = nullptr; 192 | float length = std::numeric_limits::max(); 193 | 194 | for (auto entry : m_Nodes) { 195 | Node* node = entry.second; 196 | 197 | Vector3i toPos = pos - node->GetPosition(); 198 | float checkLength = (float)toPos.Length(); 199 | 200 | if (checkLength < length) { 201 | closest = node; 202 | length = checkLength; 203 | } 204 | } 205 | 206 | return closest;*/ 207 | return nullptr; 208 | } 209 | 210 | std::shared_ptr Graph::FindPath(const Vector3i& start, const Vector3i& end) const { 211 | Node* startNode = FindClosest(start); 212 | Node* endNode = FindClosest(end); 213 | 214 | if (startNode == nullptr || endNode == nullptr) return nullptr; 215 | 216 | AStar algorithm; 217 | 218 | std::shared_ptr plan = algorithm(startNode, endNode); 219 | 220 | if (plan) 221 | plan->Reset(); 222 | 223 | return plan; 224 | } 225 | 226 | bool Graph::LinkNodes(Node* first, Node* second, float weight) { 227 | std::vector neighbors = first->GetNeighbors(); 228 | if (std::find(neighbors.begin(), neighbors.end(), second) != neighbors.end()) return false; 229 | 230 | Edge* edge = new Edge(weight); 231 | 232 | edge->LinkNodes(first, second); 233 | 234 | first->AddEdge(edge); 235 | second->AddEdge(edge); 236 | 237 | m_Edges[edge->GetNode(0)->GetPosition()].push_back(edge); 238 | return true; 239 | } 240 | 241 | } // ns path 242 | } // ns ai 243 | -------------------------------------------------------------------------------- /mcbot/Pathing.h: -------------------------------------------------------------------------------- 1 | #ifndef AI_PATHING_H_ 2 | #define AI_PATHING_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | 12 | namespace ai { 13 | namespace path { 14 | 15 | class Edge; 16 | 17 | class Node { 18 | private: 19 | mc::Vector3i m_Position; 20 | std::vector m_Edges; 21 | 22 | Edge* FindNodeEdge(Node* other) const; 23 | 24 | public: 25 | Node(mc::Vector3i position); 26 | 27 | mc::Vector3i GetPosition() const { return m_Position; } 28 | 29 | void AddEdge(Edge* edge); 30 | void RemoveEdge(Edge* edge); 31 | const std::vector& GetEdges() const { return m_Edges; } 32 | 33 | // Follow all of the edges to grab any immediately connected nodes 34 | std::vector GetNeighbors() const; 35 | float GetCostFrom(Node* node) const; 36 | }; 37 | 38 | class Edge { 39 | private: 40 | Node* m_Nodes[2]; 41 | float m_Weight; 42 | 43 | public: 44 | Edge(float weight = 1.0) { 45 | m_Weight = weight; 46 | } 47 | 48 | float GetWeight() const { return m_Weight; } 49 | 50 | Node* GetNode(std::size_t index) const { return m_Nodes[index]; } 51 | Node* GetConnected(const Node* from) const; 52 | void LinkNodes(Node* first, Node* second); 53 | }; 54 | 55 | class Plan { 56 | private: 57 | std::vector m_Path; 58 | typedef std::vector::iterator iterator; 59 | iterator m_Iterator; 60 | 61 | public: 62 | Plan() { 63 | m_Iterator = m_Path.end(); 64 | } 65 | 66 | bool HasNext() const { 67 | return m_Iterator != m_Path.end(); 68 | } 69 | 70 | std::size_t GetSize() { return m_Path.size(); } 71 | std::vector::reference operator[](std::size_t index) { return m_Path[index]; } 72 | 73 | void Reset() { 74 | m_Iterator = m_Path.begin(); 75 | } 76 | 77 | Node* GetCurrent() { 78 | if (m_Iterator == m_Path.end()) return nullptr; 79 | return *m_Iterator; 80 | } 81 | 82 | Node* GetGoal() const { 83 | if (m_Path.empty()) return nullptr; 84 | return m_Path.back(); 85 | } 86 | 87 | Node* Next() { 88 | Node* result = *m_Iterator; 89 | ++m_Iterator; 90 | return result; 91 | } 92 | 93 | void AddNode(Node* node) { 94 | m_Path.push_back(node); 95 | } 96 | 97 | std::vector& GetNodes() { return m_Path; } 98 | }; 99 | 100 | class PlanningNode { 101 | private: 102 | PlanningNode* m_Prev; 103 | Node* m_Node; 104 | Node* m_Goal; 105 | float m_GoalCost; 106 | float m_HeuristicCost; 107 | float m_FitnessCost; 108 | bool m_Closed; 109 | 110 | public: 111 | PlanningNode(PlanningNode* prev, Node* node, Node* goal); 112 | 113 | PlanningNode* GetPrevious() const { return m_Prev; } 114 | Node* GetNode() const { return m_Node; } 115 | Node* GetGoal() const { return m_Goal; } 116 | float GetGoalCost() const { return m_GoalCost; } 117 | float GetHeuristicCost() const { return m_HeuristicCost; } 118 | float GetFitnessCost() const { return m_FitnessCost; } 119 | bool IsClosed() const { return m_Closed; } 120 | void SetClosed(bool closed) { m_Closed = closed; } 121 | 122 | void SetPrevious(PlanningNode* previous) { 123 | m_Prev = previous; 124 | 125 | if (m_Prev) { 126 | m_GoalCost = m_Prev->GetGoalCost() + m_Node->GetCostFrom(m_Prev->GetNode()); 127 | } else { 128 | m_GoalCost = 0; 129 | } 130 | 131 | m_HeuristicCost = (float)(m_Node->GetPosition() - m_Goal->GetPosition()).Length(); 132 | m_FitnessCost = m_GoalCost + m_HeuristicCost; 133 | } 134 | 135 | bool IsBetterThan(const PlanningNode* other) const { 136 | return m_FitnessCost < other->GetFitnessCost(); 137 | } 138 | }; 139 | 140 | template > 141 | class PriorityQueue { 142 | private: 143 | Container m_Container; 144 | Compare m_Comp; 145 | 146 | public: 147 | void Push(T item) { 148 | m_Container.push_back(item); 149 | std::push_heap(m_Container.begin(), m_Container.end(), m_Comp); 150 | } 151 | 152 | T Pop() { 153 | T item = m_Container.front(); 154 | std::pop_heap(m_Container.begin(), m_Container.end(), m_Comp); 155 | m_Container.pop_back(); 156 | return item; 157 | } 158 | 159 | void Update() { 160 | std::make_heap(m_Container.begin(), m_Container.end(), m_Comp); 161 | } 162 | 163 | bool Empty() const { return m_Container.empty(); } 164 | Container& GetData() { return m_Container; } 165 | }; 166 | 167 | struct PlanningNodeComparator { 168 | bool operator()(PlanningNode* first, PlanningNode* second) { 169 | return !first->IsBetterThan(second); 170 | } 171 | }; 172 | 173 | class AStar { 174 | private: 175 | std::unordered_map m_NodeMap; 176 | PriorityQueue m_OpenSet; 177 | Node* m_Goal; 178 | 179 | void AddToOpenSet(Node* node, PlanningNode* prev); 180 | 181 | // Backtrace path and reverse it to get finished plan 182 | std::shared_ptr BuildPath(PlanningNode* goal); 183 | 184 | public: 185 | ~AStar(); 186 | 187 | std::shared_ptr operator()(Node* start, Node* goal); 188 | }; 189 | 190 | // Extend this and fill out nodes/edges with world data 191 | class Graph { 192 | protected: 193 | std::map m_Nodes; 194 | // Store edges by position of first node for quick lookup 195 | std::map> m_Edges; 196 | 197 | bool LinkNodes(Node* first, Node* second, float weight = 1.0); 198 | public: 199 | virtual ~Graph(); 200 | 201 | Node* FindClosest(const mc::Vector3i& pos) const; 202 | std::shared_ptr FindPath(const mc::Vector3i& start, const mc::Vector3i& end) const; 203 | 204 | void Destroy(); 205 | }; 206 | 207 | } // ns path 208 | } // ns ai 209 | 210 | #endif 211 | -------------------------------------------------------------------------------- /mcbot/PlayerList.cpp: -------------------------------------------------------------------------------- 1 | #include "PlayerList.h" 2 | 3 | #include "components/PhysicsComponent.h" 4 | #include 5 | 6 | using mc::Vector3d; 7 | using mc::Vector3i; 8 | 9 | PlayerList::PlayerList(GameClient* client) : m_Client(client) { 10 | m_Client->RegisterListener(this); 11 | m_Client->GetPlayerManager()->RegisterListener(this); 12 | } 13 | 14 | PlayerList::~PlayerList() { 15 | m_Client->UnregisterListener(this); 16 | m_Client->GetPlayerManager()->UnregisterListener(this); 17 | } 18 | 19 | std::vector PlayerList::GetPlayers() const { 20 | std::vector players; 21 | 22 | for (auto iter = m_Players.begin(); iter != m_Players.end(); ++iter) 23 | players.push_back(iter->second); 24 | 25 | return players; 26 | } 27 | 28 | void PlayerList::OnClientSpawn(mc::core::PlayerPtr player) { 29 | m_BotPlayer = player; 30 | } 31 | 32 | void PlayerList::OnPlayerJoin(mc::core::PlayerPtr player) { 33 | m_Players[player->GetName()] = player; 34 | } 35 | 36 | void PlayerList::OnPlayerLeave(mc::core::PlayerPtr player) { 37 | m_Players.erase(player->GetName()); 38 | } 39 | 40 | void PlayerList::UpdatePlayer(mc::core::PlayerPtr player) { 41 | auto entity = player->GetEntity(); 42 | if (!entity) return; 43 | 44 | auto find = m_VelocityTrackers.find(entity->GetEntityId()); 45 | if (find == m_VelocityTrackers.end()) { 46 | VelocityTracker tracker; 47 | tracker.lastPosition = entity->GetPosition(); 48 | 49 | m_VelocityTrackers[entity->GetEntityId()] = tracker; 50 | } else { 51 | Vector3d pos = entity->GetPosition(); 52 | Vector3d velocity = (pos - find->second.lastPosition); 53 | 54 | find->second.velocity.AddValue(velocity); 55 | find->second.lastPosition = pos; 56 | 57 | Vector3d smoothed = find->second.velocity.GetSmoothedValue(); 58 | //entity->SetVelocity(smoothed); 59 | } 60 | } 61 | 62 | void PlayerList::OnTick() { 63 | auto physics = GetActorComponent(m_Client, PhysicsComponent); 64 | if (physics != nullptr && m_BotPlayer) 65 | m_BotPlayer->GetEntity()->SetPosition(physics->GetPosition()); 66 | 67 | for (auto entry : m_Players) { 68 | auto player = entry.second; 69 | if (!player) continue; 70 | 71 | UpdatePlayer(player); 72 | } 73 | 74 | if (m_BotPlayer) 75 | UpdatePlayer(m_BotPlayer); 76 | } 77 | 78 | mc::core::PlayerPtr PlayerList::GetPlayerByName(const std::wstring& name) { 79 | auto iter = m_Players.find(name); 80 | if (iter != m_Players.end()) 81 | return iter->second; 82 | return nullptr; 83 | } 84 | -------------------------------------------------------------------------------- /mcbot/PlayerList.h: -------------------------------------------------------------------------------- 1 | #ifndef MCBOT_PLAYERLIST_H_ 2 | #define MCBOT_PLAYERLIST_H_ 3 | 4 | #include "GameClient.h" 5 | #include "Utility.h" 6 | 7 | #include 8 | 9 | class PlayerList : public mc::core::PlayerListener, public mc::core::ClientListener { 10 | private: 11 | GameClient* m_Client; 12 | std::map m_Players; 13 | mc::core::PlayerPtr m_BotPlayer; 14 | 15 | struct VelocityTracker { 16 | util::Smoother velocity; 17 | mc::Vector3d lastPosition; 18 | }; 19 | 20 | std::map m_VelocityTrackers; 21 | 22 | void UpdatePlayer(mc::core::PlayerPtr player); 23 | 24 | public: 25 | PlayerList(GameClient* client); 26 | 27 | ~PlayerList(); 28 | 29 | std::vector GetPlayers() const; 30 | 31 | void OnClientSpawn(mc::core::PlayerPtr player); 32 | void OnPlayerJoin(mc::core::PlayerPtr player); 33 | void OnPlayerLeave(mc::core::PlayerPtr player); 34 | 35 | void OnTick(); 36 | 37 | mc::core::PlayerPtr GetPlayerByName(const std::wstring& name); 38 | }; 39 | 40 | #endif 41 | -------------------------------------------------------------------------------- /mcbot/Steering.cpp: -------------------------------------------------------------------------------- 1 | #include "Steering.h" 2 | #include "components/PhysicsComponent.h" 3 | #include "components/JumpComponent.h" 4 | #include "Utility.h" 5 | #include "Pathing.h" 6 | #include "Collision.h" 7 | #include "Math.h" 8 | 9 | #include 10 | #include 11 | 12 | using mc::Vector3d; 13 | using mc::Vector3i; 14 | 15 | namespace ai { 16 | 17 | namespace { 18 | 19 | std::random_device dev; 20 | std::mt19937 engine(dev()); 21 | 22 | } // ns 23 | 24 | SteeringAcceleration SeekSteering::GetSteering() { 25 | SteeringAcceleration steering; 26 | 27 | auto physics = GetActorComponent(m_Player, PhysicsComponent); 28 | if (physics == nullptr) return steering; 29 | 30 | steering.movement = Vector3Normalize(m_Target - physics->GetPosition()) * physics->GetMaxAcceleration(); 31 | steering.movement.y = 0; 32 | steering.rotation = 0; 33 | 34 | return steering; 35 | } 36 | 37 | SteeringAcceleration FleeSteering::GetSteering() { 38 | SteeringAcceleration steering; 39 | 40 | auto physics = GetActorComponent(m_Player, PhysicsComponent); 41 | if (physics == nullptr) return steering; 42 | 43 | steering.movement = Vector3Normalize(physics->GetPosition() - m_Target) * physics->GetMaxAcceleration(); 44 | steering.movement.y = 0; 45 | steering.rotation = 0; 46 | 47 | return steering; 48 | } 49 | 50 | SteeringAcceleration ArriveSteering::GetSteering() { 51 | SteeringAcceleration steering; 52 | 53 | auto physics = GetActorComponent(m_Player, PhysicsComponent); 54 | if (physics == nullptr) return steering; 55 | 56 | Vector3d direction = m_Target - physics->GetPosition(); 57 | double distance = direction.Length(); 58 | 59 | if (distance < m_TargetRadius) { 60 | steering.movement = -physics->GetVelocity(); 61 | return steering; 62 | } 63 | 64 | double targetSpeed; 65 | if (distance > m_SlowRadius) 66 | targetSpeed = physics->GetMaxSpeed(); 67 | else 68 | targetSpeed = physics->GetMaxSpeed() * distance / m_SlowRadius; 69 | 70 | Vector3d targetVelocity = Vector3Normalize(direction) * targetSpeed; 71 | 72 | steering.movement = targetVelocity - physics->GetVelocity(); 73 | steering.movement /= m_TimeToTarget; 74 | 75 | return steering; 76 | } 77 | 78 | SteeringAcceleration AlignSteering::GetSteering() { 79 | SteeringAcceleration steering; 80 | 81 | auto physics = GetActorComponent(m_Player, PhysicsComponent); 82 | if (physics == nullptr) return steering; 83 | 84 | double rotation = m_TargetOrientation - physics->GetOrientation(); 85 | rotation = WrapToPi(rotation); 86 | 87 | double maxRotation = physics->GetMaxRotation(); 88 | double rotationSize = std::abs(rotation); 89 | 90 | if (rotationSize <= m_TargetRadius) 91 | return steering; 92 | 93 | double targetRotation; 94 | if (rotationSize > m_SlowRadius) 95 | targetRotation = maxRotation; 96 | else 97 | targetRotation = maxRotation * rotationSize / m_SlowRadius; 98 | 99 | targetRotation *= rotation / rotationSize; 100 | 101 | steering.rotation = (targetRotation - physics->GetRotation()) / m_TimeToTarget; 102 | 103 | return steering; 104 | } 105 | 106 | SteeringAcceleration PursueSteering::GetSteering() { 107 | auto physics = GetActorComponent(m_Player, PhysicsComponent); 108 | if (physics == nullptr) return SteeringAcceleration(); 109 | 110 | Vector3d direction = m_PursueTarget - physics->GetPosition(); 111 | double dist = direction.Length(); 112 | double speed = physics->GetVelocity().Length(); 113 | 114 | double prediction; 115 | if (speed <= dist / m_MaxPrediction) 116 | prediction = m_MaxPrediction; 117 | else 118 | prediction = dist / speed; 119 | 120 | m_Target = m_PursueTarget + m_TargetVelocity * prediction; 121 | 122 | return SeekSteering::GetSteering(); 123 | } 124 | 125 | SteeringAcceleration FaceSteering::GetSteering() { 126 | auto physics = GetActorComponent(m_Player, PhysicsComponent); 127 | if (physics == nullptr) return SteeringAcceleration(); 128 | 129 | Vector3d direction = m_Target - physics->GetPosition(); 130 | if (direction.LengthSq() == 0) return SteeringAcceleration(); 131 | 132 | m_TargetOrientation = std::atan2(-direction.x, direction.z); 133 | 134 | return AlignSteering::GetSteering(); 135 | } 136 | 137 | SteeringAcceleration LookSteering::GetSteering() { 138 | auto physics = GetActorComponent(m_Player, PhysicsComponent); 139 | if (physics == nullptr) return SteeringAcceleration(); 140 | Vector3d velocity = physics->GetVelocity(); 141 | if (velocity.LengthSq() == 0) return SteeringAcceleration(); 142 | 143 | m_TargetOrientation = std::atan2(-velocity.x, velocity.z); 144 | 145 | return AlignSteering::GetSteering(); 146 | } 147 | 148 | SteeringAcceleration WanderSteering::GetSteering() { 149 | auto physics = GetActorComponent(m_Player, PhysicsComponent); 150 | if (physics == nullptr) return SteeringAcceleration(); 151 | 152 | m_WanderOrientation += RandomBinomial(engine) * m_WanderRate; 153 | m_TargetOrientation = m_WanderOrientation + physics->GetOrientation(); 154 | 155 | m_Target = physics->GetPosition() + util::OrientationToVector(physics->GetOrientation()) * m_WanderOffset; 156 | m_Target += util::OrientationToVector(m_TargetOrientation) * m_WanderRadius; 157 | 158 | SteeringAcceleration steering = FaceSteering::GetSteering(); 159 | 160 | steering.movement = util::OrientationToVector(physics->GetOrientation()) * physics->GetMaxAcceleration(); 161 | 162 | return steering; 163 | } 164 | 165 | double GetPathLength(path::Plan* plan) { 166 | std::size_t size = plan->GetSize(); 167 | 168 | if (size == 0) return 0; 169 | double total = 0; 170 | for (std::size_t i = 0; i < plan->GetSize() - 1; ++i) { 171 | Vector3i current = (*plan)[i]->GetPosition(); 172 | Vector3i next = (*plan)[i + 1]->GetPosition(); 173 | 174 | total += (next - current).Length(); 175 | } 176 | 177 | return total; 178 | } 179 | 180 | SteeringAcceleration PathFollowSteering::GetSteering() { 181 | auto physics = GetActorComponent(m_Player, PhysicsComponent); 182 | if (physics == nullptr) return SteeringAcceleration(); 183 | 184 | // todo: find closest position on continuous path and follow that instead 185 | if (!m_Plan) return SteeringAcceleration(); 186 | 187 | path::Node* current = m_Plan->GetCurrent(); 188 | 189 | if (current && current->GetPosition() == ToVector3i(physics->GetPosition())) { 190 | current = m_Plan->Next(); 191 | } 192 | 193 | if (!current) return SteeringAcceleration(); 194 | 195 | m_Target = ToVector3d(current->GetPosition()) + Vector3d(0.5, 0, 0.5); 196 | 197 | Vector3d pos = physics->GetPosition(); 198 | Vector3i tar = current->GetPosition(); 199 | 200 | if (pos.y < tar.y && std::abs(pos.y - tar.y) >= 0.25) { 201 | auto jump = GetActorComponent(m_Player, JumpComponent); 202 | if (jump) { 203 | jump->Jump(); 204 | } 205 | } 206 | 207 | return SeekSteering::GetSteering(); 208 | } 209 | 210 | SteeringAcceleration ObstacleAvoidSteering::GetSteering() { 211 | auto physics = GetActorComponent(m_Player, PhysicsComponent); 212 | if (physics == nullptr) return SteeringAcceleration(); 213 | 214 | std::vector rayVectors(3); 215 | 216 | /*Vector3d velocity = m_SmoothedVelocity; 217 | if (velocity.LengthSq() == 0)*/ 218 | 219 | //Vector3d direction = physics->GetVelocity(); 220 | 221 | Vector3d direction = util::OrientationToVector(physics->GetOrientation()); 222 | rayVectors.push_back(Vector3Normalize(direction) * m_RayLength); 223 | rayVectors.push_back(Vector3RotateAboutY(rayVectors[0], 30.0 * M_PI / 180.0) * 0.75); 224 | rayVectors.push_back(Vector3RotateAboutY(rayVectors[0], -30.0 * M_PI / 180.0) * 0.75); 225 | 226 | Collision collision; 227 | bool collided = false; 228 | 229 | for (Vector3d rayVector : rayVectors) { 230 | rayVector.y = 0; 231 | if (m_CollisionDetector->DetectCollision(physics->GetPosition() + Vector3d(0, 0.25, 0), rayVector, &collision)) { 232 | collided = true; 233 | break; 234 | } 235 | } 236 | 237 | if (!collided) 238 | return SteeringAcceleration(); 239 | 240 | m_Target = collision.GetPosition() + collision.GetNormal() * m_AvoidDistance; 241 | 242 | return SeekSteering::GetSteering(); 243 | } 244 | 245 | } // ns ai 246 | -------------------------------------------------------------------------------- /mcbot/Steering.h: -------------------------------------------------------------------------------- 1 | #ifndef MCBOT_STEERING_H_ 2 | #define MCBOT_STEERING_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | #include "Actor.h" 8 | 9 | class CollisionDetector; 10 | 11 | namespace ai { 12 | 13 | struct SteeringAcceleration { 14 | mc::Vector3d movement; 15 | double rotation; 16 | 17 | SteeringAcceleration() : rotation(0) { } 18 | }; 19 | 20 | class SteeringBehavior { 21 | public: 22 | virtual SteeringAcceleration GetSteering() = 0; 23 | }; 24 | 25 | class SeekSteering : public SteeringBehavior { 26 | protected: 27 | Actor* m_Player; 28 | mc::Vector3d m_Target; 29 | 30 | public: 31 | SeekSteering(Actor* player, mc::Vector3d target) 32 | : m_Player(player), m_Target(target) 33 | { 34 | } 35 | 36 | SteeringAcceleration GetSteering(); 37 | }; 38 | 39 | class FleeSteering : public SteeringBehavior { 40 | protected: 41 | Actor* m_Player; 42 | mc::Vector3d m_Target; 43 | 44 | public: 45 | FleeSteering(Actor* player, mc::Vector3d target) 46 | : m_Player(player), m_Target(target) 47 | { 48 | } 49 | 50 | SteeringAcceleration GetSteering(); 51 | }; 52 | 53 | class ArriveSteering : public SteeringBehavior { 54 | protected: 55 | Actor* m_Player; 56 | mc::Vector3d m_Target; 57 | 58 | // Consider the player to have arrived if within this radius 59 | double m_TargetRadius; 60 | // Start slowing down once inside this radius 61 | double m_SlowRadius; 62 | 63 | double m_TimeToTarget; 64 | 65 | public: 66 | ArriveSteering(Actor* player, mc::Vector3d target, double targetRadius, double slowRadius, double timeToTarget) 67 | : m_Player(player), m_Target(target), m_TargetRadius(targetRadius), m_SlowRadius(slowRadius), m_TimeToTarget(timeToTarget) 68 | { 69 | 70 | } 71 | 72 | SteeringAcceleration GetSteering(); 73 | }; 74 | 75 | class AlignSteering : public SteeringBehavior { 76 | protected: 77 | Actor* m_Player; 78 | double m_TargetOrientation; 79 | 80 | double m_TargetRadius; 81 | double m_SlowRadius; 82 | double m_TimeToTarget; 83 | 84 | public: 85 | AlignSteering(Actor* player, double targetOrientation, double targetRadius, double slowRadius, double timeToTarget) 86 | : m_Player(player), m_TargetOrientation(targetOrientation), m_TargetRadius(targetRadius), m_SlowRadius(slowRadius), m_TimeToTarget(timeToTarget) 87 | { 88 | 89 | } 90 | 91 | SteeringAcceleration GetSteering(); 92 | }; 93 | 94 | class PursueSteering : public SeekSteering { 95 | private: 96 | double m_MaxPrediction; 97 | mc::Vector3d m_PursueTarget; 98 | mc::Vector3d m_TargetVelocity; 99 | 100 | public: 101 | PursueSteering(Actor* player, double maxPrediction, mc::Vector3d target, mc::Vector3d targetVelocity) 102 | : SeekSteering(player, target), 103 | m_MaxPrediction(maxPrediction), m_PursueTarget(target), m_TargetVelocity(targetVelocity) 104 | { 105 | 106 | } 107 | 108 | SteeringAcceleration GetSteering(); 109 | }; 110 | 111 | class FaceSteering : public AlignSteering { 112 | protected: 113 | mc::Vector3d m_Target; 114 | 115 | public: 116 | FaceSteering(Actor* player, mc::Vector3d target, double targetRadius, double slowRadius, double timeToTarget) 117 | : AlignSteering(player, 0, targetRadius, slowRadius, timeToTarget), m_Target(target) { } 118 | 119 | SteeringAcceleration GetSteering(); 120 | }; 121 | 122 | class LookSteering : public AlignSteering { 123 | public: 124 | LookSteering(Actor* player, double targetRadius, double slowRadius, double timeToTarget) 125 | : AlignSteering(player, 0, targetRadius, slowRadius, timeToTarget) { } 126 | 127 | SteeringAcceleration GetSteering(); 128 | }; 129 | 130 | class WanderSteering : public FaceSteering { 131 | private: 132 | double m_WanderOffset; 133 | double m_WanderRadius; 134 | double m_WanderRate; 135 | double m_WanderOrientation; 136 | 137 | public: 138 | WanderSteering(Actor* player, double offset, double radius, double rate) 139 | : FaceSteering(player, mc::Vector3d(), 0.1, 1, 1), 140 | m_WanderOffset(offset), m_WanderRadius(radius), m_WanderRate(rate), m_WanderOrientation(0) 141 | { 142 | 143 | } 144 | 145 | SteeringAcceleration GetSteering(); 146 | }; 147 | 148 | namespace path { 149 | class Plan; 150 | } 151 | 152 | class PathFollowSteering : public SeekSteering { 153 | private: 154 | path::Plan* m_Plan; 155 | double m_PathOffset; 156 | double m_PlanPos; 157 | 158 | public: 159 | PathFollowSteering(Actor* player, path::Plan* plan, double offset) 160 | : SeekSteering(player, mc::Vector3d()), 161 | m_Plan(plan), m_PathOffset(offset), m_PlanPos(0) 162 | { 163 | 164 | } 165 | 166 | SteeringAcceleration GetSteering(); 167 | }; 168 | 169 | 170 | 171 | class ObstacleAvoidSteering : public SeekSteering { 172 | private: 173 | CollisionDetector* m_CollisionDetector; 174 | double m_AvoidDistance; 175 | double m_RayLength; 176 | mc::Vector3d m_SmoothedVelocity; 177 | 178 | public: 179 | ObstacleAvoidSteering(Actor* player, mc::Vector3d smoothedVelocity, CollisionDetector* detector, double avoidDistance, double lookAhead) 180 | : SeekSteering(player, mc::Vector3d()), 181 | m_CollisionDetector(detector), m_AvoidDistance(avoidDistance), m_RayLength(lookAhead) 182 | { 183 | 184 | } 185 | 186 | SteeringAcceleration GetSteering(); 187 | }; 188 | 189 | } // ns ai 190 | 191 | #endif 192 | -------------------------------------------------------------------------------- /mcbot/Utility.cpp: -------------------------------------------------------------------------------- 1 | #include "Utility.h" 2 | 3 | #include 4 | 5 | using mc::Vector3d; 6 | 7 | namespace util { 8 | 9 | s64 GetTime() { 10 | return std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); 11 | } 12 | 13 | Vector3d OrientationToVector(double orientation) { 14 | double pitch = 0.0; 15 | 16 | return Vector3d( 17 | -std::cos(pitch) * std::sin(orientation), 18 | -std::sin(pitch), 19 | std::cos(pitch) * std::cos(orientation) 20 | ); 21 | } 22 | 23 | } // ns util 24 | -------------------------------------------------------------------------------- /mcbot/Utility.h: -------------------------------------------------------------------------------- 1 | #ifndef AI_UTILITY_H_ 2 | #define AI_UTILITY_H_ 3 | 4 | #include 5 | 6 | #undef min 7 | #undef max 8 | 9 | namespace util { 10 | 11 | s64 GetTime(); 12 | mc::Vector3d OrientationToVector(double orientation); 13 | 14 | template 15 | class Smoother { 16 | private: 17 | T m_Values[Amount]; 18 | std::size_t m_Index; 19 | 20 | public: 21 | Smoother() : m_Index(0) { 22 | for (std::size_t i = 0; i < Amount; ++i) 23 | m_Values[i] = T(); 24 | } 25 | 26 | void AddValue(T value) { 27 | m_Values[m_Index] = value; 28 | m_Index = (m_Index + 1) % Amount; 29 | } 30 | 31 | T GetSmoothedValue() { 32 | T total = T(); 33 | 34 | for (std::size_t i = 0; i < Amount; ++i) 35 | total = total + m_Values[i]; 36 | 37 | return total / Amount; 38 | } 39 | }; 40 | 41 | } // ns util 42 | 43 | #endif 44 | -------------------------------------------------------------------------------- /mcbot/WorldGraph.cpp: -------------------------------------------------------------------------------- 1 | #include "WorldGraph.h" 2 | 3 | #include "GameClient.h" 4 | #include "Utility.h" 5 | #include "components/PhysicsComponent.h" 6 | #include "Math.h" 7 | 8 | #include 9 | 10 | using mc::Vector3d; 11 | using mc::Vector3i; 12 | using mc::ToVector3i; 13 | 14 | PhysicsComponent* WorldGraph::ChunkTaskComparator::physics = nullptr; 15 | 16 | // Prioritize chunks whose y value is closest to the player's y value. 17 | bool WorldGraph::ChunkTaskComparator::operator()(Vector3i first, Vector3i second) { 18 | if (!physics) { 19 | return first < second; 20 | } 21 | 22 | Vector3i playerChunk = ToVector3i(physics->GetPosition()) / 16; 23 | 24 | static const Vector3d weights(1.2, 1.0, 1.2); 25 | 26 | double valFirst = Hadamard(ToVector3d(playerChunk - first), weights).LengthSq(); 27 | double valSecond = Hadamard(ToVector3d(playerChunk - second), weights).LengthSq(); 28 | 29 | return valFirst > valSecond; 30 | } 31 | 32 | ai::path::Node* WorldGraph::GetNode(Vector3i pos) { 33 | auto iter = m_Nodes.find(pos); 34 | 35 | ai::path::Node* node = nullptr; 36 | if (iter == m_Nodes.end()) { 37 | node = new ai::path::Node(pos); 38 | m_Nodes[pos] = node; 39 | } else { 40 | node = iter->second; 41 | } 42 | 43 | return node; 44 | } 45 | 46 | // The check pos is not solid, the block above is not solid, and the block below is solid 47 | bool WorldGraph::IsWalkable(Vector3i pos) const { 48 | mc::world::World* world = m_Client->GetWorld(); 49 | 50 | mc::block::BlockPtr checkBlock = world->GetBlock(pos).GetBlock(); 51 | 52 | mc::block::BlockPtr aBlock = world->GetBlock(pos + Vector3i(0, 1, 0)).GetBlock(); 53 | mc::block::BlockPtr bBlock = world->GetBlock(pos - Vector3i(0, 1, 0)).GetBlock(); 54 | 55 | // Current block is solid, so it's not walkable. 56 | if (checkBlock && checkBlock->IsSolid()) return false; 57 | // Above block is solid, so it's not walkable 58 | if (aBlock && aBlock->IsSolid()) return false; 59 | 60 | // Below block must be solid 61 | return bBlock && bBlock->IsSolid(); 62 | } 63 | 64 | int WorldGraph::IsSafeFall(Vector3i pos) const { 65 | mc::world::World* world = m_Client->GetWorld(); 66 | 67 | mc::block::BlockPtr checkBlock = world->GetBlock(pos).GetBlock(); 68 | 69 | mc::block::BlockPtr aBlock = world->GetBlock(pos + Vector3i(0, 1, 0)).GetBlock(); 70 | 71 | if (!checkBlock || checkBlock->IsSolid()) return 0; 72 | if (!aBlock || aBlock->IsSolid()) return 0; 73 | 74 | for (int i = 0; i < 4; ++i) { 75 | mc::block::BlockPtr bBlock = world->GetBlock(pos - Vector3i(0, i + 1, 0)).GetBlock(); 76 | 77 | if (bBlock && bBlock->IsSolid()) return i + 1; 78 | //if (bBlock && bBlock->IsSolid() || (bBlock->GetType() == 8 || bBlock->GetType() == 9)) return i + 1; 79 | } 80 | 81 | return 0; 82 | } 83 | 84 | bool WorldGraph::IsWater(Vector3i pos) const { 85 | mc::world::World* world = m_Client->GetWorld(); 86 | mc::block::BlockPtr checkBlock = world->GetBlock(pos).GetBlock(); 87 | 88 | return checkBlock && (checkBlock->GetType() == 8 || checkBlock->GetType() == 9); 89 | } 90 | 91 | 92 | WorldGraph::WorldGraph(GameClient* client) 93 | : m_Client(client) 94 | { 95 | client->GetWorld()->RegisterListener(this); 96 | } 97 | 98 | WorldGraph::~WorldGraph() { 99 | m_Client->GetWorld()->UnregisterListener(this); 100 | } 101 | 102 | void WorldGraph::OnChunkLoad(mc::world::ChunkPtr chunk, const mc::world::ChunkColumnMetadata& meta, u16 yIndex) { 103 | //for (s32 i = 0; i < 16; ++i) { 104 | //if (meta.sectionmask & (1 << i)) { 105 | m_BuildQueue.Push(Vector3i(meta.x, yIndex, meta.z)); 106 | //} 107 | //} 108 | 109 | // todo: invalidate null chunks 110 | } 111 | 112 | void WorldGraph::OnChunkUnload(mc::world::ChunkColumnPtr chunk) { 113 | if (chunk == nullptr) return; 114 | 115 | auto remove = std::remove_if(m_BuildQueue.GetData().begin(), m_BuildQueue.GetData().end(), [&](Vector3i area) { 116 | auto meta = chunk->GetMetadata(); 117 | return area.x == meta.x && area.z == meta.z; 118 | }); 119 | 120 | // Just remove the chunk from the build queue because it's not part of the graph yet. 121 | if (remove != m_BuildQueue.GetData().end()) { 122 | m_BuildQueue.GetData().erase(remove, m_BuildQueue.GetData().end()); 123 | m_BuildQueue.Update(); 124 | return; 125 | } 126 | 127 | // todo: Invalidate the nodes in the chunk 128 | } 129 | 130 | void WorldGraph::OnBlockChange(Vector3i position, mc::block::BlockState newBlock, mc::block::BlockState oldBlock) { 131 | const s32 InvalidationRadius = 1; 132 | std::vector nodes; 133 | 134 | if (position.y <= 0 || position.y >= 255) return; 135 | 136 | Vector3i chunkPos = position / 16; 137 | 138 | auto iter = std::find_if(m_ProcessedChunks.begin(), m_ProcessedChunks.end(), [chunkPos](Vector3i processed) { 139 | return processed.x == chunkPos.x && processed.z == chunkPos.z; 140 | }); 141 | // Don't process block changes when the chunk hasn't been added to the graph yet. 142 | //if (iter == m_ProcessedChunks.end()) return; 143 | 144 | // Get all of the nodes surrounding the changed position 145 | for (s32 y = -InvalidationRadius; y <= InvalidationRadius; ++y) { 146 | for (s32 z = -InvalidationRadius; z <= InvalidationRadius; ++z) { 147 | for (s32 x = -InvalidationRadius; x <= InvalidationRadius; ++x) { 148 | Vector3i nodePos = position + Vector3i(x, y, z); 149 | 150 | if (nodePos.y < 0 || nodePos.y >= 256) continue; 151 | 152 | ai::path::Node* node = GetNode(nodePos); 153 | nodes.push_back(node); 154 | } 155 | } 156 | } 157 | 158 | // Invalidate all of the edges in the contained nodes 159 | for (ai::path::Node* node : nodes) { 160 | std::vector edges = node->GetEdges(); 161 | for (ai::path::Edge* edge : edges) { 162 | Vector3i edgePos = edge->GetNode(0)->GetPosition(); 163 | // Removes the edge from both the node and its connection 164 | node->RemoveEdge(edge); 165 | 166 | auto iter = m_Edges.find(edgePos); 167 | if (iter != m_Edges.end()) { 168 | auto& edgeList = iter->second; 169 | auto edgeIter = std::find(edgeList.begin(), edgeList.end(), edge); 170 | 171 | if (edgeIter != edgeList.end()) { 172 | m_Edges.erase(iter); 173 | } 174 | } 175 | delete edge; 176 | } 177 | } 178 | 179 | // Recalculate all of the edges for the area and surrounding edge 180 | s32 EdgeRadius = InvalidationRadius + 1; 181 | for (s32 y = -EdgeRadius; y <= EdgeRadius; ++y) { 182 | for (s32 z = -EdgeRadius; z <= EdgeRadius; ++z) { 183 | for (s32 x = -EdgeRadius; x <= EdgeRadius; ++x) { 184 | Vector3i processPos = position + Vector3i(x, y, z); 185 | 186 | std::vector links = ProcessBlock(processPos); 187 | 188 | for (Link link : links) { 189 | auto first = GetNode(link.first); 190 | auto second = GetNode(link.second); 191 | 192 | LinkNodes(first, second, link.weight); 193 | } 194 | } 195 | } 196 | } 197 | } 198 | 199 | void WorldGraph::ProcessQueue() { 200 | ChunkTaskComparator::physics = GetActorComponent(m_Client, PhysicsComponent); 201 | if (m_BuildQueue.Empty()) return; 202 | 203 | m_BuildQueue.Update(); 204 | 205 | Vector3i current = m_BuildQueue.Pop(); 206 | 207 | mc::world::ChunkColumnPtr col = m_Client->GetWorld()->GetChunk(current * 16); 208 | if (!col) return; 209 | 210 | mc::world::ChunkPtr chunk = (*col)[(std::size_t)current.y]; 211 | //if (!chunk) return; 212 | 213 | m_ProcessedChunks.push_back(current); 214 | 215 | std::vector links = ProcessChunk(chunk, col->GetMetadata(), (s32)current.y); 216 | for (Link link : links) { 217 | ai::path::Node* firstNode = GetNode(link.first); 218 | ai::path::Node* secondNode = GetNode(link.second); 219 | 220 | LinkNodes(firstNode, secondNode, link.weight); 221 | } 222 | } 223 | 224 | std::vector WorldGraph::ProcessBlock(Vector3i checkPos) { 225 | mc::world::World* world = m_Client->GetWorld(); 226 | std::vector links; 227 | 228 | static const std::vector directions = { 229 | Vector3i(-1, 0, 0), Vector3i(1, 0, 0), Vector3i(0, 0, -1), Vector3i(0, 0, 1), // Directly nearby in flat area 230 | Vector3i(0, 1, 0), Vector3i(0, -1, 0), // Directly up/down 231 | Vector3i(-1, 1, 0), Vector3i(1, 1, 0), Vector3i(0, 1, -1), Vector3i(0, 1, 1), // Up one step 232 | Vector3i(-1, -1, 0), Vector3i(1, -1, 0), Vector3i(0, -1, -1), Vector3i(0, -1, 1) // Down one step 233 | }; 234 | static const std::vector waterDirections = { 235 | Vector3i(-1, 0, 0), Vector3i(1, 0, 0), Vector3i(0, 0, -1), Vector3i(0, 0, 1), // Directly nearby in flat area 236 | Vector3i(0, 1, 0), Vector3i(0, -1, 0), // Directly up/down 237 | Vector3i(-1, 1, 0), Vector3i(1, 1, 0), Vector3i(0, 1, -1), Vector3i(0, 1, 1), 238 | }; 239 | 240 | mc::block::BlockPtr checkBlock = world->GetBlock(checkPos).GetBlock(); 241 | 242 | // Skip because it's solid 243 | if (checkBlock && checkBlock->IsSolid()) return links; 244 | 245 | if (IsWalkable(checkPos)) { 246 | for (Vector3i direction : directions) { 247 | Vector3i neighborPos = checkPos + direction; 248 | 249 | if (IsWalkable(neighborPos) || (IsSafeFall(neighborPos) && direction.y == 0)) { 250 | links.emplace_back(checkPos, neighborPos, 1.0f); 251 | } 252 | } 253 | } else if (IsWater(checkPos)) { 254 | for (Vector3i direction : waterDirections) { 255 | Vector3i neighborPos = checkPos + direction; 256 | 257 | if (IsWalkable(neighborPos) || IsWater(neighborPos)) { 258 | links.emplace_back(checkPos, neighborPos, 4.0f); 259 | links.emplace_back(neighborPos, checkPos, 4.0f); 260 | } 261 | } 262 | } else { 263 | int fallDist = IsSafeFall(checkPos); 264 | if (fallDist <= 0) return links; 265 | 266 | Vector3i current = checkPos; 267 | 268 | for (int i = 0; i < fallDist; ++i) { 269 | Vector3i fallPos = checkPos - Vector3i(0, i + 1, 0); 270 | 271 | mc::block::BlockPtr block = world->GetBlock(fallPos).GetBlock(); 272 | if (block && block->IsSolid()) break; 273 | 274 | links.emplace_back(current, fallPos, 1.0f); 275 | 276 | current = fallPos; 277 | } 278 | } 279 | 280 | return links; 281 | } 282 | 283 | std::vector WorldGraph::ProcessChunk(mc::world::ChunkPtr chunk, const mc::world::ChunkColumnMetadata& meta, s32 yIndex) { 284 | mc::world::World* world = m_Client->GetWorld(); 285 | std::vector links; 286 | 287 | auto physics = GetActorComponent(m_Client, PhysicsComponent); 288 | if (physics == nullptr) return links; 289 | Vector3i position = ToVector3i(physics->GetPosition()); 290 | 291 | for (int y = 0; y < 16; ++y) { 292 | for (int z = 0; z < 16; ++z) { 293 | for (int x = 0; x < 16; ++x) { 294 | Vector3i checkPos = Vector3i(meta.x * 16 + x, yIndex * 16 + y, meta.z * 16 + z); 295 | 296 | std::vector blockLinks = ProcessBlock(checkPos); 297 | links.insert(links.end(), blockLinks.begin(), blockLinks.end()); 298 | } 299 | } 300 | } 301 | return links; 302 | } 303 | -------------------------------------------------------------------------------- /mcbot/WorldGraph.h: -------------------------------------------------------------------------------- 1 | #ifndef MCBOT_WORLD_GRAPH_H_ 2 | #define MCBOT_WORLD_GRAPH_H_ 3 | 4 | #include 5 | 6 | #include "Pathing.h" 7 | 8 | class GameClient; 9 | 10 | class PhysicsComponent; 11 | 12 | class WorldGraph : public ai::path::Graph, public mc::world::WorldListener { 13 | private: 14 | struct ChunkTaskComparator { 15 | static PhysicsComponent* physics; 16 | 17 | bool operator()(mc::Vector3i first, mc::Vector3i second); 18 | }; 19 | 20 | GameClient* m_Client; 21 | ai::path::PriorityQueue m_BuildQueue; 22 | std::vector m_ProcessedChunks; 23 | 24 | ai::path::Node* GetNode(mc::Vector3i pos); 25 | int IsSafeFall(mc::Vector3i pos) const; 26 | bool IsWater(mc::Vector3i pos) const; 27 | 28 | struct Link { 29 | mc::Vector3i first; 30 | mc::Vector3i second; 31 | float weight; 32 | 33 | Link(mc::Vector3i f, mc::Vector3i s, float w) : first(f), second(s), weight(w) { } 34 | }; 35 | 36 | std::vector ProcessBlock(mc::Vector3i checkPos); 37 | std::vector ProcessChunk(mc::world::ChunkPtr chunk, const mc::world::ChunkColumnMetadata& meta, s32 yIndex); 38 | public: 39 | WorldGraph(GameClient* client); 40 | 41 | ~WorldGraph(); 42 | 43 | bool IsWalkable(mc::Vector3i pos) const; 44 | 45 | void OnChunkLoad(mc::world::ChunkPtr chunk, const mc::world::ChunkColumnMetadata& meta, u16 yIndex) override; 46 | void OnBlockChange(mc::Vector3i position, mc::block::BlockState newBlock, mc::block::BlockState oldBlock) override; 47 | void OnChunkUnload(mc::world::ChunkColumnPtr chunk) override; 48 | 49 | void ProcessQueue(); 50 | }; 51 | 52 | 53 | #endif 54 | -------------------------------------------------------------------------------- /mcbot/actions/WanderAction.h: -------------------------------------------------------------------------------- 1 | #ifndef CHI_WANDER_ACTION_H_ 2 | #define CHI_WANDER_ACTION_H_ 3 | 4 | #include "../Decision.h" 5 | #include "../GameClient.h" 6 | #include "../components/PhysicsComponent.h" 7 | #include "../components/TargetingComponent.h" 8 | 9 | class WanderAction : public DecisionAction { 10 | private: 11 | GameClient* m_Client; 12 | ai::WanderSteering m_Wander; 13 | 14 | public: 15 | WanderAction(GameClient* client) 16 | : m_Client(client), 17 | m_Wander(client, 15.0, 2.0, 0.15) 18 | { 19 | 20 | } 21 | void Act() override { 22 | auto physics = GetActorComponent(m_Client, PhysicsComponent); 23 | if (!physics) return; 24 | 25 | auto targeting = GetActorComponent(m_Client, TargetingComponent); 26 | if (!targeting) return; 27 | 28 | ai::SteeringAcceleration steering = m_Wander.GetSteering(); 29 | 30 | physics->ApplyAcceleration(steering.movement); 31 | physics->ApplyRotation(steering.rotation); 32 | 33 | mc::Vector3d pos = physics->GetPosition(); 34 | mc::Vector3d vel = physics->GetVelocity(); 35 | 36 | mc::Vector3d projectedPos = pos + (vel * 50.0 / 1000.0); 37 | 38 | mc::block::BlockPtr projectedBlock = m_Client->GetWorld()->GetBlock(projectedPos).GetBlock(); 39 | mc::block::BlockPtr block = m_Client->GetWorld()->GetBlock(projectedPos - mc::Vector3d(0, 1, 0)).GetBlock(); 40 | 41 | if (!projectedBlock || projectedBlock->IsSolid() || !block || !block->IsSolid()) { 42 | physics->SetVelocity(-vel * 1.5); 43 | physics->ApplyRotation(3.14159f); 44 | } 45 | 46 | } 47 | }; 48 | 49 | #endif 50 | -------------------------------------------------------------------------------- /mcbot/components/EffectComponent.cpp: -------------------------------------------------------------------------------- 1 | #include "EffectComponent.h" 2 | #include "../GameClient.h" 3 | #include 4 | 5 | const char* EffectComponent::name = "Effect"; 6 | 7 | void EffectComponent::Update(double dt) { 8 | for (auto iter = m_Effects.begin(); iter != m_Effects.end(); ++iter) { 9 | iter->second.duration -= dt; 10 | } 11 | } 12 | 13 | void EffectComponent::HandlePacket(mc::protocol::packets::in::EntityEffectPacket* packet) { 14 | GameClient* client = dynamic_cast(m_Owner); 15 | if (client == nullptr) return; 16 | if (client->GetEntityManager()->GetPlayerEntity()->GetEntityId() != packet->GetEntityId()) return; 17 | 18 | Effect effect = static_cast(packet->GetEffectId()); 19 | 20 | EffectData data; 21 | data.amplifier = packet->GetAmplifier(); 22 | data.duration = packet->GetDuration(); 23 | 24 | m_Effects[effect] = data; 25 | } 26 | 27 | void EffectComponent::HandlePacket(mc::protocol::packets::in::RemoveEntityEffectPacket* packet) { 28 | GameClient* client = dynamic_cast(m_Owner); 29 | if (client == nullptr) return; 30 | if (client->GetEntityManager()->GetPlayerEntity()->GetEntityId() != packet->GetEntityId()) return; 31 | 32 | Effect effect = static_cast(packet->GetEffectId()); 33 | 34 | m_Effects.erase(effect); 35 | } 36 | 37 | void EffectComponent::HandlePacket(mc::protocol::packets::in::UpdateHealthPacket* packet) { 38 | if (packet->GetHealth() <= 0) { 39 | m_Effects.clear(); 40 | } 41 | } 42 | 43 | bool EffectComponent::HasEffect(Effect effect) const { 44 | auto iter = m_Effects.find(effect); 45 | 46 | return iter != m_Effects.end(); 47 | } 48 | 49 | const EffectComponent::EffectData* EffectComponent::GetEffectData(Effect effect) const { 50 | auto iter = m_Effects.find(effect); 51 | if (iter == m_Effects.end()) return nullptr; 52 | return &iter->second; 53 | } 54 | -------------------------------------------------------------------------------- /mcbot/components/EffectComponent.h: -------------------------------------------------------------------------------- 1 | #ifndef MCBOT_EFFECT_COMPONENT_H_ 2 | #define MCBOT_EFFECT_COMPONENT_H_ 3 | 4 | #include "../Component.h" 5 | #include 6 | #include 7 | #include 8 | 9 | enum class Effect { 10 | Speed = 1, 11 | Slowness, 12 | Haste, 13 | MiningFatigue, 14 | Strength, 15 | InstantHealth, 16 | InstantDamage, 17 | JumpBoost, 18 | Nausea, 19 | Regeneration, 20 | Resistance, 21 | FireResistance, 22 | WaterBreathing, 23 | Invisibility, 24 | Blindness, 25 | NightVision, 26 | Hunger, 27 | Weakness, 28 | Poison, 29 | Wither, 30 | HealthBoost, 31 | Absorption, 32 | Saturation, 33 | Glowing, 34 | Levitation, 35 | Luck, 36 | BadLuck 37 | }; 38 | 39 | class EffectComponent : public Component, mc::protocol::packets::PacketHandler { 40 | public: 41 | struct EffectData { 42 | double duration; 43 | s32 amplifier; 44 | bool remove; 45 | }; 46 | 47 | static const char* name; 48 | 49 | private: 50 | std::map m_Effects; 51 | 52 | public: 53 | EffectComponent(mc::protocol::packets::PacketDispatcher* dispatcher) 54 | : mc::protocol::packets::PacketHandler(dispatcher) 55 | { 56 | dispatcher->RegisterHandler(mc::protocol::State::Play, mc::protocol::play::EntityEffect, this); 57 | dispatcher->RegisterHandler(mc::protocol::State::Play, mc::protocol::play::RemoveEntityEffect, this); 58 | dispatcher->RegisterHandler(mc::protocol::State::Play, mc::protocol::play::UpdateHealth, this); 59 | } 60 | 61 | ~EffectComponent() { 62 | GetDispatcher()->UnregisterHandler(this); 63 | } 64 | 65 | void Update(double dt); 66 | void HandlePacket(mc::protocol::packets::in::EntityEffectPacket* packet) override; 67 | void HandlePacket(mc::protocol::packets::in::RemoveEntityEffectPacket* packet) override; 68 | void HandlePacket(mc::protocol::packets::in::UpdateHealthPacket* packet) override; 69 | 70 | bool HasEffect(Effect effect) const; 71 | const EffectData* GetEffectData(Effect effect) const; 72 | 73 | const char* GetName() const { return name; } 74 | }; 75 | 76 | #endif 77 | -------------------------------------------------------------------------------- /mcbot/components/JumpComponent.cpp: -------------------------------------------------------------------------------- 1 | #include "JumpComponent.h" 2 | 3 | #include "../Actor.h" 4 | #include "PhysicsComponent.h" 5 | #include "../Utility.h" 6 | 7 | #include 8 | 9 | const char* JumpComponent::name = "Jump"; 10 | const s64 JumpCooldown = 500; 11 | 12 | using mc::Vector3d; 13 | 14 | void JumpComponent::Update(double dt) { 15 | auto physics = GetActorComponent(m_Owner, PhysicsComponent); 16 | if (!physics) return; 17 | 18 | s64 time = util::GetTime(); 19 | 20 | bool onGround = m_CollisionDetector.DetectCollision(physics->GetPosition(), Vector3d(0, -32 * 50.0 / 1000.0, 0), nullptr); 21 | 22 | if (onGround && m_Jump) { 23 | if (time >= m_LastJump + JumpCooldown) { 24 | physics->ApplyAcceleration(Vector3d(0, m_Power, 0)); 25 | m_LastJump = time; 26 | } 27 | } 28 | 29 | m_Jump = false; 30 | } 31 | -------------------------------------------------------------------------------- /mcbot/components/JumpComponent.h: -------------------------------------------------------------------------------- 1 | #ifndef MCBOT_JUMP_COMPONENT_H_ 2 | #define MCBOT_JUMP_COMPONENT_H_ 3 | 4 | #include "../Component.h" 5 | #include "../Collision.h" 6 | #include 7 | 8 | class JumpComponent : public Component { 9 | public: 10 | static const char* name; 11 | 12 | private: 13 | CollisionDetector m_CollisionDetector; 14 | mc::world::World* m_World; 15 | double m_Power; 16 | s64 m_LastJump; 17 | bool m_Jump; 18 | 19 | public: 20 | JumpComponent(mc::world::World* world, double power) 21 | : m_CollisionDetector(world), m_World(world), m_Power(power), m_LastJump(0), m_Jump(false) 22 | { 23 | 24 | } 25 | 26 | void Jump() { m_Jump = true; } 27 | void Update(double dt); 28 | void SetJumpPower(double power) { m_Power = power; } 29 | 30 | const char* GetName() const { return name; } 31 | }; 32 | 33 | #endif 34 | -------------------------------------------------------------------------------- /mcbot/components/PhysicsComponent.cpp: -------------------------------------------------------------------------------- 1 | #include "PhysicsComponent.h" 2 | #include "JumpComponent.h" 3 | #include "../Math.h" 4 | #include "../Actor.h" 5 | 6 | #include 7 | #include 8 | 9 | using mc::Vector3d; 10 | 11 | namespace { 12 | 13 | std::random_device dev; 14 | std::mt19937 engine(dev()); 15 | std::uniform_real_distribution dist(0, 1); 16 | const Vector3d Up(0, 1, 0); 17 | 18 | } 19 | 20 | const char* PhysicsComponent::name = "Physics"; 21 | 22 | void PhysicsComponent::Integrate(double dt) { 23 | Vector3d horizontalAcceleration = m_Acceleration; horizontalAcceleration.y = 0; 24 | 25 | if (horizontalAcceleration.LengthSq() > m_MaxAcceleration * m_MaxAcceleration) 26 | horizontalAcceleration.Truncate(m_MaxAcceleration); 27 | 28 | // Add gravity 29 | m_Acceleration = Vector3d(horizontalAcceleration.x, -32 + m_Acceleration.y, horizontalAcceleration.z); 30 | 31 | if (m_Rotation > m_MaxRotation) 32 | m_Rotation = m_MaxRotation; 33 | 34 | m_CollisionDetector.ResolveCollisions(this, dt); 35 | 36 | const double epsilon = 0.0001; 37 | m_Velocity += m_Acceleration * dt; 38 | m_Orientation += m_Rotation * dt; 39 | 40 | if (m_Velocity.LengthSq() < epsilon) m_Velocity = Vector3d(0, 0, 0); 41 | m_Velocity *= 0.97; 42 | 43 | if (m_Orientation > M_TAU) 44 | m_Orientation -= M_TAU; 45 | else if (m_Orientation < -M_TAU) 46 | m_Orientation += M_TAU; 47 | 48 | Vector3d horizontalVelocity(m_Velocity.x, 0, m_Velocity.z); 49 | horizontalVelocity.Truncate(m_MaxSpeed); 50 | m_Velocity = horizontalVelocity + Vector3d(0, m_Velocity.y, 0); 51 | m_Acceleration = Vector3d(0, 0, 0); 52 | m_Rotation = 0; 53 | } 54 | -------------------------------------------------------------------------------- /mcbot/components/PhysicsComponent.h: -------------------------------------------------------------------------------- 1 | #ifndef MCBOT_PHYSICS_COMPONENT_H_ 2 | #define MCBOT_PHYSICS_COMPONENT_H_ 3 | 4 | #include 5 | #include 6 | #include "../Component.h" 7 | #include "../Collision.h" 8 | 9 | class PhysicsComponent : public Component { 10 | public: 11 | static const char* name; 12 | 13 | private: 14 | CollisionDetector m_CollisionDetector; 15 | mc::AABB m_BoundingBox; 16 | 17 | mc::Vector3d m_Position; 18 | mc::Vector3d m_Velocity; 19 | double m_Orientation; 20 | 21 | mc::Vector3d m_Acceleration; 22 | double m_Rotation; 23 | 24 | double m_MaxAcceleration; 25 | double m_MaxRotation; 26 | double m_MaxSpeed; 27 | 28 | public: 29 | PhysicsComponent(mc::world::World* world, mc::AABB bounds) 30 | : m_CollisionDetector(world), 31 | m_BoundingBox(bounds), 32 | m_Position(), m_Velocity(), 33 | m_Orientation(0), 34 | m_Acceleration(), m_Rotation(0), 35 | m_MaxAcceleration(1), m_MaxRotation(3.14 * 2), m_MaxSpeed(1) 36 | { 37 | 38 | } 39 | 40 | const mc::Vector3d& GetPosition() const { return m_Position; } 41 | const mc::Vector3d& GetVelocity() const { return m_Velocity; } 42 | double GetOrientation() const { return m_Orientation; } 43 | const mc::Vector3d& GetAcceleration() const { return m_Acceleration; } 44 | double GetRotation() const { return m_Rotation; } 45 | double GetMaxSpeed() const { return m_MaxSpeed; } 46 | double GetMaxAcceleration() const { return m_MaxAcceleration; } 47 | double GetMaxRotation() const { return m_MaxRotation; } 48 | mc::AABB GetBoundingBox() const { return m_BoundingBox; } 49 | 50 | void SetPosition(const mc::Vector3d& pos) { m_Position = pos; } 51 | void SetVelocity(const mc::Vector3d& velocity) { m_Velocity = velocity; } 52 | void ClearHorizontalVelocity() { m_Velocity = mc::Vector3d(0, m_Velocity.y, 0); } 53 | void SetOrientation(double orientation) { m_Orientation = orientation; } 54 | void SetMaxSpeed(double maxSpeed) { m_MaxSpeed = maxSpeed; } 55 | void SetMaxAcceleration(double maxAccel) { m_MaxAcceleration = maxAccel; } 56 | void SetMaxRotation(double maxAccel) { m_MaxRotation = maxAccel; } 57 | 58 | void ApplyAcceleration(mc::Vector3d acceleration) { 59 | m_Acceleration += acceleration; 60 | } 61 | 62 | void ApplyRotation(double rotation) { 63 | m_Rotation += rotation; 64 | } 65 | 66 | void Integrate(double dt); 67 | void Update(double dt) { } 68 | 69 | const char* GetName() const { return name; } 70 | }; 71 | 72 | #endif 73 | -------------------------------------------------------------------------------- /mcbot/components/SpeedComponent.cpp: -------------------------------------------------------------------------------- 1 | #include "SpeedComponent.h" 2 | 3 | #include "../Actor.h" 4 | #include "PhysicsComponent.h" 5 | #include "EffectComponent.h" 6 | #include "../GameClient.h" 7 | 8 | const char* SpeedComponent::name = "Speed"; 9 | 10 | #undef max 11 | 12 | using mc::Vector3d; 13 | 14 | namespace { 15 | 16 | const float WalkingSpeed = 4.3f; 17 | const float SprintingSpeed = 5.6f; 18 | const float SneakingSpeed = 1.3f; 19 | const float SwimSpeedSurface = 2.2f; 20 | const float SwimSpeedSubmerged = 1.97f; 21 | 22 | const u16 StillWater = 9; 23 | const u16 FlowingWater = 8; 24 | 25 | bool IsWater(mc::world::World* world, Vector3d pos) { 26 | mc::block::BlockPtr block = world->GetBlock(pos).GetBlock(); 27 | 28 | return block && (block->GetType() == StillWater || block->GetType() == FlowingWater); 29 | } 30 | 31 | } 32 | 33 | void SpeedComponent::Update(double dt) { 34 | auto physics = GetActorComponent(m_Owner, PhysicsComponent); 35 | if (physics == nullptr) return; 36 | 37 | auto effectComponent = GetActorComponent(m_Owner, EffectComponent); 38 | 39 | Vector3d pos = physics->GetPosition(); 40 | 41 | if (IsWater(m_World, pos)) { 42 | if (IsWater(m_World, pos + Vector3d(0, 1, 0))) { 43 | physics->SetMaxSpeed(SwimSpeedSubmerged); 44 | } else { 45 | physics->SetMaxSpeed(SwimSpeedSurface); 46 | } 47 | return; 48 | } 49 | 50 | float maxSpeed = WalkingSpeed; 51 | 52 | switch (m_Movement) { 53 | case Movement::Normal: 54 | maxSpeed = WalkingSpeed; 55 | break; 56 | case Movement::Sneaking: 57 | maxSpeed = SneakingSpeed; 58 | break; 59 | case Movement::Sprinting: 60 | maxSpeed = SprintingSpeed; 61 | break; 62 | } 63 | 64 | if (effectComponent) { 65 | { 66 | const EffectComponent::EffectData* edata = effectComponent->GetEffectData(Effect::Speed); 67 | if (edata) { 68 | s8 level = (s8)edata->amplifier; 69 | maxSpeed *= (1.0f + (level + 1) * 0.2f); 70 | } 71 | } 72 | maxSpeed = std::max(0.0f, maxSpeed); 73 | 74 | { 75 | const EffectComponent::EffectData* edata = effectComponent->GetEffectData(Effect::Slowness); 76 | if (edata) { 77 | s8 level = (s8)edata->amplifier; 78 | maxSpeed *= (1.0f - (level + 1) * 0.15f); 79 | } 80 | } 81 | maxSpeed = std::max(0.0f, maxSpeed); 82 | } 83 | 84 | physics->SetMaxSpeed(maxSpeed); 85 | } 86 | 87 | void SpeedComponent::SetMovementType(Movement movement) { 88 | if (m_Movement == movement) return; 89 | 90 | GameClient* client = dynamic_cast(m_Owner); 91 | if (!client) { 92 | m_Movement = movement; 93 | return; 94 | } 95 | 96 | auto entity = client->GetEntityManager()->GetPlayerEntity(); 97 | if (!entity) { 98 | m_Movement = movement; 99 | return; 100 | } 101 | 102 | using namespace mc::protocol::packets::out; 103 | 104 | EntityActionPacket::Action action; 105 | if (movement == Movement::Normal) { 106 | if (m_Movement == Movement::Sneaking) 107 | action = EntityActionPacket::Action::StopSneak; 108 | else if (m_Movement == Movement::Sprinting) 109 | action = EntityActionPacket::Action::StopSprint; 110 | } else if (movement == Movement::Sneaking) { 111 | action = EntityActionPacket::Action::StartSneak; 112 | 113 | // Stop sprinting before sneaking 114 | if (m_Movement == Movement::Sprinting) { 115 | EntityActionPacket packet(entity->GetEntityId(), EntityActionPacket::Action::StopSprint); 116 | m_Connection->SendPacket(&packet); 117 | } 118 | } else if (movement == Movement::Sprinting) { 119 | action = EntityActionPacket::Action::StartSprint; 120 | 121 | // Stop sneaking before sprinting 122 | if (m_Movement == Movement::Sneaking) { 123 | EntityActionPacket packet(entity->GetEntityId(), EntityActionPacket::Action::StopSneak); 124 | m_Connection->SendPacket(&packet); 125 | } 126 | } 127 | 128 | EntityActionPacket packet(entity->GetEntityId(), action); 129 | m_Connection->SendPacket(&packet); 130 | 131 | m_Movement = movement; 132 | } 133 | -------------------------------------------------------------------------------- /mcbot/components/SpeedComponent.h: -------------------------------------------------------------------------------- 1 | #ifndef MCBOT_SPEED_COMPONENT_H_ 2 | #define MCBOT_SPEED_COMPONENT_H_ 3 | 4 | #include "../Component.h" 5 | 6 | #include 7 | #include 8 | 9 | // Sets the physics max speed based on current block and whether or not sprint is enabled. 10 | class SpeedComponent : public Component { 11 | public: 12 | static const char* name; 13 | 14 | enum class Movement { Normal, Sneaking, Sprinting }; 15 | private: 16 | mc::core::Connection* m_Connection; 17 | mc::world::World* m_World; 18 | Movement m_Movement; 19 | 20 | public: 21 | SpeedComponent(mc::core::Connection* connection, mc::world::World* world) 22 | : m_Connection(connection), 23 | m_World(world), 24 | m_Movement(Movement::Normal) 25 | { 26 | 27 | } 28 | 29 | Movement GetMovementType() const { return m_Movement; } 30 | 31 | // Updates the server indicating a change in movement speed. 32 | void SetMovementType(Movement movement); 33 | 34 | void Update(double dt); 35 | 36 | const char* GetName() const { return name; } 37 | }; 38 | 39 | #endif 40 | -------------------------------------------------------------------------------- /mcbot/components/SynchronizationComponent.cpp: -------------------------------------------------------------------------------- 1 | #include "SynchronizationComponent.h" 2 | 3 | #include "../Actor.h" 4 | #include "PhysicsComponent.h" 5 | #include "../GameClient.h" 6 | #include 7 | 8 | using mc::Vector3d; 9 | using mc::Vector3i; 10 | 11 | const char* SynchronizationComponent::name = "Synchronization"; 12 | 13 | void SynchronizationComponent::OnClientSpawn(mc::core::PlayerPtr player) { 14 | auto physics = GetActorComponent(m_Owner, PhysicsComponent); 15 | if (physics == nullptr) return; 16 | 17 | Vector3d oldPos = physics->GetPosition(); 18 | physics->SetOrientation(player->GetEntity()->GetYaw()); 19 | physics->SetPosition(player->GetEntity()->GetPosition()); 20 | physics->SetVelocity(Vector3d(0, 0, 0)); 21 | 22 | m_Spawned = true; 23 | std::cout << "Position corrected to " << physics->GetPosition() << " from " << oldPos << std::endl; 24 | } 25 | 26 | void SynchronizationComponent::HandlePacket(mc::protocol::packets::in::UpdateHealthPacket* packet) { 27 | float health = packet->GetHealth(); 28 | 29 | if (health <= 0) { 30 | std::cout << "Died." << std::endl; 31 | m_Dead = true; 32 | } 33 | } 34 | 35 | void SynchronizationComponent::HandlePacket(mc::protocol::packets::in::EntityVelocityPacket* packet) { 36 | auto physics = GetActorComponent(m_Owner, PhysicsComponent); 37 | if (physics == nullptr) return; 38 | 39 | GameClient* client = dynamic_cast(m_Owner); 40 | if (!client) return; 41 | 42 | mc::EntityId eid = packet->GetEntityId(); 43 | 44 | auto playerEntity = client->GetEntityManager()->GetPlayerEntity(); 45 | if (playerEntity && playerEntity->GetEntityId() == eid) { 46 | Vector3d newVelocity = ToVector3d(packet->GetVelocity()) * 20.0 / 8000.0; 47 | std::cout << "Set velocity to " << newVelocity << std::endl; 48 | physics->SetVelocity(newVelocity); 49 | } 50 | } 51 | 52 | void SynchronizationComponent::HandlePacket(mc::protocol::packets::in::SpawnPositionPacket* packet) { 53 | s64 x = packet->GetLocation().GetX(); 54 | s64 y = packet->GetLocation().GetY(); 55 | s64 z = packet->GetLocation().GetZ(); 56 | 57 | m_SpawnPosition = Vector3i((s32)x, (s32)y, (s32)z); 58 | std::cout << "Set spawn position to " << m_SpawnPosition << std::endl; 59 | } 60 | 61 | void SynchronizationComponent::Update(double dt) { 62 | auto physics = GetActorComponent(m_Owner, PhysicsComponent); 63 | if (physics == nullptr || !m_Spawned) return; 64 | 65 | if (m_Dead) { 66 | std::cout << "Sending respawn packet. State: Alive" << std::endl; 67 | physics->SetPosition(ToVector3d(m_SpawnPosition)); 68 | 69 | auto action = mc::protocol::packets::out::ClientStatusPacket::Action::PerformRespawn; 70 | mc::protocol::packets::out::ClientStatusPacket status(action); 71 | 72 | m_Connection->SendPacket(&status); 73 | 74 | m_Dead = false; 75 | } 76 | 77 | float pitch = 0.0f; 78 | 79 | // todo: Track position and orientation. Only send the larger packets when needed. 80 | mc::protocol::packets::out::PlayerPositionAndLookPacket response(physics->GetPosition(), 81 | (float)physics->GetOrientation() * 180.0f / 3.14159f, pitch, true); 82 | 83 | m_Connection->SendPacket(&response); 84 | } 85 | -------------------------------------------------------------------------------- /mcbot/components/SynchronizationComponent.h: -------------------------------------------------------------------------------- 1 | #ifndef MCBOT_SYNCHRONIZATION_COMPONENT_H_ 2 | #define MCBOT_SYNCHRONIZATION_COMPONENT_H_ 3 | 4 | #include "../Component.h" 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | // Keeps the server and client synchronized 11 | class SynchronizationComponent : public Component, public mc::core::PlayerListener, public mc::protocol::packets::PacketHandler { 12 | public: 13 | static const char* name; 14 | 15 | private: 16 | mc::core::Connection* m_Connection; 17 | mc::core::PlayerManager* m_PlayerManager; 18 | mc::Vector3i m_SpawnPosition; 19 | bool m_Spawned; 20 | bool m_Dead; 21 | 22 | public: 23 | SynchronizationComponent(mc::protocol::packets::PacketDispatcher* dispatcher, mc::core::Connection* connection, mc::core::PlayerManager* playerManager) 24 | : mc::protocol::packets::PacketHandler(dispatcher), 25 | m_Connection(connection), 26 | m_PlayerManager(playerManager), 27 | m_Spawned(false), 28 | m_Dead(false) 29 | { 30 | m_PlayerManager->RegisterListener(this); 31 | 32 | dispatcher->RegisterHandler(mc::protocol::State::Play, mc::protocol::play::UpdateHealth, this); 33 | dispatcher->RegisterHandler(mc::protocol::State::Play, mc::protocol::play::EntityVelocity, this); 34 | dispatcher->RegisterHandler(mc::protocol::State::Play, mc::protocol::play::SpawnPosition, this); 35 | } 36 | 37 | ~SynchronizationComponent() { 38 | m_PlayerManager->UnregisterListener(this); 39 | GetDispatcher()->UnregisterHandler(this); 40 | } 41 | 42 | bool HasSpawned() const { return m_Spawned; } 43 | void OnClientSpawn(mc::core::PlayerPtr player); 44 | 45 | void HandlePacket(mc::protocol::packets::in::UpdateHealthPacket* packet); 46 | void HandlePacket(mc::protocol::packets::in::EntityVelocityPacket* packet); 47 | void HandlePacket(mc::protocol::packets::in::SpawnPositionPacket* packet); 48 | 49 | void Update(double dt); 50 | 51 | const char* GetName() const { return name; } 52 | }; 53 | 54 | #endif 55 | -------------------------------------------------------------------------------- /mcbot/components/TargetingComponent.cpp: -------------------------------------------------------------------------------- 1 | #include "TargetingComponent.h" 2 | 3 | const char* TargetingComponent::name = "Targeting"; -------------------------------------------------------------------------------- /mcbot/components/TargetingComponent.h: -------------------------------------------------------------------------------- 1 | #ifndef MCBOT_TARGETING_COMPONENT_H_ 2 | #define MCBOT_TARGETING_COMPONENT_H_ 3 | 4 | #include "../Component.h" 5 | #include "../Collision.h" 6 | 7 | class TargetingComponent : public Component { 8 | public: 9 | static const char* name; 10 | 11 | private: 12 | mc::Vector3i m_Target; 13 | mc::EntityId m_TargetEntity; 14 | 15 | public: 16 | TargetingComponent() 17 | : m_TargetEntity(-1) 18 | { 19 | 20 | } 21 | 22 | void Update(double dt) { } 23 | 24 | mc::Vector3i GetTarget() const { return m_Target; } 25 | void SetTarget(mc::Vector3i target) { m_Target = target; } 26 | 27 | mc::EntityId GetTargetEntity() const { return m_TargetEntity; } 28 | void SetTargetEntity(mc::EntityId eid) { m_TargetEntity = eid; } 29 | 30 | const char* GetName() const { return name; } 31 | }; 32 | 33 | #endif 34 | -------------------------------------------------------------------------------- /mcbot/main.cpp: -------------------------------------------------------------------------------- 1 | #include "BotUpdate.h" 2 | #include "components/EffectComponent.h" 3 | #include "components/JumpComponent.h" 4 | #include "components/PhysicsComponent.h" 5 | #include "components/SpeedComponent.h" 6 | #include "components/TargetingComponent.h" 7 | #include "actions/WanderAction.h" 8 | #include 9 | 10 | using mc::Vector3d; 11 | using mc::Vector3i; 12 | using mc::ToVector3d; 13 | using mc::ToVector3i; 14 | 15 | namespace { 16 | 17 | const std::string name = "bot"; 18 | const std::string password = "pw"; 19 | const std::string server = "127.0.0.1"; 20 | const u16 port = 25565; 21 | 22 | const std::wstring TargetPlayer = L"plushmonkey"; 23 | 24 | } // ns 25 | 26 | class TargetPlayerAction : public DecisionAction { 27 | private: 28 | GameClient* m_Client; 29 | 30 | public: 31 | TargetPlayerAction(GameClient* client) : m_Client(client) { } 32 | 33 | bool HasTarget() { 34 | mc::core::PlayerPtr targetPlayer = m_Client->GetPlayerManager()->GetPlayerByName(TargetPlayer); 35 | if (!targetPlayer) return false; 36 | 37 | mc::entity::EntityPtr entity = targetPlayer->GetEntity(); 38 | return entity != nullptr; 39 | } 40 | 41 | void Act() override { 42 | mc::core::PlayerPtr targetPlayer = m_Client->GetPlayerManager()->GetPlayerByName(TargetPlayer); 43 | auto entity = targetPlayer->GetEntity(); 44 | 45 | auto targeting = GetActorComponent(m_Client, TargetingComponent); 46 | if (!targeting) return; 47 | 48 | targeting->SetTargetEntity(entity->GetEntityId()); 49 | targeting->SetTarget(mc::ToVector3i(entity->GetPosition())); 50 | } 51 | }; 52 | 53 | class AttackAction : public DecisionAction { 54 | protected: 55 | GameClient* m_Client; 56 | s64 m_AttackCooldown; 57 | s64 m_LastAttack; 58 | 59 | bool SelectItem(s32 id) { 60 | mc::inventory::Inventory* inventory = m_Client->GetInventory(); 61 | 62 | if (!inventory) return false; 63 | 64 | auto& hotbar = m_Client->GetHotbar(); 65 | auto slot = hotbar.GetCurrentItem(); 66 | 67 | if (slot.GetItemId() != id) { 68 | s32 itemIndex = inventory->FindItemById(id); 69 | 70 | std::cout << "Selecting item id " << id << std::endl; 71 | if (itemIndex == -1) { 72 | std::cout << "Not carrying item with id of " << id << std::endl; 73 | return false; 74 | } else { 75 | std::cout << "Item is in index " << itemIndex << std::endl; 76 | } 77 | 78 | s32 hotbarIndex = itemIndex - mc::inventory::Inventory::HOTBAR_SLOT_START; 79 | if (hotbarIndex >= 0) 80 | hotbar.SelectSlot(hotbarIndex); 81 | } 82 | 83 | return true; 84 | } 85 | 86 | public: 87 | AttackAction(GameClient* client, s64 attackCooldown) : m_Client(client), m_LastAttack(0), m_AttackCooldown(attackCooldown) { } 88 | 89 | void Act() override { 90 | mc::core::PlayerPtr targetPlayer = m_Client->GetPlayerManager()->GetPlayerByName(TargetPlayer); 91 | auto entity = targetPlayer->GetEntity(); 92 | auto physics = GetActorComponent(m_Client, PhysicsComponent); 93 | auto targeting = GetActorComponent(m_Client, TargetingComponent); 94 | 95 | ai::FaceSteering align(m_Client, entity->GetPosition(), 0.1, 1, 1); 96 | 97 | physics->ApplyRotation(align.GetSteering().rotation); 98 | //physics->ClearHorizontalVelocity(); 99 | Vector3d entityHeading = util::OrientationToVector(entity->GetYaw()); 100 | 101 | Vector3d target = entity->GetPosition() - entityHeading * 2; 102 | targeting->SetTarget(ToVector3i(target)); 103 | 104 | s64 time = util::GetTime(); 105 | 106 | if (time >= m_LastAttack + m_AttackCooldown) { 107 | Attack(entity); 108 | 109 | m_LastAttack = time; 110 | } 111 | } 112 | 113 | virtual void Attack(mc::entity::EntityPtr entity) = 0; 114 | }; 115 | 116 | class MeleeAction : public AttackAction { 117 | private: 118 | const double AttackRangeSq = 3 * 3; 119 | 120 | enum { AttackDelay = 2000 }; 121 | 122 | public: 123 | MeleeAction(GameClient* client) 124 | : AttackAction(client, AttackDelay) 125 | { 126 | 127 | } 128 | 129 | void Attack(mc::entity::EntityPtr entity) { 130 | auto physics = GetActorComponent(m_Client, PhysicsComponent); 131 | 132 | Vector3d botPos = physics->GetPosition(); 133 | Vector3d targetPos = entity->GetPosition(); 134 | 135 | if ((targetPos - botPos).LengthSq() > AttackRangeSq) return; 136 | 137 | using namespace mc::protocol::packets::out; 138 | 139 | const s32 StoneSwordId = 272; 140 | 141 | SelectItem(StoneSwordId); 142 | 143 | // Send attack 144 | UseEntityPacket useEntityPacket(entity->GetEntityId(), UseEntityPacket::Action::Attack); 145 | m_Client->GetConnection()->SendPacket(&useEntityPacket); 146 | 147 | // Send arm swing 148 | AnimationPacket animationPacket(mc::Hand::Main); 149 | m_Client->GetConnection()->SendPacket(&animationPacket); 150 | } 151 | 152 | double DistanceToTarget() { 153 | mc::core::PlayerPtr targetPlayer = m_Client->GetPlayerManager()->GetPlayerByName(TargetPlayer); 154 | 155 | if (targetPlayer) { 156 | mc::entity::EntityPtr entity = targetPlayer->GetEntity(); 157 | if (entity) { 158 | auto physics = GetActorComponent(m_Client, PhysicsComponent); 159 | if (physics) 160 | return entity->GetPosition().Distance(physics->GetPosition()); 161 | } 162 | } 163 | return std::numeric_limits::max(); 164 | } 165 | }; 166 | 167 | void CreateBot(BotUpdate* update) { 168 | GameClient* client = update->GetClient(); 169 | 170 | auto effectComponent = std::make_unique(client->GetDispatcher()); 171 | update->AddComponent(std::move(effectComponent)); 172 | 173 | auto jump = std::make_unique(client->GetWorld(), 200); 174 | update->AddComponent(std::move(jump)); 175 | 176 | auto targeting = std::make_unique(); 177 | update->AddComponent(std::move(targeting)); 178 | 179 | auto targetPlayer = std::make_shared(client); 180 | 181 | std::shared_ptr pathfindDecision = std::make_shared>( 182 | targetPlayer, std::make_shared(client), std::bind(&TargetPlayerAction::HasTarget, targetPlayer), true, true 183 | ); 184 | 185 | auto meleeAction = std::make_shared(client); 186 | auto tree = std::make_shared>( 187 | meleeAction, pathfindDecision, std::bind(&MeleeAction::DistanceToTarget, meleeAction), 0.0, 2.0 188 | ); 189 | 190 | update->SetDecisionTree(tree); 191 | } 192 | 193 | void CleanupBot(BotUpdate* update) { 194 | GameClient* client = update->GetClient(); 195 | 196 | client->RemoveComponent(Component::GetIdFromName(EffectComponent::name)); 197 | client->RemoveComponent(Component::GetIdFromName(JumpComponent::name)); 198 | client->RemoveComponent(Component::GetIdFromName(TargetingComponent::name)); 199 | } 200 | 201 | int main(void) { 202 | mc::block::BlockRegistry::GetInstance()->RegisterVanillaBlocks(); 203 | mc::util::VersionFetcher versionFetcher(server, port); 204 | 205 | auto version = versionFetcher.GetVersion(); 206 | 207 | GameClient game(version); 208 | BotUpdate update(&game); 209 | 210 | CreateBot(&update); 211 | 212 | game.login(server, port, name, password); 213 | 214 | game.run(); 215 | 216 | CleanupBot(&update); 217 | return 0; 218 | } 219 | -------------------------------------------------------------------------------- /mcbot/mcbot.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug-Fast 6 | Win32 7 | 8 | 9 | Debug 10 | Win32 11 | 12 | 13 | Release 14 | Win32 15 | 16 | 17 | 18 | {70A1A00B-BE7F-4EF2-8D5F-90A07D3F192A} 19 | Win32Proj 20 | mcbot 21 | 22 | 23 | 24 | Application 25 | true 26 | v141 27 | Unicode 28 | 29 | 30 | Application 31 | true 32 | v141 33 | Unicode 34 | 35 | 36 | Application 37 | false 38 | v141 39 | true 40 | Unicode 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | true 57 | ../lib/mclib/lib/jsoncpp/include;../lib/mclib/mclib/include;$(VC_IncludePath);$(WindowsSDK_IncludePath); 58 | ../lib/mclib/lib/jsoncpp/lib;../lib/mclib/Debug;$(VC_LibraryPath_x86);$(WindowsSDK_LibraryPath_x86); 59 | 60 | 61 | true 62 | ../lib/mclib/lib/jsoncpp/include;../lib/mclib/mclib/include;$(VC_IncludePath);$(WindowsSDK_IncludePath); 63 | ../lib/mclib/Debug;$(VC_LibraryPath_x86);$(WindowsSDK_LibraryPath_x86); 64 | 65 | 66 | false 67 | ../lib/mclib/lib/jsoncpp/include;../lib/mclib/mclib/include;$(VC_IncludePath);$(WindowsSDK_IncludePath); 68 | ../lib/mclib/lib/jsoncpp/lib;../lib/mclib/Release;$(VC_LibraryPath_x86);$(WindowsSDK_LibraryPath_x86); 69 | 70 | 71 | 72 | 73 | 74 | Level3 75 | Disabled 76 | _CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) 77 | stdcpp14 78 | 79 | 80 | Console 81 | true 82 | jsoncppd-msvc-2017.lib;mclibd.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) 83 | 84 | 85 | 86 | 87 | 88 | 89 | Level3 90 | Disabled 91 | _CRT_SECURE_NO_WARNINGS;_HAS_ITERATOR_DEBUGGING=0;WIN32;_CONSOLE;_LIB;%(PreprocessorDefinitions) 92 | 93 | 94 | Console 95 | true 96 | mclibd.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) 97 | 98 | 99 | 100 | 101 | Level3 102 | 103 | 104 | MaxSpeed 105 | true 106 | true 107 | _CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) 108 | Fast 109 | stdcpp14 110 | 111 | 112 | Console 113 | true 114 | true 115 | true 116 | jsoncpp-msvc-2017.lib;mclib.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | -------------------------------------------------------------------------------- /mcbot/mcbot.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;hm;inl;inc;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | {41e55536-61e9-4f8a-b818-b055aca6946f} 18 | 19 | 20 | {abe8d9d7-b138-49eb-b36c-63654f80b5bf} 21 | 22 | 23 | {df0cff57-ca71-4b6a-a2b1-9e5d18c57a9f} 24 | 25 | 26 | 27 | 28 | Source Files 29 | 30 | 31 | Source Files 32 | 33 | 34 | Source Files 35 | 36 | 37 | Source Files 38 | 39 | 40 | Source Files 41 | 42 | 43 | Source Files 44 | 45 | 46 | Source Files 47 | 48 | 49 | Source Files 50 | 51 | 52 | Source Files\components 53 | 54 | 55 | Source Files\components 56 | 57 | 58 | Source Files\components 59 | 60 | 61 | Source Files\components 62 | 63 | 64 | Source Files\components 65 | 66 | 67 | Source Files 68 | 69 | 70 | Source Files 71 | 72 | 73 | Source Files 74 | 75 | 76 | Source Files\components 77 | 78 | 79 | 80 | 81 | Header Files 82 | 83 | 84 | Header Files 85 | 86 | 87 | Header Files 88 | 89 | 90 | Header Files 91 | 92 | 93 | Header Files 94 | 95 | 96 | Header Files 97 | 98 | 99 | Header Files 100 | 101 | 102 | Header Files 103 | 104 | 105 | Header Files 106 | 107 | 108 | Header Files\components 109 | 110 | 111 | Header Files\components 112 | 113 | 114 | Header Files\components 115 | 116 | 117 | Header Files\components 118 | 119 | 120 | Header Files\components 121 | 122 | 123 | Header Files 124 | 125 | 126 | Header Files 127 | 128 | 129 | Header Files 130 | 131 | 132 | Header Files 133 | 134 | 135 | Header Files\actions 136 | 137 | 138 | Header Files\components 139 | 140 | 141 | -------------------------------------------------------------------------------- /mcbot/mcbot.vcxproj.user: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | false 5 | 6 | --------------------------------------------------------------------------------