summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Entity.hpp4
-rw-r--r--src/GameState.cpp20
-rw-r--r--src/NetworkClient.cpp6
-rw-r--r--src/Packet.hpp21
-rw-r--r--src/Render.cpp96
-rw-r--r--src/RendererSection.cpp2
-rw-r--r--src/RendererWidget.cpp76
-rw-r--r--src/RendererWidget.hpp15
-rw-r--r--src/RendererWorld.cpp2
-rw-r--r--src/Stream.cpp7
-rw-r--r--src/Widget.cpp124
-rw-r--r--src/Widget.hpp74
-rw-r--r--src/Window.cpp32
-rw-r--r--src/Window.hpp8
-rw-r--r--src/World.cpp3
15 files changed, 170 insertions, 320 deletions
diff --git a/src/Entity.hpp b/src/Entity.hpp
index 279f127..c818abe 100644
--- a/src/Entity.hpp
+++ b/src/Entity.hpp
@@ -126,8 +126,8 @@ struct Entity {
unsigned int entityId = 0;
double yaw = 0;
double pitch = 0;
- double width = 0.1;
- double height = 0.1;
+ double width = 1.0;
+ double height = 1.0;
glm::vec3 renderColor;
int entityType=0;
bool isMob=false;
diff --git a/src/GameState.cpp b/src/GameState.cpp
index 46b236c..98b49f7 100644
--- a/src/GameState.cpp
+++ b/src/GameState.cpp
@@ -140,8 +140,17 @@ void GameState::UpdatePacket()
world.ParseChunkData(packet);
break;
}
- case ConfirmTransactionCB:
+ case ConfirmTransactionCB: {
+ auto packet = std::static_pointer_cast<PacketConfirmTransactionCB>(ptr);
+ if (packet->WindowId == 0) {
+ try {
+ playerInventory.ConfirmTransaction(*packet);
+ } catch (std::exception &e) {
+ EventAgregator::PushEvent(EventType::Disconnect, DisconnectData{ "Transaction failed" });
+ }
+ }
break;
+ }
case CloseWindowCB:
break;
case OpenWindow: {
@@ -160,8 +169,13 @@ void GameState::UpdatePacket()
}
case WindowProperty:
break;
- case SetSlot:
+ case SetSlot: {
+ auto packet = std::static_pointer_cast<PacketSetSlot>(ptr);
+ if (packet->WindowId == 0) {
+ playerInventory.slots[packet->Slot] = packet->SlotData;
+ }
break;
+ }
case SetCooldown:
break;
case PluginMessageCB:
@@ -433,7 +447,7 @@ void GameState::HandleMovement(GameState::Direction direction, float deltaTime)
break;
case JUMP:
if (g_OnGround) {
- vel.y += 10;
+ vel.y += 5;
g_OnGround = false;
}
break;
diff --git a/src/NetworkClient.cpp b/src/NetworkClient.cpp
index 5877952..027eeb5 100644
--- a/src/NetworkClient.cpp
+++ b/src/NetworkClient.cpp
@@ -5,7 +5,7 @@ NetworkClient::NetworkClient(std::string address, unsigned short port, std::stri
state = Handshaking;
PacketHandshake handshake;
- handshake.protocolVersion = 335;
+ handshake.protocolVersion = 338;
handshake.serverAddress = address;
handshake.serverPort = port;
handshake.nextState = 2;
@@ -17,6 +17,10 @@ NetworkClient::NetworkClient(std::string address, unsigned short port, std::stri
network.SendPacket(loginStart);
auto response = std::static_pointer_cast<PacketLoginSuccess>(network.ReceivePacket(Login));
+
+ while (!response)
+ response = std::static_pointer_cast<PacketLoginSuccess>(network.ReceivePacket(Login));
+
if (response->Username != username) {
throw std::logic_error("Received username is not sended username");
}
diff --git a/src/Packet.hpp b/src/Packet.hpp
index e00f1ed..f71922f 100644
--- a/src/Packet.hpp
+++ b/src/Packet.hpp
@@ -8,7 +8,6 @@ enum PacketNameLoginSB {
};
enum PacketNamePlaySB {
TeleportConfirm = 0x00,
- PrepareCraftingGrid,
TabCompleteSB,
ChatMessageSB,
ClientStatus,
@@ -26,6 +25,7 @@ enum PacketNamePlaySB {
PlayerLook,
VehicleMoveSB,
SteerBoat,
+ CraftRecipeRequest,
PlayerAbilitiesSB,
PlayerDigging,
EntityAction,
@@ -95,6 +95,7 @@ enum PacketNamePlayCB {
EntityCB,
VehicleMove,
OpenSignEditor,
+ CraftRecipeResponse,
PlayerAbilitiesCB,
CombatEvent,
PlayerListItem,
@@ -986,7 +987,7 @@ struct PacketClickWindow : Packet {
stream->WriteShort(Slot);
stream->WriteByte(Button);
stream->WriteShort(ActionNumber);
- stream->WriteInt(Mode);
+ stream->WriteVarInt(Mode);
stream->WriteSlot(ClickedItem);
}
@@ -1023,4 +1024,20 @@ struct PacketCloseWindowSB : Packet {
}
unsigned char WindowId;
+};
+
+struct PacketDisconnect : Packet {
+ void ToStream(StreamOutput *stream) override {
+
+ }
+
+ void FromStream(StreamInput *stream) override {
+ Reason = stream->ReadChat();
+ }
+
+ int GetPacketId() override {
+ return PacketNameLoginCB::Disconnect;
+ }
+
+ std::string Reason;
}; \ No newline at end of file
diff --git a/src/Render.cpp b/src/Render.cpp
index 2382ec2..1473ae0 100644
--- a/src/Render.cpp
+++ b/src/Render.cpp
@@ -153,6 +153,7 @@ void Render::HandleEvents() {
SetMouseCapture(false);
if (state == GlobalState::Playing)
state = GlobalState::Paused;
+ isDisplayInventory = false;
break;
}
break;
@@ -298,8 +299,10 @@ void Render::RenderGui() {
ImGui::Text("TPS: %.1f (%.2fms)", 1000.0f/gameTime, gameTime);
ImGui::Text("Sections loaded: %d", (int)DebugInfo::totalSections);
ImGui::Text("SectionsRenderer: %d (%d)", (int)DebugInfo::renderSections, (int)DebugInfo::readyRenderer);
- if (world)
+ if (world) {
ImGui::Text("Player pos: %.1f %.1f %.1f", world->GameStatePtr()->g_PlayerX, world->GameStatePtr()->g_PlayerY, world->GameStatePtr()->g_PlayerZ);
+ ImGui::Text("Player health: %.1f/%.1f", world->GameStatePtr()->g_PlayerHealth, 20.0f);
+ }
ImGui::End();
@@ -324,25 +327,90 @@ void Render::RenderGui() {
break;
case GlobalState::Playing:
if (isDisplayInventory) {
+ auto renderSlot = [](const SlotData &slot, int i) -> bool {
+ return ImGui::Button(((slot.BlockId == -1 ? " ##" :
+ AssetManager::Instance().GetAssetNameByBlockId(BlockId{ (unsigned short)slot.BlockId,0 }) +" x"+std::to_string(slot.ItemCount) + "##")
+ + std::to_string(i)).c_str());
+ };
ImGui::SetNextWindowPosCenter();
ImGui::Begin("Inventory", 0, windowFlags);
Window& inventory = world->GameStatePtr()->playerInventory;
- for (int i = 0; i < inventory.slots.size()+1; i++) {
- SlotData slot;
- if (i == inventory.slots.size())
- slot = inventory.handSlot;
- else
- slot = inventory.slots[i];
-
- if (slot.BlockId == -1) {
- ImGui::Button("Empty");
- continue;
+ //Hand and drop slots
+ if (renderSlot(inventory.handSlot, -1)) {
+
+ }
+ ImGui::SameLine();
+ if (ImGui::Button("Drop")) {
+ inventory.MakeClick(-1, true, true);
+ }
+ ImGui::SameLine();
+ ImGui::Text("Hand slot and drop mode");
+ ImGui::Separator();
+ //Crafting
+ if (renderSlot(inventory.slots[1], 1)) {
+ inventory.MakeClick(1, true);
+ }
+ ImGui::SameLine();
+ if (renderSlot(inventory.slots[2], 2)) {
+ inventory.MakeClick(2, true);
+ }
+ //Crafting result
+ ImGui::SameLine();
+ ImGui::Text("Result");
+ ImGui::SameLine();
+ if (renderSlot(inventory.slots[0], 0)) {
+ inventory.MakeClick(0, true);
+ }
+ //Crafting second line
+ if (renderSlot(inventory.slots[3], 3)) {
+ inventory.MakeClick(3, true);
+ }
+ ImGui::SameLine();
+ if (renderSlot(inventory.slots[4], 4)) {
+ inventory.MakeClick(4, true);
+ }
+ ImGui::Separator();
+ //Armor and offhand
+ for (int i = 5; i < 8+1; i++) {
+ if (renderSlot(inventory.slots[i], i)) {
+ inventory.MakeClick(i, true);
}
- std::string slotName = AssetManager::Instance().GetAssetNameByBlockId(BlockId{(unsigned short) slot.BlockId,0 });
- if (ImGui::Button((slotName + "##"+std::to_string(i)).c_str())) {
+ ImGui::SameLine();
+ }
+ if (renderSlot(inventory.slots[45], 45)) {
+ inventory.MakeClick(45, true);
+ }
+ ImGui::SameLine();
+ ImGui::Text("Armor and offhand");
+ ImGui::Separator();
+ for (int i = 36; i < 44+1; i++) {
+ if (renderSlot(inventory.slots[i], i)) {
+ inventory.MakeClick(i, true);
+ }
+ ImGui::SameLine();
+ }
+ ImGui::Text("Hotbar");
+ ImGui::Separator();
+ ImGui::Text("Main inventory");
+ for (int i = 9; i < 17 + 1; i++) {
+ if (renderSlot(inventory.slots[i], i)) {
+ inventory.MakeClick(i, true);
+ }
+ ImGui::SameLine();
+ }
+ ImGui::Text("");
+ for (int i = 18; i < 26 + 1; i++) {
+ if (renderSlot(inventory.slots[i], i)) {
+ inventory.MakeClick(i, true);
+ }
+ ImGui::SameLine();
+ }
+ ImGui::Text("");
+ for (int i = 27; i < 35 + 1; i++) {
+ if (renderSlot(inventory.slots[i], i)) {
inventory.MakeClick(i, true);
- LOG(INFO) << "Clicked " << slotName << "("<<slot.BlockId<<") in slot " << i;
}
+ ImGui::SameLine();
}
ImGui::End();
}
diff --git a/src/RendererSection.cpp b/src/RendererSection.cpp
index 22acc61..8262cef 100644
--- a/src/RendererSection.cpp
+++ b/src/RendererSection.cpp
@@ -154,7 +154,7 @@ void swap(RendererSection & lhs, RendererSection & rhs) {
void RendererSection::Render(RenderState &renderState) {
renderState.SetActiveVao(Vao);
- glDrawArraysInstanced(GL_TRIANGLES, 0, 6, numOfFaces);
+ glDrawArraysInstanced(GL_TRIANGLES, 0, 6, numOfFaces);
glCheckError();
}
diff --git a/src/RendererWidget.cpp b/src/RendererWidget.cpp
deleted file mode 100644
index 6b732e3..0000000
--- a/src/RendererWidget.cpp
+++ /dev/null
@@ -1,76 +0,0 @@
-#include "RendererWidget.hpp"
-
-const GLfloat vertices[] = {
- 0.0f,0.0f,0.0f,
- 1.0f,1.0f,0.0f,
- 0.0f,1.0f,0.0f,
-
- 0.0f,0.0f,0.0f,
- 1.0f,0.0f,0.0f,
- 1.0f,1.0f,0.0f,
-};
-
-const GLfloat uvs[] = {
- 0.0f,0.0f,
- 1.0f,1.0f,
- 0.0f,1.0f,
-
- 0.0f,0.0f,
- 1.0f,0.0f,
- 1.0f,1.0f,
-};
-
-static GLuint VboVertices, VboUvs, Vao;
-static Shader* guiShader = nullptr;
-
-RendererWidget::RendererWidget(RootWidget *widget) {
- this->tree = widget;
-
- if (guiShader == nullptr) {
- guiShader = new Shader("./shaders/gui.vs", "./shaders/gui.fs");
- guiShader->Use();
- glUniform1i(glGetUniformLocation(guiShader->Program, "textureAtlas"), 0);
-
- glGenBuffers(1, &VboVertices);
- glBindBuffer(GL_ARRAY_BUFFER, VboVertices);
- glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
-
- glGenBuffers(1, &VboUvs);
- glBindBuffer(GL_ARRAY_BUFFER, VboUvs);
- glBufferData(GL_ARRAY_BUFFER, sizeof(uvs), uvs, GL_STATIC_DRAW);
-
- glGenVertexArrays(1, &Vao);
- glBindVertexArray(Vao);
- {
- glBindBuffer(GL_ARRAY_BUFFER, VboVertices);
- glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), (GLvoid*)0);
- glEnableVertexAttribArray(0);
-
- glBindBuffer(GL_ARRAY_BUFFER, VboUvs);
- glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(GLfloat), (GLvoid*)0);
- glEnableVertexAttribArray(1);
- }
- glBindVertexArray(0);
- }
-}
-
-RendererWidget::~RendererWidget() {
-
-}
-
-void RendererWidget::Render(RenderState &state) {
- state.SetActiveVao(Vao);
- state.SetActiveShader(guiShader->Program);
-
- auto toRender = tree->GetRenderList();
- for (auto& it : toRender) {
- auto[x, y, w, h] = it->GetTexture();
- glUniform4f(glGetUniformLocation(guiShader->Program, "widgetTexture"), x, y, w, h);
-
- glUniform4f(glGetUniformLocation(guiShader->Program, "transform"), it->x, it->y, it->w, it->h);
-
- glDrawArrays(GL_TRIANGLES, 0, 36);
- }
-
- glCheckError();
-} \ No newline at end of file
diff --git a/src/RendererWidget.hpp b/src/RendererWidget.hpp
deleted file mode 100644
index a979c88..0000000
--- a/src/RendererWidget.hpp
+++ /dev/null
@@ -1,15 +0,0 @@
-#pragma once
-
-#include "Renderer.hpp"
-#include "Widget.hpp"
-#include "Shader.hpp"
-
-class RendererWidget {
- RootWidget *tree;
-
-public:
- RendererWidget(RootWidget *widget);
- ~RendererWidget();
-
- void Render(RenderState &state);
-}; \ No newline at end of file
diff --git a/src/RendererWorld.cpp b/src/RendererWorld.cpp
index 3e5868b..a641cf2 100644
--- a/src/RendererWorld.cpp
+++ b/src/RendererWorld.cpp
@@ -253,7 +253,7 @@ void RendererWorld::Render(RenderState & renderState) {
}
double lengthToSection = (VectorF(gs->g_PlayerX, gs->g_PlayerY, gs->g_PlayerZ) - VectorF(section.first.x*16,section.first.y*16,section.first.z*16)).GetLength();
- if (isBreak && lengthToSection > 30.0f && false) {
+ if (isBreak && lengthToSection > 30.0f) {
sectionsMutex.lock();
continue;
}
diff --git a/src/Stream.cpp b/src/Stream.cpp
index 665247d..6f93c9c 100644
--- a/src/Stream.cpp
+++ b/src/Stream.cpp
@@ -275,7 +275,12 @@ void StreamOutput::WriteSlot(SlotData value) {
return;
WriteByte(value.ItemCount);
WriteShort(value.ItemDamage);
- WriteUByte(0);
+ /*unsigned char nbt[] = {
+ //0x0a, 0x00, 0x02, 0x68, 0x69, 0x00,
+ 0x01, 0x04, 0xCA, 0xFE, 0xBA, 0xBE,
+ };
+ WriteByteArray(std::vector<unsigned char>(nbt,nbt + sizeof(nbt)));*/
+ WriteByte(0);
}
void StreamOutput::WriteNbtTag(std::vector<unsigned char> value) {
diff --git a/src/Widget.cpp b/src/Widget.cpp
deleted file mode 100644
index 1a522ca..0000000
--- a/src/Widget.cpp
+++ /dev/null
@@ -1,124 +0,0 @@
-#include "Widget.hpp"
-
-void RootWidget::AttachWidget(std::unique_ptr<Widget> widget, Widget * parent)
-{
- parent->childs.push_back(widget.get());
- this->allWidgets.push_back(std::move(widget));
-}
-
-void RootWidget::AttachWidget(std::unique_ptr<Widget> widget) {
- widget->parent = nullptr;
- this->childs.push_back(widget.get());
- this->allWidgets.push_back(std::move(widget));
-}
-
-std::vector<Widget*> RootWidget::GetRenderList()
-{
- std::vector<Widget*> renderList;
-
- std::function<void(Widget*)> treeWalker = [&](Widget* node) {
- for (auto it : node->childs)
- treeWalker(it);
- renderList.push_back(node);
- };
-
- for (auto& it : this->childs)
- treeWalker(it);
-
- return renderList;
-}
-
-void RootWidget::UpdateEvents(double mouseX, double mouseY, bool mouseButton) {
-
- LOG(INFO) << mouseX << "x" << mouseY;
-
- auto testIsHover = [&](double x, double y, Widget* widget) {
- bool isOnX = widget->x > x && widget->x + widget->w < x;
- bool isOnY = widget->y > y && widget->y + widget->h < y;
- if (mouseButton)
- LOG(INFO) << "X: " << isOnX << " Y: " << isOnY;
- return isOnX && isOnY;
- };
-
- std::function<void(Widget*)> treeWalker = [&](Widget* node) {
- for (auto it : node->childs)
- treeWalker(it);
-
- if (testIsHover(mouseX,mouseY,node)) {
- if (node->onHover)
- node->onHover(node);
- if (mouseButton && !prevBut)
- if (node->onPress)
- node->onPress(node);
- else if (!mouseButton && prevBut)
- if (node->onRelease)
- node->onRelease(node);
- }
- else {
- if (testIsHover(prevX, prevY, node))
- if (node->onUnhover)
- node->onUnhover(node);
- }
-
- if (node->onUpdate)
- node->onUpdate(node);
- };
-
- for (auto it : childs)
- treeWalker(it);
-
- prevX = mouseX;
- prevY = mouseY;
- prevBut = mouseButton;
-}
-
-WidgetButton::WidgetButton()
-{
- this->state = WidgetState::Idle;
-
- onHover = [](Widget* widget) {
- WidgetButton* w = dynamic_cast<WidgetButton*>(widget);
- if (w->state != WidgetState::Pressed)
- w->state = WidgetState::Hovering;
- LOG(INFO) << "Hover";
- };
-
- onPress = [](Widget* widget) {
- WidgetButton* w = dynamic_cast<WidgetButton*>(widget);
- w->state = WidgetState::Pressed;
- LOG(INFO) << "Press";
- };
-
- onRelease = [](Widget* widget) {
- WidgetButton* w = dynamic_cast<WidgetButton*>(widget);
- w->state = WidgetState::Idle;
- w->onClick(w);
- LOG(INFO) << "Release";
- };
-
- onUnhover = [](Widget *widget) {
- WidgetButton* w = dynamic_cast<WidgetButton*>(widget);
- if (w->state!=WidgetState::Pressed)
- w->state = WidgetState::Idle;
- LOG(INFO) << "Unhover";
- };
-
-}
-
-std::tuple<double, double, double, double> WidgetButton::GetTexture()
-{
- double yOffset;
- switch (this->state) {
- case WidgetState::Idle:
- yOffset = 0.2578;
- break;
- case WidgetState::Hovering:
- yOffset = 0.3359;
- break;
- case WidgetState::Pressed:
- yOffset = 0.1796;
- }
-
- TextureCoordinates texture = AssetManager::Instance().GetTextureByAssetName("minecraft/textures/gui/widgets");
- return { texture.x,texture.y + texture.h * yOffset,texture.w * 0.7812,texture.h * 0.07812 };
-}
diff --git a/src/Widget.hpp b/src/Widget.hpp
deleted file mode 100644
index 6d78ebe..0000000
--- a/src/Widget.hpp
+++ /dev/null
@@ -1,74 +0,0 @@
-#pragma once
-
-#include <vector>
-#include <memory>
-#include <functional>
-
-#include "AssetManager.hpp"
-
-class Widget;
-class RootWidget {
- std::vector<std::unique_ptr<Widget>> allWidgets;
-
- std::vector<Widget*> childs;
-
- double prevX, prevY;
- bool prevBut;
-public:
- RootWidget() = default;
-
- ~RootWidget() = default;
-
- void AttachWidget(std::unique_ptr<Widget> widget, Widget* parent);
-
- void AttachWidget(std::unique_ptr<Widget> widget);
-
- std::vector<Widget*> GetRenderList();
-
- void UpdateEvents(double mouseX, double mouseY, bool mouseButton);
-};
-
-struct Widget {
- Widget() = default;
-
- virtual ~Widget() = default;
-
- Widget *parent;
-
- std::vector<Widget*> childs;
-
- double x, y, w, h; //In OGL screen-coordinates
-
- virtual std::tuple<double, double, double, double> GetTexture() = 0;
-
-
- using Handler = std::function<void(Widget*)>;
-
- Handler onPress;
-
- Handler onRelease;
-
- Handler onHover;
-
- Handler onUnhover;
-
- Handler onUpdate;
-};
-
-struct WidgetButton : Widget {
- WidgetButton();
-
- ~WidgetButton() override = default;
-
- std::string Text;
-
- Handler onClick;
-
- std::tuple<double, double, double, double> GetTexture() override;
-
- enum class WidgetState {
- Idle,
- Hovering,
- Pressed,
- } state;
-}; \ No newline at end of file
diff --git a/src/Window.cpp b/src/Window.cpp
index fbf9aaa..f3ad6d5 100644
--- a/src/Window.cpp
+++ b/src/Window.cpp
@@ -1,6 +1,32 @@
#include "Window.hpp"
-void Window::MakeClick(short ClickedSlot, bool Lmb) {
- PacketClickWindow packet(WindowId, ClickedSlot, Lmb? 0 : 1, actions++, 0, slots[ClickedSlot]);
- this->pendingTransactions.push(packet);
+void Window::MakeClick(short ClickedSlot, bool Lmb, bool dropMode) {
+ if (!dropMode) {
+ PacketClickWindow packet(WindowId, ClickedSlot, Lmb ? 0 : 1, actions++, 0, slots[ClickedSlot]);
+ this->pendingTransactions.push(packet);
+ std::swap(slots[ClickedSlot], handSlot);
+ transactions.push_back(std::make_pair(actions, std::make_pair(ClickedSlot, -1)));
+ } else {
+ PacketClickWindow packet(WindowId, ClickedSlot, Lmb ? 0 : 1, actions++, 0, SlotData());
+ this->pendingTransactions.push(packet);
+ transactions.push_back(std::make_pair(actions, std::make_pair(ClickedSlot, -1)));
+ }
+}
+
+void Window::ConfirmTransaction(PacketConfirmTransactionCB packet) {
+ if (!packet.Accepted) {
+ throw std::logic_error("Transaction failed");
+ }
+ /*auto toDelete = transactions.begin();
+ for (auto it = transactions.begin(); it != transactions.end(); ++it) {
+ if (it->first == packet.ActionNumber) {
+ toDelete = it;
+ if (!packet.Accepted) {
+ std::swap(slots[std::get<0>(it->second)], slots[std::get<1>(it->second)]);
+ }
+ break;
+ }
+ }
+ if (toDelete->first == packet.ActionNumber)
+ transactions.erase(toDelete);*/
} \ No newline at end of file
diff --git a/src/Window.hpp b/src/Window.hpp
index 4dae294..a6bccd9 100644
--- a/src/Window.hpp
+++ b/src/Window.hpp
@@ -8,13 +8,17 @@
struct Window {
unsigned char WindowId = 0;
std::string type;
-
+
SlotData handSlot;
+ const short HandSlotId = -1;
std::vector<SlotData> slots;
short actions = 1;
- void MakeClick(short ClickedSlot, bool Lmb);
+ void MakeClick(short ClickedSlot, bool Lmb, bool dropMode = false);
std::queue<PacketClickWindow> pendingTransactions;
+ std::vector<std::pair<short,std::pair<short,short>>> transactions;
+
+ void ConfirmTransaction(PacketConfirmTransactionCB packet);
}; \ No newline at end of file
diff --git a/src/World.cpp b/src/World.cpp
index 5b4bb03..8cff53a 100644
--- a/src/World.cpp
+++ b/src/World.cpp
@@ -173,7 +173,8 @@ void World::AddEntity(Entity entity)
entitiesMutex.lock();
for (auto& it : entities) {
if (it.entityId == entity.entityId) {
- LOG(ERROR) << "Adding already existing entity" << entity.entityId;
+ LOG(ERROR) << "Adding already existing entity: " << entity.entityId;
+ entitiesMutex.unlock();
return;
}
}