2nd attempt.

This commit is contained in:
Quentin Bazin 2020-12-29 05:38:06 +01:00
parent 707e2b38c4
commit ecdf504bef
16 changed files with 150 additions and 42 deletions

View File

@ -260,6 +260,12 @@ Packet sent at the beginning of every server tick.
| Hotbar slot | u8 | ID of the current hotbar slot |
| Item ID | u16 | Current item ID (to check match with server) |
#### PlayerReady
Packet sent from a client when it is ready to receive chunks.
_This packet has no field._
#### BlockActivated
| Field name | Field type | Notes |

2
external/gamekit vendored

@ -1 +1 @@
Subproject commit 01a81d1c06f562bc8c8d11f002f5906880c64292
Subproject commit 30a083b78a58042f261a716cded21706caf44719

View File

@ -91,6 +91,12 @@ void ClientCommandHandler::sendPlayerHeldItemChanged(u8 hotbarSlot, u16 itemID)
m_client.send(packet);
}
void ClientCommandHandler::sendPlayerReady() {
Network::Packet packet;
packet << Network::Command::PlayerReady;
m_client.send(packet);
}
void ClientCommandHandler::sendBlockActivated(const glm::ivec4 &selectedBlock) {
Network::Packet packet;
packet << Network::Command::BlockActivated

View File

@ -52,6 +52,7 @@ class ClientCommandHandler {
void sendPlayerDigBlock(const glm::ivec4 &selectedBlock);
void sendPlayerPlaceBlock(s32 x, s32 y, s32 z, u32 block);
void sendPlayerHeldItemChanged(u8 hotbarSlot, u16 itemID);
void sendPlayerReady();
void sendBlockActivated(const glm::ivec4 &selectedBlock);
void sendBlockInvUpdate(Inventory &inventory);
void sendItemActivated(const glm::ivec4 &selectedBlock);

View File

@ -84,10 +84,12 @@ void ServerLoadingState::update() {
if (m_game.clientCommandHandler().isRegistryInitialized()) {
if (m_game.textureAtlas().isReady() && (m_hasBeenRendered || !m_showLoadingState)) {
m_game.world().changeDimension(m_game.player().dimension());
if (m_showLoadingState)
std::this_thread::sleep_for(std::chrono::milliseconds(500));
m_game.world().changeDimension(m_game.player().dimension());
m_game.clientCommandHandler().sendPlayerReady();
m_stateStack->pop();

View File

@ -56,7 +56,7 @@ void ClientWorld::update(bool allowWorldReload) {
if (World::isReloadRequested && allowWorldReload)
it->second->setChanged(true);
if (it->second->areAllNeighboursInitialized())
// if (it->second->areAllNeighboursInitialized())
it->second->update();
++it;
@ -162,7 +162,7 @@ void ClientWorld::receiveChunkData(Network::Packet &packet) {
m_eventHandler->emplaceEvent<ChunkCreatedEvent>(gk::Vector3i{cx, cy, cz}, true);
// if (cx == 2 && cy == 0 && cz == 1)
// std::cout << "Chunk at (" << cx << ", " << cy << ", " << cz << ") received" << std::endl;
gkDebug() << "Chunk at" << cx << cy << cz << "received";
}
void ClientWorld::removeChunk(ChunkMap::iterator &it) {

View File

@ -51,6 +51,7 @@ std::string Network::commandToString(Network::Command command) {
{Network::Command::PlayerSpawn, "PlayerSpawn"},
{Network::Command::PlayerChangeDimension, "PlayerChangeDimension"},
{Network::Command::PlayerHeldItemChanged, "PlayerHeldItemChanged"},
{Network::Command::PlayerReady, "PlayerReady"},
{Network::Command::BlockUpdate, "BlockUpdate"},
{Network::Command::BlockActivated, "BlockActivated"},

View File

@ -40,41 +40,42 @@ namespace Network {
ClientOk = 0x02,
ClientRefused = 0x03,
ServerTick = 0x04,
ServerClosed = 0x05,
ServerTick = 0x10,
ServerClosed = 0x11,
ChunkData = 0x06,
ChunkRequest = 0x07,
ChunkData = 0x20,
ChunkRequest = 0x21,
PlayerPlaceBlock = 0x08,
PlayerDigBlock = 0x09,
PlayerInvUpdate = 0x0a,
PlayerPosUpdate = 0x0b,
PlayerRotUpdate = 0x0c,
PlayerSpawn = 0x0d,
PlayerChangeDimension = 0x0e,
PlayerHeldItemChanged = 0x0f,
PlayerPlaceBlock = 0x30,
PlayerDigBlock = 0x31,
PlayerInvUpdate = 0x32,
PlayerPosUpdate = 0x33,
PlayerRotUpdate = 0x34,
PlayerSpawn = 0x35,
PlayerChangeDimension = 0x36,
PlayerHeldItemChanged = 0x37,
PlayerReady = 0x38,
BlockUpdate = 0x10,
BlockActivated = 0x11,
BlockGUIData = 0x12,
BlockInvUpdate = 0x13,
BlockDataUpdate = 0x14,
BlockUpdate = 0x40,
BlockActivated = 0x41,
BlockGUIData = 0x42,
BlockInvUpdate = 0x43,
BlockDataUpdate = 0x44,
ItemActivated = 0x15,
EntitySpawn = 0x50,
EntityDespawn = 0x51,
EntityPosition = 0x52,
EntityRotation = 0x53,
EntityAnimation = 0x54,
EntityDrawableDef = 0x55,
RegistryData = 0x16,
ItemActivated = 0x60,
ChatMessage = 0x17,
RegistryData = 0x70,
EntitySpawn = 0x18,
EntityDespawn = 0x19,
EntityPosition = 0x1a,
EntityRotation = 0x1b,
EntityAnimation = 0x1c,
EntityDrawableDef = 0x1d,
ChatMessage = 0x80,
KeyPressed = 0x1e,
KeyPressed = 0x90,
};
std::string commandToString(Command command);

View File

@ -41,6 +41,14 @@ u8 Player::getOppositeDirection() const {
return getDirection() ^ 2;
}
gk::Vector3i Player::getCurrentChunk() const {
return {
(static_cast<s32>(m_x) & -CHUNK_WIDTH) / CHUNK_WIDTH,
(static_cast<s32>(m_y) & -CHUNK_DEPTH) / CHUNK_DEPTH,
(static_cast<s32>(m_z) & -CHUNK_HEIGHT) / CHUNK_HEIGHT
};
}
void Player::serialize(sf::Packet &packet) const {
packet << m_x << m_y << m_z << m_dimension << m_viewAngleH << m_viewAngleV << m_viewAngleRoll << m_inventory << m_heldItemSlot;
}

View File

@ -43,6 +43,8 @@ class Player : public gk::ISerializable {
u8 getDirection() const;
u8 getOppositeDirection() const;
gk::Vector3i getCurrentChunk() const;
void serialize(sf::Packet &packet) const override;
void deserialize(sf::Packet &packet) override;

View File

@ -35,6 +35,9 @@
u8 ServerConfig::maxPlayers = 5;
u16 ServerConfig::maxItemStackSize = 64;
// World
u8 ServerConfig::renderDistance = 10;
// Mod-defined options
std::unordered_map<std::string, sol::object> ServerConfig::options;
@ -48,6 +51,8 @@ void ServerConfig::loadConfigFromFile(const char *file) {
maxPlayers = lua["max_players"].get_or(maxPlayers);
maxItemStackSize = lua["max_item_stack_size"].get_or(maxItemStackSize);
renderDistance = lua["render_distance"].get_or(renderDistance);
if (lua["mod_options"].valid() && lua["mod_options"].get_type() == sol::type::table) {
for (auto &it : lua["mod_options"].get<sol::table>()) {
options.emplace(it.first.as<std::string>(), it.second);
@ -66,6 +71,7 @@ void ServerConfig::saveConfigToFile(const char *filename) {
std::ofstream file{filename, std::ofstream::out | std::ofstream::trunc};
file << "max_players = " << (u16)maxPlayers << std::endl;
file << "max_item_stack_size = " << maxItemStackSize << std::endl;
file << "render_distance = " << (u16)renderDistance << std::endl;
file << "mod_options = {" << std::endl;
for (auto &it : options) {

View File

@ -39,6 +39,9 @@ namespace ServerConfig {
extern u8 maxPlayers;
extern u16 maxItemStackSize;
// World
extern u8 renderDistance;
// Mod-defined options
extern std::unordered_map<std::string, sol::object> options;

View File

@ -192,6 +192,7 @@ void ServerCommandHandler::setupCallbacks() {
return;
}
// Try to find a valid spawn point (WIP)
if (player->isNewPlayer()) {
// FIXME: Default dimension hardcoded here
ServerWorld &world = m_worldController.getWorld(0);
@ -239,6 +240,7 @@ void ServerCommandHandler::setupCallbacks() {
player->setHeldItemSlot(0);
}
// Send the registry
Network::Packet packet;
packet << Network::Command::RegistryData;
m_registry.serialize(packet);
@ -253,9 +255,11 @@ void ServerCommandHandler::setupCallbacks() {
client.tcpSocket->send(spawnPacket);
}
// Triggers the 'PlayerConnected' Lua event
if (player->isNewPlayer())
m_scriptEngine.luaCore().onEvent(LuaEventType::PlayerConnected, glm::ivec3{player->x(), player->y(), player->z()}, player, client, *this);
// Send inventory
sendPlayerInvUpdate(client.id, &client);
// Send spawn packet to all clients for this player
@ -277,7 +281,7 @@ void ServerCommandHandler::setupCallbacks() {
s32 cx, cy, cz;
packet >> cx >> cy >> cz;
getWorldForClient(client.id).sendRequestedData(client, cx, cy, cz);
// getWorldForClient(client.id).sendRequestedData(client, cx, cy, cz);
});
m_server.setCommandCallback(Network::Command::PlayerInvUpdate, [this](ClientInfo &client, Network::Packet &packet) {
@ -373,6 +377,15 @@ void ServerCommandHandler::setupCallbacks() {
gkError() << ("Failed to change held item of player " + std::to_string(client.id) + ": Player not found").c_str();
});
m_server.setCommandCallback(Network::Command::PlayerReady, [this](ClientInfo &client, Network::Packet &) {
ServerPlayer *player = m_players.getPlayerFromClientID(client.id);
if (player) {
player->setReady(true);
}
else
gkError() << ("Failed to change held item of player " + std::to_string(client.id) + ": Player not found").c_str();
});
m_server.setCommandCallback(Network::Command::BlockActivated, [this](ClientInfo &client, Network::Packet &packet) {
ServerPlayer *player = m_players.getPlayerFromClientID(client.id);
if (player) {

View File

@ -27,6 +27,8 @@
#ifndef SERVERPLAYER_HPP_
#define SERVERPLAYER_HPP_
#include <unordered_set>
#include <gk/core/Debug.hpp>
#include "ClientInfo.hpp"
@ -46,12 +48,25 @@ class ServerPlayer : public Player {
bool isNewPlayer() const { return m_isNewPlayer; }
void setNewPlayer(bool isNewPlayer) { m_isNewPlayer = isNewPlayer; }
bool isReady() const { return m_isReady; }
void setReady(bool isReady) { m_isReady = isReady; }
bool isChunkLoaded(const gk::Vector3i &chunk) { return m_loadedChunks.find(chunk) != m_loadedChunks.end(); }
void addLoadedChunk(const gk::Vector3i &chunk) { m_loadedChunks.emplace(chunk); }
void removeLoadedChunk(const gk::Vector3i &chunk) { m_loadedChunks.erase(chunk); }
static void initUsertype(sol::state &lua);
public:
gk::Vector3i lastChunkUpdate{0, 0, 0}; // FIXME
private:
ClientInfo *m_client = nullptr;
bool m_isNewPlayer = false;
bool m_isReady = false; // Is player ready to receive chunks?
std::unordered_set<gk::Vector3i> m_loadedChunks;
};
#endif // SERVERPLAYER_HPP_

View File

@ -48,6 +48,44 @@ ServerWorld::ServerWorld(PlayerList &players, const Dimension &dimension, gk::Ga
}
void ServerWorld::update(bool doTick) {
{
for (auto &it : m_players) {
if (it.second.isReady() && it.second.dimension() == m_dimension.id()) {
gk::Vector3i currentChunk = it.second.getCurrentChunk();
if (!it.second.isChunkLoaded(currentChunk) || it.second.lastChunkUpdate != currentChunk) {
m_chunksToSend.emplace(std::make_pair(currentChunk, std::ref(it.second)));
it.second.addLoadedChunk(currentChunk);
it.second.lastChunkUpdate = currentChunk;
}
}
}
auto addChunkToSend = [this](gk::Vector3i pos, s8 dx, s8 dy, s8 dz, ServerPlayer &player) {
pos.x += dx; pos.y += dy; pos.z += dz;
m_chunksToSend.emplace(std::make_pair(pos, std::ref(player)));
};
while (!m_chunksToSend.empty()) {
auto &[chunkPos, player] = m_chunksToSend.front();
if (!player.isChunkLoaded(chunkPos)) {
sendRequestedData(*player.client(), chunkPos.x, chunkPos.y, chunkPos.z);
player.addLoadedChunk(chunkPos);
}
gk::Vector3i playerChunkPos = player.getCurrentChunk();
if ((playerChunkPos - chunkPos).length() <= ServerConfig::renderDistance) {
addChunkToSend(chunkPos, 1, 0, 0, player);
addChunkToSend(chunkPos, -1, 0, 0, player);
addChunkToSend(chunkPos, 0, 1, 0, player);
addChunkToSend(chunkPos, 0, -1, 0, player);
addChunkToSend(chunkPos, 0, 0, 1, player);
addChunkToSend(chunkPos, 0, 0, -1, player);
}
m_chunksToSend.pop();
}
}
for (auto &it : m_chunks) {
if (doTick)
it.second->tick(*this, *m_server);
@ -56,13 +94,15 @@ void ServerWorld::update(bool doTick) {
it.second->updateLights();
}
if (it.second->isInitialized() && !it.second->isSent()) {
for (auto &client : m_server->server().info().clients())
if (m_players.getPlayer(client.playerName)->dimension() == m_dimension.id())
sendChunkData(client, *it.second.get());
// gkDebug() << "Chunk updated at" << it.second->x() << it.second->y() << it.second->z();
}
// if (it.second->isInitialized() && !it.second->isSent()) {
// for (auto &client : m_server->server().info().clients()) {
// ServerPlayer *player = m_players.getPlayer(client.playerName);
// if (player->isReady() && player->dimension() == m_dimension.id())
// sendChunkData(client, *it.second.get());
// }
//
// // gkDebug() << "Chunk updated at" << it.second->x() << it.second->y() << it.second->z();
// }
}
if (doTick)
@ -141,10 +181,10 @@ void ServerWorld::sendChunkData(const ClientInfo &client, ServerChunk &chunk) {
chunk.setSent(true);
chunk.setChanged(false);
// gkDebug() << "Chunk at" << chunk.x() << chunk.y() << chunk.z() << "sent to client";
gkDebug() << "Chunk at" << chunk.x() << chunk.y() << chunk.z() << "sent to client";
}
void ServerWorld::sendRequestedData(ClientInfo &client, int cx, int cy, int cz) {
void ServerWorld::sendRequestedData(const ClientInfo &client, int cx, int cy, int cz) {
ServerChunk &chunk = getOrCreateChunk(cx, cy, cz);
generateChunk(chunk);

View File

@ -54,7 +54,7 @@ class ServerWorld : public World {
void createChunkNeighbours(ServerChunk &chunk);
void sendChunkData(const ClientInfo &client, ServerChunk &chunk);
void sendRequestedData(ClientInfo &client, s32 cx, s32 cy, s32 cz);
void sendRequestedData(const ClientInfo &client, s32 cx, s32 cy, s32 cz);
ServerChunk &getOrCreateChunk(s32 cx, s32 cy, s32 cz);
@ -96,6 +96,10 @@ class ServerWorld : public World {
ServerScene m_scene;
s32 m_seed = 0;
std::queue<std::pair<gk::Vector3i, ServerPlayer &>> m_chunksToSend;
std::unordered_map<u16, std::queue<gk::Vector3i>> m_chunkQueues;
};
#endif // SERVERWORLD_HPP_