filename:
src/network/session/NetworkClient.cpp
branch:
feature/world
back to repo
/*
*
* _____ _ _
* / ___|| | | |
* \ `--. | |_ _ __ __ _ | |_ ___ ___
* `--. \| __|| '__| / _` || __| / _ \ / __|
* /\__/ /| |_ | | | (_| || |_ | (_) |\__ \
* \____/ \__||_| \__,_| \__| \___/ |___/
*
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Copyright (C) 2025 Armen Deroian
*
*/
#include "NetworkClient.h"
#include "nbt/CompoundTag.h"
#include "nbt/ListTag.h"
#include "nbt/NBT.h"
#include "nbt/PrimitiveTag.h"
#include "nbt/StringTag.h"
#include "network/Network.h"
#include "network/protocol/PacketSerialization.h"
#include "Server.h"
#include "spdlog/logger.h"
namespace stratos {
void NetworkSession::tick() {
if (!isConnected()) return;
// Empty received queue
// TODO: Should we always empty or consume a fix amount per tick?
processReceived();
}
void NetworkSession::beginConfiguration() const {
// TODO: R&D the proper configuration process
// Send Registries
// TODO: Registry API
// TODO: Dimension API (really part of the World API)
const nbt::CompoundTag dimensionType{
nbt::CompoundTag::CompoundElement{"has_skylight", nbt::ByteTag{true}},
nbt::CompoundTag::CompoundElement{"has_ceiling", nbt::ByteTag{false}},
nbt::CompoundTag::CompoundElement{"ultrawarm", nbt::ByteTag{false}},
nbt::CompoundTag::CompoundElement{"natural", nbt::ByteTag{true}},
nbt::CompoundTag::CompoundElement{"coordinate_scale", nbt::DoubleTag{1.0}},
nbt::CompoundTag::CompoundElement{"bed_works", nbt::ByteTag{true}},
nbt::CompoundTag::CompoundElement{"respawn_anchor_works", nbt::ByteTag{false}},
nbt::CompoundTag::CompoundElement{"min_y", nbt::IntTag{-64}},
nbt::CompoundTag::CompoundElement{"height", nbt::IntTag{384}},
nbt::CompoundTag::CompoundElement{"logical_height", nbt::IntTag{384}},
nbt::CompoundTag::CompoundElement{"infiniburn", nbt::StringTag{"#minecraft:infiniburn_overworld"}},
nbt::CompoundTag::CompoundElement{"effects", nbt::StringTag{"minecraft:overworld"}},
nbt::CompoundTag::CompoundElement{"ambient_light", nbt::FloatTag{0.0}},
nbt::CompoundTag::CompoundElement{"piglin_safe", nbt::ByteTag{false}},
nbt::CompoundTag::CompoundElement{"has_raids", nbt::ByteTag{false}},
nbt::CompoundTag::CompoundElement{"monster_spawn_light_level", nbt::CompoundTag{nbt::CompoundTag::CompoundElement{"type", nbt::StringTag{"minecraft:uniform"}},
nbt::CompoundTag::CompoundElement{"max_inclusive", nbt::IntTag{7}},
nbt::CompoundTag::CompoundElement{"min_inclusive", nbt::IntTag{0}}}},
nbt::CompoundTag::CompoundElement{"monster_spawn_block_light_limit", nbt::IntTag{0}}};
const nbt::CompoundTag plainsBiome{
nbt::CompoundTag::CompoundElement{"has_precipitation", nbt::ByteTag{true}}, nbt::CompoundTag::CompoundElement{"temperature", nbt::FloatTag{0.8}},
nbt::CompoundTag::CompoundElement{"downfall", nbt::FloatTag{0.4}},
nbt::CompoundTag::CompoundElement{
"effects",
nbt::CompoundTag{nbt::CompoundTag::CompoundElement{"fog_color", nbt::IntTag{12638463}}, nbt::CompoundTag::CompoundElement{"water_color", nbt::IntTag{4159204}},
nbt::CompoundTag::CompoundElement{"water_fog_color", nbt::IntTag{329011}}, nbt::CompoundTag::CompoundElement{"sky_color", nbt::IntTag{329011}},
nbt::CompoundTag::CompoundElement{"mood_sound", nbt::CompoundTag{nbt::CompoundTag::CompoundElement{"tick_delay", nbt::IntTag{6000}},
nbt::CompoundTag::CompoundElement{"block_search_extent", nbt::IntTag{8}},
nbt::CompoundTag::CompoundElement{"sound", nbt::StringTag{"minecraft:ambient.cave"}},
nbt::CompoundTag::CompoundElement{"offset", nbt::FloatTag{2.0f}}}}}}};
const nbt::CompoundTag catVariant{
nbt::CompoundTag::CompoundElement{"asset_id", nbt::StringTag{"minecraft:entity/cat/all_black"}}, nbt::CompoundTag::CompoundElement{"spawn_conditions", nbt::ListTag{}}
};
const nbt::CompoundTag cowVariant{
nbt::CompoundTag::CompoundElement{"asset_id", nbt::StringTag{"minecraft:entity/cow/temperate_cow"}}, nbt::CompoundTag::CompoundElement{"spawn_conditions", nbt::ListTag{}}
};
const nbt::CompoundTag chickenVariant{
nbt::CompoundTag::CompoundElement{"asset_id", nbt::StringTag{"minecraft:entity/chicken/temperate_chicken"}}, nbt::CompoundTag::CompoundElement{"spawn_conditions", nbt::ListTag{}}
};
const nbt::CompoundTag frogVariant{
nbt::CompoundTag::CompoundElement{"asset_id", nbt::StringTag{"minecraft:entity/frog/temperate_frog"}}, nbt::CompoundTag::CompoundElement{"spawn_conditions", nbt::ListTag{}}
};
const nbt::CompoundTag paintingVariant{
nbt::CompoundTag::CompoundElement{"asset_id", nbt::StringTag{"minecraft:earth"}}, nbt::CompoundTag::CompoundElement{"height", nbt::IntTag{2}},
nbt::CompoundTag::CompoundElement{"width", nbt::IntTag{2}},nbt::CompoundTag::CompoundElement{"title", nbt::CompoundTag{
nbt::CompoundTag::CompoundElement{"translate", nbt::StringTag{"painting.minecraft.earth.title"}},
nbt::CompoundTag::CompoundElement{"color", nbt::StringTag{"yellow"}}
}}
};
const nbt::CompoundTag pigVariant{
nbt::CompoundTag::CompoundElement{"asset_id", nbt::StringTag{"minecraft:entity/pig/temperate_pig"}}, nbt::CompoundTag::CompoundElement{"spawn_conditions", nbt::ListTag{}}
};
const nbt::CompoundTag wolfVariant{
nbt::CompoundTag::CompoundElement{"assets", nbt::CompoundTag{
nbt::CompoundTag::CompoundElement{"angry", nbt::StringTag{"minecraft:entity/wolf/wolf_ashen_angry"}},
nbt::CompoundTag::CompoundElement{"tame", nbt::StringTag{"minecraft:entity/wolf/wolf_ashen_tame"}},
nbt::CompoundTag::CompoundElement{"wild", nbt::StringTag{"minecraft:entity/wolf/wolf_ashen"}}
}}, nbt::CompoundTag::CompoundElement{"spawn_conditions", nbt::ListTag{}}
};
const nbt::CompoundTag wolfSoundVariant{
nbt::CompoundTag::CompoundElement{"ambient_sound", nbt::StringTag{"minecraft:entity.wolf_angry.ambient"}},
nbt::CompoundTag::CompoundElement{"death_sound", nbt::StringTag{"minecraft:entity.wolf_angry.death"}},
nbt::CompoundTag::CompoundElement{"growl_sound", nbt::StringTag{"minecraft:entity.wolf_angry.growl"}},
nbt::CompoundTag::CompoundElement{"hurt_sound", nbt::StringTag{"minecraft:entity.wolf_angry.hurt"}},
nbt::CompoundTag::CompoundElement{"pant_sound", nbt::StringTag{"minecraft:entity.wolf_angry.pant"}},
nbt::CompoundTag::CompoundElement{"whine_sound", nbt::StringTag{"minecraft:entity.wolf_angry.whine"}}
};
const nbt::CompoundTag inFireDamageEntry{
nbt::CompoundTag::CompoundElement{"exhaustion", nbt::FloatTag{0.1}},
nbt::CompoundTag::CompoundElement{"message_id", nbt::StringTag{"inFire"}},
nbt::CompoundTag::CompoundElement{"scaling", nbt::StringTag{"when_caused_by_living_non_player"}}
};
const nbt::CompoundTag campfireDamageEntry{
nbt::CompoundTag::CompoundElement{"exhaustion", nbt::FloatTag{0.1}},
nbt::CompoundTag::CompoundElement{"message_id", nbt::StringTag{"inFire"}},
nbt::CompoundTag::CompoundElement{"scaling", nbt::StringTag{"when_caused_by_living_non_player"}}
};
const nbt::CompoundTag lightningDamageEntry{
nbt::CompoundTag::CompoundElement{"exhaustion", nbt::FloatTag{0.1}},
nbt::CompoundTag::CompoundElement{"message_id", nbt::StringTag{"lightningBold"}},
nbt::CompoundTag::CompoundElement{"scaling", nbt::StringTag{"when_caused_by_living_non_player"}}
};
const nbt::CompoundTag onFireDamageEntry{
nbt::CompoundTag::CompoundElement{"exhaustion", nbt::FloatTag{0.0}},
nbt::CompoundTag::CompoundElement{"message_id", nbt::StringTag{"onFire"}},
nbt::CompoundTag::CompoundElement{"scaling", nbt::StringTag{"when_caused_by_living_non_player"}}
};
const nbt::CompoundTag lavaDamageEntry{
nbt::CompoundTag::CompoundElement{"exhaustion", nbt::FloatTag{0.1}},
nbt::CompoundTag::CompoundElement{"message_id", nbt::StringTag{"lava"}},
nbt::CompoundTag::CompoundElement{"scaling", nbt::StringTag{"when_caused_by_living_non_player"}}
};
const nbt::CompoundTag hotFloorDamageEntry{
nbt::CompoundTag::CompoundElement{"exhaustion", nbt::FloatTag{0.1}},
nbt::CompoundTag::CompoundElement{"message_id", nbt::StringTag{"hotFloor"}},
nbt::CompoundTag::CompoundElement{"scaling", nbt::StringTag{"when_caused_by_living_non_player"}}
};
const nbt::CompoundTag inWallDamageEntry{
nbt::CompoundTag::CompoundElement{"exhaustion", nbt::FloatTag{0.0}},
nbt::CompoundTag::CompoundElement{"message_id", nbt::StringTag{"inWall"}},
nbt::CompoundTag::CompoundElement{"scaling", nbt::StringTag{"when_caused_by_living_non_player"}}
};
const nbt::CompoundTag crammingDamageEntry{
nbt::CompoundTag::CompoundElement{"exhaustion", nbt::FloatTag{0.0}},
nbt::CompoundTag::CompoundElement{"message_id", nbt::StringTag{"cramming"}},
nbt::CompoundTag::CompoundElement{"scaling", nbt::StringTag{"when_caused_by_living_non_player"}}
};
const nbt::CompoundTag drownDamageEntry{
nbt::CompoundTag::CompoundElement{"exhaustion", nbt::FloatTag{0.0}},
nbt::CompoundTag::CompoundElement{"message_id", nbt::StringTag{"drown"}},
nbt::CompoundTag::CompoundElement{"scaling", nbt::StringTag{"when_caused_by_living_non_player"}}
};
const nbt::CompoundTag starveDamageEntry{
nbt::CompoundTag::CompoundElement{"exhaustion", nbt::FloatTag{0.0}},
nbt::CompoundTag::CompoundElement{"message_id", nbt::StringTag{"starve"}},
nbt::CompoundTag::CompoundElement{"scaling", nbt::StringTag{"when_caused_by_living_non_player"}}
};
const nbt::CompoundTag cactusDamageEntry{
nbt::CompoundTag::CompoundElement{"exhaustion", nbt::FloatTag{0.1}},
nbt::CompoundTag::CompoundElement{"message_id", nbt::StringTag{"cactus"}},
nbt::CompoundTag::CompoundElement{"scaling", nbt::StringTag{"when_caused_by_living_non_player"}}
};
const nbt::CompoundTag fallDamageEntry{
nbt::CompoundTag::CompoundElement{"exhaustion", nbt::FloatTag{0.0}},
nbt::CompoundTag::CompoundElement{"message_id", nbt::StringTag{"fall"}},
nbt::CompoundTag::CompoundElement{"scaling", nbt::StringTag{"when_caused_by_living_non_player"}}
};
const nbt::CompoundTag enderPearlDamageEntry{
nbt::CompoundTag::CompoundElement{"exhaustion", nbt::FloatTag{0.0}},
nbt::CompoundTag::CompoundElement{"message_id", nbt::StringTag{"fall"}},
nbt::CompoundTag::CompoundElement{"scaling", nbt::StringTag{"when_caused_by_living_non_player"}}
};
const nbt::CompoundTag flyIntoWallDamageEntry{
nbt::CompoundTag::CompoundElement{"exhaustion", nbt::FloatTag{0.0}},
nbt::CompoundTag::CompoundElement{"message_id", nbt::StringTag{"flyIntoWall"}},
nbt::CompoundTag::CompoundElement{"scaling", nbt::StringTag{"when_caused_by_living_non_player"}}
};
const nbt::CompoundTag outOfWorldDamageEntry{
nbt::CompoundTag::CompoundElement{"exhaustion", nbt::FloatTag{0.0}},
nbt::CompoundTag::CompoundElement{"message_id", nbt::StringTag{"outOfWorld"}},
nbt::CompoundTag::CompoundElement{"scaling", nbt::StringTag{"when_caused_by_living_non_player"}}
};
const nbt::CompoundTag genericDamageEntry{
nbt::CompoundTag::CompoundElement{"exhaustion", nbt::FloatTag{0.0}},
nbt::CompoundTag::CompoundElement{"message_id", nbt::StringTag{"generic"}},
nbt::CompoundTag::CompoundElement{"scaling", nbt::StringTag{"when_caused_by_living_non_player"}}
};
const nbt::CompoundTag magicDamageEntry{
nbt::CompoundTag::CompoundElement{"exhaustion", nbt::FloatTag{0.0}},
nbt::CompoundTag::CompoundElement{"message_id", nbt::StringTag{"magic"}},
nbt::CompoundTag::CompoundElement{"scaling", nbt::StringTag{"when_caused_by_living_non_player"}}
};
const nbt::CompoundTag witherDamageEntry{
nbt::CompoundTag::CompoundElement{"exhaustion", nbt::FloatTag{0.0}},
nbt::CompoundTag::CompoundElement{"message_id", nbt::StringTag{"wither"}},
nbt::CompoundTag::CompoundElement{"scaling", nbt::StringTag{"when_caused_by_living_non_player"}}
};
const nbt::CompoundTag dragonBreathDamageEntry{
nbt::CompoundTag::CompoundElement{"exhaustion", nbt::FloatTag{0.0}},
nbt::CompoundTag::CompoundElement{"message_id", nbt::StringTag{"dragonBreath"}},
nbt::CompoundTag::CompoundElement{"scaling", nbt::StringTag{"when_caused_by_living_non_player"}}
};
const nbt::CompoundTag dryOutDamageEntry{
nbt::CompoundTag::CompoundElement{"exhaustion", nbt::FloatTag{0.0}},
nbt::CompoundTag::CompoundElement{"message_id", nbt::StringTag{"dryOut"}},
nbt::CompoundTag::CompoundElement{"scaling", nbt::StringTag{"when_caused_by_living_non_player"}}
};
const nbt::CompoundTag sweetBerryBushDamageEntry{
nbt::CompoundTag::CompoundElement{"exhaustion", nbt::FloatTag{0.0}},
nbt::CompoundTag::CompoundElement{"message_id", nbt::StringTag{"sweetBerryBush"}},
nbt::CompoundTag::CompoundElement{"scaling", nbt::StringTag{"when_caused_by_living_non_player"}}
};
const nbt::CompoundTag freezeDamageEntry{
nbt::CompoundTag::CompoundElement{"exhaustion", nbt::FloatTag{0.0}},
nbt::CompoundTag::CompoundElement{"message_id", nbt::StringTag{"freeze"}},
nbt::CompoundTag::CompoundElement{"scaling", nbt::StringTag{"when_caused_by_living_non_player"}}
};
const nbt::CompoundTag stalagmiteDamageEntry{
nbt::CompoundTag::CompoundElement{"exhaustion", nbt::FloatTag{0.0}},
nbt::CompoundTag::CompoundElement{"message_id", nbt::StringTag{"stalagmite"}},
nbt::CompoundTag::CompoundElement{"scaling", nbt::StringTag{"when_caused_by_living_non_player"}}
};
const nbt::CompoundTag outsideBorderDamageEntry{
nbt::CompoundTag::CompoundElement{"exhaustion", nbt::FloatTag{0.0}},
nbt::CompoundTag::CompoundElement{"message_id", nbt::StringTag{"outsideBorder"}},
nbt::CompoundTag::CompoundElement{"scaling", nbt::StringTag{"when_caused_by_living_non_player"}}
};
const nbt::CompoundTag genericKillDamageEntry{
nbt::CompoundTag::CompoundElement{"exhaustion", nbt::FloatTag{0.0}},
nbt::CompoundTag::CompoundElement{"message_id", nbt::StringTag{"genericKill"}},
nbt::CompoundTag::CompoundElement{"scaling", nbt::StringTag{"when_caused_by_living_non_player"}}
};
RegistryEntry dimensionTypeEntry{utils::Identifier{"minecraft", "overworld"}, std::make_optional(nbt::writeNetworkNBT(dimensionType))};
RegistryEntry plainsBiomeEntry{utils::Identifier{"minecraft", "plains"}, std::make_optional(nbt::writeNetworkNBT(plainsBiome))};
RegistryEntry catVariantEntry{utils::Identifier{"minecraft", "cat/all_black"}, std::make_optional(nbt::writeNetworkNBT(catVariant))};
RegistryEntry cowVariantEntry{utils::Identifier{"minecraft", "cow/temperate"}, std::make_optional(nbt::writeNetworkNBT(cowVariant))};
RegistryEntry chickenVariantEntry{utils::Identifier{"minecraft", "chicken/temperate"}, std::make_optional(nbt::writeNetworkNBT(chickenVariant))};
RegistryEntry frogVariantEntry{utils::Identifier{"minecraft", "frog/temperate"}, std::make_optional(nbt::writeNetworkNBT(frogVariant))};
RegistryEntry paintingVariantEntry{utils::Identifier{"minecraft", "earth"}, std::make_optional(nbt::writeNetworkNBT(paintingVariant))};
RegistryEntry pigVariantEntry{utils::Identifier{"minecraft", "pig/temperate"}, std::make_optional(nbt::writeNetworkNBT(pigVariant))};
RegistryEntry wolfVariantEntry{utils::Identifier{"minecraft", "ashen"}, std::make_optional(nbt::writeNetworkNBT(wolfVariant))};
RegistryEntry wolfSoundVariantEntry{utils::Identifier{"minecraft", "angry"}, std::make_optional(nbt::writeNetworkNBT(wolfSoundVariant))};
send(RegistryDataPacket(utils::Identifier{"minecraft", "dimension_type"}, std::vector{std::move(dimensionTypeEntry)}));
send(RegistryDataPacket(utils::Identifier{"minecraft", "worldgen/biome"}, std::vector{std::move(plainsBiomeEntry)}));
send(RegistryDataPacket(utils::Identifier{"minecraft", "cat_variant"}, std::vector{std::move(catVariantEntry)}));
send(RegistryDataPacket(utils::Identifier{"minecraft", "cow_variant"}, std::vector{std::move(cowVariantEntry)}));
send(RegistryDataPacket(utils::Identifier{"minecraft", "chicken_variant"}, std::vector{std::move(chickenVariantEntry)}));
send(RegistryDataPacket(utils::Identifier{"minecraft", "frog_variant"}, std::vector{std::move(frogVariantEntry)}));
send(RegistryDataPacket(utils::Identifier{"minecraft", "painting_variant"}, std::vector{std::move(paintingVariantEntry)}));
send(RegistryDataPacket(utils::Identifier{"minecraft", "pig_variant"}, std::vector{std::move(pigVariantEntry)}));
send(RegistryDataPacket(utils::Identifier{"minecraft", "wolf_variant"}, std::vector{std::move(wolfVariantEntry)}));
send(RegistryDataPacket(utils::Identifier{"minecraft", "wolf_sound_variant"}, std::vector{std::move(wolfSoundVariantEntry)}));
send(RegistryDataPacket(utils::Identifier{"minecraft", "damage_type"}, std::vector{
RegistryEntry{utils::Identifier{"minecraft", "in_fire"}, std::make_optional(nbt::writeNetworkNBT(inFireDamageEntry))},
RegistryEntry{utils::Identifier{"minecraft", "campfire"}, std::make_optional(nbt::writeNetworkNBT(campfireDamageEntry))},
RegistryEntry{utils::Identifier{"minecraft", "lightning_bolt"}, std::make_optional(nbt::writeNetworkNBT(lightningDamageEntry))},
RegistryEntry{utils::Identifier{"minecraft", "on_fire"}, std::make_optional(nbt::writeNetworkNBT(onFireDamageEntry))},
RegistryEntry{utils::Identifier{"minecraft", "lava"}, std::make_optional(nbt::writeNetworkNBT(lavaDamageEntry))},
RegistryEntry{utils::Identifier{"minecraft", "hot_floor"}, std::make_optional(nbt::writeNetworkNBT(hotFloorDamageEntry))},
RegistryEntry{utils::Identifier{"minecraft", "in_wall"}, std::make_optional(nbt::writeNetworkNBT(inWallDamageEntry))},
RegistryEntry{utils::Identifier{"minecraft", "cramming"}, std::make_optional(nbt::writeNetworkNBT(crammingDamageEntry))},
RegistryEntry{utils::Identifier{"minecraft", "drown"}, std::make_optional(nbt::writeNetworkNBT(drownDamageEntry))},
RegistryEntry{utils::Identifier{"minecraft", "starve"}, std::make_optional(nbt::writeNetworkNBT(starveDamageEntry))},
RegistryEntry{utils::Identifier{"minecraft", "cactus"}, std::make_optional(nbt::writeNetworkNBT(cactusDamageEntry))},
RegistryEntry{utils::Identifier{"minecraft", "fall"}, std::make_optional(nbt::writeNetworkNBT(fallDamageEntry))},
RegistryEntry{utils::Identifier{"minecraft", "ender_pearl"}, std::make_optional(nbt::writeNetworkNBT(enderPearlDamageEntry))},
RegistryEntry{utils::Identifier{"minecraft", "fly_into_wall"}, std::make_optional(nbt::writeNetworkNBT(flyIntoWallDamageEntry))},
RegistryEntry{utils::Identifier{"minecraft", "out_of_world"}, std::make_optional(nbt::writeNetworkNBT(outOfWorldDamageEntry))},
RegistryEntry{utils::Identifier{"minecraft", "generic"}, std::make_optional(nbt::writeNetworkNBT(genericDamageEntry))},
RegistryEntry{utils::Identifier{"minecraft", "magic"}, std::make_optional(nbt::writeNetworkNBT(magicDamageEntry))},
RegistryEntry{utils::Identifier{"minecraft", "wither"}, std::make_optional(nbt::writeNetworkNBT(witherDamageEntry))},
RegistryEntry{utils::Identifier{"minecraft", "dragon_breath"}, std::make_optional(nbt::writeNetworkNBT(dragonBreathDamageEntry))},
RegistryEntry{utils::Identifier{"minecraft", "dry_out"}, std::make_optional(nbt::writeNetworkNBT(dryOutDamageEntry))},
RegistryEntry{utils::Identifier{"minecraft", "sweet_berry_bush"}, std::make_optional(nbt::writeNetworkNBT(sweetBerryBushDamageEntry))},
RegistryEntry{utils::Identifier{"minecraft", "freeze"}, std::make_optional(nbt::writeNetworkNBT(freezeDamageEntry))},
RegistryEntry{utils::Identifier{"minecraft", "stalagmite"}, std::make_optional(nbt::writeNetworkNBT(stalagmiteDamageEntry))},
RegistryEntry{utils::Identifier{"minecraft", "outside_border"}, std::make_optional(nbt::writeNetworkNBT(outsideBorderDamageEntry))},
RegistryEntry{utils::Identifier{"minecraft", "generic_kill"}, std::make_optional(nbt::writeNetworkNBT(genericKillDamageEntry))}
}));
send(FinishConfiguration());
}
void NetworkSession::changeState(const ProtocolState newState) const {
connection->changeState(newState);
}
void NetworkSession::loginPlayer() {
send(LoginPlay(
0,
false,
std::vector{utils::Identifier{"minecraft", "overworld"}},
networkManager->getServer()->getMaxPlayers(),
12, // TODO: View distance from server.properties
10, // TODO: Simulation distance from server.properties
false,
true,
false,
0, // TODO: Figure out what Dimension ID is
utils::Identifier{"minecraft", "overworld"},
0, // TODO: Hashed seed, should be generated by the server
1,
-1, // Previous gamemode, -1 means no previous gamemode
false,
false,
false,
std::nullopt,
std::nullopt,
0, // Portal cooldown
63, // Sea level, default value
true
));
send(SetDefaultSpawnPosition(math::Position{8, 64, 8}, 0)); // TODO: Spawn position should be determined by the server
send(SetCenterChunk(0, 0));
send(SynchronizePlayerPosition(0, 8, 64, 8, 0, 0, 0, 0, 0));
send(GameEventPacket(GameEvent::StartWaitingForChunks));
}
void NetworkSession::processReceived() {
while (true) {
std::optional<std::unique_ptr<ServerboundPacket>> optionalPacket = connection->receivePacket();
if (!optionalPacket) break; // No more packets to process
if (const auto packet = std::move(*optionalPacket); !packet->accept(*packetHandler)) {
networkManager->getLogger()->warn("Unhandled packet with ID {} from client {}:{}", packet->getId(), sessionId.ip, sessionId.port);
}
}
}
std::optional<std::unique_ptr<ServerboundPacket>> NetworkConnection::receivePacket() {
if (isDisconnected()) return std::nullopt;
if (std::unique_ptr<ServerboundPacket> packet; receiveQueue.try_dequeue(packet)) {
return std::move(packet);
}
return std::nullopt;
}
void NetworkConnection::sendPacket(std::unique_ptr<ClientboundPacket>&& packet) {
if (isDisconnected()) return;
sendQueue.enqueue(std::move(packet));
if (bool expected = false; dirty.compare_exchange_strong(expected, true)) eventLoop->notifySend(socketFd);
}
int NetworkConnection::handleReceive() {
const int received = flushReceive();
if (received <= 0) return received; // Connection closed or error
size_t offset = 0;
while (!receiveBuf.empty()) {
if (handleLegacyPing()) {
// Legacy ping handled, continue to next iteration
continue;
}
try {
const int packetLength = readVarInt(receiveBuf, offset);
if (packetLength <= 0 || packetLength > receiveBuf.size() - offset) {
// Not enough data to read a full packet, wait for more data
return received;
}
PacketBuffer packetBuffer(receiveBuf, offset);
try {
const int packetId = packetBuffer.readVarInt();
auto packetKey = PacketKey{state, Serverbound, packetId};
auto packet = PacketRegistry::instance().create(packetKey);
if (!packet) {
logger->error("Received unknown packet with ID {} from client {}:{}", packetId, address, port);
receiveBuf.erase(receiveBuf.begin(), receiveBuf.begin() + offset + packetLength);
return received;
}
packet->decrypt(packetBuffer);
if (!packet->accept(*packetHandler)) {
std::unique_ptr<ServerboundPacket> casted{
dynamic_cast<ServerboundPacket*>(packet.release())
};
receiveQueue.enqueue(std::move(casted)); // Enqueue the packet for further processing
}
} catch (const PacketSerializationException& e) {
logger->error("Failed to read packet from client {}:{}: {}", address, port, e.what());
} catch (const std::exception& e) {
logger->error("Encountered an exception when handing a packet for client {}:{}: {}", address, port, e.what());
}
receiveBuf.erase(receiveBuf.begin(), receiveBuf.begin() + offset + packetLength);
offset = 0; // Reset offset for the next packet
} catch (const PacketSerializationException& ignored) { // Failed to read packet length
// Not enough data to read a packet, wait for more data
break;
} catch (const std::exception& e) {
logger->error("Failed to read packet from client {}:{}: {}", address, port, e.what());
}
}
return received;
}
void NetworkConnection::changeState(const ProtocolState newState) {
state = newState;
switch (state) {
case Handshaking:
packetHandler = std::make_unique<HandshakePacketHandler>(this);
receiveBuf.reserve(512);
break;
case Status:
packetHandler = std::make_unique<StatusPacketHandler>(this);
receiveBuf.reserve(512);
break;
case Login:
packetHandler = std::make_unique<LoginPacketHandler>(this);
receiveBuf.reserve(512);
break;
case Configuration:
packetHandler = std::make_unique<PacketHandler>(); // Network connection does not handle configuration packets directly
receiveBuf.reserve(1024);
break;
case Play:
packetHandler = std::make_unique<PacketHandler>(); // Network connection does not handle play packets directly
receiveBuf.reserve(4096);
}
}
bool NetworkConnection::disconnect() {
if (bool expected = false; !disconnected.compare_exchange_strong(expected, true, std::memory_order_acq_rel)) return false;
return true;
}
bool NetworkConnection::disconnect(const std::string& reason) {
if (bool expected = false; !disconnected.compare_exchange_strong(expected, true, std::memory_order_acq_rel)) return false;
if (state == Login)
sendPacket(std::make_unique<LoginDisconnect>(reason.data()));
return true;
}
bool NetworkConnection::close() {
if (TCPConnection::close()) {
bool expected = false;
disconnected.compare_exchange_strong(expected, true, std::memory_order_acq_rel);
// Clear queues and buffers
receiveBuf.clear();
std::unique_ptr<ServerboundPacket> receiveConsumer;
while (receiveQueue.try_dequeue(receiveConsumer)) {
}
std::unique_ptr<ClientboundPacket> sendConsumer;
while (sendQueue.try_dequeue(sendConsumer)) {
}
return true;
}
return false;
}
void NetworkConnection::updateSessionInfo(SessionInfo&& info) {
if (sessionInfo) {
sessionInfo->username = std::move(info.username);
sessionInfo->uuid = std::move(info.uuid);
} else {
sessionInfo = std::make_unique<SessionInfo>(std::move(info));
}
}
void NetworkConnection::createNetworkSession() {
const std::shared_ptr<NetworkConnection> self = eventLoop->getConnection(socketFd);
if (!self) { // Edge case, should never happen
logger->critical("Cannot create network session for client {}:{}, failed to get instance of self", address, port);
disconnect("Internal server error.");
}
network->createSession(self);
}
int NetworkConnection::flushReceive () {
int totalReceived = 0;
ByteVec segment;
segment.resize(1050);
while (true) {
const int received = receive(1050, segment);
if (received == 0) return 0; // Connection closed
if (received < 0) break;
totalReceived += received;
if (encryptionEnabled) {
try {
ByteVec decrypted = aesDecryptCFB8(clientSecret, clientSecret, segment);
if (decrypted.empty()) {
logger->error("Failed to decrypt received data, disconnecting client {}:{}", address, port);
disconnect("Decryption failed");
return -1; // Decryption failed
}
receiveBuf.insert(receiveBuf.end(), decrypted.begin(), decrypted.begin() + received);
} catch (std::runtime_error & e) {
logger->error("Decryption error for client {}:{}: {}", address, port, e.what());
disconnect("Decryption error");
return -1; // Decryption error
}
} else {
receiveBuf.insert(receiveBuf.end(), segment.begin(), segment.begin() + received);
}
segment.clear();
}
return totalReceived > 0 ? totalReceived : -1;
}
int NetworkConnection::flushSend() {
ByteVec sendBuffer;
std::unique_ptr<ClientboundPacket> packet;
while (sendQueue.try_dequeue(packet)) {
if (!packet) continue; // Skip empty packets
PacketBuffer pkBuf;
pkBuf.writeVarInt(packet->getId());
packet->encrypt(pkBuf);
PacketBuffer framedBuf;
framedBuf.writeVarInt(static_cast<int>(pkBuf.getSize()));
framedBuf.append(pkBuf.getBuffer());
if (encryptionEnabled) {
ByteVec encrypted = aesEncryptCFB8(clientSecret, clientSecret, framedBuf.getBuffer());
sendBuffer.insert(sendBuffer.end(), std::make_move_iterator(encrypted.begin()), std::make_move_iterator(encrypted.end()));
} else {
sendBuffer.insert(sendBuffer.end(), framedBuf.begin(), framedBuf.end());
}
}
if (sendBuffer.empty()) return 0;
return send(sendBuffer, sendBuffer.size(), 0);
}
bool NetworkConnection::handleLegacyPing() {
if (state != Handshaking) return false;
try {
size_t legacyPingOffset = 0;
if (const uint8_t id = readUnsignedByte(receiveBuf, legacyPingOffset); id == LegacyServerListPing::ID) {
PacketBuffer buffer(receiveBuf, legacyPingOffset);
LegacyServerListPing packet;
packet.decrypt(buffer);
packetHandler->handle(packet); // Handle legacy ping packet
const size_t newOffset = buffer.getOffset();
receiveBuf.erase(receiveBuf.begin(), receiveBuf.begin() + newOffset);
return true; // Legacy ping handled
}
} catch (PacketSerializationException & ignored) {}
return false; // No legacy ping found
}
} // stratos