2020-07-24 10:17:36 +01:00
|
|
|
#include "DedicatedServer.h"
|
2020-07-25 04:44:00 +01:00
|
|
|
#include "config/ServerProperties.h"
|
2020-08-13 04:10:44 +01:00
|
|
|
#include "data/Registry.h"
|
2020-07-24 10:17:36 +01:00
|
|
|
|
2020-07-31 02:53:40 +01:00
|
|
|
#include "PacketReader.h"
|
|
|
|
|
2020-08-06 02:25:36 +01:00
|
|
|
#include <chrono>
|
2020-08-07 05:48:54 +01:00
|
|
|
#include <string>
|
2020-08-06 02:25:36 +01:00
|
|
|
|
2020-08-13 04:55:43 +01:00
|
|
|
using std::string;
|
|
|
|
|
2020-07-24 10:17:36 +01:00
|
|
|
namespace Feather
|
|
|
|
{
|
2020-07-25 04:44:00 +01:00
|
|
|
DedicatedServer::DedicatedServer(ServerProperties* properties) :
|
|
|
|
m_properties(properties),
|
2020-07-29 01:46:31 +01:00
|
|
|
m_listener(properties->serverPort.GetValue(), this),
|
2020-08-03 06:07:32 +01:00
|
|
|
m_status()
|
2020-07-24 10:17:36 +01:00
|
|
|
{
|
2020-07-25 04:44:00 +01:00
|
|
|
m_status.descriptionText = properties->motd.GetValue();
|
2020-08-07 05:48:54 +01:00
|
|
|
m_status.maxPlayers = properties->maxPlayers;
|
2020-08-13 04:10:44 +01:00
|
|
|
|
|
|
|
Registry::Init();
|
|
|
|
|
2020-08-07 05:48:54 +01:00
|
|
|
m_worldManager.LoadWorld(m_properties->levelName);
|
2020-07-25 04:44:00 +01:00
|
|
|
|
2020-07-25 03:01:16 +01:00
|
|
|
while (1)
|
|
|
|
{
|
2020-07-31 02:53:40 +01:00
|
|
|
auto [clients, clientsLock] = m_clients.borrow();
|
|
|
|
for (auto& client : clients)
|
|
|
|
{
|
2020-08-01 04:38:20 +01:00
|
|
|
auto [data, clientLock] = client.GetTCPClient()->GetData().borrow();
|
2020-07-31 02:53:40 +01:00
|
|
|
if (data.empty())
|
|
|
|
continue;
|
2020-08-01 05:53:22 +01:00
|
|
|
uint32_t offset = 0;
|
|
|
|
while (offset != data.size())
|
|
|
|
{
|
|
|
|
auto reader = PacketReader(&data[offset]);
|
2020-08-03 06:07:32 +01:00
|
|
|
Protocol::ProcessPacket(*this, client, reader, client.GetContext().GetState());
|
2020-08-01 05:53:22 +01:00
|
|
|
offset += reader.Size();
|
|
|
|
}
|
2020-07-31 02:53:40 +01:00
|
|
|
data.clear();
|
|
|
|
}
|
2020-08-06 02:25:36 +01:00
|
|
|
|
|
|
|
// 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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-07-25 03:01:16 +01:00
|
|
|
}
|
2020-07-24 10:17:36 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
DedicatedServer::~DedicatedServer()
|
|
|
|
{
|
|
|
|
}
|
2020-07-29 01:46:31 +01:00
|
|
|
|
2020-08-01 04:38:20 +01:00
|
|
|
void DedicatedServer::OnClientConnect(Network::TCPClientHandle&& client)
|
2020-07-29 01:46:31 +01:00
|
|
|
{
|
2020-08-02 00:35:54 +01:00
|
|
|
const auto& address = client->GetAddress();
|
2020-08-07 06:23:12 +01:00
|
|
|
Log::Info("New connection from {}:{}", address.ip, address.port);
|
2020-08-02 00:35:54 +01:00
|
|
|
|
2020-07-29 01:46:31 +01:00
|
|
|
auto [clients, lock] = m_clients.borrow();
|
|
|
|
clients.emplace_back(std::move(client));
|
|
|
|
}
|
|
|
|
|
2020-08-01 04:54:31 +01:00
|
|
|
void DedicatedServer::OnClientDisconnect(const Network::TCPClient* client)
|
2020-07-29 01:46:31 +01:00
|
|
|
{
|
2020-08-02 00:35:54 +01:00
|
|
|
const auto& address = client->GetAddress();
|
2020-08-07 06:23:12 +01:00
|
|
|
Log::Info("Disconnected from {}:{}", address.ip, address.port);
|
2020-08-02 00:35:54 +01:00
|
|
|
|
2020-07-29 01:46:31 +01:00
|
|
|
auto [clients, lock] = m_clients.borrow();
|
2020-08-01 04:54:31 +01:00
|
|
|
clients.remove_if([&](MinecraftClient& other) { return other.GetTCPClient().get() == client; });
|
2020-07-29 01:46:31 +01:00
|
|
|
}
|
2020-08-03 06:07:32 +01:00
|
|
|
|
|
|
|
using namespace Protocol;
|
|
|
|
|
2020-08-06 02:16:28 +01:00
|
|
|
void DedicatedServer::HandleUnknownPacket(MinecraftClient &client, int32_t id, const PacketReader &packet)
|
|
|
|
{
|
2020-08-07 06:23:12 +01:00
|
|
|
Log::Trace("Got unknown packet with ID {} from client.", id);
|
2020-08-06 02:16:28 +01:00
|
|
|
}
|
|
|
|
|
2020-08-03 06:07:32 +01:00
|
|
|
void DedicatedServer::HandleLegacyPing(MinecraftClient& client)
|
|
|
|
{
|
2020-08-07 06:23:12 +01:00
|
|
|
Log::Info("Got legacy server list ping.");
|
2020-08-03 06:07:32 +01:00
|
|
|
}
|
|
|
|
|
2020-08-06 02:16:28 +01:00
|
|
|
#pragma region Handshake & Status
|
|
|
|
|
2020-08-03 06:07:32 +01:00
|
|
|
template <>
|
|
|
|
void DedicatedServer::HandlePacket(MinecraftClient& client, const Handholding::ServerboundHandshake& handshake)
|
|
|
|
{
|
2020-08-07 06:23:12 +01:00
|
|
|
Log::Info("Client Intention Packet: version={}, serverIp={}, port={}, intention={}\n",
|
2020-08-03 06:07:32 +01:00
|
|
|
handshake.protocolVersion,
|
2020-08-20 01:41:28 +01:00
|
|
|
handshake.serverIP.data(),
|
2020-08-03 06:07:32 +01:00
|
|
|
handshake.port,
|
|
|
|
handshake.intention
|
|
|
|
);
|
|
|
|
|
|
|
|
client.GetContext().SetState(handshake.intention);
|
|
|
|
}
|
|
|
|
|
|
|
|
template <>
|
|
|
|
void DedicatedServer::HandlePacket(MinecraftClient& client, const Status::ServerboundRequest& request)
|
|
|
|
{
|
2020-08-07 06:23:12 +01:00
|
|
|
Log::Info("Client sent STATUS_PING_REQUEST");
|
2020-08-03 06:07:32 +01:00
|
|
|
|
|
|
|
Status::ClientboundResponse message =
|
|
|
|
{
|
|
|
|
.jsonResponse = m_status.GetServerStatusJSON()
|
|
|
|
};
|
|
|
|
|
|
|
|
client.SendMessage(message);
|
|
|
|
}
|
|
|
|
|
|
|
|
template <>
|
|
|
|
void DedicatedServer::HandlePacket(MinecraftClient& client, const Status::ServerboundPing& ping)
|
|
|
|
{
|
2020-08-07 06:23:12 +01:00
|
|
|
Log::Info("Client sent STATUS_PING: {}", ping.timestamp);
|
2020-08-03 06:07:32 +01:00
|
|
|
|
|
|
|
Status::ClientboundPong message =
|
|
|
|
{
|
|
|
|
.timestamp = ping.timestamp
|
|
|
|
};
|
|
|
|
|
|
|
|
client.SendMessage(message);
|
|
|
|
}
|
|
|
|
|
2020-08-06 02:16:28 +01:00
|
|
|
#pragma endregion
|
|
|
|
|
2020-08-03 06:07:32 +01:00
|
|
|
template <>
|
2020-08-04 02:40:15 +01:00
|
|
|
void DedicatedServer::HandlePacket(MinecraftClient& client,const Login::ServerboundStart& start)
|
2020-08-03 06:07:32 +01:00
|
|
|
{
|
2020-08-03 07:31:33 +01:00
|
|
|
//Login::ClientboundSuccess success =
|
|
|
|
//{
|
|
|
|
// .uuid = { 4658857991808325907ull, 7518717155607718277ull },
|
|
|
|
// .username = start.username
|
|
|
|
//};
|
2020-08-20 01:41:28 +01:00
|
|
|
// TODO: This is copying...
|
|
|
|
client.SetUsername(std::string(start.username));
|
2020-08-08 01:25:32 +01:00
|
|
|
|
2020-08-03 07:31:33 +01:00
|
|
|
std::string uuid = "ecb99913-96a8-40a7-8529-a2ca6ad95768";
|
|
|
|
Login::ClientboundSuccess success =
|
2020-08-03 06:07:32 +01:00
|
|
|
{
|
2020-08-03 07:31:33 +01:00
|
|
|
.uuid = uuid,
|
2020-08-03 06:07:32 +01:00
|
|
|
.username = start.username
|
|
|
|
};
|
|
|
|
|
2020-08-03 07:31:33 +01:00
|
|
|
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);
|
2020-08-04 02:40:15 +01:00
|
|
|
|
|
|
|
Play::ClientboundSpawnPosition spawnPos =
|
|
|
|
{
|
|
|
|
.location = BlockPos(0, 64, 0),
|
|
|
|
};
|
|
|
|
|
|
|
|
client.SendMessage(spawnPos);
|
|
|
|
|
2020-08-13 10:24:29 +01:00
|
|
|
// SEND CHUNKS
|
2020-08-13 04:55:43 +01:00
|
|
|
|
2020-08-13 18:06:28 +01:00
|
|
|
auto levelData = m_worldManager.GetOverworld()->GetLevelData();
|
|
|
|
ChunkPos spawnChunk = BlockPos(levelData.spawnX, levelData.spawnY, levelData.spawnZ);
|
|
|
|
int32_t viewDistance = m_properties->viewDistance;
|
|
|
|
for (int32_t x = spawnChunk.x - viewDistance; x < spawnChunk.x + viewDistance; x++)
|
|
|
|
{
|
|
|
|
for (int32_t z = spawnChunk.z - viewDistance; z < spawnChunk.z + viewDistance; z++)
|
|
|
|
{
|
|
|
|
NetworkMessage chunkData(5KB);
|
|
|
|
// Packet ID
|
|
|
|
chunkData.WriteVarInt(0x22);
|
2020-08-13 04:55:43 +01:00
|
|
|
|
|
|
|
|
2020-08-13 18:06:28 +01:00
|
|
|
// Chunk X, Z
|
|
|
|
chunkData.Write<int32_t>(x);
|
|
|
|
chunkData.Write<int32_t>(z);
|
2020-08-13 04:55:43 +01:00
|
|
|
|
2020-08-13 18:06:28 +01:00
|
|
|
// Full chunk
|
|
|
|
chunkData.Write<bool>(true);
|
2020-08-13 04:55:43 +01:00
|
|
|
|
2020-08-13 18:06:28 +01:00
|
|
|
Chunk* chunk = m_worldManager.GetOverworld()->GetChunk({ x, z });
|
|
|
|
if (!chunk)
|
|
|
|
continue;
|
2020-08-13 04:55:43 +01:00
|
|
|
|
2020-08-13 18:06:28 +01:00
|
|
|
NBT::CompoundTag* chunkNBT = chunk->nbt.get();
|
2020-08-13 04:55:43 +01:00
|
|
|
|
2020-08-13 18:06:28 +01:00
|
|
|
int32_t sectionsBits = 0;
|
2020-08-13 04:55:43 +01:00
|
|
|
|
2020-08-13 18:06:28 +01:00
|
|
|
NetworkMessage sections(3KB);
|
2020-08-13 04:55:43 +01:00
|
|
|
|
2020-08-13 18:06:28 +01:00
|
|
|
for (int i = 0; i < Chunk::NUM_SECTIONS; i++)
|
|
|
|
{
|
|
|
|
const ChunkSection& sect = chunk->sections[i];
|
2020-08-13 10:24:29 +01:00
|
|
|
|
2020-08-14 02:42:21 +01:00
|
|
|
//Log::Trace("Chunk: Got Section {} with local palette size {}", i, sect.localPalette.Size());
|
2020-08-13 04:55:43 +01:00
|
|
|
|
2020-08-13 18:06:28 +01:00
|
|
|
sectionsBits |= 1 << i;
|
2020-08-13 04:55:43 +01:00
|
|
|
|
2020-08-13 18:06:28 +01:00
|
|
|
// Block Count: fudge
|
|
|
|
sections.Write<int16_t>(1024);
|
2020-08-13 04:55:43 +01:00
|
|
|
|
2020-10-31 01:57:43 +00:00
|
|
|
const uint8_t bitsPerBlock = sect.blockStates.bits;
|
2020-08-13 18:06:28 +01:00
|
|
|
sections.Write<uint8_t>(bitsPerBlock);
|
2020-08-13 04:55:43 +01:00
|
|
|
|
2020-10-31 01:57:43 +00:00
|
|
|
const size_t dataLength = ChunkSection::NUM_BLOCKS * bitsPerBlock / bitsizeof(uint64);
|
2020-08-13 10:24:29 +01:00
|
|
|
|
2020-08-13 18:06:28 +01:00
|
|
|
if (bitsPerBlock <= 8)
|
|
|
|
{
|
|
|
|
// Palette
|
2020-10-31 01:57:43 +00:00
|
|
|
sections.WriteVarInt(sect.blockStates.palette.Size());
|
2020-08-13 04:55:43 +01:00
|
|
|
|
2020-10-31 01:57:43 +00:00
|
|
|
for (size_t j = 0; j < sect.blockStates.palette.Size(); j++)
|
|
|
|
sections.WriteVarInt(sect.blockStates.palette.ByID(j));
|
2020-08-13 18:06:28 +01:00
|
|
|
}
|
2020-08-13 04:55:43 +01:00
|
|
|
|
2020-11-04 22:59:50 +00:00
|
|
|
// Block State Data
|
|
|
|
|
2020-08-13 18:06:28 +01:00
|
|
|
sections.WriteVarInt(dataLength);
|
2020-11-04 22:59:50 +00:00
|
|
|
|
|
|
|
{
|
|
|
|
auto [data, lock] = sect.blockStates.data.borrow();
|
|
|
|
|
|
|
|
// Can't just copy because endian-ness!
|
|
|
|
for (size_t i = 0; i < dataLength; i++)
|
|
|
|
sections.Write<uint64_t>(data[i]);
|
|
|
|
}
|
2020-08-13 18:06:28 +01:00
|
|
|
}
|
2020-08-13 04:55:43 +01:00
|
|
|
|
2020-08-14 02:42:21 +01:00
|
|
|
//Log::Trace("Section bits: {:#b}", sectionsBits);
|
2020-08-13 04:55:43 +01:00
|
|
|
|
2020-08-13 18:06:28 +01:00
|
|
|
chunkData.WriteVarInt(sectionsBits);
|
2020-08-13 04:55:43 +01:00
|
|
|
|
2020-08-13 18:06:28 +01:00
|
|
|
// Heightmaps
|
|
|
|
// TODO: Use chunk->heightmaps
|
|
|
|
NBT::DataBuffer buf = chunkNBT->Get<NBT::CompoundTag>("Heightmaps").GetData();
|
|
|
|
chunkData.WriteData(buf.data, buf.length);
|
2020-08-13 04:55:43 +01:00
|
|
|
|
2020-08-13 18:06:28 +01:00
|
|
|
// Biomes
|
|
|
|
NBT::IntArrayTag biomes = chunkNBT->Get<NBT::IntArrayTag>("Biomes");
|
|
|
|
for (uint32_t i = 0; i < 1024; i++)
|
|
|
|
chunkData.Write<int32_t>(biomes[i]);
|
2020-08-13 04:55:43 +01:00
|
|
|
|
2020-08-13 18:06:28 +01:00
|
|
|
sections.Finalize();
|
|
|
|
chunkData.WriteSubMessage(sections);
|
2020-08-13 04:55:43 +01:00
|
|
|
|
2020-08-13 18:06:28 +01:00
|
|
|
// Block Ents
|
|
|
|
chunkData.WriteVarInt(0);
|
2020-08-13 04:55:43 +01:00
|
|
|
|
2020-08-13 18:06:28 +01:00
|
|
|
chunkData.Finalize();
|
|
|
|
client.SendMessage(chunkData);
|
|
|
|
}
|
|
|
|
}
|
2020-08-13 04:55:43 +01:00
|
|
|
|
2020-08-13 10:24:29 +01:00
|
|
|
// UPDATE POSITION
|
|
|
|
using RelativeFlags = Play::ClientboundPlayerPositionAndLook::RelativeFlags;
|
|
|
|
|
|
|
|
Play::ClientboundPlayerPositionAndLook playerPos =
|
|
|
|
{
|
2020-08-13 18:06:28 +01:00
|
|
|
.x = double(levelData.spawnX),
|
|
|
|
.y = double(levelData.spawnY),
|
|
|
|
.z = double(levelData.spawnZ),
|
2020-08-13 10:24:29 +01:00
|
|
|
.xRot = 0,
|
|
|
|
.yRot = 0,
|
|
|
|
.flags = RelativeFlags(0),
|
|
|
|
.id = 0,
|
|
|
|
};
|
|
|
|
|
|
|
|
client.SendMessage(playerPos);
|
2020-08-03 06:07:32 +01:00
|
|
|
}
|
2020-08-06 02:25:36 +01:00
|
|
|
|
|
|
|
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
|
|
|
|
}
|
2020-08-08 01:25:32 +01:00
|
|
|
|
|
|
|
template <>
|
|
|
|
void DedicatedServer::HandlePacket(MinecraftClient& client, const Play::ServerboundChatMessage& chat)
|
|
|
|
{
|
|
|
|
Log::Info("{}: {}", client.GetUsername(), chat.message);
|
|
|
|
|
|
|
|
std::string json = fmt::format(R"({{ "text": "<{}> {}" }})", client.GetUsername(), chat.message);
|
|
|
|
Play::ClientboundChatMessage message =
|
|
|
|
{
|
|
|
|
.jsonData = json,
|
|
|
|
.position = 0
|
|
|
|
};
|
|
|
|
|
|
|
|
// m_clients is already locked for us!
|
|
|
|
auto& clients = m_clients.steal();
|
|
|
|
for (auto& client : clients)
|
|
|
|
client.SendMessage(message);
|
|
|
|
}
|
2020-10-31 08:20:06 +00:00
|
|
|
|
|
|
|
template <>
|
|
|
|
void DedicatedServer::HandlePacket(MinecraftClient& client, const Play::ServerboundPlayerAction& action)
|
|
|
|
{
|
|
|
|
|
|
|
|
}
|
2020-07-24 10:17:36 +01:00
|
|
|
}
|