Packets are now compressed using zlib. Legacy and unused UDP code removed.

This commit is contained in:
Quentin Bazin 2020-05-10 19:36:39 +02:00
parent 05a652bbb6
commit c277dc7d87
23 changed files with 276 additions and 190 deletions

4
.gitmodules vendored
View File

@ -9,3 +9,7 @@
[submodule "external/gamekit"]
path = external/gamekit
url = git://github.com/Unarelith/GameKit.git
[submodule "external/zlib"]
path = external/zlib
url = git://github.com/Unarelith/zlib.git
ignore = dirty

View File

@ -201,6 +201,12 @@ set(BUILD_SHARED_LIBS OFF)
add_subdirectory(external/SFML)
#------------------------------------------------------------------------------
# - zlib
#------------------------------------------------------------------------------
add_subdirectory(external/zlib)
include_directories(external/zlib)
#------------------------------------------------------------------------------
# Subdirectories
#------------------------------------------------------------------------------

1
external/zlib vendored Submodule

@ -0,0 +1 @@
Subproject commit 9c920698c36f1f059d5ed4501e38b08421780164

View File

@ -61,5 +61,6 @@ target_link_libraries(${PROJECT_NAME}
${LUA_LIBRARIES}
sfml-system
sfml-network
zlib
${UNIX_LIBS})

View File

@ -42,11 +42,11 @@ void Client::connect(sf::IpAddress serverAddress, u16 serverPort) {
if (m_socket.bind(0) != sf::Socket::Done)
throw ClientConnectException("Network error: Bind failed");
sf::Packet packet;
Network::Packet packet;
packet << Network::Command::ClientConnect << sf::IpAddress::getLocalAddress().toString() << m_socket.getLocalPort();
m_tcpSocket->send(packet);
sf::Packet answer;
Network::Packet answer;
m_tcpSocket->receive(answer);
Network::Command command;
@ -69,52 +69,31 @@ void Client::connect(sf::IpAddress serverAddress, u16 serverPort) {
}
void Client::disconnect() {
sf::Packet packet;
Network::Packet packet;
packet << Network::Command::ClientDisconnect;
m_tcpSocket->send(packet);
m_tcpSocket->disconnect();
}
void Client::send(sf::Packet &packet) {
void Client::send(Network::Packet &packet) {
if (m_tcpSocket)
m_tcpSocket->send(packet);
else
throw EXCEPTION("Network error: Trying to send a packet without being connected");
}
void Client::sendKeyState() {
if (!m_keyUpdateTimer.isStarted())
m_keyUpdateTimer.start();
if (m_keyUpdateTimer.time() > 15) {
gk::InputHandler *inputHandler = gk::GamePad::getInputHandler();
if (inputHandler) {
sf::Packet packet;
packet << Network::Command::KeyState << gk::GameClock::getInstance().getTicks() << m_id;
for (auto &it : inputHandler->keysPressed()) {
packet << static_cast<u8>(it.first) << it.second;
}
m_socket.send(packet, m_serverAddress, m_serverPort);
}
m_keyUpdateTimer.reset();
m_keyUpdateTimer.start();
}
}
void Client::update() {
sf::Packet packet;
sf::IpAddress senderAddress;
u16 senderPort;
while (m_socket.receive(packet, senderAddress, senderPort) == sf::Socket::Done) {
Network::Command command;
packet >> command;
// gkDebug() << "UDP Message of type" << Network::commandToString(command) << "received from:" << senderAddress << ":" << senderPort;
}
// sf::IpAddress senderAddress;
// u16 senderPort;
// while (m_socket.receive(packet, senderAddress, senderPort) == sf::Socket::Done) {
// Network::Command command;
// packet >> command;
//
// // gkDebug() << "UDP Message of type" << Network::commandToString(command) << "received from:" << senderAddress << ":" << senderPort;
// }
Network::Packet packet;
while (m_tcpSocket->receive(packet) == sf::Socket::Done) {
Network::Command command;
packet >> command;

View File

@ -53,14 +53,13 @@ class ClientConnectException {
};
class Client {
using CommandCallback = std::function<void(sf::Packet &packet)>;
using CommandCallback = std::function<void(Network::Packet &packet)>;
public:
void connect(sf::IpAddress serverAddress, u16 serverPort);
void disconnect();
void send(sf::Packet &packet);
void sendKeyState();
void send(Network::Packet &packet);
void update();

View File

@ -43,7 +43,7 @@
#include "Registry.hpp"
void ClientCommandHandler::sendPlayerInvUpdate() {
sf::Packet invPacket;
Network::Packet invPacket;
invPacket << Network::Command::PlayerInvUpdate;
// FIXME: Sending client id shouldn't be necessary
invPacket << m_client.id();
@ -52,7 +52,7 @@ void ClientCommandHandler::sendPlayerInvUpdate() {
}
void ClientCommandHandler::sendPlayerPosUpdate() {
sf::Packet packet;
Network::Packet packet;
packet << Network::Command::PlayerPosUpdate;
// FIXME: Sending client id shouldn't be necessary
packet << m_client.id();
@ -64,7 +64,7 @@ void ClientCommandHandler::sendPlayerPosUpdate() {
}
void ClientCommandHandler::sendPlayerDigBlock(const glm::ivec4 &selectedBlock) {
sf::Packet packet;
Network::Packet packet;
packet << Network::Command::PlayerDigBlock
<< s32(selectedBlock.x)
<< s32(selectedBlock.y)
@ -73,27 +73,27 @@ void ClientCommandHandler::sendPlayerDigBlock(const glm::ivec4 &selectedBlock) {
}
void ClientCommandHandler::sendPlayerPlaceBlock(s32 x, s32 y, s32 z, u32 block) {
sf::Packet packet;
Network::Packet packet;
packet << Network::Command::PlayerPlaceBlock << x << y << z << block;
m_client.send(packet);
}
void ClientCommandHandler::sendPlayerInventoryRequest() {
sf::Packet packet;
Network::Packet packet;
packet << Network::Command::PlayerInventory
<< u16(Config::screenWidth) << u16(Config::screenHeight) << u8(Config::guiScale);
m_client.send(packet);
}
void ClientCommandHandler::sendPlayerCreativeWindowRequest() {
sf::Packet packet;
Network::Packet packet;
packet << Network::Command::PlayerCreativeWindow
<< u16(Config::screenWidth) << u16(Config::screenHeight) << u8(Config::guiScale);
m_client.send(packet);
}
void ClientCommandHandler::sendBlockActivated(const glm::ivec4 &selectedBlock) {
sf::Packet packet;
Network::Packet packet;
packet << Network::Command::BlockActivated
<< s32(selectedBlock.x)
<< s32(selectedBlock.y)
@ -103,7 +103,7 @@ void ClientCommandHandler::sendBlockActivated(const glm::ivec4 &selectedBlock) {
}
void ClientCommandHandler::sendBlockInvUpdate(Inventory &inventory) {
sf::Packet packet;
Network::Packet packet;
packet << Network::Command::BlockInvUpdate;
packet << s32(inventory.blockPos().x) << s32(inventory.blockPos().y) << s32(inventory.blockPos().z);
packet << inventory;
@ -111,14 +111,14 @@ void ClientCommandHandler::sendBlockInvUpdate(Inventory &inventory) {
}
void ClientCommandHandler::sendChunkRequest(s32 chunkX, s32 chunkY, s32 chunkZ) {
sf::Packet packet;
Network::Packet packet;
packet << Network::Command::ChunkRequest;
packet << chunkX << chunkY << chunkZ;
m_client.send(packet);
}
void ClientCommandHandler::sendChatMessage(const std::string &message) {
sf::Packet packet;
Network::Packet packet;
packet << Network::Command::ChatMessage;
// FIXME: Sending client id shouldn't be necessary
packet << m_client.id();
@ -127,7 +127,7 @@ void ClientCommandHandler::sendChatMessage(const std::string &message) {
}
void ClientCommandHandler::setupCallbacks() {
m_client.setCommandCallback(Network::Command::ClientDisconnect, [this](sf::Packet &packet) {
m_client.setCommandCallback(Network::Command::ClientDisconnect, [this](Network::Packet &packet) {
u16 clientID;
packet >> clientID;
@ -136,7 +136,7 @@ void ClientCommandHandler::setupCallbacks() {
m_playerBoxes.erase(it);
});
m_client.setCommandCallback(Network::Command::RegistryData, [this](sf::Packet &packet) {
m_client.setCommandCallback(Network::Command::RegistryData, [this](Network::Packet &packet) {
// FIXME: This is a quick fix for concurrency between client and server in singleplayer
if (!m_isSingleplayer)
Registry::getInstance().deserialize(packet);
@ -144,11 +144,11 @@ void ClientCommandHandler::setupCallbacks() {
m_isRegistryInitialized = true;
});
m_client.setCommandCallback(Network::Command::ChunkData, [this](sf::Packet &packet) {
m_client.setCommandCallback(Network::Command::ChunkData, [this](Network::Packet &packet) {
m_world.receiveChunkData(packet);
});
m_client.setCommandCallback(Network::Command::BlockUpdate, [this](sf::Packet &packet) {
m_client.setCommandCallback(Network::Command::BlockUpdate, [this](Network::Packet &packet) {
s32 x, y, z;
u32 block;
packet >> x >> y >> z >> block;
@ -156,7 +156,7 @@ void ClientCommandHandler::setupCallbacks() {
m_world.setData(x, y, z, block >> 16);
});
m_client.setCommandCallback(Network::Command::PlayerInvUpdate, [this](sf::Packet &packet) {
m_client.setCommandCallback(Network::Command::PlayerInvUpdate, [this](Network::Packet &packet) {
u16 clientId;
packet >> clientId;
@ -166,7 +166,7 @@ void ClientCommandHandler::setupCallbacks() {
packet >> m_playerBoxes.at(clientId).inventory();
});
m_client.setCommandCallback(Network::Command::PlayerPosUpdate, [this](sf::Packet &packet) {
m_client.setCommandCallback(Network::Command::PlayerPosUpdate, [this](Network::Packet &packet) {
double x, y, z;
u16 clientId;
bool isTeleportation;
@ -184,7 +184,7 @@ void ClientCommandHandler::setupCallbacks() {
}
});
m_client.setCommandCallback(Network::Command::PlayerSpawn, [this](sf::Packet &packet) {
m_client.setCommandCallback(Network::Command::PlayerSpawn, [this](Network::Packet &packet) {
u16 clientId;
gk::Vector3d pos;
packet >> clientId >> pos.x >> pos.y >> pos.z;
@ -199,7 +199,7 @@ void ClientCommandHandler::setupCallbacks() {
}
});
m_client.setCommandCallback(Network::Command::PlayerChangeDimension, [this](sf::Packet &packet) {
m_client.setCommandCallback(Network::Command::PlayerChangeDimension, [this](Network::Packet &packet) {
u16 clientId, dimension;
s32 x, y, z;
packet >> clientId >> x >> y >> z >> dimension;
@ -213,11 +213,11 @@ void ClientCommandHandler::setupCallbacks() {
}
});
m_client.setCommandCallback(Network::Command::BlockGUIData, [this](sf::Packet &packet) {
m_client.setCommandCallback(Network::Command::BlockGUIData, [this](Network::Packet &packet) {
gk::ApplicationStateStack::getInstance().push<LuaGUIState>(*this, m_player, m_world, packet, &gk::ApplicationStateStack::getInstance().top());
});
m_client.setCommandCallback(Network::Command::BlockInvUpdate, [this](sf::Packet &packet) {
m_client.setCommandCallback(Network::Command::BlockInvUpdate, [this](Network::Packet &packet) {
gk::Vector3<s32> pos;
packet >> pos.x >> pos.y >> pos.z;
@ -229,7 +229,7 @@ void ClientCommandHandler::setupCallbacks() {
packet >> data->inventory;
});
m_client.setCommandCallback(Network::Command::BlockDataUpdate, [this](sf::Packet &packet) {
m_client.setCommandCallback(Network::Command::BlockDataUpdate, [this](Network::Packet &packet) {
gk::Vector3<s32> pos;
packet >> pos.x >> pos.y >> pos.z;
@ -251,7 +251,7 @@ void ClientCommandHandler::setupCallbacks() {
}
});
m_client.setCommandCallback(Network::Command::EntitySpawn, [this](sf::Packet &packet) {
m_client.setCommandCallback(Network::Command::EntitySpawn, [this](Network::Packet &packet) {
entt::entity entityID;
packet >> entityID;
@ -268,7 +268,7 @@ void ClientCommandHandler::setupCallbacks() {
}
});
m_client.setCommandCallback(Network::Command::EntityDespawn, [this](sf::Packet &packet) {
m_client.setCommandCallback(Network::Command::EntityDespawn, [this](Network::Packet &packet) {
entt::entity entityID;
packet >> entityID;
@ -280,7 +280,7 @@ void ClientCommandHandler::setupCallbacks() {
gkError() << "EntityDespawn: Entity ID" << std::underlying_type_t<entt::entity>(entityID) << "is invalid";
});
m_client.setCommandCallback(Network::Command::EntityPosition, [this](sf::Packet &packet) {
m_client.setCommandCallback(Network::Command::EntityPosition, [this](Network::Packet &packet) {
entt::entity entityID;
packet >> entityID;
@ -293,7 +293,7 @@ void ClientCommandHandler::setupCallbacks() {
gkError() << "EntityPosition: Entity ID" << std::underlying_type_t<entt::entity>(entityID) << "is invalid";
});
m_client.setCommandCallback(Network::Command::EntityRotation, [this](sf::Packet &packet) {
m_client.setCommandCallback(Network::Command::EntityRotation, [this](Network::Packet &packet) {
entt::entity entityID;
packet >> entityID;
@ -309,7 +309,7 @@ void ClientCommandHandler::setupCallbacks() {
gkError() << "EntityRotation: Entity ID" << std::underlying_type_t<entt::entity>(entityID) << "is invalid";
});
m_client.setCommandCallback(Network::Command::EntityAnimation, [this](sf::Packet &packet) {
m_client.setCommandCallback(Network::Command::EntityAnimation, [this](Network::Packet &packet) {
entt::entity entityID;
packet >> entityID;
@ -322,7 +322,7 @@ void ClientCommandHandler::setupCallbacks() {
gkError() << "EntityAnimation: Entity ID" << std::underlying_type_t<entt::entity>(entityID) << "is invalid";
});
m_client.setCommandCallback(Network::Command::EntityDrawableDef, [this](sf::Packet &packet) {
m_client.setCommandCallback(Network::Command::EntityDrawableDef, [this](Network::Packet &packet) {
entt::entity entityID;
packet >> entityID;

View File

@ -114,7 +114,7 @@ void ClientWorld::updateSky(u16 dimensionID) {
m_sky = &sky;
}
void ClientWorld::receiveChunkData(sf::Packet &packet) {
void ClientWorld::receiveChunkData(Network::Packet &packet) {
s32 cx, cy, cz;
packet >> cx >> cy >> cz;

View File

@ -55,7 +55,7 @@ class ClientWorld : public World, public gk::Drawable {
void updateSky(u16 dimensionID);
void receiveChunkData(sf::Packet &packet);
void receiveChunkData(Network::Packet &packet);
void removeChunk(ChunkMap::iterator &it);
Chunk *getChunk(int cx, int cy, int cz) const override;

View File

@ -18,7 +18,7 @@ endforeach(HEADER_FILE)
# Add library
#------------------------------------------------------------------------------
add_library(${PROJECT_NAME} STATIC ${SOURCE_FILES})
add_dependencies(${PROJECT_NAME} EnTT sfml-network gamekit)
add_dependencies(${PROJECT_NAME} zlib EnTT sfml-network gamekit)
#------------------------------------------------------------------------------
# Compiler flags
@ -35,5 +35,5 @@ target_compile_options(${PROJECT_NAME} PRIVATE -DSOL_CHECK_ARGUMENTS -DSOL_PRINT
# target_compile_options(${PROJECT_NAME} PRIVATE -pg)
target_link_libraries(${PROJECT_NAME} sfml-system sfml-network)
target_link_libraries(${PROJECT_NAME} sfml-system sfml-network zlib)

View File

@ -0,0 +1,94 @@
/*
* =====================================================================================
*
* OpenMiner
*
* Copyright (C) 2018-2020 Unarelith, Quentin Bazin <openminer@unarelith.net>
* Copyright (C) 2019-2020 the OpenMiner contributors (see CONTRIBUTORS.md)
*
* This file is part of OpenMiner.
*
* OpenMiner 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.
*
* OpenMiner 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 OpenMiner; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* =====================================================================================
*/
#include <cassert>
#include "CompressedPacket.hpp"
// Note: This class was implemented thanks to this SFML forum topic:
// https://en.sfml-dev.org/forums/index.php?topic=14344.0
const void* CompressedPacket::onSend(std::size_t& size) {
// We only support data with a maximum size of
// an unsigned short (so the size can be sent
// in the first two bytes of the packet)
assert(size <= 65535);
// Cast the data to a bytef pointer
const Bytef* srcData = static_cast<const Bytef*>(getData());
// Get the size of the packet to send
uLong srcSize = getDataSize();
// Compute the size of the compressed data
uLong dstSize = compressBound(srcSize);
// Resize the vector to accomodate the compressed data,
// plus two bytes for our uncompressed size
m_compressionBuffer.resize(dstSize + 2);
// Take the first 8 bytes of srcSize
m_compressionBuffer[0] = srcSize & 0xFF;
// And the second 8 bytes
m_compressionBuffer[1] = (srcSize >> 8) & 0xFF;
// Compress the data into the rest of the buffer
compress(m_compressionBuffer.data() + 2, &dstSize, srcData, srcSize);
// Set the size to the compressed size plus
// two bytes for the size marker
size = (dstSize + 2);
// Return data to send
return m_compressionBuffer.data();
}
void CompressedPacket::onReceive(const void* data, std::size_t size) {
// Cast the data to Bytef*, the format zlib deals with
const Bytef* srcData = static_cast<const Bytef*>(data);
// Extract the uncompressed data size from the first two
// bytes in the packet so we can use it for the buffer
sf::Uint16 uncompressedSize = srcData[1] << 8 | srcData[0];
// Resize the vector to accomodate the uncompressed data
m_compressionBuffer.resize(uncompressedSize);
// Declare a variable for the destination size
uLong dstSize;
// Uncompress the data (remove the first two bytes)
uncompress(m_compressionBuffer.data(), &dstSize, (srcData + 2), size - 2);
// Assert that the uncompressed size is the same as the
// size we were sent for the buffer
assert(dstSize == uncompressedSize);
// Append it to the packet
append(m_compressionBuffer.data(), dstSize);
}

View File

@ -0,0 +1,44 @@
/*
* =====================================================================================
*
* OpenMiner
*
* Copyright (C) 2018-2020 Unarelith, Quentin Bazin <openminer@unarelith.net>
* Copyright (C) 2019-2020 the OpenMiner contributors (see CONTRIBUTORS.md)
*
* This file is part of OpenMiner.
*
* OpenMiner 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.
*
* OpenMiner 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 OpenMiner; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* =====================================================================================
*/
#ifndef COMPRESSEDPACKET_HPP_
#define COMPRESSEDPACKET_HPP_
#include <gk/core/IntTypes.hpp>
#include <SFML/Network/Packet.hpp>
#include <zlib.h>
class CompressedPacket : public sf::Packet {
private:
const void* onSend(std::size_t& size) override;
void onReceive(const void* data, std::size_t size) override;
std::vector<Bytef> m_compressionBuffer;
};
#endif // COMPRESSEDPACKET_HPP_

View File

@ -37,8 +37,6 @@ std::string Network::commandToString(Network::Command command) {
{Network::Command::ClientOk, "ClientOk"},
{Network::Command::ClientRefused, "ClientRefused"},
{Network::Command::KeyState, "KeyState"},
{Network::Command::ChunkData, "ChunkData"},
{Network::Command::ChunkRequest, "ChunkRequest"},

View File

@ -27,53 +27,52 @@
#ifndef NETWORK_HPP_
#define NETWORK_HPP_
#include <SFML/Network/Packet.hpp>
#include "CompressedPacket.hpp"
namespace Network {
using Packet = CompressedPacket;
enum class Command {
// Client commands
ClientConnect = 0, // <TCP> [NetworkCommand][u16 udp port] (from Client only)
ClientDisconnect = 1, // <TCP> [NetworkCommand] (from Client only)
ClientOk = 2, // <TCP> [NetworkCommand][u16 client id][bool isSingleplayer] (from Server only)
ClientRefused = 3, // <TCP> [NetworkCommand] (from Server only)
// Input commands
KeyState = 4, // <UDP> [NetworkCommand][u32 timestamp][u16 client id][u32 keycode][bool isPressed]...
ClientConnect = 0x00, // <TCP> [NetworkCommand][u16 udp port] (from Client only)
ClientDisconnect = 0x01, // <TCP> [NetworkCommand] (from Client only)
ClientOk = 0x02, // <TCP> [NetworkCommand][u16 client id][bool isSingleplayer] (from Server only)
ClientRefused = 0x03, // <TCP> [NetworkCommand] (from Server only)
// Chunk commands
ChunkData = 5, // <TCP> [NetworkCommand][s32 cx, cy, cz][u32...] (from Server only)
ChunkRequest = 6, // <TCP> [NetworkCommand][s32 cx, cy, cz] (from Client only)
ChunkData = 0x04, // <TCP> [NetworkCommand][s32 cx, cy, cz][u32...] (from Server only)
ChunkRequest = 0x05, // <TCP> [NetworkCommand][s32 cx, cy, cz] (from Client only)
// Player commands
PlayerPlaceBlock = 7, // <TCP> [NetworkCommand][s32 x, y, z][u32 block] (from Client only)
PlayerDigBlock = 8, // <TCP> [NetworkCommand][s32 x, y, z] (from Client only)
PlayerInvUpdate = 9, // <TCP> [NetworkCommand][u16 client id][[std::string item][u16 amount][u8 x, y]...] (both) [FIXME]
PlayerPosUpdate = 10, // <TCP> [NetworkCommand][u16 client id][s32 x, y, z][bool isTeleportation] (both) // FIXME
PlayerSpawn = 11, // <TCP> [NetworkCommand][u16 client id][s32 x, y, z] (from Server only)
PlayerInventory = 12, // <TCP> [NetworkCommand][u16 screenWidth, screenHeight][u8 guiScale] (from Client only)
PlayerCreativeWindow = 13, // <TCP> [NetworkCommand][u16 screenWidth, screenHeight][u8 guiScale] (from Client only)
PlayerChangeDimension = 14, // <TCP> [NetworkCommand][u16 client id][s32 x, y, z][u16 dimension] (from Server only)
PlayerPlaceBlock = 0x06, // <TCP> [NetworkCommand][s32 x, y, z][u32 block] (from Client only)
PlayerDigBlock = 0x07, // <TCP> [NetworkCommand][s32 x, y, z] (from Client only)
PlayerInvUpdate = 0x08, // <TCP> [NetworkCommand][u16 client id][[std::string item][u16 amount][u8 x, y]...] (both) [FIXME]
PlayerPosUpdate = 0x09, // <TCP> [NetworkCommand][u16 client id][s32 x, y, z][bool isTeleportation] (both) // FIXME
PlayerSpawn = 0x0a, // <TCP> [NetworkCommand][u16 client id][s32 x, y, z] (from Server only)
PlayerInventory = 0x0b, // <TCP> [NetworkCommand][u16 screenWidth, screenHeight][u8 guiScale] (from Client only)
PlayerCreativeWindow = 0x0c, // <TCP> [NetworkCommand][u16 screenWidth, screenHeight][u8 guiScale] (from Client only)
PlayerChangeDimension = 0x0d, // <TCP> [NetworkCommand][u16 client id][s32 x, y, z][u16 dimension] (from Server only)
// Block commands
BlockUpdate = 15, // <TCP> [NetworkCommand][s32 x, y, z][u32 block] (from Server only)
BlockActivated = 16, // <TCP> [NetworkCommand][s32 x, y, z][u16 screenWidth, screenHeight][u8 guiScale] (from Client only)
BlockGUIData = 17, // <TCP> [NetworkCommand][LuaGUIData data] (from Server only)
BlockInvUpdate = 18, // <TCP> [NetworkCommand][s32 x, y, z][[std::string item][u16 amount][u8 x, y]...] (both) [FIXME]
BlockDataUpdate = 19, // <TCP> [NetworkCommand][s32 x, y, z][u64 data] (both) [FIXME]
BlockUpdate = 0x0e, // <TCP> [NetworkCommand][s32 x, y, z][u32 block] (from Server only)
BlockActivated = 0x0f, // <TCP> [NetworkCommand][s32 x, y, z][u16 screenWidth, screenHeight][u8 guiScale] (from Client only)
BlockGUIData = 0x10, // <TCP> [NetworkCommand][LuaGUIData data] (from Server only)
BlockInvUpdate = 0x11, // <TCP> [NetworkCommand][s32 x, y, z][[std::string item][u16 amount][u8 x, y]...] (both) [FIXME]
BlockDataUpdate = 0x12, // <TCP> [NetworkCommand][s32 x, y, z][u64 data] (both) [FIXME]
// Registry commands
RegistryData = 20, // <TCP> [NetworkCommand][Block block] (from Server only)
RegistryData = 0x13, // <TCP> [NetworkCommand][Block block] (from Server only)
// Chat commands
ChatMessage = 21, // <TCP> [NetworkCommand][u16 client id][std::string message] (both)
ChatMessage = 0x14, // <TCP> [NetworkCommand][u16 client id][std::string message] (both)
// Entity commands
EntitySpawn = 22, // <TCP> [NetworkCommand][u32 entity id] (from Server only)
EntityDespawn = 23, // <TCP> [NetworkCommand][u32 entity id] (from Server only)
EntityPosition = 24, // <TCP> [NetworkCommand][u32 entity id][double x, double y, double z] (from Server only)
EntityRotation = 25, // <TCP> [NetworkCommand][u32 entity id][float w, float x, float y, float z] (from Server only)
EntityAnimation = 26, // <TCP> [NetworkCommand][u32 entity id][AnimationComponent anim] (from Server only)
EntityDrawableDef = 27, // <TCP> [NetworkCommand][u32 entity id][DrawableDef def] (from Server only)
EntitySpawn = 0x15, // <TCP> [NetworkCommand][u32 entity id] (from Server only)
EntityDespawn = 0x16, // <TCP> [NetworkCommand][u32 entity id] (from Server only)
EntityPosition = 0x17, // <TCP> [NetworkCommand][u32 entity id][double x, double y, double z] (from Server only)
EntityRotation = 0x18, // <TCP> [NetworkCommand][u32 entity id][float w, float x, float y, float z] (from Server only)
EntityAnimation = 0x19, // <TCP> [NetworkCommand][u32 entity id][AnimationComponent anim] (from Server only)
EntityDrawableDef = 0x1a, // <TCP> [NetworkCommand][u32 entity id][DrawableDef def] (from Server only)
};
std::string commandToString(Command command);

View File

@ -80,5 +80,6 @@ target_link_libraries(${PROJECT_NAME}
${LUA_LIBRARIES}
sfml-system
sfml-network
zlib
${UNIX_LIBS})

View File

@ -121,8 +121,6 @@ void ServerApplication::mainLoop() {
while (m_server.isRunning()) {
m_server.handleGameEvents();
m_server.handleKeyState();
m_clock.updateGame([this] {
update();
});

View File

@ -92,7 +92,7 @@ void LuaGUI::addInventory(const sol::table &table) {
}
void LuaGUI::show(ClientInfo &client) {
sf::Packet packet;
Network::Packet packet;
packet << Network::Command::BlockGUIData;
packet << m_width << m_height << m_isCentered;

View File

@ -27,12 +27,7 @@
#include "Server.hpp"
void Server::init(u16 port) {
if (m_udpSocket.bind(port) != sf::Socket::Done)
throw EXCEPTION("Network error: Bind failed");
m_udpSocket.setBlocking(false);
m_port = m_udpSocket.getLocalPort();
m_port = port;
if (m_tcpListener.listen(m_port) != sf::Socket::Done)
throw EXCEPTION("Network error: Listen failed");
@ -42,38 +37,6 @@ void Server::init(u16 port) {
m_selector.add(m_tcpListener);
}
void Server::handleKeyState() {
sf::Packet packet;
sf::IpAddress senderAddress;
u16 senderPort;
while (m_udpSocket.receive(packet, senderAddress, senderPort) == sf::Socket::Status::Done) {
Network::Command command;
u32 timestamp;
u16 clientId;
packet >> command >> timestamp >> clientId;
// gkDebug() << "UDP Message of type" << Network::commandToString(command) << "received from:" << senderAddress << ":" << senderPort;
if (command == Network::Command::KeyState) {
ClientInfo *client = m_info.getClient(clientId);
if (client && client->previousKeyTimestamp < timestamp) {
client->previousKeyTimestamp = timestamp;
while (!packet.endOfPacket()) {
u8 key;
bool isPressed;
packet >> key >> isPressed;
if (client->inputHandler.keysPressed().at(key) != isPressed)
gkDebug() << (int)key << "changed state to" << (isPressed ? "true" : "false");
client->inputHandler.setKeyPressed(key, isPressed);
}
}
}
}
}
void Server::handleGameEvents() {
if (m_selector.wait(sf::milliseconds(10))) {
if (m_selector.isReady(m_tcpListener)) {
@ -88,7 +51,7 @@ void Server::handleGameEvents() {
void Server::handleNewConnections() {
std::shared_ptr<sf::TcpSocket> clientSocket = std::make_shared<sf::TcpSocket>();
if (m_tcpListener.accept(*clientSocket) == sf::Socket::Done) {
sf::Packet packet;
Network::Packet packet;
clientSocket->receive(packet);
Network::Command command;
@ -104,7 +67,7 @@ void Server::handleNewConnections() {
ClientInfo &client = m_info.addClient(address, port, clientSocket);
m_selector.add(*client.tcpSocket);
sf::Packet outPacket;
Network::Packet outPacket;
outPacket << Network::Command::ClientOk << client.id << m_isSingleplayer;
client.tcpSocket->send(outPacket);
// client.tcpSocket->setBlocking(false);
@ -113,7 +76,7 @@ void Server::handleNewConnections() {
m_connectionCallback(client);
}
else {
sf::Packet outPacket;
Network::Packet outPacket;
outPacket << Network::Command::ClientRefused;
clientSocket->send(outPacket);
}
@ -127,7 +90,7 @@ void Server::handleClientMessages() {
for (size_t i = 0 ; i < m_info.clients().size() ; ++i) {
ClientInfo &client = m_info.clients()[i];
if (m_selector.isReady(*client.tcpSocket)) {
sf::Packet packet;
Network::Packet packet;
sf::Socket::Status status = client.tcpSocket->receive(packet);
if (status == sf::Socket::Done) {
Network::Command command;
@ -158,7 +121,7 @@ void Server::handleClientMessages() {
}
void Server::disconnectClient(ClientInfo &client) {
sf::Packet packet;
Network::Packet packet;
packet << Network::Command::ClientDisconnect << client.id;
sendToAllClients(packet);
@ -173,7 +136,7 @@ void Server::disconnectClient(ClientInfo &client) {
}
}
void Server::sendToAllClients(sf::Packet &packet) const {
void Server::sendToAllClients(Network::Packet &packet) const {
for (const ClientInfo &client : m_info.clients()) {
client.tcpSocket->send(packet);
}

View File

@ -39,16 +39,14 @@
class Server {
using ConnectionCallback = std::function<void(ClientInfo &)>;
using CommandCallback = std::function<void(ClientInfo &, sf::Packet &packet)>;
using CommandCallback = std::function<void(ClientInfo &, Network::Packet &packet)>;
public:
void init(u16 port = 4242);
void handleKeyState();
void handleGameEvents();
void sendToAllClients(sf::Packet &packet) const;
void sendToAllClients(Network::Packet &packet) const;
bool isRunning() const { return m_isRunning; }
@ -56,8 +54,6 @@ class Server {
const ServerInfo &info() const { return m_info; }
sf::UdpSocket &udpSocket() { return m_udpSocket; }
void setRunning(bool isRunning) { m_isRunning = isRunning; }
void setSingleplayer(bool isSingleplayer) { m_isSingleplayer = isSingleplayer; }
@ -77,8 +73,6 @@ class Server {
ServerInfo m_info;
sf::UdpSocket m_udpSocket;
sf::TcpListener m_tcpListener;
sf::SocketSelector m_selector;

View File

@ -37,7 +37,7 @@
#include "WorldController.hpp"
void ServerCommandHandler::sendBlockDataUpdate(s32 x, s32 y, s32 z, const BlockData *blockData, const ClientInfo *client) const {
sf::Packet packet;
Network::Packet packet;
packet << Network::Command::BlockDataUpdate << x << y << z
<< blockData->meta << blockData->useAltTiles;
@ -48,7 +48,7 @@ void ServerCommandHandler::sendBlockDataUpdate(s32 x, s32 y, s32 z, const BlockD
}
void ServerCommandHandler::sendBlockInvUpdate(s32 x, s32 y, s32 z, const Inventory &inventory, const ClientInfo *client) const {
sf::Packet packet;
Network::Packet packet;
packet << Network::Command::BlockInvUpdate << x << y << z << inventory;
if (!client)
@ -60,7 +60,7 @@ void ServerCommandHandler::sendBlockInvUpdate(s32 x, s32 y, s32 z, const Invento
void ServerCommandHandler::sendPlayerPosUpdate(u16 clientID, bool isTeleportation, const ClientInfo *client) const {
const ServerPlayer *player = m_players.getPlayer(clientID);
if (player) {
sf::Packet packet;
Network::Packet packet;
packet << Network::Command::PlayerPosUpdate;
packet << clientID;
packet << player->x() << player->y() << player->z();
@ -78,7 +78,7 @@ void ServerCommandHandler::sendPlayerPosUpdate(u16 clientID, bool isTeleportatio
void ServerCommandHandler::sendPlayerInvUpdate(u16 clientID, const ClientInfo *client) const {
ServerPlayer *player = m_players.getPlayer(clientID);
if (player) {
sf::Packet packet;
Network::Packet packet;
packet << Network::Command::PlayerInvUpdate;
packet << clientID << player->inventory();
@ -92,7 +92,7 @@ void ServerCommandHandler::sendPlayerInvUpdate(u16 clientID, const ClientInfo *c
}
void ServerCommandHandler::sendPlayerChangeDimension(u16 clientID, s32 x, s32 y, s32 z, u16 dimension, const ClientInfo *client) const {
sf::Packet packet;
Network::Packet packet;
packet << Network::Command::PlayerChangeDimension;
packet << clientID << x << y << z << dimension;
@ -109,7 +109,7 @@ void ServerCommandHandler::sendPlayerChangeDimension(u16 clientID, s32 x, s32 y,
}
void ServerCommandHandler::sendChatMessage(u16 clientID, const std::string &message, const ClientInfo *client) const {
sf::Packet packet;
Network::Packet packet;
packet << Network::Command::ChatMessage << clientID << message;
if (!client)
@ -119,7 +119,7 @@ void ServerCommandHandler::sendChatMessage(u16 clientID, const std::string &mess
}
void ServerCommandHandler::sendEntitySpawn(entt::entity entityID, const ClientInfo *client) const {
sf::Packet packet;
Network::Packet packet;
packet << Network::Command::EntitySpawn << entityID;
if (!client)
@ -129,7 +129,7 @@ void ServerCommandHandler::sendEntitySpawn(entt::entity entityID, const ClientIn
}
void ServerCommandHandler::sendEntityDespawn(entt::entity entityID, const ClientInfo *client) const {
sf::Packet packet;
Network::Packet packet;
packet << Network::Command::EntityDespawn << entityID;
if (!client)
@ -139,7 +139,7 @@ void ServerCommandHandler::sendEntityDespawn(entt::entity entityID, const Client
}
void ServerCommandHandler::sendEntityPosition(entt::entity entityID, double x, double y, double z, const ClientInfo *client) const {
sf::Packet packet;
Network::Packet packet;
packet << Network::Command::EntityPosition << entityID << x << y << z;
if (!client)
@ -149,7 +149,7 @@ void ServerCommandHandler::sendEntityPosition(entt::entity entityID, double x, d
}
void ServerCommandHandler::sendEntityRotation(entt::entity entityID, float w, float x, float y, float z, const ClientInfo *client) const {
sf::Packet packet;
Network::Packet packet;
packet << Network::Command::EntityRotation << entityID << w << x << y << z;
if (!client)
@ -159,7 +159,7 @@ void ServerCommandHandler::sendEntityRotation(entt::entity entityID, float w, fl
}
void ServerCommandHandler::sendEntityAnimation(entt::entity entityID, const AnimationComponent &animation, const ClientInfo *client) const {
sf::Packet packet;
Network::Packet packet;
packet << Network::Command::EntityAnimation << entityID << animation;
if (!client)
@ -169,7 +169,7 @@ void ServerCommandHandler::sendEntityAnimation(entt::entity entityID, const Anim
}
void ServerCommandHandler::sendEntityDrawableDef(entt::entity entityID, const DrawableDef &drawableDef, const ClientInfo *client) const {
sf::Packet packet;
Network::Packet packet;
packet << Network::Command::EntityDrawableDef << entityID << drawableDef;
if (!client)
@ -180,14 +180,14 @@ void ServerCommandHandler::sendEntityDrawableDef(entt::entity entityID, const Dr
void ServerCommandHandler::setupCallbacks() {
m_server.setConnectionCallback([this](ClientInfo &client) {
sf::Packet packet;
Network::Packet packet;
packet << Network::Command::RegistryData;
m_registry.serialize(packet);
client.tcpSocket->send(packet);
// Send already connected players to the new client
for (auto &it : m_players) {
sf::Packet spawnPacket;
Network::Packet spawnPacket;
spawnPacket << Network::Command::PlayerSpawn << it.first;
spawnPacket << it.second.x() << it.second.y() << it.second.z();
client.tcpSocket->send(spawnPacket);
@ -199,13 +199,13 @@ void ServerCommandHandler::setupCallbacks() {
// FIXME: Find a better way to give starting items
m_scriptEngine.lua()["init"](player);
sf::Packet invPacket;
Network::Packet invPacket;
invPacket << Network::Command::PlayerInvUpdate << client.id;
invPacket << player.inventory();
client.tcpSocket->send(invPacket);
// Send spawn packet to all clients for this player
sf::Packet spawnPacket;
Network::Packet spawnPacket;
spawnPacket << Network::Command::PlayerSpawn << client.id;
spawnPacket << m_spawnPosition.x << m_spawnPosition.y << m_spawnPosition.z;
m_server.sendToAllClients(spawnPacket);
@ -214,18 +214,18 @@ void ServerCommandHandler::setupCallbacks() {
m_worldController.getWorld(player.dimension()).scene().sendEntities(client);
});
m_server.setCommandCallback(Network::Command::ClientDisconnect, [this](ClientInfo &client, sf::Packet &) {
m_server.setCommandCallback(Network::Command::ClientDisconnect, [this](ClientInfo &client, Network::Packet &) {
m_players.removePlayer(client.id);
});
m_server.setCommandCallback(Network::Command::ChunkRequest, [this](ClientInfo &client, sf::Packet &packet) {
m_server.setCommandCallback(Network::Command::ChunkRequest, [this](ClientInfo &client, Network::Packet &packet) {
s32 cx, cy, cz;
packet >> cx >> cy >> cz;
getWorldForClient(client.id).sendRequestedData(client, cx, cy, cz);
});
m_server.setCommandCallback(Network::Command::PlayerInvUpdate, [this](ClientInfo &client, sf::Packet &packet) {
m_server.setCommandCallback(Network::Command::PlayerInvUpdate, [this](ClientInfo &client, Network::Packet &packet) {
u16 clientId;
packet >> clientId;
@ -239,7 +239,7 @@ void ServerCommandHandler::setupCallbacks() {
gkError() << ("Failed to update inventory of player " + std::to_string(client.id) + ": Player not found").c_str();
});
m_server.setCommandCallback(Network::Command::PlayerPosUpdate, [this](ClientInfo &client, sf::Packet &packet) {
m_server.setCommandCallback(Network::Command::PlayerPosUpdate, [this](ClientInfo &client, Network::Packet &packet) {
double x, y, z;
u16 clientId;
packet >> clientId;
@ -254,7 +254,7 @@ void ServerCommandHandler::setupCallbacks() {
gkError() << ("Failed to update position of player " + std::to_string(client.id) + ": Player not found").c_str();
});
m_server.setCommandCallback(Network::Command::PlayerPlaceBlock, [this](ClientInfo &client, sf::Packet &packet) {
m_server.setCommandCallback(Network::Command::PlayerPlaceBlock, [this](ClientInfo &client, Network::Packet &packet) {
ServerPlayer *player = m_players.getPlayer(client.id);
if (player) {
s32 x, y, z;
@ -267,7 +267,7 @@ void ServerCommandHandler::setupCallbacks() {
m_scriptEngine.luaCore().onEvent(LuaEventType::OnBlockPlaced, glm::ivec3{x, y, z}, *player, world, client, *this);
sf::Packet answer;
Network::Packet answer;
answer << Network::Command::BlockUpdate << x << y << z << block;
m_server.sendToAllClients(answer);
}
@ -275,7 +275,7 @@ void ServerCommandHandler::setupCallbacks() {
gkError() << ("Failed to place block using player " + std::to_string(client.id) + ": Player not found").c_str();
});
m_server.setCommandCallback(Network::Command::PlayerDigBlock, [this](ClientInfo &client, sf::Packet &packet) {
m_server.setCommandCallback(Network::Command::PlayerDigBlock, [this](ClientInfo &client, Network::Packet &packet) {
ServerPlayer *player = m_players.getPlayer(client.id);
if (player) {
s32 x, y, z;
@ -287,7 +287,7 @@ void ServerCommandHandler::setupCallbacks() {
m_scriptEngine.luaCore().onEvent(LuaEventType::OnBlockDigged, glm::ivec3{x, y, z}, *player, world, client, *this);
sf::Packet answer;
Network::Packet answer;
answer << Network::Command::BlockUpdate << x << y << z << u32(0);
m_server.sendToAllClients(answer);
}
@ -295,7 +295,7 @@ void ServerCommandHandler::setupCallbacks() {
gkError() << ("Failed to dig block using player " + std::to_string(client.id) + ": Player not found").c_str();
});
m_server.setCommandCallback(Network::Command::PlayerInventory, [this](ClientInfo &client, sf::Packet &packet) {
m_server.setCommandCallback(Network::Command::PlayerInventory, [this](ClientInfo &client, Network::Packet &packet) {
u16 screenWidth, screenHeight;
u8 guiScale;
packet >> screenWidth >> screenHeight >> guiScale;
@ -310,7 +310,7 @@ void ServerCommandHandler::setupCallbacks() {
}
});
m_server.setCommandCallback(Network::Command::PlayerCreativeWindow, [this](ClientInfo &client, sf::Packet &packet) {
m_server.setCommandCallback(Network::Command::PlayerCreativeWindow, [this](ClientInfo &client, Network::Packet &packet) {
u16 screenWidth, screenHeight;
u8 guiScale;
packet >> screenWidth >> screenHeight >> guiScale;
@ -325,7 +325,7 @@ void ServerCommandHandler::setupCallbacks() {
}
});
m_server.setCommandCallback(Network::Command::BlockActivated, [this](ClientInfo &client, sf::Packet &packet) {
m_server.setCommandCallback(Network::Command::BlockActivated, [this](ClientInfo &client, Network::Packet &packet) {
ServerPlayer *player = m_players.getPlayer(client.id);
if (player) {
s32 x, y, z;
@ -346,7 +346,7 @@ void ServerCommandHandler::setupCallbacks() {
gkError() << ("Failed to activate block using player " + std::to_string(client.id) + ": Player not found").c_str();
});
m_server.setCommandCallback(Network::Command::BlockInvUpdate, [this](ClientInfo &client, sf::Packet &packet) {
m_server.setCommandCallback(Network::Command::BlockInvUpdate, [this](ClientInfo &client, Network::Packet &packet) {
gk::Vector3<s32> pos;
packet >> pos.x >> pos.y >> pos.z;
@ -357,7 +357,7 @@ void ServerCommandHandler::setupCallbacks() {
gkError() << "BlockInvUpdate: No block data found at" << pos.x << pos.y << pos.z;
});
m_server.setCommandCallback(Network::Command::BlockDataUpdate, [this](ClientInfo &client, sf::Packet &packet) {
m_server.setCommandCallback(Network::Command::BlockDataUpdate, [this](ClientInfo &client, Network::Packet &packet) {
gk::Vector3<s32> pos;
packet >> pos.x >> pos.y >> pos.z;
@ -367,7 +367,7 @@ void ServerCommandHandler::setupCallbacks() {
}
});
m_server.setCommandCallback(Network::Command::ChatMessage, [this](ClientInfo &client, sf::Packet &packet) {
m_server.setCommandCallback(Network::Command::ChatMessage, [this](ClientInfo &client, Network::Packet &packet) {
u16 clientID;
std::string message;
packet >> clientID >> message;

View File

@ -49,6 +49,11 @@ class ServerPlayer;
class ServerWorld;
class WorldController;
//=================================================================================================
// IMPORTANT: ServerCommandHandler is the only class that should be creating and sending packets.
// The only exceptions to this rule are ServerWorld::sendRequestedChunks and LuaGUI::show
//=================================================================================================
class ServerCommandHandler {
public:
ServerCommandHandler(ScriptEngine &scriptEngine, Server &server, WorldController &worldController, PlayerList &players, Registry &registry)

View File

@ -104,7 +104,7 @@ void ServerWorld::createChunkNeighbours(ServerChunk &chunk) {
}
void ServerWorld::sendChunkData(const ClientInfo &client, ServerChunk &chunk) {
sf::Packet packet;
Network::Packet packet;
packet << Network::Command::ChunkData;
packet << chunk.x() << chunk.y() << chunk.z();
for (u16 z = 0 ; z < CHUNK_HEIGHT ; ++z) {

View File

@ -54,7 +54,7 @@ void WorldController::load(const std::string &name) {
char *buffer = new char[length];
file.read(buffer, length);
sf::Packet save;
Network::Packet save;
save.append(buffer, length);
delete[] buffer;
@ -98,9 +98,9 @@ void WorldController::save(const std::string &name) {
std::ofstream file(name + ".dat", std::ofstream::binary | std::ofstream::trunc);
sf::Packet save;
Network::Packet save;
for (auto &world : m_worldList) {
sf::Packet chunks;
Network::Packet chunks;
unsigned int chunkCount = 0;
for (auto &chunk : world.chunks()) {
if (!chunk.second->isInitialized()) continue;