FeatherMC/src/DedicatedServer.cpp

298 lines
8.7 KiB
C++

#include "DedicatedServer.h"
#include "config/ServerProperties.h"
#include "data/Registry.h"
#include "PacketReader.h"
#include <chrono>
#include <string>
using std::string;
namespace Feather
{
DedicatedServer::DedicatedServer(ServerProperties* properties) :
m_properties(properties),
m_listener(properties->serverPort.GetValue(), this),
m_status()
{
m_status.descriptionText = properties->motd.GetValue();
m_status.maxPlayers = properties->maxPlayers;
Registry::Init();
m_worldManager.LoadWorld(m_properties->levelName);
while (1)
{
auto [clients, clientsLock] = m_clients.borrow();
for (auto& client : clients)
{
auto [data, clientLock] = client.GetTCPClient()->GetData().borrow();
if (data.empty())
continue;
uint32_t offset = 0;
while (offset != data.size())
{
auto reader = PacketReader(&data[offset]);
Protocol::ProcessPacket(*this, client, reader, client.GetContext().GetState());
offset += reader.Size();
}
data.clear();
}
// TODO: Move to tick thread !!
{
using namespace Protocol;
using namespace std::chrono;
// TODO: high_resolution_clock on mingw is innacurate and slow
static auto lastKeepAlive = high_resolution_clock::now();
auto now = high_resolution_clock::now();
milliseconds td = duration_cast<milliseconds>(now - lastKeepAlive);
if (td >= 1000ms) {
lastKeepAlive = now;
Play::ClientboundKeepAlive keepAlive =
{
// MC probably sends time since epoch
.id = duration_cast<milliseconds>(now.time_since_epoch()).count(),
};
for (auto &client : clients)
{
client.SendMessage(keepAlive);
}
}
}
}
}
DedicatedServer::~DedicatedServer()
{
}
void DedicatedServer::OnClientConnect(Network::TCPClientHandle&& client)
{
const auto& address = client->GetAddress();
Log::Info("New connection from {}:{}", address.ip, address.port);
auto [clients, lock] = m_clients.borrow();
clients.emplace_back(std::move(client));
}
void DedicatedServer::OnClientDisconnect(const Network::TCPClient* client)
{
const auto& address = client->GetAddress();
Log::Info("Disconnected from {}:{}", address.ip, address.port);
auto [clients, lock] = m_clients.borrow();
clients.remove_if([&](MinecraftClient& other) { return other.GetTCPClient().get() == client; });
}
using namespace Protocol;
void DedicatedServer::HandleUnknownPacket(MinecraftClient &client, int32_t id, const PacketReader &packet)
{
Log::Trace("Got unknown packet with ID {} from client.", id);
}
void DedicatedServer::HandleLegacyPing(MinecraftClient& client)
{
Log::Info("Got legacy server list ping.");
}
#pragma region Handshake & Status
template <>
void DedicatedServer::HandlePacket(MinecraftClient& client, const Handholding::ServerboundHandshake& handshake)
{
Log::Info("Client Intention Packet: version={}, serverIp={}, port={}, intention={}\n",
handshake.protocolVersion,
handshake.serverIP.c_str(),
handshake.port,
handshake.intention
);
client.GetContext().SetState(handshake.intention);
}
template <>
void DedicatedServer::HandlePacket(MinecraftClient& client, const Status::ServerboundRequest& request)
{
Log::Info("Client sent STATUS_PING_REQUEST");
Status::ClientboundResponse message =
{
.jsonResponse = m_status.GetServerStatusJSON()
};
client.SendMessage(message);
}
template <>
void DedicatedServer::HandlePacket(MinecraftClient& client, const Status::ServerboundPing& ping)
{
Log::Info("Client sent STATUS_PING: {}", ping.timestamp);
Status::ClientboundPong message =
{
.timestamp = ping.timestamp
};
client.SendMessage(message);
}
#pragma endregion
template <>
void DedicatedServer::HandlePacket(MinecraftClient& client,const Login::ServerboundStart& start)
{
//Login::ClientboundSuccess success =
//{
// .uuid = { 4658857991808325907ull, 7518717155607718277ull },
// .username = start.username
//};
std::string uuid = "ecb99913-96a8-40a7-8529-a2ca6ad95768";
Login::ClientboundSuccess success =
{
.uuid = uuid,
.username = start.username
};
client.SendMessage(success);
client.GetContext().SetState(ProtocolState::Play);
std::string type = "default";
Play::ClientboundJoinGame join =
{
.entityId = 0,
.gamemode = 0,
.dimension = 0,
.seedHash = 0,
.maxPlayers = uint8_t(m_properties->maxPlayers.GetValue()),
.levelType = type,
.viewDistance = m_properties->viewDistance,
.reducedDebugInfo = false,
.enableRespawnScreen = true
};
client.SendMessage(join);
Play::ClientboundSpawnPosition spawnPos =
{
.location = BlockPos(0, 64, 0),
};
client.SendMessage(spawnPos);
using RelativeFlags = Play::ClientboundPlayerPositionAndLook::RelativeFlags;
Play::ClientboundPlayerPositionAndLook playerPos =
{
.x = 0,
.y = 70,
.z = 0,
.xRot = 0,
.yRot = 0,
.flags = (RelativeFlags)0,
.id = 0,
};
client.SendMessage(playerPos);
NetworkMessage chunkData(5KB);
// Packet ID
chunkData.WriteVarInt(0x22);
// Chunk X, Z
chunkData.Write<int32_t>(-10);
chunkData.Write<int32_t>(-12);
// Full chunk
chunkData.Write(true);
Chunk* chunk = m_worldManager.GetOverworld()->m_chunk;
NBT::CompoundTag* chunkNBT = m_worldManager.GetOverworld()->m_chunkNBT;
int32_t sectionsBits = 0;
NetworkMessage sections(3KB);
for (int i = 0; i < Chunk::NUM_SECTIONS; i++)
{
ChunkSection sect = chunk->sections[i];
Log::Trace("Chunk: Got Section {} with local palette size {}", i, sect.localPalette.Size());
sectionsBits |= 1 << i;
// Block Count: fudge
sections.Write<uint16_t>(1024);
// Bits Per Block
sections.Write<uint8_t>(4);
// Palette
NetworkMessage paletteData(512);
for (int i = 0; i < sect.localPalette.Size(); i++)
{
int id = sect.localPalette.ByID(i);
Log::Trace("Palette: {} -> {}", i, id);
paletteData.WriteVarInt(id);
i++;
}
paletteData.Finalize();
sections.WriteData(paletteData.GetData(), paletteData.GetDataSize());
// Block Data
//NBT::LongArrayTag blocks = sect.Get<NBT::LongArrayTag>("BlockStates");
sections.WriteVarInt(ChunkSection::NUM_LONGS);
sections.WriteData(sect.blockStates, ChunkSection::NUM_LONGS * sizeof(int64_t));
}
//Log::Trace("Section bits: {:#b}", sectionsBits);
chunkData.WriteVarInt(sectionsBits);
// Heightmaps
// TODO: Use chunk->heightmaps
NBT::DataBuffer buf = chunkNBT->Get<NBT::CompoundTag>("Heightmaps").GetData();
chunkData.WriteData(buf.data, buf.length);
// Biomes
for (int i = 0; i < 1024; i++)
chunkData.Write<int32_t>(5);
//NBT::IntArrayTag biomes = chunk->Get<NBT::IntArrayTag>("Biomes");
//NBT::DataBuffer buf1 = biomes.GetData();
//chunkData.WriteData(buf1.data, buf1.length);
sections.Finalize();
chunkData.WriteVarInt(sections.GetDataSize());
chunkData.WriteData(sections.GetData(), sections.GetDataSize());
// Block Ents
chunkData.WriteVarInt(0);
chunkData.Finalize();
client.SendMessage(chunkData);
}
template <>
void DedicatedServer::HandlePacket(MinecraftClient& client, const Play::ServerboundKeepAlive& keepAlive)
{
// TODO: check these and kick clients that don't send em right or at all
}
}