mirror of
https://github.com/EQEmu/Server.git
synced 2025-12-11 16:51:29 +00:00
347 lines
10 KiB
C++
347 lines
10 KiB
C++
#pragma once
|
|
|
|
#include "../random.h"
|
|
#include "packet.h"
|
|
#include "reliable_stream_structs.h"
|
|
#include "reliable_stream_pooling.h"
|
|
#include <uv.h>
|
|
#include <chrono>
|
|
#include <functional>
|
|
#include <memory>
|
|
#include <map>
|
|
#include <queue>
|
|
#include <list>
|
|
|
|
namespace EQ
|
|
{
|
|
namespace Net
|
|
{
|
|
enum ReliableStreamProtocolOpcode
|
|
{
|
|
OP_Padding = 0x00,
|
|
OP_SessionRequest = 0x01,
|
|
OP_SessionResponse = 0x02,
|
|
OP_Combined = 0x03,
|
|
OP_SessionDisconnect = 0x05,
|
|
OP_KeepAlive = 0x06,
|
|
OP_SessionStatRequest = 0x07,
|
|
OP_SessionStatResponse = 0x08,
|
|
OP_Packet = 0x09,
|
|
OP_Packet2 = 0x0a,
|
|
OP_Packet3 = 0x0b,
|
|
OP_Packet4 = 0x0c,
|
|
OP_Fragment = 0x0d,
|
|
OP_Fragment2 = 0x0e,
|
|
OP_Fragment3 = 0x0f,
|
|
OP_Fragment4 = 0x10,
|
|
OP_OutOfOrderAck = 0x11,
|
|
OP_OutOfOrderAck2 = 0x12,
|
|
OP_OutOfOrderAck3 = 0x13,
|
|
OP_OutOfOrderAck4 = 0x14,
|
|
OP_Ack = 0x15,
|
|
OP_Ack2 = 0x16,
|
|
OP_Ack3 = 0x17,
|
|
OP_Ack4 = 0x18,
|
|
OP_AppCombined = 0x19,
|
|
OP_OutboundPing = 0x1c,
|
|
OP_OutOfSession = 0x1d
|
|
};
|
|
|
|
enum DbProtocolStatus
|
|
{
|
|
StatusConnecting,
|
|
StatusConnected,
|
|
StatusDisconnecting,
|
|
StatusDisconnected
|
|
};
|
|
|
|
enum ReliableStreamEncodeType
|
|
{
|
|
EncodeNone = 0,
|
|
EncodeCompression = 1,
|
|
EncodeXOR = 4,
|
|
};
|
|
|
|
enum SequenceOrder
|
|
{
|
|
SequenceCurrent,
|
|
SequenceFuture,
|
|
SequencePast
|
|
};
|
|
|
|
typedef std::chrono::steady_clock::time_point Timestamp;
|
|
typedef std::chrono::steady_clock Clock;
|
|
|
|
struct ReliableStreamConnectionStats
|
|
{
|
|
ReliableStreamConnectionStats() {
|
|
recv_bytes = 0;
|
|
sent_bytes = 0;
|
|
recv_packets = 0;
|
|
sent_packets = 0;
|
|
sync_recv_packets = 0;
|
|
sync_sent_packets = 0;
|
|
sync_remote_recv_packets = 0;
|
|
sync_remote_sent_packets = 0;
|
|
min_ping = 0xFFFFFFFFFFFFFFFFUL;
|
|
max_ping = 0;
|
|
avg_ping = 0;
|
|
created = Clock::now();
|
|
dropped_datarate_packets = 0;
|
|
resent_packets = 0;
|
|
resent_fragments = 0;
|
|
resent_full = 0;
|
|
datarate_remaining = 0.0;
|
|
bytes_after_decode = 0;
|
|
bytes_before_encode = 0;
|
|
}
|
|
|
|
void Reset() {
|
|
recv_bytes = 0;
|
|
sent_bytes = 0;
|
|
min_ping = 0xFFFFFFFFFFFFFFFFUL;
|
|
max_ping = 0;
|
|
avg_ping = 0;
|
|
created = Clock::now();
|
|
dropped_datarate_packets = 0;
|
|
resent_packets = 0;
|
|
resent_fragments = 0;
|
|
resent_full = 0;
|
|
datarate_remaining = 0.0;
|
|
bytes_after_decode = 0;
|
|
bytes_before_encode = 0;
|
|
}
|
|
|
|
uint64_t recv_bytes;
|
|
uint64_t sent_bytes;
|
|
uint64_t recv_packets;
|
|
uint64_t sent_packets;
|
|
uint64_t sync_recv_packets;
|
|
uint64_t sync_sent_packets;
|
|
uint64_t sync_remote_recv_packets;
|
|
uint64_t sync_remote_sent_packets;
|
|
uint64_t min_ping;
|
|
uint64_t max_ping;
|
|
uint64_t avg_ping;
|
|
uint64_t last_ping;
|
|
Timestamp created;
|
|
uint64_t dropped_datarate_packets; //packets dropped due to datarate limit, couldn't think of a great name
|
|
uint64_t resent_packets;
|
|
uint64_t resent_fragments;
|
|
uint64_t resent_full;
|
|
double datarate_remaining;
|
|
uint64_t bytes_after_decode;
|
|
uint64_t bytes_before_encode;
|
|
};
|
|
|
|
class ReliableStreamConnectionManager;
|
|
class ReliableStreamConnection;
|
|
class ReliableStreamConnection
|
|
{
|
|
public:
|
|
ReliableStreamConnection(ReliableStreamConnectionManager *owner, const ReliableStreamConnect &connect, const std::string &endpoint, int port);
|
|
ReliableStreamConnection(ReliableStreamConnectionManager *owner, const std::string &endpoint, int port);
|
|
~ReliableStreamConnection();
|
|
|
|
const std::string& RemoteEndpoint() const { return m_endpoint; }
|
|
int RemotePort() const { return m_port; }
|
|
|
|
void Close();
|
|
void QueuePacket(Packet &p);
|
|
void QueuePacket(Packet &p, int stream);
|
|
void QueuePacket(Packet &p, int stream, bool reliable);
|
|
|
|
ReliableStreamConnectionStats GetStats();
|
|
void ResetStats();
|
|
size_t GetRollingPing() const { return m_rolling_ping; }
|
|
DbProtocolStatus GetStatus() const { return m_status; }
|
|
|
|
const ReliableStreamEncodeType* GetEncodePasses() const { return m_encode_passes; }
|
|
const ReliableStreamConnectionManager* GetManager() const { return m_owner; }
|
|
ReliableStreamConnectionManager* GetManager() { return m_owner; }
|
|
private:
|
|
ReliableStreamConnectionManager *m_owner;
|
|
std::string m_endpoint;
|
|
int m_port;
|
|
uint32_t m_connect_code;
|
|
uint32_t m_encode_key;
|
|
uint32_t m_max_packet_size;
|
|
uint32_t m_crc_bytes;
|
|
ReliableStreamEncodeType m_encode_passes[2];
|
|
|
|
Timestamp m_last_send;
|
|
Timestamp m_last_recv;
|
|
DbProtocolStatus m_status;
|
|
Timestamp m_hold_time;
|
|
std::list<DynamicPacket> m_buffered_packets;
|
|
size_t m_buffered_packets_length;
|
|
std::unique_ptr<char[]> m_combined;
|
|
ReliableStreamConnectionStats m_stats;
|
|
Timestamp m_last_session_stats;
|
|
size_t m_rolling_ping;
|
|
Timestamp m_close_time;
|
|
double m_outgoing_budget;
|
|
|
|
// resend tracking
|
|
size_t m_resend_packets_sent = 0;
|
|
size_t m_resend_bytes_sent = 0;
|
|
bool m_acked_since_last_resend = false;
|
|
Timestamp m_last_ack;
|
|
|
|
struct ReliableStreamSentPacket
|
|
{
|
|
DynamicPacket packet;
|
|
Timestamp last_sent;
|
|
Timestamp first_sent;
|
|
size_t times_resent;
|
|
size_t resend_delay;
|
|
};
|
|
|
|
struct ReliableStream
|
|
{
|
|
ReliableStream() {
|
|
sequence_in = 0;
|
|
sequence_out = 0;
|
|
fragment_current_bytes = 0;
|
|
fragment_total_bytes = 0;
|
|
}
|
|
|
|
uint16_t sequence_in;
|
|
uint16_t sequence_out;
|
|
std::map<uint16_t, Packet*> packet_queue;
|
|
|
|
DynamicPacket fragment_packet;
|
|
uint32_t fragment_current_bytes;
|
|
uint32_t fragment_total_bytes;
|
|
|
|
std::map<uint16_t, ReliableStreamSentPacket> sent_packets;
|
|
};
|
|
|
|
ReliableStream m_streams[4];
|
|
std::weak_ptr<ReliableStreamConnection> m_self;
|
|
|
|
void Process();
|
|
void ProcessPacket(Packet &p);
|
|
void ProcessQueue();
|
|
void RemoveFromQueue(int stream, uint16_t seq);
|
|
void AddToQueue(int stream, uint16_t seq, const Packet &p);
|
|
void ProcessDecodedPacket(const Packet &p);
|
|
void ChangeStatus(DbProtocolStatus new_status);
|
|
bool ValidateCRC(Packet &p);
|
|
void AppendCRC(Packet &p);
|
|
bool PacketCanBeEncoded(Packet &p) const;
|
|
void Decode(Packet &p, size_t offset, size_t length);
|
|
void Encode(Packet &p, size_t offset, size_t length);
|
|
void Decompress(Packet &p, size_t offset, size_t length);
|
|
void Compress(Packet &p, size_t offset, size_t length);
|
|
void ProcessResend();
|
|
void ProcessResend(int stream);
|
|
void Ack(int stream, uint16_t seq);
|
|
void OutOfOrderAck(int stream, uint16_t seq);
|
|
void UpdateDataBudget(double budget_add);
|
|
|
|
void SendConnect();
|
|
void SendKeepAlive();
|
|
void SendAck(int stream, uint16_t seq);
|
|
void SendOutOfOrderAck(int stream, uint16_t seq);
|
|
void SendDisconnect();
|
|
void InternalBufferedSend(Packet &p);
|
|
void InternalSend(Packet &p);
|
|
void InternalQueuePacket(Packet &p, int stream_id, bool reliable);
|
|
void FlushBuffer();
|
|
SequenceOrder CompareSequence(uint16_t expected, uint16_t actual) const;
|
|
|
|
friend class ReliableStreamConnectionManager;
|
|
};
|
|
|
|
struct ReliableStreamConnectionManagerOptions
|
|
{
|
|
ReliableStreamConnectionManagerOptions() {
|
|
max_connection_count = 0;
|
|
keepalive_delay_ms = 9000;
|
|
resend_delay_ms = 30;
|
|
resend_delay_factor = 1.25;
|
|
resend_delay_min = 150;
|
|
resend_delay_max = 5000;
|
|
connect_delay_ms = 500;
|
|
stale_connection_ms = 60000;
|
|
connect_stale_ms = 5000;
|
|
crc_length = 2;
|
|
max_packet_size = 512;
|
|
encode_passes[0] = ReliableStreamEncodeType::EncodeNone;
|
|
encode_passes[1] = ReliableStreamEncodeType::EncodeNone;
|
|
port = 0;
|
|
hold_size = 512;
|
|
hold_length_ms = 50;
|
|
simulated_in_packet_loss = 0;
|
|
simulated_out_packet_loss = 0;
|
|
tic_rate_hertz = 60.0;
|
|
resend_timeout = 30000;
|
|
connection_close_time = 2000;
|
|
outgoing_data_rate = 0.0;
|
|
}
|
|
|
|
size_t max_packet_size;
|
|
size_t max_connection_count;
|
|
size_t keepalive_delay_ms;
|
|
double resend_delay_factor;
|
|
size_t resend_delay_ms;
|
|
size_t resend_delay_min;
|
|
size_t resend_delay_max;
|
|
size_t connect_delay_ms;
|
|
size_t connect_stale_ms;
|
|
size_t stale_connection_ms;
|
|
size_t crc_length;
|
|
size_t hold_size;
|
|
size_t hold_length_ms;
|
|
size_t simulated_in_packet_loss;
|
|
size_t simulated_out_packet_loss;
|
|
double tic_rate_hertz;
|
|
size_t resend_timeout;
|
|
size_t connection_close_time;
|
|
ReliableStreamEncodeType encode_passes[2];
|
|
int port;
|
|
double outgoing_data_rate;
|
|
};
|
|
|
|
class ReliableStreamConnectionManager
|
|
{
|
|
public:
|
|
ReliableStreamConnectionManager();
|
|
ReliableStreamConnectionManager(const ReliableStreamConnectionManagerOptions &opts);
|
|
~ReliableStreamConnectionManager();
|
|
|
|
void Connect(const std::string &addr, int port);
|
|
void Process();
|
|
void UpdateDataBudget();
|
|
void ProcessResend();
|
|
void OnNewConnection(std::function<void(std::shared_ptr<ReliableStreamConnection>)> func) { m_on_new_connection = func; }
|
|
void OnConnectionStateChange(std::function<void(std::shared_ptr<ReliableStreamConnection>, DbProtocolStatus, DbProtocolStatus)> func) { m_on_connection_state_change = func; }
|
|
void OnPacketRecv(std::function<void(std::shared_ptr<ReliableStreamConnection>, const Packet &)> func) { m_on_packet_recv = func; }
|
|
void OnErrorMessage(std::function<void(const std::string&)> func) { m_on_error_message = func; }
|
|
|
|
ReliableStreamConnectionManagerOptions& GetOptions() { return m_options; }
|
|
private:
|
|
void Attach(uv_loop_t *loop);
|
|
void Detach();
|
|
|
|
EQ::Random m_rand;
|
|
uv_timer_t m_timer;
|
|
uv_udp_t m_socket;
|
|
uv_loop_t *m_attached;
|
|
ReliableStreamConnectionManagerOptions m_options;
|
|
std::function<void(std::shared_ptr<ReliableStreamConnection>)> m_on_new_connection;
|
|
std::function<void(std::shared_ptr<ReliableStreamConnection>, DbProtocolStatus, DbProtocolStatus)> m_on_connection_state_change;
|
|
std::function<void(std::shared_ptr<ReliableStreamConnection>, const Packet&)> m_on_packet_recv;
|
|
std::function<void(const std::string&)> m_on_error_message;
|
|
std::map<std::pair<std::string, int>, std::shared_ptr<ReliableStreamConnection>> m_connections;
|
|
|
|
void ProcessPacket(const std::string &endpoint, int port, const char *data, size_t size);
|
|
std::shared_ptr<ReliableStreamConnection> FindConnectionByEndpoint(std::string addr, int port);
|
|
void SendDisconnect(const std::string &addr, int port);
|
|
|
|
friend class ReliableStreamConnection;
|
|
};
|
|
}
|
|
}
|