diff --git a/client/include/gui/Text.hpp b/client/include/gui/Text.hpp index d68d140cb..599f7a40e 100644 --- a/client/include/gui/Text.hpp +++ b/client/include/gui/Text.hpp @@ -25,6 +25,7 @@ #include +#include #include class Text : public gk::Drawable, public gk::Transformable { @@ -39,6 +40,13 @@ class Text : public gk::Drawable, public gk::Transformable { const gk::Vector2i &getSize() const { return m_size; } + void setBackgroundColor(const gk::Color &color) { m_background.setFillColor(color); } + void setBackgroundSize(unsigned int width, unsigned int height) { m_background.setSize(width, height); } + + void setPadding(int x, int y) { m_padding.x = x; m_padding.y = y; updateTextSprites(); } + + void setMaxLineLength(unsigned int maxLineLength) { m_maxLineLength = maxLineLength; updateTextSprites(); } + private: void draw(gk::RenderTarget &target, gk::RenderStates states) const override; @@ -54,8 +62,13 @@ class Text : public gk::Drawable, public gk::Transformable { gk::VertexBuffer m_vbo; gk::Vector2i m_size; + gk::Vector2i m_padding{0, 0}; gk::Color m_color = gk::Color::White; + + gk::RectangleShape m_background; + + unsigned int m_maxLineLength = 0; }; #endif // TEXT_HPP_ diff --git a/client/include/gui/TextInput.hpp b/client/include/gui/TextInput.hpp new file mode 100644 index 000000000..8a8d14148 --- /dev/null +++ b/client/include/gui/TextInput.hpp @@ -0,0 +1,53 @@ +/* + * ===================================================================================== + * + * OpenMiner + * Copyright (C) 2018-2020 Unarelith, Quentin Bazin + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * ===================================================================================== + */ +#ifndef TEXTINPUT_HPP_ +#define TEXTINPUT_HPP_ + +#include + +#include "Text.hpp" + +class TextInput : public gk::Drawable, public gk::Transformable { + public: + TextInput(); + + void onEvent(const SDL_Event &event); + + const std::string &text() const { return m_content; } + + void setBackgroundColor(const gk::Color &color) { m_text.setBackgroundColor(color); } + void setBackgroundSize(unsigned int width, unsigned int height) { m_text.setBackgroundSize(width, height); } + + void setPadding(int x, int y) { m_text.setPadding(x, y); } + + private: + void draw(gk::RenderTarget &target, gk::RenderStates states) const override; + + Text m_text; + std::string m_content; + char m_cursor = '_'; + + u16 m_characterLimit = 0; +}; + +#endif // TEXTINPUT_HPP_ diff --git a/client/include/hud/Chat.hpp b/client/include/hud/Chat.hpp new file mode 100644 index 000000000..833d74c01 --- /dev/null +++ b/client/include/hud/Chat.hpp @@ -0,0 +1,46 @@ +/* + * ===================================================================================== + * + * OpenMiner + * Copyright (C) 2018-2020 Unarelith, Quentin Bazin + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * ===================================================================================== + */ +#ifndef CHAT_HPP_ +#define CHAT_HPP_ + +#include + +#include "ChatMessage.hpp" + +class Client; + +class Chat : public gk::Drawable, public gk::Transformable { + public: + Chat(Client &client); + + void setMessageVisibility(bool areMessagesVisible); + + private: + void draw(gk::RenderTarget &target, gk::RenderStates states) const override; + + std::deque m_chatMessages; + + u32 m_posY = 0; +}; + +#endif // CHAT_HPP_ diff --git a/client/include/hud/ChatMessage.hpp b/client/include/hud/ChatMessage.hpp new file mode 100644 index 000000000..bb33a0b00 --- /dev/null +++ b/client/include/hud/ChatMessage.hpp @@ -0,0 +1,46 @@ +/* + * ===================================================================================== + * + * OpenMiner + * Copyright (C) 2018-2020 Unarelith, Quentin Bazin + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * ===================================================================================== + */ +#ifndef CHATMESSAGE_HPP_ +#define CHATMESSAGE_HPP_ + +#include "Text.hpp" + +class ChatMessage : public gk::Drawable, public gk::Transformable { + public: + ChatMessage(u16 clientID, const std::string &message, u32 posY); + + void setVisible(bool isVisible) { m_isVisible = isVisible; } + + const Text &text() const { return m_text; } + + private: + void draw(gk::RenderTarget &target, gk::RenderStates states) const override; + + Text m_text; + + gk::Timer m_timer; + + bool m_isVisible = false; +}; + +#endif // CHATMESSAGE_HPP_ diff --git a/client/include/hud/HUD.hpp b/client/include/hud/HUD.hpp index 9242dc031..e39ba4451 100644 --- a/client/include/hud/HUD.hpp +++ b/client/include/hud/HUD.hpp @@ -27,6 +27,7 @@ #include "BlockCursor.hpp" #include "BlockInfoWidget.hpp" +#include "Chat.hpp" #include "Crosshair.hpp" #include "DebugOverlay.hpp" #include "Hotbar.hpp" @@ -41,6 +42,8 @@ class HUD : public gk::Transformable, public gk::Drawable { void update(); + Chat &chat() { return m_chat; } + private: void draw(gk::RenderTarget &target, gk::RenderStates states) const override; @@ -58,6 +61,8 @@ class HUD : public gk::Transformable, public gk::Drawable { BlockInfoWidget m_blockInfoWidget; Text m_fpsText; + + Chat m_chat; }; #endif // HUD_HPP_ diff --git a/client/include/network/ClientCommandHandler.hpp b/client/include/network/ClientCommandHandler.hpp index d183add08..89800ffdd 100644 --- a/client/include/network/ClientCommandHandler.hpp +++ b/client/include/network/ClientCommandHandler.hpp @@ -48,9 +48,12 @@ class ClientCommandHandler { void sendBlockActivated(const glm::ivec4 &selectedBlock); void sendBlockInvUpdate(Inventory &inventory); void sendChunkRequest(s32 chunkX, s32 chunkY, s32 chunkZ); + void sendChatMessage(const std::string &message); void setupCallbacks(); + Client &client() { return m_client; } + bool isRegistryInitialized() const { return m_isRegistryInitialized; } private: diff --git a/client/include/states/ChatState.hpp b/client/include/states/ChatState.hpp new file mode 100644 index 000000000..1e40a09f0 --- /dev/null +++ b/client/include/states/ChatState.hpp @@ -0,0 +1,52 @@ +/* + * ===================================================================================== + * + * OpenMiner + * Copyright (C) 2018-2020 Unarelith, Quentin Bazin + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * ===================================================================================== + */ +#ifndef CHATSTATE_HPP_ +#define CHATSTATE_HPP_ + +#include "InterfaceState.hpp" +#include "TextInput.hpp" + +class ClientCommandHandler; +class Chat; + +class ChatState : public InterfaceState { + public: + ChatState(ClientCommandHandler &clientCommandHandler, Chat &chat, gk::ApplicationState *parent = nullptr); + + void updateTextInputGeometry(); + + void onEvent(const SDL_Event &event) override; + + void update() override; + + private: + void draw(gk::RenderTarget &target, gk::RenderStates states) const override; + + TextInput m_textInput; + + ClientCommandHandler &m_clientCommandHandler; + + Chat &m_chat; +}; + +#endif // CHATSTATE_HPP_ diff --git a/client/include/states/InterfaceState.hpp b/client/include/states/InterfaceState.hpp index 25160d28b..f8842829a 100644 --- a/client/include/states/InterfaceState.hpp +++ b/client/include/states/InterfaceState.hpp @@ -37,6 +37,8 @@ class InterfaceState : public gk::ApplicationState { void prepareDraw(gk::RenderTarget &target, gk::RenderStates &states) const; + bool m_drawBackground = true; + private: gk::Shader m_shader; // gk::View m_view; diff --git a/client/include/world/ClientPlayer.hpp b/client/include/world/ClientPlayer.hpp index 751dbca07..613bd910d 100644 --- a/client/include/world/ClientPlayer.hpp +++ b/client/include/world/ClientPlayer.hpp @@ -66,6 +66,8 @@ class ClientPlayer : public Player { float y() const { return m_y; } float z() const { return m_z; } + void setPosition(float x, float y, float z); + const gk::Camera &camera() { return m_camera; } private: diff --git a/client/source/gui/Text.cpp b/client/source/gui/Text.cpp index f61a67053..2c94981dd 100644 --- a/client/source/gui/Text.cpp +++ b/client/source/gui/Text.cpp @@ -27,6 +27,8 @@ #include "Text.hpp" Text::Text() : m_texture(gk::ResourceHandler::getInstance().get("texture-font")) { + m_background.setFillColor(gk::Color::Transparent); + updateCharWidth(); } @@ -47,6 +49,10 @@ void Text::setColor(const gk::Color &color) { void Text::draw(gk::RenderTarget &target, gk::RenderStates states) const { states.transform *= getTransform(); + target.draw(m_background, states); + + states.transform.translate(m_padding.x, m_padding.y); + for(const gk::Sprite &sprite : m_textSprites) { target.draw(sprite, states); } @@ -56,12 +62,12 @@ void Text::draw(gk::RenderTarget &target, gk::RenderStates states) const { void Text::updateTextSprites() { m_textSprites.clear(); - int x = 0; - int y = 0; - int maxX = 0; + unsigned int x = 0; + unsigned int y = 0; + unsigned int maxX = 0; gk::Color color = gk::Color{70, 70, 70, 255}; for(char c : m_text) { - if (c == '\n') { + if (c == '\n' || (m_maxLineLength && x + m_charWidth[(u8)c] >= m_maxLineLength)) { y += 9; x = 0; continue; @@ -78,7 +84,7 @@ void Text::updateTextSprites() { y = 0; color = m_color; for(char c : m_text) { - if (c == '\n') { + if (c == '\n' || (m_maxLineLength && x + m_charWidth[(u8)c] >= m_maxLineLength)) { maxX = std::max(x, maxX); y += 9; x = 0; @@ -96,7 +102,12 @@ void Text::updateTextSprites() { } m_size.x = std::max(x, maxX); - m_size.y = 8 + y * 9; + m_size.y = y + 9; + + unsigned int backgroundX = std::max(m_background.getSize().x, m_size.x + m_padding.x); + unsigned int backgroundY = std::max(m_background.getSize().y, m_size.y + m_padding.y); + + m_background.setSize(backgroundX, backgroundY); } // FIXME: Since I use the font from Minecraft assets, I needed to use diff --git a/client/source/gui/TextInput.cpp b/client/source/gui/TextInput.cpp new file mode 100644 index 000000000..9c02e09e4 --- /dev/null +++ b/client/source/gui/TextInput.cpp @@ -0,0 +1,55 @@ +/* + * ===================================================================================== + * + * OpenMiner + * Copyright (C) 2018-2020 Unarelith, Quentin Bazin + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * ===================================================================================== + */ +#include + +#include "TextInput.hpp" + +TextInput::TextInput() { + m_text.setText(std::string{m_cursor}); +} + +void TextInput::onEvent(const SDL_Event &event) { + if (event.type == SDL_KEYDOWN && event.key.keysym.sym == SDLK_BACKSPACE && !m_content.empty()) { + m_content.erase(m_content.begin() + m_content.length() - 1); + + m_text.setText(m_content + m_cursor); + } + + if (event.type == SDL_TEXTINPUT) { + std::string text = event.text.text; + for (char c : text) { + if (isprint(c) && (!m_characterLimit || m_content.size() < m_characterLimit)) { + m_content += c; + } + + m_text.setText(m_content + m_cursor); + } + } +} + +void TextInput::draw(gk::RenderTarget &target, gk::RenderStates states) const { + states.transform *= getTransform(); + + target.draw(m_text, states); +} + diff --git a/client/source/hud/Chat.cpp b/client/source/hud/Chat.cpp new file mode 100644 index 000000000..7fc02555c --- /dev/null +++ b/client/source/hud/Chat.cpp @@ -0,0 +1,54 @@ +/* + * ===================================================================================== + * + * OpenMiner + * Copyright (C) 2018-2020 Unarelith, Quentin Bazin + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * ===================================================================================== + */ +#include "Chat.hpp" +#include "Client.hpp" +#include "Config.hpp" + +Chat::Chat(Client &client) { + setPosition(2, Config::screenHeight / Config::guiScale - 50); + + client.setCommandCallback(Network::Command::ChatMessage, [this](sf::Packet &packet) { + u16 clientID; + std::string message; + packet >> clientID >> message; + + m_chatMessages.emplace_back(clientID, message, m_posY); + + m_posY += m_chatMessages.back().text().getSize().y + 1; + + move(0, -m_chatMessages.back().text().getSize().y - 1); + }); +} + +void Chat::setMessageVisibility(bool areMessagesVisible) { + for (auto &it : m_chatMessages) + it.setVisible(areMessagesVisible); +} + +void Chat::draw(gk::RenderTarget &target, gk::RenderStates states) const { + states.transform *= getTransform(); + + for (auto &it : m_chatMessages) + target.draw(it, states); +} + diff --git a/client/source/hud/ChatMessage.cpp b/client/source/hud/ChatMessage.cpp new file mode 100644 index 000000000..7ef2d5cec --- /dev/null +++ b/client/source/hud/ChatMessage.cpp @@ -0,0 +1,45 @@ +/* + * ===================================================================================== + * + * OpenMiner + * Copyright (C) 2018-2020 Unarelith, Quentin Bazin + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * ===================================================================================== + */ +#include "ChatMessage.hpp" + +ChatMessage::ChatMessage(u16 clientID, const std::string &message, u32 posY) { + if (clientID > 0) + m_text.setText(" " + message); + else + m_text.setText(message); + + m_text.setPosition(0, posY); + m_text.setBackgroundColor(gk::Color{0, 0, 0, 127}); + m_text.setBackgroundSize(300, 10); + m_text.setMaxLineLength(300); + m_text.setPadding(1, 1); + + m_timer.reset(); + m_timer.start(); +} + +void ChatMessage::draw(gk::RenderTarget &target, gk::RenderStates states) const { + if (m_timer.time() <= 10000 || m_isVisible) + target.draw(m_text, states); +} + diff --git a/client/source/hud/HUD.cpp b/client/source/hud/HUD.cpp index ea968ef22..7e7903e69 100644 --- a/client/source/hud/HUD.cpp +++ b/client/source/hud/HUD.cpp @@ -24,6 +24,7 @@ #include +#include "ClientCommandHandler.hpp" #include "ClientPlayer.hpp" #include "Config.hpp" #include "HUD.hpp" @@ -31,7 +32,8 @@ HUD::HUD(ClientPlayer &player, ClientWorld &world, ClientCommandHandler &client) : m_hotbar(player.inventory()), m_blockCursor(player, world, client), - m_debugOverlay(player, world) + m_debugOverlay(player, world), + m_chat(client.client()) { setScale(Config::guiScale, Config::guiScale, 1); @@ -98,6 +100,7 @@ void HUD::draw(gk::RenderTarget &target, gk::RenderStates states) const { target.draw(m_blockInfoWidget, states); target.draw(m_hotbar, states); target.draw(m_fpsText, states); + target.draw(m_chat, states); states.transform = gk::Transform::Identity; diff --git a/client/source/network/ClientCommandHandler.cpp b/client/source/network/ClientCommandHandler.cpp index 26e2f17b5..6af9be5b7 100644 --- a/client/source/network/ClientCommandHandler.cpp +++ b/client/source/network/ClientCommandHandler.cpp @@ -47,6 +47,7 @@ void ClientCommandHandler::sendPlayerPosUpdate() { packet << m_player.Player::x(); packet << m_player.Player::y(); packet << m_player.Player::z(); + packet << false; m_client.send(packet); } @@ -97,6 +98,15 @@ void ClientCommandHandler::sendChunkRequest(s32 chunkX, s32 chunkY, s32 chunkZ) m_client.send(packet); } +void ClientCommandHandler::sendChatMessage(const std::string &message) { + sf::Packet packet; + packet << Network::Command::ChatMessage; + // FIXME: Sending client id shouldn't be necessary + packet << m_client.id(); + packet << message; + m_client.send(packet); +} + void ClientCommandHandler::setupCallbacks() { m_client.setCommandCallback(Network::Command::RegistryData, [this](sf::Packet &packet) { Registry::getInstance().deserialize(packet); @@ -128,15 +138,16 @@ void ClientCommandHandler::setupCallbacks() { m_client.setCommandCallback(Network::Command::PlayerPosUpdate, [this](sf::Packet &packet) { s32 x, y, z; u16 clientId; + bool isTeleportation; packet >> clientId; packet >> x >> y >> z; + packet >> isTeleportation; if (clientId != m_client.id()) m_playerBoxes.at(clientId).setPosition(x, y, z); - // else { - // m_camera.setPosition(x, y, z); - // m_player.setPosition(x, y, z); - // } + else if (isTeleportation) { + m_player.setPosition(x, y, z); + } }); m_client.setCommandCallback(Network::Command::PlayerSpawn, [this](sf::Packet &packet) { diff --git a/client/source/states/ChatState.cpp b/client/source/states/ChatState.cpp new file mode 100644 index 000000000..aef5f2ad7 --- /dev/null +++ b/client/source/states/ChatState.cpp @@ -0,0 +1,100 @@ +/* + * ===================================================================================== + * + * OpenMiner + * Copyright (C) 2018-2020 Unarelith, Quentin Bazin + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * ===================================================================================== + */ +#include +#include + +#include "Chat.hpp" +#include "ChatState.hpp" +#include "ClientCommandHandler.hpp" +#include "Config.hpp" + +ChatState::ChatState(ClientCommandHandler &clientCommandHandler, Chat &chat, gk::ApplicationState *parent) + : InterfaceState(parent), m_clientCommandHandler(clientCommandHandler), m_chat(chat) +{ + gk::Mouse::setCursorGrabbed(false); + gk::Mouse::setCursorVisible(true); + gk::Mouse::resetToWindowCenter(); + + m_drawBackground = false; + + m_textInput.setScale(Config::guiScale, Config::guiScale); + m_textInput.setBackgroundColor(gk::Color{0, 0, 0, 127}); + m_textInput.setPadding(1, 1); + + updateTextInputGeometry(); + + m_chat.setMessageVisibility(true); +} + +void ChatState::updateTextInputGeometry() { + m_textInput.setPosition(4, Config::screenHeight - 12 * Config::guiScale); + m_textInput.setBackgroundSize(Config::screenWidth / Config::guiScale - 4, 10); +} + +void ChatState::onEvent(const SDL_Event &event) { + InterfaceState::onEvent(event); + + m_textInput.onEvent(event); + + if (event.type == SDL_WINDOWEVENT && event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED) { + updateTextInputGeometry(); + } + + if (event.type == SDL_KEYDOWN && event.key.keysym.sym == SDLK_ESCAPE) { + gk::Mouse::setCursorGrabbed(true); + gk::Mouse::setCursorVisible(false); + gk::Mouse::resetToWindowCenter(); + + m_chat.setMessageVisibility(false); + + m_stateStack->pop(); + } + + if (event.type == SDL_KEYDOWN && event.key.keysym.sym == SDLK_RETURN) { + if (!m_textInput.text().empty()) + m_clientCommandHandler.sendChatMessage(m_textInput.text()); + + gk::Mouse::setCursorGrabbed(true); + gk::Mouse::setCursorVisible(false); + gk::Mouse::resetToWindowCenter(); + + m_chat.setMessageVisibility(false); + + m_stateStack->pop(); + } +} + +void ChatState::update() { +} + +void ChatState::draw(gk::RenderTarget &target, gk::RenderStates states) const { + if (m_parent) + target.draw(*m_parent, states); + + if (&m_stateStack->top() == this) { + prepareDraw(target, states); + + target.draw(m_textInput, states); + } +} + diff --git a/client/source/states/GameState.cpp b/client/source/states/GameState.cpp index 05394a130..22826522c 100644 --- a/client/source/states/GameState.cpp +++ b/client/source/states/GameState.cpp @@ -32,6 +32,7 @@ #include #include +#include "ChatState.hpp" #include "GameKey.hpp" #include "GameState.hpp" #include "LuaGUIState.hpp" @@ -78,6 +79,10 @@ void GameState::onEvent(const SDL_Event &event) { else if (event.type == SDL_KEYDOWN && event.key.keysym.sym == SDLK_ESCAPE) { m_stateStack->push(this); } + // FIXME: Use GamePad/GameKey instead of a hardcoded keycode + else if (event.type == SDL_KEYDOWN && event.key.keysym.sym == SDLK_t) { + m_stateStack->push(m_clientCommandHandler, m_hud.chat(), this); + } else if (event.type == SDL_WINDOWEVENT) { if (event.window.event == SDL_WINDOWEVENT_FOCUS_LOST) { // FIXME diff --git a/client/source/states/InterfaceState.cpp b/client/source/states/InterfaceState.cpp index 89910b06e..239c304d9 100644 --- a/client/source/states/InterfaceState.cpp +++ b/client/source/states/InterfaceState.cpp @@ -67,7 +67,7 @@ void InterfaceState::prepareDraw(gk::RenderTarget &target, gk::RenderStates &sta // target.setView(m_view); - if (m_parent) + if (m_parent && m_drawBackground) target.draw(m_background, states); } diff --git a/client/source/world/ClientPlayer.cpp b/client/source/world/ClientPlayer.cpp index 690573a1d..2fb909e5a 100644 --- a/client/source/world/ClientPlayer.cpp +++ b/client/source/world/ClientPlayer.cpp @@ -143,6 +143,15 @@ void ClientPlayer::updatePosition(const ClientWorld &world) { m_velocity.z = 0; } +void ClientPlayer::setPosition(float x, float y, float z) { + m_x = x; + m_y = y; + m_z = z; + + Player::setPosition(x, y, z); + m_camera.setPosition(x, y, z - 0.1); +} + void ClientPlayer::checkCollisions(const ClientWorld &world) { #if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__) || defined(__ANDROID__) const float PLAYER_HEIGHT = 1.8; diff --git a/common/include/network/Network.hpp b/common/include/network/Network.hpp index 280625613..f2e397fdb 100644 --- a/common/include/network/Network.hpp +++ b/common/include/network/Network.hpp @@ -44,7 +44,7 @@ namespace Network { PlayerPlaceBlock = 7, // [NetworkCommand][s32 x, y, z][u32 block] (from Client only) PlayerDigBlock = 8, // [NetworkCommand][s32 x, y, z] (from Client only) PlayerInvUpdate = 9, // [NetworkCommand][u16 client id][[std::string item][u16 amount][u8 x, y]...] (both) [FIXME] - PlayerPosUpdate = 10, // [NetworkCommand][u16 client id][s32 x, y, z] (both) // FIXME + PlayerPosUpdate = 10, // [NetworkCommand][u16 client id][s32 x, y, z][bool isTeleportation] (both) // FIXME PlayerSpawn = 11, // [NetworkCommand][u16 client id][s32 x, y, z] (from Server only) PlayerInventory = 12, // [NetworkCommand][u16 screenWidth, screenHeight][u8 guiScale] (from Client only) @@ -57,6 +57,9 @@ namespace Network { // Registry commands RegistryData = 18, // [NetworkCommand][Block block] (from Server only) + + // Chat commands + ChatMessage = 19, // [NetworkCommand][u16 client id][std::string message] (both) }; std::string commandToString(Command command); diff --git a/common/source/network/Network.cpp b/common/source/network/Network.cpp index d94e7f7cd..de2c0ad44 100644 --- a/common/source/network/Network.cpp +++ b/common/source/network/Network.cpp @@ -52,6 +52,8 @@ std::string Network::commandToString(Network::Command command) { {Network::Command::BlockDataUpdate, "BlockDataUpdate"}, {Network::Command::RegistryData, "RegistryData"}, + + {Network::Command::ChatMessage, "ChatMessage"} }; return commandNames[command]; } diff --git a/server/include/network/ServerCommandHandler.hpp b/server/include/network/ServerCommandHandler.hpp index b56ccf8c6..e981b5fe1 100644 --- a/server/include/network/ServerCommandHandler.hpp +++ b/server/include/network/ServerCommandHandler.hpp @@ -44,7 +44,8 @@ class ServerCommandHandler { void sendBlockDataUpdate(s32 x, s32 y, s32 z, const BlockData *blockData, const Client *client = nullptr) const; void sendBlockInvUpdate(s32 x, s32 y, s32 z, const Inventory &inventory, const Client *client = nullptr) const; - void sendPlayerPosUpdate(u16 clientID, const ServerPlayer &player, const Client *client = nullptr) const; + void sendPlayerPosUpdate(u16 clientID, bool isTeleportation = false, const Client *client = nullptr) const; + void sendChatMessage(u16 clientID, const std::string &message, const Client *client = nullptr) const; void setupCallbacks(); diff --git a/server/source/core/ServerApplication.cpp b/server/source/core/ServerApplication.cpp index 2ab5eb728..6b110ebc6 100644 --- a/server/source/core/ServerApplication.cpp +++ b/server/source/core/ServerApplication.cpp @@ -71,7 +71,7 @@ void ServerApplication::update() { if (gk::GameClock::getTicks() % 1000 < 10) { for (auto &it : m_players) { - m_serverCommandHandler.sendPlayerPosUpdate(it.first, it.second); + m_serverCommandHandler.sendPlayerPosUpdate(it.first); } } } diff --git a/server/source/network/ServerCommandHandler.cpp b/server/source/network/ServerCommandHandler.cpp index 5e76c1aed..ceea3d058 100644 --- a/server/source/network/ServerCommandHandler.cpp +++ b/server/source/network/ServerCommandHandler.cpp @@ -50,11 +50,24 @@ void ServerCommandHandler::sendBlockInvUpdate(s32 x, s32 y, s32 z, const Invento client->tcpSocket->send(packet); } -void ServerCommandHandler::sendPlayerPosUpdate(u16 clientID, const ServerPlayer &player, const Client *client) const { +void ServerCommandHandler::sendPlayerPosUpdate(u16 clientID, bool isTeleportation, const Client *client) const { + const ServerPlayer &player = m_players.at(clientID); + sf::Packet packet; packet << Network::Command::PlayerPosUpdate; packet << clientID; packet << player.x() << player.y() << player.z(); + packet << isTeleportation; + + if (!client) + m_server.sendToAllClients(packet); + else + client->tcpSocket->send(packet); +} + +void ServerCommandHandler::sendChatMessage(u16 clientID, const std::string &message, const Client *client) const { + sf::Packet packet; + packet << Network::Command::ChatMessage << clientID << message; if (!client) m_server.sendToAllClients(packet); @@ -184,5 +197,46 @@ void ServerCommandHandler::setupCallbacks() { packet >> data->meta >> data->useAltTiles; } }); + + m_server.setCommandCallback(Network::Command::ChatMessage, [this](Client &client, sf::Packet &packet) { + u16 clientID; + std::string message; + packet >> clientID >> message; + + if (message[0] != '/') { + sendChatMessage(clientID, message); + } + // FIXME: Do a proper implementation later + else { + std::stringstream sstream; + sstream << message.substr(1); + + std::vector command; + std::string line; + while (std::getline(sstream, line, ' ')) { + command.emplace_back(line); + } + + if (!command.empty()) { + if (command.at(0) == "tp") { + if (command.size() != 4) { + // FIXME: ID 0 should be server messages + sendChatMessage(0, "Usage: /tp x y z", &client); + } + else { + s32 x = std::stoi(command.at(1)); + s32 y = std::stoi(command.at(2)); + s32 z = std::stoi(command.at(3)); + + m_players.at(clientID).setPosition(x, y, z); + + sendPlayerPosUpdate(clientID, true); + + sendChatMessage(0, "Teleported to " + std::to_string(x) + " " + std::to_string(y) + " " + std::to_string(z), &client); + } + } + } + } + }); } diff --git a/server/source/network/ServerInfo.cpp b/server/source/network/ServerInfo.cpp index 4dafbee39..9ee896745 100644 --- a/server/source/network/ServerInfo.cpp +++ b/server/source/network/ServerInfo.cpp @@ -23,7 +23,7 @@ #include "ServerInfo.hpp" Client &ServerInfo::addClient(sf::IpAddress address, u16 port, const std::shared_ptr &socket) { - m_clients.emplace_back(m_clients.size(), address, port, socket); + m_clients.emplace_back(m_clients.size() + 1, address, port, socket); return m_clients.back(); }