Open source Star Ruler 2 source code!

This commit is contained in:
Lucas de Vries
2018-07-17 14:15:37 +02:00
commit cc307720ff
4342 changed files with 2365070 additions and 0 deletions
+225
View File
@@ -0,0 +1,225 @@
#include <network/address.h>
#include <cstring>
#include <sstream>
#ifdef _MSC_VER
#include <WinSock2.h>
#include <ws2def.h>
#include <ws2ipdef.h>
#include <WS2tcpip.h>
#elif defined(__GNUC__)
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#endif
namespace net {
Address::Address() : type(AT_INVALID) {
}
Address::Address(int ip4, int port)
: type(AT_IPv4), adr4(ip4), port(port) {
}
Address::Address(uint8_t* ip6, int port)
: type(AT_IPv6), port(port) {
memcpy(adr6, ip6, 16);
}
bool Address::operator<(const Address& other) const {
if(type != other.type)
return type < other.type;
if(type == AT_IPv4) {
if(adr4 < other.adr4)
return true;
else if(adr4 == other.adr4)
return port < other.port;
return false;
}
else if(type == AT_IPv6) {
int lt = memcmp(&adr6, &other.adr6, sizeof(adr6));
if(lt < 0)
return true;
if(lt == 0)
return port < other.port;
return false;
}
else { //All invalid addresses are equal
return false;
}
}
bool Address::operator==(const Address& other) const {
if(type != other.type)
return false;
if(type == AT_IPv4) {
return port == other.port && adr4 == other.adr4;
}
else if(type == AT_IPv6) {
return port == other.port && memcmp(&adr6, &other.adr6, sizeof(adr6)) == 0;
}
else { //All invalid addresses are equal
return true;
}
}
bool Address::ipEquals(const Address& other) const {
if(type != other.type)
return false;
if(type == AT_IPv4)
return adr4 == other.adr4;
else if(type == AT_IPv6)
return memcmp(&adr6, &other.adr6, sizeof(adr6)) == 0;
else
return false;
}
struct addrinfo* lookup(const std::string& hostname, int port, AddressType type) {
struct addrinfo* res;
struct addrinfo hints;
memset(&hints, 0, sizeof(hints));
hints.ai_socktype = SOCK_DGRAM;
hints.ai_protocol = IPPROTO_UDP;
switch(type) {
case AT_IPv4: hints.ai_family = AF_INET; break;
case AT_IPv6: hints.ai_family = AF_INET6; break;
default: hints.ai_family = AF_UNSPEC; break;
}
char strport[64];
#ifndef _MSC_VER
snprintf(strport, 64, "%d", port);
#else
_snprintf(strport, 64, "%d", port);
#endif
int code = 0;
if(hostname.empty()) {
hints.ai_flags = AI_PASSIVE;
code = getaddrinfo(0, strport, &hints, &res);
}
else {
code = getaddrinfo(hostname.c_str(), strport, &hints, &res);
}
if(code != 0) {
fprintf(stderr, "getaddrinfo failed: %s\n", gai_strerror(code));
return 0;
}
return res;
}
Address::Address(const std::string& Hostname, int Port, AddressType Type) : type(AT_INVALID) {
struct addrinfo* res, *head;
res = head = lookup(Hostname, Port, Type);
if(!res) {
fprintf(stderr, "ERROR: Could not resolve hostname \"%s\".\n", Hostname.c_str());
return;
}
from_sockaddr(*(sockaddr_storage*)res->ai_addr);
freeaddrinfo(head);
}
std::string Address::toString(bool showPort) const {
std::stringstream out;
switch(type) {
case AT_IPv4: {
struct in_addr adr;
adr.s_addr = adr4;
#ifndef _MSC_VER
char buf[INET_ADDRSTRLEN];
inet_ntop(AF_INET, &adr, buf, INET_ADDRSTRLEN);
out << buf;
#else
out << inet_ntoa(adr);
#endif
if(showPort) {
out << ":";
out << port;
}
} break;
case AT_IPv6: {
if(showPort)
out << "[";
#ifndef _MSC_VER
char buf[INET6_ADDRSTRLEN];
struct in6_addr adr;
memcpy(&adr, adr6, 16);
inet_ntop(AF_INET6, &adr, buf, INET6_ADDRSTRLEN);
out << buf;
#else
//TODO: Actually support this, inet_ntop not reliable on Windows XP
out << "IPv6";
#endif
if(showPort) {
out << "]:";
out << port;
}
} break;
}
return out.str();
}
void Address::to_sockaddr(sockaddr_storage& adr, socklen_t* size) const {
switch(type) {
case AT_IPv4: {
sockaddr_in* st = (sockaddr_in*)&adr;
st->sin_family = AF_INET;
st->sin_port = htons(port);
st->sin_addr.s_addr = adr4;
if(size)
*size = sizeof(sockaddr_in);
} break;
case AT_IPv6: {
sockaddr_in6* st = (sockaddr_in6*)&adr;
st->sin6_family = AF_INET6;
st->sin6_flowinfo = 0;
st->sin6_port = htons(port);
st->sin6_scope_id = 0;
memcpy(&st->sin6_addr, adr6, 16);
if(size)
*size = sizeof(sockaddr_in6);
} break;
}
}
void Address::from_sockaddr(sockaddr_storage& adr) {
switch(adr.ss_family) {
case AF_INET: {
sockaddr_in* ad = (sockaddr_in*)&adr;
type = AT_IPv4;
adr4 = ad->sin_addr.s_addr;
port = ntohs(ad->sin_port);
} break;
case AF_INET6: {
sockaddr_in6* ad = (sockaddr_in6*)&adr;
type = AT_IPv6;
memcpy(adr6, &(ad->sin6_addr), 16);
port = ntohs(ad->sin6_port);
} break;
default:
fprintf(stderr, "ERROR: Invalid hostname family detected.\n");
break;
}
}
};
+209
View File
@@ -0,0 +1,209 @@
#include <network/client.h>
#include <network/init.h>
namespace net {
Client::Client(Address connectTo, bool makeConnection)
: trans(new Transport(connectTo.type)), conn(0), address(connectTo),
established(false), resolved(true), hasConnection(makeConnection) {
if(makeConnection) {
conn = new Connection(*trans, address);
Message cmsg(MT_Connect, MF_Reliable);
*this << cmsg;
}
if(trans->active)
addTransport(trans);
}
Client::Client(const std::string& hostname, int port, bool makeConnection, AddressType type)
: trans(new Transport(type)), conn(0), hostname(hostname), established(false),
resolved(false), hasConnection(makeConnection) {
address.port = port;
}
void Client::resolve() {
threads::Lock lock(handlerMutex);
if(!resolved) {
address = net::Address(hostname, address.port);
if(trans->active)
addTransport(trans);
if(hasConnection) {
conn = new Connection(*trans, address);
resolved = true;
Message cmsg(MT_Connect, MF_Reliable);
*this << cmsg;
}
else {
resolved = true;
}
}
}
Client::~Client() {
stop();
}
void Client::stop() {
if(active) {
if(conn) {
Message dmsg(MT_Disconnect, MF_Reliable);
dmsg << DR_Close;
conn->send(dmsg, false);
}
MessageHandler::stop();
if(conn) {
conn->drop();
conn = 0;
}
}
}
void Client::handle(uint8_t type, Client::clMessageHandler func) {
threads::Lock lock(handlerMutex);
handlers[type] = func;
}
void Client::handleClear(uint8_t type) {
threads::Lock lock(handlerMutex);
handlers.erase(type);
}
Client& Client::operator<<(Message& msg) {
if(!resolved)
resolve();
if(conn)
*conn << msg;
else
trans->send(msg, address);
return *this;
}
void Client::sendPing() {
if(conn)
conn->sendPing();
}
unsigned Client::getLastPing() {
if(conn)
return conn->ping;
return 0;
}
Connection* Client::getConnection() {
return conn;
}
void Client::handleMessage(Transport* transport, Address addr, Message* msg) {
uint8_t type = msg->getType();
if(type == MT_Disconnect)
active = false;
if(conn) {
if(type == MT_Connect)
established = true;
if(!conn->preHandle(*this, msg)) {
MessageHandler::handleMessage(transport, addr, msg);
return;
}
}
clMessageHandler handler = nullptr;
{
threads::Lock lock(handlerMutex);
auto it = handlers.find(type);
if(it != handlers.end())
handler = it->second;
}
if(handler)
handler(*this, *msg);
if(conn)
conn->postHandle(*this, msg);
MessageHandler::handleMessage(transport, addr, msg);
}
bool Client::mainTick() {
//Resolve the address if we have to
if(!resolved)
resolve();
//Do normal message handler stuff
bool received = MessageHandler::mainTick();
//Let the connection process things
if(conn) {
if(!conn->active) {
{
Message* dmsg = new Message(MT_Disconnect);
if(trans->active)
*dmsg << DR_Timeout;
else
*dmsg << DR_Error;
trans->grab();
handleMessage(trans, address, dmsg);
}
active = false;
}
else {
conn->process(*this);
}
}
return received;
}
BroadcastClient::BroadcastClient(int Port, AddressType Type)
: trans(Type), port(Port) {
if(trans.active)
addTransport(&trans);
}
BroadcastClient::~BroadcastClient() {
stop();
}
void BroadcastClient::handle(uint8_t type, BroadcastClient::bcMessageHandler func) {
threads::Lock lock(handlerMutex);
handlers[type] = func;
}
void BroadcastClient::handleClear(uint8_t type) {
threads::Lock lock(handlerMutex);
handlers.erase(type);
}
void BroadcastClient::handleMessage(Transport* transport, Address addr, Message* msg) {
uint8_t type = msg->getType();
bcMessageHandler handler = nullptr;
{
threads::Lock lock(handlerMutex);
auto it = handlers.find(type);
if(it != handlers.end())
handler = it->second;
}
if(handler)
handler(*this, addr, *msg);
MessageHandler::handleMessage(transport, addr, msg);
}
void BroadcastClient::send(Message& msg, Address addr) {
trans.send(msg, addr);
}
void BroadcastClient::broadcast(Message& msg) {
trans.broadcast(msg, port);
}
};
+726
View File
@@ -0,0 +1,726 @@
#include <network/connection.h>
#include <network/sequence.h>
#include <stdlib.h>
#include <algorithm>
const unsigned UDP_Header_IPv4 = 28;
const unsigned UDP_Header_IPv6 = 48;
namespace net {
Connection::Connection(Transport& trans, Address addr)
: references(1), nextOutgoingID(1), nextAck(1), nextSequenceID(1), nextReliablePeriod(0),
pingPongWait(rand() % NET_PING_INTERVAL), pingSent(false),
transport(trans), active(true), address(addr), id(-1), inBytes(0), outBytes(0), availBytes(0),
windowBytes(500000), windowLength(1.0), windowUpdate(1.0), windowUsed(0)
{
transport.grab();
time_now(lastMessageReceived);
time_now(lastProcessTime);
}
Connection::~Connection() {
transport.drop();
}
void Connection::grab() const {
++references;
}
void Connection::drop() const {
if(--references == 0)
delete this;
}
void Connection::queueReliable(Message* msg, bool immediate) {
threads::Lock lock(reliableMutex);
if(immediate)
queuedReliable.push_front(msg);
else
queuedReliable.push_back(msg);
}
OutSequence* Connection::sequence() {
threads::Lock lock(sequenceMutex);
OutSequence* ws = new OutSequence(*this);
ws->id = nextSequenceID++;
outSequences[ws->id] = ws;
return ws;
}
void Connection::queueSeqAck(unsigned short sequenceID, unsigned short messageID) {
threads::Lock lock(ackMutex);
SeqAck ack = {sequenceID, messageID};
queuedSeqAcks.push_back(ack);
}
void Connection::queue(Message* msg) {
threads::Lock lock(msgQueueLock);
#ifdef _DEBUG
if(msg->getFlag(MF_Reliable) && !msg->hasID())
return;
#endif
queuedMessages.push_back(msg);
}
Connection& Connection::operator<<(Message& msg) {
if(msg.getFlag(MF_Reliable))
queueReliable(new Message(msg));
else
queue(new Message(msg));
return *this;
}
bool Connection::split(Message& msg) {
if(msg.size() <= NET_FRAGMENT_LIMIT)
return false;
//Send the message in fragments
unsigned fragId = nextFragmentID++;
msize_t startAt = 0;
msize_t size = msg.size();
unsigned short index = 0;
msize_t maxPerFrag = (NET_FRAGMENT_LIMIT - NET_FRAGMENT_OVERHEAD);
msize_t frags = (size + maxPerFrag - 1) / maxPerFrag;
msize_t bytesPerFrag = (size / frags) + (frags - 1);
if(bytesPerFrag > maxPerFrag)
bytesPerFrag = maxPerFrag;
while(startAt < size) {
msize_t fragSize = size - startAt;
bool lastFragment = true;
if(fragSize > bytesPerFrag) {
fragSize = bytesPerFrag;
lastFragment = false;
}
Message* fragment = new Message(lastFragment ? MT_LastFragment : MT_Fragment, msg.getFlag(MF_Reliable) ? MF_Reliable : 0);
*fragment << fragId;
*fragment << index;
msg.copyTo(*fragment, startAt, startAt+fragSize);
{
threads::Lock lock(reliableMutex);
queueReliable(fragment);
}
startAt += fragSize;
index += 1;
}
//Fragments are reliable if the message is, so the original message no longer needs acked
if(msg.getFlag(MF_Sequenced)) {
threads::Lock lock(sequenceMutex);
auto seq = outSequences.find(msg.getSeqID());
if(seq != outSequences.end())
seq->second->handleAck(msg.getID());
}
else if(msg.getFlag(MF_Reliable)) {
handleAck(msg.getID());
}
return true;
}
bool Connection::send(Message& msg, bool isResend) {
if((isResend || !msg.getFlag(MF_Reliable)) && availBytes < 0)
return false;
if(msg.getFlag(MF_Reliable) && !msg.hasID()) {
threads::Lock lock(reliableMutex);
queueReliable(new Message(msg));
return false;
}
auto attemptSend = [this](int size) -> bool {
if(availBytes < 0)
return false;
availBytes -= size;
return true;
};
if(attemptSend(UDP_Header_IPv4 + msg.size())) {
outBytes += UDP_Header_IPv4 + msg.size();
transport.send(msg, address);
return true;
}
return false;
}
void Connection::process(MessageHandler& handler) {
time now;
time_now(now);
double time;
{
auto ms = time_diff(lastProcessTime, now);
if(ms > 0) {
time_now(lastProcessTime);
availBytes = std::min(availBytes + (int)((double)transport.rate * (double)ms / 1000.0), (int)transport.rate / 10);
}
time = (double)ms / 1000.0;
}
//Check if our transport is still active
if(!transport.active) {
active = false;
return;
}
//Check if the last received message was long ago
if(time_diff(lastMessageReceived, now) > NET_PING_INTERVAL) {
if(pingSent) {
if(time_diff(firstPingSent, now) > NET_PING_TIMEOUT) {
//Disconnect if we don't hear any messages for a while
if(time_diff(lastMessageReceived, now) > NET_PING_TIMEOUT) {
active = false;
return;
}
}
else if(time_diff(lastPingSent, now) > pingPongWait) {
sendPing();
}
}
else {
sendPing();
}
}
//Network window
//1. Check for out of date messages (messages sent at least windowLength seconds ago)
// If any packet was out of date, reduce window bytes by a %
// If no packets are behind, and our window was full, expand the window by a fixed amount.
//2. Transmit any queued acks as bandwidth is available.
//3. Append and transmit any packet that fits in our network window.
//4. Retransmit each front packet we can, moving it to the end, based on estimated available bandwidth.
windowUpdate -= time;
if(windowUpdate <= 0.0) { //Check for out of date packets
windowUpdate = 1.0;
bool congested = false;
unsigned packets = 0, late = 0;
threads::Lock lock(windowMutex);
uint64_t winLen_ms = (uint64_t)(windowLength * 1000.0);
for(auto i = window.begin(), end = window.end(); i != end; ++i) {
auto& packet = *i;
if(!packet.msg->getFlag(MF_Reliable))
continue;
++packets;
auto ms = time_diff(packet.added, now);
if(ms > winLen_ms) {
++late;
packet.added = now;
}
}
if((double)late / (double)(packets + 8) > 0.2)
congested = true;
//Scale our window based on the response
if(congested) {
windowBytes = std::max<size_t>(1000, (size_t)(double(windowBytes) * 0.8));
windowLength = std::min<double>(windowLength + 0.1, 3.0);
}
else {
if(!queuedMessages.empty()) {
windowBytes += 50000;
windowLength = std::max<double>(windowLength * 0.9, 0.5);
}
}
transport.rate = (int)(1.1 * (double)std::max(windowBytes, windowUsed) / windowLength);
}
//Send any acks queued since the last process tick
if(availBytes > 0) {
std::vector<Message*> acks;
auto transmitAck = [&acks](Message& msg) {
acks.push_back(new Message(msg));
};
//Batch acks into small groups and duplicate the message to improve the chances the acks are received
if(!queuedAcks.empty()) {
std::vector<unsigned short> ackIDs;
threads::Lock lock(ackMutex);
auto it = queuedAcks.begin(), end = queuedAcks.end();
unsigned readied = 0;
Message ack(MT_Ack);
for(; it != end; ++it) {
ack << *it;
ackIDs.push_back(*it);
++readied;
if(readied >= 20) {
transmitAck(ack);
readied = 0;
ack.reset();
}
}
if(readied != 0)
transmitAck(ack);
queuedAcks.clear();
}
if(!queuedSeqAcks.empty()) {
std::vector<unsigned> ackIndexes;
unsigned readied = 0;
threads::Lock lock(ackMutex);
Message ack(MT_SeqAck);
for(unsigned i = 0; i < queuedSeqAcks.size(); ++i) {
auto& seqAck = queuedSeqAcks[i];
ack << seqAck.seqID << seqAck.msgID;
ackIndexes.push_back(i);
++readied;
if(readied >= 10) {
transmitAck(ack);
readied = 0;
ack.reset();
}
}
if(readied != 0)
transmitAck(ack);
queuedSeqAcks.clear();
}
if(!acks.empty()) {
threads::Lock lock(windowMutex);
for(unsigned i = 0; i < acks.size(); ++i) {
WindowPacket packet;
packet.msg = new Message(*acks[i]);
packet.added = now;
packet.sent = now;
time_add(packet.sent, -10000);
window.push_front(packet);
windowUsed += packet.msg->size();
}
for(unsigned i = 0; i < acks.size(); ++i) {
WindowPacket packet;
packet.msg = acks[i];
packet.added = now;
packet.sent = now;
time_add(packet.sent, -10000);
window.push_front(packet);
windowUsed += packet.msg->size();
}
}
}
{ //Ask sequences to be queued
threads::Lock lock(sequenceMutex);
//Check for resending outgoing sequenced messages
{
auto it = outSequences.begin(), end = outSequences.end();
while(it != end) {
OutSequence* seq = it->second;
if(seq->closed) {
delete seq;
it = outSequences.erase(it);
continue;
}
while(Message* msg = seq->getNextMessage()) {
//Split the message if necessary
if(!split(*msg))
queue(msg);
else
delete msg;
}
++it;
}
}
//Check for requeueing ingoing sequenced messages
{
auto it = inSequences.begin(), end = inSequences.end();
while(it != end) {
InSequence* seq = it->second;
if(seq->closed) {
delete seq;
it = inSequences.erase(it);
continue;
}
seq->process(handler, now);
++it;
}
}
}
{ //Queue reliables
std::deque<Message*> addQueue;
{
threads::Lock lock(reliableMutex);
while(!queuedReliable.empty() && (unsigned short)(nextOutgoingID - nextAck) <= (unsigned short)0x7D00) {
auto* msg = queuedReliable.front();
queuedReliable.pop_front();
msg->setID(nextOutgoingID++);
if(!split(*msg))
addQueue.push_back(msg);
else
delete msg;
}
}
if(!addQueue.empty()) {
threads::Lock lock(msgQueueLock);
while(!addQueue.empty()) {
queuedMessages.push_front(addQueue.front());
addQueue.pop_front();
}
}
}
{ //If we have available bytes in our window, queue packets. We also transmit them if we have available bytes.
std::deque<WindowPacket> addQueue;
if(!queuedMessages.empty()) {
threads::Lock lock(msgQueueLock);
size_t addedBytes = 0;
while(windowUsed + addedBytes < windowBytes && !queuedMessages.empty()) {
WindowPacket packet;
packet.added = now;
packet.sent = now;
time_add(packet.sent, -10000);
packet.msg = queuedMessages.front();
queuedMessages.pop_front();
addQueue.push_back(packet);
addedBytes += packet.msg->size();
}
}
if(!addQueue.empty()) {
threads::Lock lock(windowMutex);
while(!addQueue.empty()) {
window.push_back(addQueue.front());
windowUsed += window.back().msg->size();
addQueue.pop_front();
}
}
}
//If we have available transmit bytes, resend anything in the window, cycling packets
if(!window.empty()) {
threads::Lock lock(windowMutex);
unsigned count = window.size();
while(availBytes > 0 && count > 0) {
--count;
WindowPacket packet = window.front();
window.pop_front();
if(time_diff(packet.sent, now) > (unsigned)(windowLength * 0.2 * 1000.0)) {
send(*packet.msg, false);
packet.sent = now;
}
if(packet.msg->getFlag(MF_Reliable)) {
window.push_back(packet);
}
else {
windowUsed -= packet.msg->size();
delete packet.msg;
}
}
}
}
bool Connection::shouldHandleID(unsigned short id) {
//Track the last ~32,000 messages we've handled, and remove very old ones as we reach new ids
threads::Lock lock(reliableMutex);
auto old = handledMessages.find(id - 0x8000);
if(old != handledMessages.end())
handledMessages.erase(old);
if(handledMessages.find(id) != handledMessages.end())
return false;
handledMessages.insert(id);
return true;
}
void Connection::sendPing() {
//Send a ping message
Message msg(MT_Ping);
*this << msg;
time_now(lastPingSent);
if(!pingSent)
firstPingSent = lastPingSent;
pingSent = true;
pingPongWait = rand() % NET_PING_INTERVAL;
}
void Connection::handleAck(unsigned short id) {
threads::Lock lock(ackMutex);
if(id == nextAck) {
unorderedAcks.erase(nextAck);
++nextAck;
while(unorderedAcks.find(nextAck) != unorderedAcks.end())
unorderedAcks.erase(nextAck++);
}
else if((unsigned short)(id - nextAck) < (unsigned short)0x8000) {
unorderedAcks.insert(id);
}
}
bool Connection::preHandle(MessageHandler& handler, Message* msg) {
time_now(lastMessageReceived);
inBytes += UDP_Header_IPv4 + msg->size();
switch(msg->getType()) {
case MT_Ack: {
//Remove the acked message from the queue
std::vector<unsigned short> ids;
{
threads::Lock lock(windowMutex);
while(msg->canRead<unsigned short>()) {
unsigned short id;
*msg >> id;
ids.push_back(id);
for(auto i = window.begin(), end = window.end(); i != end; ++i) {
auto* msg = i->msg;
if(msg->getFlag(MF_Reliable) && !msg->getFlag(MF_Sequenced) && msg->getID() == id) {
windowUsed -= msg->size();
delete msg;
window.erase(i);
break;
}
}
}
}
if(!ids.empty()) {
threads::Lock lock(ackMutex);
for(unsigned i = 0; i < ids.size(); ++i) {
unsigned short id = ids[i];
if(id == nextAck) {
unorderedAcks.erase(id);
++nextAck;
while(unorderedAcks.find(nextAck) != unorderedAcks.end())
unorderedAcks.erase(nextAck++);
}
else if((unsigned short)(id - nextAck) < (unsigned short)0x8000) {
unorderedAcks.insert(id);
}
}
}
} return false;
case MT_Ping: {
Message pong(MT_Pong);
*this << pong;
} return true;
case MT_Pong: {
pingSent = false;
ping = (unsigned)time_diff(lastPingSent, lastMessageReceived);
} return true;
case MT_SeqAck: {
std::unordered_set<unsigned> sacks;
{
threads::Lock lock(sequenceMutex);
while(msg->canRead<unsigned short>()) {
unsigned short seqID, id;
*msg >> seqID;
if(!msg->canRead<unsigned short>())
break;
*msg >> id;
sacks.insert((unsigned)seqID << 16 | (unsigned)id);
auto it = outSequences.find(seqID);
if(it != outSequences.end())
it->second->handleAck(id);
}
}
threads::Lock lock(windowMutex);
for(auto i = window.begin(); i != window.end();) {
auto* msg = i->msg;
if(msg->getFlag(MF_Reliable) && msg->getFlag(MF_Sequenced)) {
if(sacks.find((unsigned)msg->getSeqID() << 16 | (unsigned)msg->getID()) != sacks.end()) {
windowUsed -= msg->size();
delete msg;
i = window.erase(i);
continue;
}
}
++i;
}
} return false;
case MT_LastFragment:
case MT_Fragment: {
{
threads::Lock lock(ackMutex);
queuedAcks.insert(msg->getID());
}
if(shouldHandleID(msg->getID()))
handleFragment(handler, msg);
} return false;
}
if(msg->getFlag(MF_Sequenced)) {
threads::Lock lock(sequenceMutex);
InSequence* seq = 0;
unsigned short seqID = msg->getSeqID();
auto it = inSequences.find(seqID);
if(it == inSequences.end()) {
seq = new InSequence(*this, seqID);
inSequences[seqID] = seq;
}
else {
seq = it->second;
}
if(!seq->preHandle(handler, msg))
return false;
}
else if(msg->getFlag(MF_Reliable)) {
//Queue acks for reliable messages
{
threads::Lock lock(ackMutex);
queuedAcks.insert(msg->getID());
}
//Check if we've already handled this message
if(!shouldHandleID(msg->getID()))
return false;
}
return true;
}
void Connection::handleFragment(MessageHandler& handler, Message* msg) {
unsigned id;
unsigned short fragIndex;
*msg >> id;
*msg >> fragIndex;
{
threads::Lock lock(fragmentMutex);
auto it = waitingFragments.find(id);
//Record this fragmented message
Fragment* frag;
if(it != waitingFragments.end()) {
frag = it->second;
}
else {
frag = new Fragment();
frag->fragmentCount = 0;
waitingFragments[id] = frag;
}
time_now(frag->lastActivity);
//If we received the last fragment, we know
//how many fragments we're waiting for.
if(msg->getType() == MT_LastFragment)
frag->fragmentCount = fragIndex + 1;
//Save the fragment
if(fragIndex >= frag->received.size()) {
size_t prev = frag->received.size();
frag->received.resize(fragIndex + 1);
for(size_t i = prev; i < fragIndex; ++i)
frag->received[i] = 0;
}
frag->received[fragIndex] = new Message(*msg);
//Check if we have every fragment
if(frag->fragmentCount > 0) {
bool completed = true;
for(size_t i = 0, cnt = frag->received.size(); i < cnt; ++i) {
if(!frag->received[i]) {
completed = false;
break;
}
}
if(completed) {
//Reconstruct the message
char* pBytes; msize_t size;
frag->received[0]->getAsPacket(pBytes, size);
Message* recMsg = new Message((uint8_t*)(pBytes+NET_FRAGMENT_OVERHEAD), size-NET_FRAGMENT_OVERHEAD);
for(size_t i = 1, cnt = frag->received.size(); i < cnt; ++i)
frag->received[i]->copyTo(*recMsg, NET_FRAGMENT_OVERHEAD);
//printf("Fragment reconstructed message %d:%d\n", recMsg->getSeqID(), recMsg->getID());
//Send reconstructed message into handler
handler.queueMessage(&transport, address, recMsg);
//Clean up the stored fragment
if(it == waitingFragments.end())
it = waitingFragments.find(id);
waitingFragments.erase(it);
for(size_t i = 0, cnt = frag->received.size(); i < cnt; ++i)
delete frag->received[i];
delete frag;
}
}
}
}
void Connection::postHandle(MessageHandler& handler, Message* msg) {
if(msg->getFlag(MF_Sequenced)) {
threads::Lock lock(sequenceMutex);
auto it = inSequences.find(msg->getSeqID());
if(it != inSequences.end())
it->second->postHandle(handler, msg);
}
}
void Connection::getTraffic(unsigned& in_bytes, unsigned& out_bytes, unsigned& queuedPackets) {
in_bytes = inBytes;
inBytes = 0;
out_bytes = outBytes;
outBytes = 0;
queuedPackets = queuedReliable.size() + queuedAcks.size() + queuedSeqAcks.size() + queuedMessages.size();
threads::Lock lock(sequenceMutex);
for(auto i = outSequences.begin(), end = outSequences.end(); i != end; ++i)
queuedPackets += i->second->queuedMessages.size();
}
};
+56
View File
@@ -0,0 +1,56 @@
#include <network/init.h>
#include "threads.h"
#ifdef _MSC_VER
#include <WinSock2.h>
#include <ws2def.h>
#include <ws2ipdef.h>
#include <WS2tcpip.h>
#endif
namespace net {
netErrorCallback errorCallback = nullptr;
void setErrorCallback(netErrorCallback cb) {
errorCallback = cb;
}
void netError(const char* err, int code) {
if(errorCallback)
errorCallback(err, code);
}
#ifdef _MSC_VER
threads::Mutex ws_mutex;
unsigned ws_usage = 0;
bool prepare() {
threads::Lock lock(ws_mutex);
if(ws_usage == 0) {
WSAData winSockInfo;
int result = WSAStartup(MAKEWORD(2,2), &winSockInfo);
if(result != 0) {
netError("Failed to initialize network", WSAGetLastError());
return false;
}
}
ws_usage += 1;
return true;
}
void clear() {
threads::Lock lock(ws_mutex);
if(--ws_usage == 0)
WSACleanup();
}
#else
bool prepare() {
return true;
}
void clear() {
}
#endif
};
+678
View File
@@ -0,0 +1,678 @@
#include <network/lobby.h>
#include <network/time.h>
#include <time.h>
#include <stdio.h>
#include <stdarg.h>
namespace net {
void log(const char* format, ...) {
#ifdef __GNUC__
va_list ap;
va_start(ap, format);
char line[2048];
vsnprintf(line, 2048, format, ap);
char date[2048];
time_t t;
::time(&t);
struct tm tmp;
localtime_r(&t, &tmp);
strftime(date, 2048, "%d %b %Y %H:%M:%S", &tmp);
printf("[%s] %s\n", date, line);
fflush(stdout);
va_end(ap);
#endif
}
void Game::write(Message& msg) {
msg << name << mod;
msg << players << maxPlayers;
msg << address;
msg << punchPort;
msg.writeSmall(version);
msg.writeBit(started);
msg.writeBit(password);
msg.writeBit(listed);
}
void Game::read(Message& msg) {
msg >> name >> mod;
msg >> players >> maxPlayers;
msg >> address;
msg >> punchPort;
version = msg.readSmall();
started = msg.readBit();
password = msg.readBit();
listed = msg.readBit();
isLocal = false;
}
struct LobbyFilters {
std::string name, mod;
uint8_t full, started;
};
inline bool filterLobby(Game& desc, LobbyFilters& filters) {
if(!filters.name.empty()) {
if(desc.name.find(filters.name) == std::string::npos)
return false;
}
if(!filters.mod.empty()) {
if(desc.mod.find(filters.mod) == std::string::npos)
return false;
}
switch(filters.full) {
case LFM_True:
if(desc.players != desc.maxPlayers)
return false;
break;
case LFM_False:
if(desc.players == desc.maxPlayers)
return false;
break;
}
switch(filters.started) {
case LFM_True:
if(!desc.started)
return false;
break;
case LFM_False:
if(desc.started)
return false;
break;
}
return true;
}
threads::threadreturn threadcall _heartbeatLoop(void* data) {
LobbyHeartbeat* beat = (LobbyHeartbeat*)data;
time timer, now;
time_now(now);
timer = now;
while(beat->active) {
time_now(now);
if(time_diff(timer, now) >= beat->interval) {
beat->heartbeat();
timer = now;
}
threads::sleep(10);
}
beat->running.signalDown();
return 0;
}
threads::threadreturn threadcall _identifyLoop(void* data) {
LobbyHeartbeat* beat = (LobbyHeartbeat*)data;
beat->identRequest();
time timer, now;
time_now(now);
timer = now;
while(beat->active && !beat->identified) {
time_now(now);
if(time_diff(timer, now) >= beat->interval) {
beat->identRequest();
timer = now;
}
threads::sleep(100);
}
beat->running.signalDown();
return 0;
}
LobbyHeartbeat::LobbyHeartbeat(Address serverAddress, int Port)
: client(serverAddress, false), broadcastPort(Port), identified(false), externalIP(0),
externalPort(0), interval(NET_LOBBY_HEARTBEAT_INTERVAL), fullCounter(0), active(true) {
broadcast.genHandle(LM_Query, [this](Server& srv, Transport& trans, Address addr, Message& mess) {
//Read filters
LobbyFilters filters;
mess >> filters.name >> filters.mod;
mess >> filters.full >> filters.started;
//Apply filters
if(!filterLobby(*this, filters))
return;
//Send result to requester
Message msg(LM_Result);
write(msg);
trans.send(msg, addr);
});
client.handle(LM_Identify, [this](net::Client& cl, Message& msg) {
identified = true;
unsigned char type = 0;
msg >> type;
if(type == net::AT_IPv4) {
msg >> externalIP;
msg >> externalPort;
}
if(msg.hasError())
externalIP = 0;
});
}
void LobbyHeartbeat::enablePunchthrough(Server* server) {
if(punchPort < 0)
punchPort = 0;
server->addTransport(client.trans);
client.clearTransports();
//Punchthrough messages should be redirected to open the port
server->genHandle(MT_Punchthrough, [this](Server& srv, Transport& trans, Address adr, Message& msg) {
if(&trans != client.trans)
return;
net::Address punchTo;
msg >> punchTo;
net::Message reply(MT_Punchthrough);
trans.send(reply, punchTo);
});
server->genHandle(LM_Identify, [this](Server& srv, Transport& trans, Address adr, Message& msg) {
identified = true;
unsigned char type = 0;
msg >> type;
if(type == net::AT_IPv4) {
msg >> externalIP;
msg >> externalPort;
}
if(msg.hasError())
externalIP = 0;
});
}
void LobbyHeartbeat::run(bool doHeartbeat, bool doBroadcast) {
if(doHeartbeat) {
heartbeat();
running.signalUp();
threads::createThread(_heartbeatLoop, this);
}
if(doBroadcast && broadcastPort != -1) {
broadcast.listen(broadcastPort, "", true);
broadcast.runThreads(1);
}
running.signalUp();
threads::createThread(_identifyLoop, this);
client.runThreads(1);
}
void LobbyHeartbeat::stop() {
if(active) {
active = false;
running.wait(0);
client.stop();
broadcast.stop();
}
}
void LobbyHeartbeat::heartbeat() {
Message msg(LM_Heartbeat);
//Every 4 messages, send a full heartbeat
if(fullCounter == 0) {
msg.write1();
write(msg);
}
else {
msg.write0();
}
client << msg;
fullCounter = (fullCounter + 1) % 4;
}
void LobbyHeartbeat::identRequest() {
Message msg(LM_Identify);
client << msg;
}
LobbyHeartbeat::~LobbyHeartbeat() {
stop();
}
threads::threadreturn threadcall _punchthroughLoop(void* data) {
LobbyPunchthrough* punch = (LobbyPunchthrough*)data;
time timer, now;
time_now(now);
timer = now;
while(punch->active) {
time_now(now);
if(time_diff(timer, now) >= punch->interval) {
punch->heartbeat();
timer = now;
}
threads::sleep(10);
}
punch->running.signalDown();
return 0;
}
LobbyPunchthrough::LobbyPunchthrough(Address serverAddress, Client* cl)
: interval(250), lobbyAddress(serverAddress),
active(true), client(cl), established(false) {
heartbeat();
running.signalUp();
threads::createThread(_punchthroughLoop, this);
}
void LobbyPunchthrough::stop() {
active = false;
running.wait(0);
}
void LobbyPunchthrough::heartbeat() {
if(client) {
if(!client->established && client->active) {
net::Message req(MT_Punchthrough);
net::Address reqAddr = client->address;
req << reqAddr;
client->trans->send(req, lobbyAddress);
}
else {
active = false;
}
}
}
LobbyPunchthrough::~LobbyPunchthrough() {
stop();
}
threads::threadreturn threadcall _serverLoop(void* data) {
LobbyServer* srv = (LobbyServer*)data;
time now;
while(srv->active) {
time_now(now);
//Deactivate if the server goes down
if(!srv->server.active) {
srv->active = false;
break;
}
//Prune all timed out games
{
threads::WriteLock lock(srv->mutex);
auto it = srv->games.begin(), end = srv->games.end();
while(it != end) {
LobbyServer::GameDesc& desc = it->second;
if(time_diff(desc.lastHeartbeat, now) > NET_LOBBY_HEARTBEAT_TIMEOUT) {
if(srv->logging)
log("[DD] Server \"%s\" timed out from %s. (comm port %d)",
desc.name.c_str(), desc.address.toString().c_str(), it->first.port);
it = srv->games.erase(it);
}
else
++it;
}
}
threads::sleep(500);
}
srv->running.signalDown();
return 0;
}
LobbyServer::LobbyServer(int port, const std::string& address, bool Logging)
: server(port, address), active(true), logging(Logging) {
if(logging)
log("[II] Starting master server on port %d", port);
//Clients are constantly sending heartbeats, we track
//all the active servers. Full heartbeats add timed out servers
//back to the list, normal heartbeats only keep it there
server.genHandle(LM_Heartbeat, [this](Server& srv, Transport& trans, Address addr, Message& msg) {
if(msg.readBit()) {
GameDesc desc;
desc.read(msg);
int port = desc.address.port;
desc.address = addr;
desc.address.port = port;
if(desc.punchPort >= 0)
desc.punchPort = addr.port;
time_now(desc.lastHeartbeat);
bool has = false;
{
threads::ReadLock lock(mutex);
auto it = games.find(addr);
has = (it != games.end());
if(has) {
if(logging) {
if(desc.players != it->second.players)
log(" [PP] Server \"%s\" on %s now has %d players.",
desc.name.c_str(), desc.address.toString().c_str(), desc.players);
}
it->second = desc;
}
}
if(!has) {
threads::WriteLock lock(mutex);
games[addr] = desc;
if(logging)
log("[SS] New server \"%s\" on %s. (comm port %d)",
desc.name.c_str(), desc.address.toString().c_str(), addr.port);
}
}
else {
threads::ReadLock lock(mutex);
auto it = games.find(addr);
if(it != games.end()) {
GameDesc& desc = it->second;
time_now(desc.lastHeartbeat);
if(desc.punchPort >= 0)
desc.punchPort = addr.port;
}
}
});
//Respond to lobby queries from clients
server.genHandle(LM_Query, [this](Server& srv, Transport& trans, Address addr, Message& msg) {
//Read filters
LobbyFilters filters;
msg >> filters.name >> filters.mod;
msg >> filters.full >> filters.started;
Message* mess = 0;
unsigned short num = 0;
msize_t numpos = 0;
unsigned short total = 0;
threads::ReadLock lock(mutex);
auto it = games.begin(), end = games.end();
for(; it != end; ++it) {
LobbyServer::GameDesc& desc = it->second;
//Don't list unlisted games
if(!desc.listed)
continue;
//Apply filters
if(!filterLobby(desc, filters))
continue;
//Create a new message if necessary
if(!mess) {
mess = new Message(LM_Result);
numpos = mess->reserve<unsigned short>();
}
//Send result to client
desc.write(*mess);
++num;
++total;
if(num > 128) {
mess->fill(numpos, num);
trans.send(*mess, addr);
delete mess;
mess = 0;
num = 0;
}
}
if(mess) {
mess->fill(numpos, num);
trans.send(*mess, addr);
delete mess;
}
Message final(LM_Results_End);
final << total;
trans.send(final, addr);
});
//Second stage of punchthrough
server.genHandle(MT_Punchthrough, [this](Server& srv, Transport& trans, Address addr, Message& msg) {
net::Address findAddr;
msg >> findAddr;
{
threads::ReadLock lock(mutex);
auto it = games.find(findAddr);
if(it != games.end()) {
net::Message reply(MT_Punchthrough);
if(it->second.punchPort != -1) {
reply << addr;
net::Address replyAddr = it->first;
trans.send(reply, replyAddr);
}
}
}
});
server.genHandle(LM_Identify, [this](Server& srv, Transport& trans, Address addr, Message& msg) {
Message m(LM_Identify);
m << (unsigned char)addr.type;
if(addr.type == AT_IPv4)
m << (unsigned)addr.adr4;
else
m.writeBits(addr.adr6, 16 * 8);
{
threads::ReadLock lock(mutex);
auto it = games.find(addr);
if(it != games.end()) {
GameDesc& desc = it->second;
if(desc.punchPort == -1)
m << (unsigned short)0;
else
m << (unsigned short)desc.punchPort;
}
else {
m << (unsigned short)0;
}
}
trans.send(m, addr);
});
}
void LobbyServer::listen(int port, const std::string& address) {
server.listen(port, address);
}
void LobbyServer::runThreads(int workerThreads) {
running.signal(1);
threads::createThread(_serverLoop, this);
server.runThreads(workerThreads);
}
void LobbyServer::stop() {
running.wait(0);
server.stop();
}
LobbyServer::~LobbyServer() {
stop();
}
threads::threadreturn threadcall _queryThread(void* data) {
LobbyQuery* query = (LobbyQuery*)data;
time timer, now;
time_now(now);
timer = now;
while(query->active) {
if(query->doUpdate) {
query->update(true);
query->doUpdate = false;
}
time_now(now);
if(time_diff(timer, now) >= NET_LOBBY_QUERY_TIMEOUT) {
if(query->totalFromServer == (unsigned short)-1 || query->receivedFromServer < query->totalFromServer) {
query->update(false);
}
else {
query->updating = false;
}
timer = now;
}
threads::sleep(10);
}
query->running.signalDown();
return 0;
}
LobbyQuery::LobbyQuery(Address addr, int BroadcastPort)
: client(addr, false), broadcast(BroadcastPort, addr.type),
broadcastPort(BroadcastPort), handler(nullptr), receivedFromServer(0), totalFromServer((unsigned short)-1), updating(false), active(true),
queryServer(true), queryBroadcast(BroadcastPort != -1), doUpdate(true) {
bind();
}
LobbyQuery::LobbyQuery(const std::string& hostname, int port, int BroadcastPort, AddressType type)
: client(hostname, port, false, type), broadcast(BroadcastPort, type),
broadcastPort(BroadcastPort), handler(nullptr), receivedFromServer(0), totalFromServer((unsigned short)-1), updating(false), active(true),
queryServer(true), queryBroadcast(BroadcastPort != -1), doUpdate(true), full(LFM_Ignore), started(LFM_Ignore) {
bind();
}
void LobbyQuery::bind() {
client.handle(LM_Result, [this](Client& cl, Message& msg) {
Game game;
unsigned short num;
msg >> num;
receivedFromServer += num;
for(unsigned short i = 0; i < num; ++i) {
game.read(msg);
auto it = handled_lobbies.find(game.address);
if(it == handled_lobbies.end()) {
if(handler)
handler(game);
handled_lobbies.insert(game.address);
}
}
if(receivedFromServer >= totalFromServer)
updating = false;
});
client.handle(LM_Results_End, [this](Client& cl, Message& msg) {
msg >> totalFromServer;
if(receivedFromServer >= totalFromServer)
updating = false;
});
broadcast.handle(LM_Result, [this](BroadcastClient& cl, Address addr, Message& msg) {
Game game;
game.read(msg);
int port = game.address.port;
game.address = addr;
game.address.port = port;
game.punchPort = -1;
game.isLocal = true;
auto it = handled_lobbies.find(game.address);
if(it == handled_lobbies.end()) {
if(handler)
handler(game);
handled_lobbies.insert(game.address);
}
});
client.runThreads(1);
running.signalUp();
threads::createThread(_queryThread, this);
if(broadcastPort != -1)
broadcast.runThreads(1);
}
void LobbyQuery::refresh(bool doQuery, bool doBroadcast) {
queryServer = doQuery;
queryBroadcast = doBroadcast;
doUpdate = true;
updating = true;
totalFromServer = (unsigned short)-1;
}
void LobbyQuery::update(bool clear) {
if(clear)
handled_lobbies.clear();
//Build query
Message msg(LM_Query);
msg << name << mod;
msg << (uint8_t)full;
msg << (uint8_t)started;
//Query the lobby server
if(queryServer) {
client << msg;
receivedFromServer = 0;
totalFromServer = -1;
updating = true;
}
//Query on broadcast
if(queryBroadcast && broadcastPort != -1)
broadcast.broadcast(msg);
}
void LobbyQuery::stop() {
client.stop();
updating = false;
active = false;
running.wait(0);
if(broadcastPort != -1)
broadcast.stop();
}
LobbyQuery::~LobbyQuery() {
stop();
}
};
File diff suppressed because it is too large Load Diff
+219
View File
@@ -0,0 +1,219 @@
#ifdef __GNUC__
#include <sys/socket.h>
#include <sys/select.h>
#elif defined(_MSC_VER)
#include <WS2tcpip.h>
#endif
#include <network/message_handler.h>
#include <network/init.h>
namespace net {
threads::threadreturn threadcall _mainLoop(void* data) {
MessageHandler* handler = (MessageHandler*)data;
if(handler->threadInit)
handler->threadInit(true);
while(handler->active) {
if(!handler->mainTick())
threads::sleep(NET_IDLE_SLEEP);
}
if(handler->threadExit)
handler->threadExit(true);
handler->threadsRunning.signalDown();
return 0;
}
threads::threadreturn threadcall _queueLoop(void* data) {
MessageHandler* handler = (MessageHandler*)data;
if(handler->threadInit)
handler->threadInit(false);
while(handler->active) {
if(!handler->queueTick())
threads::sleep(NET_IDLE_SLEEP);
}
if(handler->threadExit)
handler->threadExit(false);
handler->threadsRunning.signalDown();
return 0;
}
MessageHandler::MessageHandler()
: active(true) {
}
void MessageHandler::queueMessage(Transport* transport, Address addr, Message* msg) {
QueuedMessage q;
q.transport = transport;
q.addr = addr;
q.msg = msg;
transport->grab();
{
threads::Lock lock(queueMutex);
messageQueue.push(q);
}
}
void MessageHandler::handleMessage(Transport* transport, Address addr, Message* msg) {
delete msg;
transport->drop();
}
bool MessageHandler::queueTick() {
if(messageQueue.empty())
return false;
queueMutex.lock();
if(messageQueue.empty()) {
queueMutex.release();
return false;
}
QueuedMessage q = messageQueue.front();
messageQueue.pop();
queueMutex.release();
handleMessage(q.transport, q.addr, q.msg);
return true;
}
void MessageHandler::addTransport(Transport* transport) {
threads::Lock lock(transportMutex);
transport->grab();
transports.push_back(transport);
}
void MessageHandler::clearTransports() {
threads::Lock lock(transportMutex);
for(auto it = transports.begin(); it != transports.end(); ++it)
(*it)->drop();
transports.clear();
}
bool MessageHandler::mainTick() {
struct timeval timeout;
bool received = false;
//Populate the fd_set forselect
fd_set polling;
int nfds = 0;
FD_ZERO(&polling);
{
threads::Lock lock(transportMutex);
if(transports.empty())
return false;
for(auto it = transports.begin(), end = transports.end(); it != end;) {
Transport* trans = *it;
if(!trans->active) {
it = transports.erase(it);
end = transports.end();
trans->drop();
if(transports.empty())
active = false;
}
else {
//Process the transport
trans->process();
//Add the transport to the fd set
int fd = trans->sockfd;
FD_SET(fd, &polling);
if(fd >= nfds)
nfds = fd + 1;
++it;
}
}
}
//Calculate the timeout
timeout.tv_sec = NET_SELECT_TIMEOUT / 1000;
timeout.tv_usec = (NET_SELECT_TIMEOUT % 1000) * 1000;
int ready = select(nfds, &polling, 0, 0, &timeout);
//Intercept errors
if(ready < 0) {
#ifdef __GNUC__
perror("Select error");
#else
netError("Socket polling failed: ", WSAGetLastError());
#endif
active = false;
return true;
}
//Skip the rest if no transports are ready
if(ready == 0)
return false;
//Read all messages that should be received
{
threads::Lock lock(transportMutex);
for(auto it = transports.begin(), end = transports.end(); it != end; ++it) {
Transport* trans = *it;
Message msg;
Address adr;
while(trans->receive(msg, adr)) {
Message* qmsg = new Message();
msg.move(*qmsg);
queueMessage(trans, adr, qmsg);
received = true;
}
}
}
return received;
}
void MessageHandler::runThreads(int workerThreads) {
if(workerThreads <= 0)
throw "Threaded server needs at least one worker thread";
threadsRunning.signal(workerThreads + 1);
//Create a thread for the main connection loop
threads::createThread(_mainLoop, this);
//Create worker threads
for(int i = 0; i < workerThreads; ++i)
threads::createThread(_queueLoop, this);
}
void MessageHandler::stop() {
if(active) {
active = false;
threadsRunning.wait(0);
{
threads::Lock lock(transportMutex);
for(auto it = transports.begin(), end = transports.end(); it != end; ++it) {
(*it)->close();
(*it)->drop();
}
transports.clear();
}
}
}
MessageHandler::~MessageHandler() {
stop();
}
};
+146
View File
@@ -0,0 +1,146 @@
#include <network/sequence.h>
namespace net {
Sequence::Sequence(Connection& conn)
: ws(conn.sequence()) {
}
Sequence::Sequence(Client& client)
: ws(0) {
Connection* conn = client.getConnection();
if(conn)
ws = conn->sequence();
}
Sequence::~Sequence() {
if(ws)
ws->close();
}
Sequence& Sequence::operator<<(Message& msg) {
if(ws)
*ws << msg;
return *this;
}
unsigned short Sequence::id() {
if(!ws)
return -1;
return ws->id;
}
OutSequence::OutSequence(Connection& connection)
: conn(connection), id(-1), nextOutgoingID(1), nextAck(1), resendPeriod(0), closed(false) {
}
OutSequence& OutSequence::operator<<(Message& msg) {
if(!(msg.getFlags() & MF_Sequenced))
throw "Attempted to write non-sequenced message to sequence.";
msg.setSeqID(id);
queue(new Message(msg));
return *this;
}
void OutSequence::queue(Message* msg) {
threads::Lock lock(conn.sequenceMutex);
queuedMessages.push_back(msg);
}
Message* OutSequence::getNextMessage() {
if(queuedMessages.empty())
return nullptr;
if((unsigned short)(nextOutgoingID - nextAck) > (unsigned short)0x7D00)
return nullptr;
threads::Lock lock(conn.sequenceMutex);
auto* msg = queuedMessages.front();
queuedMessages.pop_front();
msg->setID(nextOutgoingID++);
waitingAcks.insert(msg->getID());
return msg;
}
void OutSequence::handleAck(unsigned short num) {
auto it = waitingAcks.find(num);
if(it != waitingAcks.end())
waitingAcks.erase(it);
if(nextAck == num)
++nextAck;
while(nextAck != nextOutgoingID) {
if(waitingAcks.find(nextAck) == waitingAcks.end())
++nextAck;
else
break;
}
}
void OutSequence::close() {
Message msg(MT_Close_Sequence, MF_Sequenced);
msg << id;
*this << msg;
}
InSequence::InSequence(Connection& connection, unsigned short ID)
: conn(connection), id(ID), closed(false), nextHandleID(1), handlingID(0) {
}
bool InSequence::preHandle(MessageHandler& handler, Message* msg) {
unsigned short msgID = msg->getID();
//Acknowledge the message if we haven't done so before
if(!msg->getFlag(MF_Acknowledged)) {
conn.queueSeqAck(id, msgID);
}
//Check if we should handle this message or not
//NOTE: (msgID - nextHandleID) will only work with less than ~SHORT_MAX messages in flight
if((short)(msgID - nextHandleID) < 0|| msgID == handlingID) {
//This message was already handled previously
return false;
}
else if(msgID == nextHandleID) {
//Handle close sequence messages here
if(msg->getType() == MT_Close_Sequence) {
closed = true;
return false;
}
//Yay, we can immediately handle this
handlingID = msgID;
return true;
}
else {
//Boo, we have to queue it
Message* qmsg = new Message();
msg->move(*qmsg);
msg->setFlags(msg->getFlags() | MF_Acknowledged);
unhandledMessages[msgID] = qmsg;
return false;
}
}
void InSequence::postHandle(MessageHandler& handler, Message* msg) {
nextHandleID++;
time now;
time_now(now);
process(handler, now);
}
void InSequence::process(MessageHandler& handler, time& now) {
//Check if we can handle a message from the queue
auto it = unhandledMessages.find(nextHandleID);
if(it != unhandledMessages.end()) {
handler.queueMessage(&conn.transport, conn.address, it->second);
unhandledMessages.erase(it);
}
}
};
+240
View File
@@ -0,0 +1,240 @@
#include <network/server.h>
#ifdef __GNUC__
#include <netdb.h>
#include <sys/socket.h>
#elif defined(_MSC_VER)
#include <WS2tcpip.h>
#endif
namespace net {
Server::Server() : nextConnectionID(1) {
}
Server::Server(int port, const std::string& address, bool broadcast)
: nextConnectionID(1) {
listen(port, address, broadcast);
}
Server::~Server() {
stop();
}
void Server::listen(int port, const std::string& address, bool broadcast) {
struct addrinfo* res, *head;
res = head = lookup(address, port);
if(!res) {
fprintf(stderr, "ERROR: Could not resolve hostname \"%s\".\n", address.c_str());
return;
}
for(; res; res = res->ai_next) {
if(res->ai_family != AF_INET && res->ai_family != AF_INET6)
continue;
Address addr;
addr.from_sockaddr(*(sockaddr_storage*)res->ai_addr);
Transport* transport = new Transport(addr.type);
transport->listen(addr, broadcast);
if(transport->active)
addTransport(transport);
transport->drop();
#ifdef __GNUC__
//TODO: Figure out if this actually works.
break;
#endif
}
freeaddrinfo(head);
}
void Server::connHandle(uint8_t type, Server::connMessageHandler func) {
threads::Lock lock(handlerMutex);
connHandlers[type] = func;
}
void Server::genHandle(uint8_t type, Server::genMessageHandler func) {
threads::Lock lock(handlerMutex);
genHandlers[type] = func;
}
void Server::connHandleClear(uint8_t type) {
threads::Lock lock(handlerMutex);
connHandlers.erase(type);
}
void Server::genHandleClear(uint8_t type) {
threads::Lock lock(handlerMutex);
genHandlers.erase(type);
}
void Server::send(int connId, Message& message) {
if(connId < 0)
sendAll(message);
else
*getConnectionByID(connId) << message;
}
void Server::sendAll(Message& message) {
threads::Lock lock(connMutex);
auto it = connections.begin(), end = connections.end();
for(; it != end; ++it)
*it->second << message;
}
void Server::doAll(connFunction func) {
if(!func)
return;
threads::Lock lock(connMutex);
auto it = connections.begin(), end = connections.end();
for(; it != end; ++it)
func(*it->second);
}
void Server::pingAll() {
threads::Lock lock(connMutex);
auto it = connections.begin(), end = connections.end();
for(; it != end; ++it)
it->second->sendPing();
}
Connection* Server::getConnectionByID(int id) {
threads::Lock lock(connMutex);
auto it = connectionIDs.find(id);
if(it == connectionIDs.end())
return 0;
it->second->grab();
return it->second;
}
void Server::kick(Connection& conn, DisconnectReason reason) {
net::Message msg(MT_Disconnect);
msg << reason;
conn.send(msg, false);
queueMessage(&conn.transport, conn.address, new Message(msg));
}
void Server::handleMessage(Transport* transport, Address addr, Message* msg) {
//Find the connection that this address belongs to
Connection* conn = 0;
{
threads::Lock lock(connMutex);
auto it = connections.find(addr);
if(it != connections.end()) {
conn = it->second;
conn->grab();
}
}
//Handle the message
uint8_t type = msg->getType();
switch(type) {
case MT_Disconnect:
if(conn) {
threads::Lock lock(connMutex);
connections.erase(addr);
connectionIDs.erase(conn->id);
conn->active = false;
conn->drop();
}
break;
case MT_Connect:
if(!conn) {
conn = new Connection(*transport, addr);
conn->id = nextConnectionID++;
conn->grab();
threads::Lock lock(connMutex);
connections[addr] = conn;
connectionIDs[conn->id] = conn;
{
Message response(MT_Connect, MF_Reliable);
*conn << response;
}
}
break;
}
//Send the message to the server handlers
if(conn) {
if(conn->preHandle(*this, msg)) {
connMessageHandler handler = nullptr;
{
threads::Lock lock(handlerMutex);
auto it = connHandlers.find(type);
if(it != connHandlers.end())
handler = it->second;
}
if(handler)
handler(*this, *conn, *msg);
conn->postHandle(*this, msg);
}
conn->drop();
}
else {
genMessageHandler handler = nullptr;
{
threads::Lock lock(handlerMutex);
auto it = genHandlers.find(type);
if(it != genHandlers.end())
handler = it->second;
}
if(handler)
handler(*this, *transport, addr, *msg);
}
MessageHandler::handleMessage(transport, addr, msg);
}
bool Server::mainTick() {
//Do normal message handler stuff
bool received = MessageHandler::mainTick();
//Let the connections process things
{
threads::Lock lock(connMutex);
for(auto it = connections.begin(), end = connections.end(); it != end; ++it) {
Connection* conn = it->second;
if(!conn->active) {
Message* msg = new Message(MT_Disconnect);
if(conn->transport.active)
*msg << DR_Timeout;
else
*msg << DR_Error;
queueMessage(&conn->transport, conn->address, msg);
}
else {
conn->process(*this);
}
}
}
return received;
}
void Server::stop() {
if(active) {
MessageHandler::stop();
{
threads::Lock lock(connMutex);
for(auto it = connections.begin(), end = connections.end(); it != end; ++it)
it->second->drop();
connections.clear();
}
}
}
};
+32
View File
@@ -0,0 +1,32 @@
#include <network/time.h>
#ifdef _MSC_VER
#include <Windows.h>
#endif
namespace net {
#ifdef _MSC_VER
void time_now(time& tm) {
tm = timeGetTime();
}
#elif defined(__GNUC__)
void time_now(time& tm) {
timeval tv;
gettimeofday(&tv, 0);
uint64_t value = 0;
value += tv.tv_sec * 1000;
value += tv.tv_usec / 1000;
tm = (time)value;
}
#endif
uint64_t time_diff(time& from, time& to) {
return to - from;
}
void time_add(time& base, int64_t add_ms) {
base += add_ms;
}
};
+319
View File
@@ -0,0 +1,319 @@
#include <network/transport.h>
#include <network/init.h>
#ifdef _MSC_VER
#include <WinSock2.h>
#include <ws2def.h>
#include <ws2ipdef.h>
#include <WS2tcpip.h>
#include <time.h>
#elif defined(__GNUC__)
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <errno.h>
#include <unistd.h>
#define INVALID_SOCKET -1
#define SOCKET_ERROR -1
#endif
#ifdef __APPLE__
#define IPV6_ADD_MEMBERSHIP IPV6_JOIN_GROUP
#endif
namespace net {
#ifdef _MSC_VER
static char zero_arg = 0;
static char true_arg = 1;
#else
static int zero_arg = 0;
static int true_arg = 1;
#endif
uint8_t IPV6_MCAST_ALL_NODES[]
= {0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01};
int Transport::RATE_LIMIT = 1024*250;
Transport::Transport(AddressType Type)
: references(1), active(true), canBroadcast(false), type(Type), rate(RATE_LIMIT)
{
net::prepare();
int ai_family = (type == AT_IPv4) ? AF_INET : AF_INET6;
sockfd = socket(ai_family, SOCK_DGRAM, IPPROTO_UDP);
#ifdef __GNUC__
ioctl(sockfd, FIONBIO, &true_arg);
#elif defined(_MSC_VER)
if(sockfd == INVALID_SOCKET) {
netError("Failed to bind a socket", WSAGetLastError());
close();
return;
}
int result = ioctlsocket(sockfd, FIONBIO, (u_long*)&true_arg);
if(result != 0) {
netError("Failed to set socket state", WSAGetLastError());
close();
return;
}
#endif
}
void Transport::grab() const {
++references;
}
void Transport::drop() const {
if(--references == 0)
delete this;
}
Transport::~Transport() {
close();
}
void Transport::close() {
if(active) {
if(sockfd != INVALID_SOCKET) {
#ifdef _MSC_VER
closesocket(sockfd);
#else
::close(sockfd);
#endif
sockfd = INVALID_SOCKET;
}
active = false;
net::clear();
}
}
void Transport::listen(Address& address, bool rcvBroadcast) {
if(!active)
return;
canBroadcast = rcvBroadcast;
sockaddr_storage saddr;
socklen_t len;
address.to_sockaddr(saddr, &len);
int result = bind(sockfd, (sockaddr*)&saddr, len);
if(result != 0) {
active = false;
#ifdef _MSC_VER
closesocket(sockfd);
netError("Error binding to socket", WSAGetLastError());
#else
::close(sockfd);
perror("Error binding socket");
#endif
}
if(canBroadcast) {
if(type == AT_IPv4) {
setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &true_arg, sizeof(true_arg));
}
else {
//TODO: We gotta test ipv6 somehow
ipv6_mreq mcast;
memcpy(&mcast.ipv6mr_multiaddr, IPV6_MCAST_ALL_NODES, 16);
mcast.ipv6mr_interface = 0;
setsockopt(sockfd, SOL_SOCKET, IPV6_ADD_MEMBERSHIP, (const char*)&mcast, sizeof(mcast));
setsockopt(sockfd, SOL_SOCKET, IPV6_MULTICAST_IF, &zero_arg, sizeof(zero_arg));
}
}
}
void Transport::process() {
if(queuedSends.empty() && queuedBroadcasts.empty())
return;
threads::Lock queueLock(queueMutex);
while(!queuedSends.empty()) {
Message* msg = queuedSends.front().first;
Address& adr = queuedSends.front().second;
if(send(*msg, adr, false)) {
delete msg;
queuedSends.pop();
}
else
break;
}
while(!queuedBroadcasts.empty()) {
Message* msg = queuedBroadcasts.front().first;
int port = queuedBroadcasts.front().second;
if(broadcast(*msg, port, false)) {
delete msg;
queuedBroadcasts.pop();
}
else
break;
}
}
bool Transport::send(Message& msg, Address& address, bool queue) {
if(!active)
return false;
sockaddr_storage saddr;
socklen_t len;
address.to_sockaddr(saddr, &len);
char* pBytes; msize_t byteCount;
msg.finalize();
msg.getAsPacket(pBytes, byteCount);
int bytes = sendto(sockfd, pBytes, byteCount, 0, (sockaddr*)&saddr, len);
if(bytes == SOCKET_ERROR) {
#ifdef _MSC_VER
if(WSAGetLastError() == WSAEWOULDBLOCK) {
#else
if(errno == EAGAIN) {
#endif
if(queue) {
threads::Lock queueLock(queueMutex);
Message* q = new Message(msg);
queuedSends.push(std::pair<Message*,Address>(q, address));
}
return false;
}
#ifdef _MSC_VER
netError("Socket write failed", WSAGetLastError());
#else
perror("Error writing to socket");
#endif
close();
return false;
}
return true;
}
bool Transport::broadcast(Message& msg, int port, bool queue) {
if(!active)
return false;
if(!canBroadcast) {
if(type == AT_IPv4)
setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &true_arg, sizeof(true_arg));
else
setsockopt(sockfd, SOL_SOCKET, IPV6_MULTICAST_IF, &zero_arg, sizeof(zero_arg));
canBroadcast = true;
}
sockaddr_storage saddr;
socklen_t len;
switch(type) {
case AT_IPv4: {
sockaddr_in* st = (sockaddr_in*)&saddr;
st->sin_family = AF_INET;
st->sin_port = htons(port);
st->sin_addr.s_addr = INADDR_BROADCAST;
len = sizeof(sockaddr_in);
} break;
case AT_IPv6: {
sockaddr_in6* st = (sockaddr_in6*)&saddr;
st->sin6_family = AF_INET6;
st->sin6_flowinfo = 0;
st->sin6_port = htons(port);
st->sin6_scope_id = 0;
memcpy(&st->sin6_addr, IPV6_MCAST_ALL_NODES, 16);
len = sizeof(sockaddr_in6);
} break;
#ifdef _MSC_VER
default:
__assume(0);
#elif defined(__GNUC__)
default:
__builtin_unreachable();
#endif
}
char* pBytes; msize_t byteCount;
msg.finalize();
msg.getAsPacket(pBytes, byteCount);
int bytes = sendto(sockfd, pBytes, byteCount, 0, (sockaddr*)&saddr, len);
if(bytes == SOCKET_ERROR) {
#ifdef _MSC_VER
if(WSAGetLastError() == WSAEWOULDBLOCK) {
#else
if(errno == EAGAIN) {
#endif
if(queue) {
threads::Lock queueLock(queueMutex);
Message* q = new Message(msg);
queuedBroadcasts.push(std::pair<Message*,int>(q, port));
}
return false;
}
#ifdef _MSC_VER
netError("Socket write failed", WSAGetLastError());
#else
perror("Error writing to socket");
#endif
close();
}
return true;
}
bool Transport::receive(Message& msg, Address& adr) {
if(!active)
return false;
char buffer[USHRT_MAX];
sockaddr_storage saddr;
socklen_t len = sizeof(saddr);
int bytes = recvfrom(sockfd, buffer, USHRT_MAX, 0, (sockaddr*)&saddr, &len);
#ifdef _MSC_VER
int error = (bytes == SOCKET_ERROR ? WSAGetLastError() : -1);
#endif
if(bytes > 0) {
msg.setPacket(buffer, bytes);
adr.from_sockaddr(saddr);
return true;
}
#ifdef _MSC_VER
else if(error == WSAEWOULDBLOCK || error == WSAECONNRESET) {
#else
else if(errno == EAGAIN) {
#endif
return false;
}
else {
close();
#ifdef __GNUC__
perror("Error reading from socket");
#else
netError("Error reading from socket", WSAGetLastError());
#endif
return false;
}
}
};