100 lines
3.0 KiB
C++
100 lines
3.0 KiB
C++
#pragma once
|
|
|
|
#include <cstddef>
|
|
#include <cstdint>
|
|
#include <memory>
|
|
#include <string>
|
|
#include <vector>
|
|
#include "NetSession.h"
|
|
|
|
namespace vf {
|
|
|
|
struct ServerConfig {
|
|
uint32_t maxClients = 8;
|
|
int viewRadius = 2;
|
|
uint32_t chunksPerClientPerTick = 2;
|
|
std::size_t bytesPerClientPerTick = 0;
|
|
uint32_t keepAliveIntervalTicks = 20;
|
|
uint32_t keepAliveTimeoutTicks = 100;
|
|
std::size_t multiBlockThreshold = 4;
|
|
int32_t protocolVersion = 47;
|
|
uint64_t seedHash = 0;
|
|
std::string levelType = "default";
|
|
TransportConfig transport;
|
|
LinkQuality link;
|
|
};
|
|
|
|
struct ServerStats {
|
|
uint32_t connectedClients = 0;
|
|
uint64_t bytesSentThisTick = 0;
|
|
uint64_t totalBytesSent = 0;
|
|
uint64_t chunksSent = 0;
|
|
uint64_t blockUpdatesSent = 0;
|
|
uint64_t queuedPackets = 0;
|
|
uint64_t queuedBytes = 0;
|
|
uint64_t acceptedConnections = 0;
|
|
uint64_t rejectedConnections = 0;
|
|
uint64_t timeouts = 0;
|
|
uint64_t editsApplied = 0;
|
|
};
|
|
|
|
// One connected player: its own link, its own transport, its own session. The
|
|
// slot outlives the connection so the index handed out at accept stays valid
|
|
// until the slot is reused.
|
|
struct ClientSlot {
|
|
bool active = false;
|
|
int32_t entityId = 0;
|
|
uint64_t lastServicedTick = 0;
|
|
uint64_t lastBytesFlushed = 0;
|
|
uint64_t lastChunksSent = 0;
|
|
uint64_t lastBlockUpdates = 0;
|
|
std::unique_ptr<LoopbackLink> link;
|
|
std::unique_ptr<Transport> transport;
|
|
std::unique_ptr<NetSession> session;
|
|
};
|
|
|
|
// Many clients over one authoritative world. Every block edit is applied once
|
|
// and then offered to each session, which decides on its own whether that
|
|
// client is close enough to care.
|
|
class NetServer {
|
|
public:
|
|
NetServer(World& world, ServerConfig config = {});
|
|
|
|
// The slot index, or -1 when every slot is taken.
|
|
int acceptClient();
|
|
void dropClient(int slot);
|
|
|
|
LoopbackLink* linkFor(int slot);
|
|
NetSession* sessionFor(int slot);
|
|
const ClientSlot& slotAt(int slot) const { return slots[static_cast<std::size_t>(slot)]; }
|
|
std::size_t slotCount() const { return slots.size(); }
|
|
uint32_t activeClients() const;
|
|
bool isActive(int slot) const;
|
|
|
|
// Authoritative edit: the world, the dirty feed and every session's work
|
|
// list move together, so no client can be told about a block the world
|
|
// does not already have.
|
|
void setBlock(int gx, int gy, int gz, Block block);
|
|
void moveEntity(int32_t entityId, const Vec3& position, float yaw, float pitch, bool onGround);
|
|
|
|
void update(uint64_t tick);
|
|
|
|
World& world() { return *worldRef; }
|
|
ServerChunkSync& chunkSync() { return sync; }
|
|
const ServerConfig& settings() const { return config; }
|
|
const ServerStats& stats() const { return statistics; }
|
|
|
|
private:
|
|
void applyEdit(const BlockChange& change);
|
|
void collectStats();
|
|
void reap();
|
|
|
|
World* worldRef;
|
|
ServerConfig config;
|
|
ServerChunkSync sync;
|
|
std::vector<ClientSlot> slots;
|
|
ServerStats statistics;
|
|
};
|
|
|
|
} // namespace vf
|