2nd attempt.
This commit is contained in:
parent
707e2b38c4
commit
ecdf504bef
@ -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
2
external/gamekit
vendored
@ -1 +1 @@
|
||||
Subproject commit 01a81d1c06f562bc8c8d11f002f5906880c64292
|
||||
Subproject commit 30a083b78a58042f261a716cded21706caf44719
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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"},
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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_
|
||||
|
@ -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);
|
||||
|
@ -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_
|
||||
|
Loading…
x
Reference in New Issue
Block a user