Initial commit
This commit is contained in:
18
src/network/CMakeLists.txt
Normal file
18
src/network/CMakeLists.txt
Normal file
@@ -0,0 +1,18 @@
|
||||
set(common_network_SRCS
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/address.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/connection.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/connectionthreads.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/networkpacket.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/serverpackethandler.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/serveropcodes.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/socket.cpp
|
||||
PARENT_SCOPE
|
||||
)
|
||||
|
||||
if (BUILD_CLIENT)
|
||||
set(client_network_SRCS
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/clientopcodes.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/clientpackethandler.cpp
|
||||
PARENT_SCOPE
|
||||
)
|
||||
endif()
|
||||
259
src/network/address.cpp
Normal file
259
src/network/address.cpp
Normal file
@@ -0,0 +1,259 @@
|
||||
/*
|
||||
Minetest
|
||||
Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2.1 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "address.h"
|
||||
|
||||
#include <cstdio>
|
||||
#include <iostream>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <cerrno>
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
#include "network/networkexceptions.h"
|
||||
#include "util/string.h"
|
||||
#include "util/numeric.h"
|
||||
#include "constants.h"
|
||||
#include "debug.h"
|
||||
#include "settings.h"
|
||||
#include "log.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
// Without this some of the network functions are not found on mingw
|
||||
#ifndef _WIN32_WINNT
|
||||
#define _WIN32_WINNT 0x0501
|
||||
#endif
|
||||
#include <windows.h>
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
#define LAST_SOCKET_ERR() WSAGetLastError()
|
||||
typedef SOCKET socket_t;
|
||||
typedef int socklen_t;
|
||||
#else
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <fcntl.h>
|
||||
#include <netdb.h>
|
||||
#include <unistd.h>
|
||||
#include <arpa/inet.h>
|
||||
#define LAST_SOCKET_ERR() (errno)
|
||||
typedef int socket_t;
|
||||
#endif
|
||||
|
||||
/*
|
||||
Address
|
||||
*/
|
||||
|
||||
Address::Address()
|
||||
{
|
||||
memset(&m_address, 0, sizeof(m_address));
|
||||
}
|
||||
|
||||
Address::Address(u32 address, u16 port)
|
||||
{
|
||||
memset(&m_address, 0, sizeof(m_address));
|
||||
setAddress(address);
|
||||
setPort(port);
|
||||
}
|
||||
|
||||
Address::Address(u8 a, u8 b, u8 c, u8 d, u16 port)
|
||||
{
|
||||
memset(&m_address, 0, sizeof(m_address));
|
||||
setAddress(a, b, c, d);
|
||||
setPort(port);
|
||||
}
|
||||
|
||||
Address::Address(const IPv6AddressBytes *ipv6_bytes, u16 port)
|
||||
{
|
||||
memset(&m_address, 0, sizeof(m_address));
|
||||
setAddress(ipv6_bytes);
|
||||
setPort(port);
|
||||
}
|
||||
|
||||
// Equality (address family, IP and port must be equal)
|
||||
bool Address::operator==(const Address &other)
|
||||
{
|
||||
if (other.m_addr_family != m_addr_family || other.m_port != m_port)
|
||||
return false;
|
||||
|
||||
if (m_addr_family == AF_INET) {
|
||||
return m_address.ipv4.s_addr == other.m_address.ipv4.s_addr;
|
||||
}
|
||||
|
||||
if (m_addr_family == AF_INET6) {
|
||||
return memcmp(m_address.ipv6.s6_addr,
|
||||
other.m_address.ipv6.s6_addr, 16) == 0;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void Address::Resolve(const char *name)
|
||||
{
|
||||
if (!name || name[0] == 0) {
|
||||
if (m_addr_family == AF_INET)
|
||||
setAddress(static_cast<u32>(0));
|
||||
else if (m_addr_family == AF_INET6)
|
||||
setAddress(static_cast<IPv6AddressBytes*>(nullptr));
|
||||
return;
|
||||
}
|
||||
|
||||
struct addrinfo *resolved, hints;
|
||||
memset(&hints, 0, sizeof(hints));
|
||||
|
||||
// Setup hints
|
||||
if (g_settings->getBool("enable_ipv6")) {
|
||||
// AF_UNSPEC allows both IPv6 and IPv4 addresses to be returned
|
||||
hints.ai_family = AF_UNSPEC;
|
||||
} else {
|
||||
hints.ai_family = AF_INET;
|
||||
}
|
||||
|
||||
// Do getaddrinfo()
|
||||
int e = getaddrinfo(name, NULL, &hints, &resolved);
|
||||
if (e != 0)
|
||||
throw ResolveError(gai_strerror(e));
|
||||
|
||||
// Copy data
|
||||
if (resolved->ai_family == AF_INET) {
|
||||
struct sockaddr_in *t = (struct sockaddr_in *)resolved->ai_addr;
|
||||
m_addr_family = AF_INET;
|
||||
m_address.ipv4 = t->sin_addr;
|
||||
} else if (resolved->ai_family == AF_INET6) {
|
||||
struct sockaddr_in6 *t = (struct sockaddr_in6 *)resolved->ai_addr;
|
||||
m_addr_family = AF_INET6;
|
||||
m_address.ipv6 = t->sin6_addr;
|
||||
} else {
|
||||
m_addr_family = 0;
|
||||
}
|
||||
freeaddrinfo(resolved);
|
||||
}
|
||||
|
||||
// IP address -> textual representation
|
||||
std::string Address::serializeString() const
|
||||
{
|
||||
// windows XP doesnt have inet_ntop, maybe use better func
|
||||
#ifdef _WIN32
|
||||
if (m_addr_family == AF_INET) {
|
||||
return inet_ntoa(m_address.ipv4);
|
||||
} else if (m_addr_family == AF_INET6) {
|
||||
std::ostringstream os;
|
||||
os << std::hex;
|
||||
for (int i = 0; i < 16; i += 2) {
|
||||
u16 section = (m_address.ipv6.s6_addr[i] << 8) |
|
||||
(m_address.ipv6.s6_addr[i + 1]);
|
||||
os << section;
|
||||
if (i < 14)
|
||||
os << ":";
|
||||
}
|
||||
return os.str();
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
#else
|
||||
char str[INET6_ADDRSTRLEN];
|
||||
if (inet_ntop(m_addr_family, (void*) &m_address, str, sizeof(str)) == nullptr)
|
||||
return "";
|
||||
return str;
|
||||
#endif
|
||||
}
|
||||
|
||||
struct in_addr Address::getAddress() const
|
||||
{
|
||||
return m_address.ipv4;
|
||||
}
|
||||
|
||||
struct in6_addr Address::getAddress6() const
|
||||
{
|
||||
return m_address.ipv6;
|
||||
}
|
||||
|
||||
u16 Address::getPort() const
|
||||
{
|
||||
return m_port;
|
||||
}
|
||||
|
||||
bool Address::isZero() const
|
||||
{
|
||||
if (m_addr_family == AF_INET) {
|
||||
return m_address.ipv4.s_addr == 0;
|
||||
}
|
||||
|
||||
if (m_addr_family == AF_INET6) {
|
||||
static const char zero[16] = {0};
|
||||
return memcmp(m_address.ipv6.s6_addr, zero, 16) == 0;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void Address::setAddress(u32 address)
|
||||
{
|
||||
m_addr_family = AF_INET;
|
||||
m_address.ipv4.s_addr = htonl(address);
|
||||
}
|
||||
|
||||
void Address::setAddress(u8 a, u8 b, u8 c, u8 d)
|
||||
{
|
||||
u32 addr = (a << 24) | (b << 16) | (c << 8) | d;
|
||||
setAddress(addr);
|
||||
}
|
||||
|
||||
void Address::setAddress(const IPv6AddressBytes *ipv6_bytes)
|
||||
{
|
||||
m_addr_family = AF_INET6;
|
||||
if (ipv6_bytes)
|
||||
memcpy(m_address.ipv6.s6_addr, ipv6_bytes->bytes, 16);
|
||||
else
|
||||
memset(m_address.ipv6.s6_addr, 0, 16);
|
||||
}
|
||||
|
||||
void Address::setPort(u16 port)
|
||||
{
|
||||
m_port = port;
|
||||
}
|
||||
|
||||
void Address::print(std::ostream& s) const
|
||||
{
|
||||
if (m_addr_family == AF_INET6)
|
||||
s << "[" << serializeString() << "]:" << m_port;
|
||||
else if (m_addr_family == AF_INET)
|
||||
s << serializeString() << ":" << m_port;
|
||||
else
|
||||
s << "(undefined)";
|
||||
}
|
||||
|
||||
bool Address::isLocalhost() const
|
||||
{
|
||||
if (isIPv6()) {
|
||||
static const u8 localhost_bytes[] = {
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1};
|
||||
static const u8 mapped_ipv4_localhost[] = {
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 0x7f, 0, 0, 0};
|
||||
|
||||
auto addr = m_address.ipv6.s6_addr;
|
||||
|
||||
return memcmp(addr, localhost_bytes, 16) == 0 ||
|
||||
memcmp(addr, mapped_ipv4_localhost, 13) == 0;
|
||||
}
|
||||
|
||||
auto addr = ntohl(m_address.ipv4.s_addr);
|
||||
return (addr >> 24) == 0x7f;
|
||||
}
|
||||
82
src/network/address.h
Normal file
82
src/network/address.h
Normal file
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
Minetest
|
||||
Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2.1 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef _WIN32
|
||||
#ifndef _WIN32_WINNT
|
||||
#define _WIN32_WINNT 0x0501
|
||||
#endif
|
||||
#include <windows.h>
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
#else
|
||||
#include <netinet/in.h>
|
||||
#include <sys/socket.h>
|
||||
#endif
|
||||
|
||||
#include <ostream>
|
||||
#include <cstring>
|
||||
#include "irrlichttypes.h"
|
||||
#include "networkexceptions.h"
|
||||
|
||||
struct IPv6AddressBytes
|
||||
{
|
||||
u8 bytes[16];
|
||||
IPv6AddressBytes() { memset(bytes, 0, 16); }
|
||||
};
|
||||
|
||||
class Address
|
||||
{
|
||||
public:
|
||||
Address();
|
||||
Address(u32 address, u16 port);
|
||||
Address(u8 a, u8 b, u8 c, u8 d, u16 port);
|
||||
Address(const IPv6AddressBytes *ipv6_bytes, u16 port);
|
||||
|
||||
bool operator==(const Address &address);
|
||||
bool operator!=(const Address &address) { return !(*this == address); }
|
||||
|
||||
struct in_addr getAddress() const;
|
||||
struct in6_addr getAddress6() const;
|
||||
u16 getPort() const;
|
||||
int getFamily() const { return m_addr_family; }
|
||||
bool isIPv6() const { return m_addr_family == AF_INET6; }
|
||||
bool isZero() const;
|
||||
void print(std::ostream &s) const;
|
||||
std::string serializeString() const;
|
||||
bool isLocalhost() const;
|
||||
|
||||
// Resolve() may throw ResolveError (address is unchanged in this case)
|
||||
void Resolve(const char *name);
|
||||
|
||||
void setAddress(u32 address);
|
||||
void setAddress(u8 a, u8 b, u8 c, u8 d);
|
||||
void setAddress(const IPv6AddressBytes *ipv6_bytes);
|
||||
void setPort(u16 port);
|
||||
|
||||
private:
|
||||
unsigned short m_addr_family = 0;
|
||||
union
|
||||
{
|
||||
struct in_addr ipv4;
|
||||
struct in6_addr ipv6;
|
||||
} m_address;
|
||||
u16 m_port = 0; // Port is separate from sockaddr structures
|
||||
};
|
||||
226
src/network/clientopcodes.cpp
Normal file
226
src/network/clientopcodes.cpp
Normal file
@@ -0,0 +1,226 @@
|
||||
/*
|
||||
Minetest
|
||||
Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
|
||||
Copyright (C) 2015 nerzhul, Loic Blot <loic.blot@unix-experience.fr>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2.1 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "clientopcodes.h"
|
||||
|
||||
const static ToClientCommandHandler null_command_handler = {"TOCLIENT_NULL", TOCLIENT_STATE_ALL, &Client::handleCommand_Null};
|
||||
|
||||
const ToClientCommandHandler toClientCommandTable[TOCLIENT_NUM_MSG_TYPES] =
|
||||
{
|
||||
null_command_handler, // 0x00 (never use this)
|
||||
null_command_handler, // 0x01
|
||||
{ "TOCLIENT_HELLO", TOCLIENT_STATE_NOT_CONNECTED, &Client::handleCommand_Hello }, // 0x02
|
||||
{ "TOCLIENT_AUTH_ACCEPT", TOCLIENT_STATE_NOT_CONNECTED, &Client::handleCommand_AuthAccept }, // 0x03
|
||||
{ "TOCLIENT_ACCEPT_SUDO_MODE", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_AcceptSudoMode}, // 0x04
|
||||
{ "TOCLIENT_DENY_SUDO_MODE", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_DenySudoMode}, // 0x05
|
||||
null_command_handler, // 0x06
|
||||
null_command_handler, // 0x07
|
||||
null_command_handler, // 0x08
|
||||
null_command_handler, // 0x09
|
||||
{ "TOCLIENT_ACCESS_DENIED", TOCLIENT_STATE_NOT_CONNECTED, &Client::handleCommand_AccessDenied }, // 0x0A
|
||||
null_command_handler, // 0x0B
|
||||
null_command_handler, // 0x0C
|
||||
null_command_handler, // 0x0D
|
||||
null_command_handler, // 0x0E
|
||||
null_command_handler, // 0x0F
|
||||
null_command_handler, // 0x10
|
||||
null_command_handler,
|
||||
null_command_handler,
|
||||
null_command_handler,
|
||||
null_command_handler,
|
||||
null_command_handler,
|
||||
null_command_handler,
|
||||
null_command_handler,
|
||||
null_command_handler,
|
||||
null_command_handler,
|
||||
null_command_handler,
|
||||
null_command_handler,
|
||||
null_command_handler,
|
||||
null_command_handler,
|
||||
null_command_handler,
|
||||
null_command_handler,
|
||||
{ "TOCLIENT_BLOCKDATA", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_BlockData }, // 0x20
|
||||
{ "TOCLIENT_ADDNODE", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_AddNode }, // 0x21
|
||||
{ "TOCLIENT_REMOVENODE", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_RemoveNode }, // 0x22
|
||||
null_command_handler,
|
||||
null_command_handler,
|
||||
null_command_handler,
|
||||
null_command_handler,
|
||||
{ "TOCLIENT_INVENTORY", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_Inventory }, // 0x27
|
||||
null_command_handler,
|
||||
{ "TOCLIENT_TIME_OF_DAY", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_TimeOfDay }, // 0x29
|
||||
{ "TOCLIENT_CSM_RESTRICTION_FLAGS", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_CSMRestrictionFlags }, // 0x2A
|
||||
{ "TOCLIENT_PLAYER_SPEED", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_PlayerSpeed }, // 0x2B
|
||||
{ "TOCLIENT_MEDIA_PUSH", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_MediaPush }, // 0x2C
|
||||
null_command_handler,
|
||||
null_command_handler,
|
||||
{ "TOCLIENT_CHAT_MESSAGE", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_ChatMessage }, // 0x2F
|
||||
null_command_handler, // 0x30
|
||||
{ "TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_ActiveObjectRemoveAdd }, // 0x31
|
||||
{ "TOCLIENT_ACTIVE_OBJECT_MESSAGES", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_ActiveObjectMessages }, // 0x32
|
||||
{ "TOCLIENT_HP", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_HP }, // 0x33
|
||||
{ "TOCLIENT_MOVE_PLAYER", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_MovePlayer }, // 0x34
|
||||
{ "TOCLIENT_ACCESS_DENIED_LEGACY", TOCLIENT_STATE_NOT_CONNECTED, &Client::handleCommand_AccessDenied }, // 0x35
|
||||
{ "TOCLIENT_FOV", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_Fov }, // 0x36
|
||||
{ "TOCLIENT_DEATHSCREEN", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_DeathScreen }, // 0x37
|
||||
{ "TOCLIENT_MEDIA", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_Media }, // 0x38
|
||||
null_command_handler,
|
||||
{ "TOCLIENT_NODEDEF", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_NodeDef }, // 0x3a
|
||||
null_command_handler,
|
||||
{ "TOCLIENT_ANNOUNCE_MEDIA", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_AnnounceMedia }, // 0x3c
|
||||
{ "TOCLIENT_ITEMDEF", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_ItemDef }, // 0x3d
|
||||
null_command_handler,
|
||||
{ "TOCLIENT_PLAY_SOUND", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_PlaySound }, // 0x3f
|
||||
{ "TOCLIENT_STOP_SOUND", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_StopSound }, // 0x40
|
||||
{ "TOCLIENT_PRIVILEGES", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_Privileges }, // 0x41
|
||||
{ "TOCLIENT_INVENTORY_FORMSPEC", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_InventoryFormSpec }, // 0x42
|
||||
{ "TOCLIENT_DETACHED_INVENTORY", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_DetachedInventory }, // 0x43
|
||||
{ "TOCLIENT_SHOW_FORMSPEC", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_ShowFormSpec }, // 0x44
|
||||
{ "TOCLIENT_MOVEMENT", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_Movement }, // 0x45
|
||||
{ "TOCLIENT_SPAWN_PARTICLE", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_SpawnParticle }, // 0x46
|
||||
{ "TOCLIENT_ADD_PARTICLESPAWNER", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_AddParticleSpawner }, // 0x47
|
||||
null_command_handler,
|
||||
{ "TOCLIENT_HUDADD", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_HudAdd }, // 0x49
|
||||
{ "TOCLIENT_HUDRM", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_HudRemove }, // 0x4a
|
||||
{ "TOCLIENT_HUDCHANGE", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_HudChange }, // 0x4b
|
||||
{ "TOCLIENT_HUD_SET_FLAGS", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_HudSetFlags }, // 0x4c
|
||||
{ "TOCLIENT_HUD_SET_PARAM", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_HudSetParam }, // 0x4d
|
||||
{ "TOCLIENT_BREATH", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_Breath }, // 0x4e
|
||||
{ "TOCLIENT_SET_SKY", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_HudSetSky }, // 0x4f
|
||||
{ "TOCLIENT_OVERRIDE_DAY_NIGHT_RATIO", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_OverrideDayNightRatio }, // 0x50
|
||||
{ "TOCLIENT_LOCAL_PLAYER_ANIMATIONS", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_LocalPlayerAnimations }, // 0x51
|
||||
{ "TOCLIENT_EYE_OFFSET", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_EyeOffset }, // 0x52
|
||||
{ "TOCLIENT_DELETE_PARTICLESPAWNER", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_DeleteParticleSpawner }, // 0x53
|
||||
{ "TOCLIENT_CLOUD_PARAMS", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_CloudParams }, // 0x54
|
||||
{ "TOCLIENT_FADE_SOUND", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_FadeSound }, // 0x55
|
||||
{ "TOCLIENT_UPDATE_PLAYER_LIST", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_UpdatePlayerList }, // 0x56
|
||||
{ "TOCLIENT_MODCHANNEL_MSG", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_ModChannelMsg }, // 0x57
|
||||
{ "TOCLIENT_MODCHANNEL_SIGNAL", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_ModChannelSignal }, // 0x58
|
||||
{ "TOCLIENT_NODEMETA_CHANGED", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_NodemetaChanged }, // 0x59
|
||||
{ "TOCLIENT_SET_SUN", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_HudSetSun }, // 0x5a
|
||||
{ "TOCLIENT_SET_MOON", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_HudSetMoon }, // 0x5b
|
||||
{ "TOCLIENT_SET_STARS", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_HudSetStars }, // 0x5c
|
||||
null_command_handler,
|
||||
null_command_handler,
|
||||
null_command_handler,
|
||||
{ "TOCLIENT_SRP_BYTES_S_B", TOCLIENT_STATE_NOT_CONNECTED, &Client::handleCommand_SrpBytesSandB }, // 0x60
|
||||
{ "TOCLIENT_FORMSPEC_PREPEND", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_FormspecPrepend }, // 0x61,
|
||||
{ "TOCLIENT_MINIMAP_MODES", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_MinimapModes }, // 0x62,
|
||||
{ "TOCLIENT_SET_LIGHTING", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_SetLighting }, // 0x63,
|
||||
};
|
||||
|
||||
const static ServerCommandFactory null_command_factory = { "TOSERVER_NULL", 0, false };
|
||||
|
||||
/*
|
||||
Channels used for Client -> Server communication
|
||||
2: Notifications back to the server (e.g. GOTBLOCKS)
|
||||
1: Init and Authentication
|
||||
0: everything else
|
||||
|
||||
Packet order is only guaranteed inside a channel, so packets that operate on
|
||||
the same objects are *required* to be in the same channel.
|
||||
*/
|
||||
|
||||
const ServerCommandFactory serverCommandFactoryTable[TOSERVER_NUM_MSG_TYPES] =
|
||||
{
|
||||
null_command_factory, // 0x00
|
||||
null_command_factory, // 0x01
|
||||
{ "TOSERVER_INIT", 1, false }, // 0x02
|
||||
null_command_factory, // 0x03
|
||||
null_command_factory, // 0x04
|
||||
null_command_factory, // 0x05
|
||||
null_command_factory, // 0x06
|
||||
null_command_factory, // 0x07
|
||||
null_command_factory, // 0x08
|
||||
null_command_factory, // 0x09
|
||||
null_command_factory, // 0x0a
|
||||
null_command_factory, // 0x0b
|
||||
null_command_factory, // 0x0c
|
||||
null_command_factory, // 0x0d
|
||||
null_command_factory, // 0x0e
|
||||
null_command_factory, // 0x0f
|
||||
null_command_factory, // 0x10
|
||||
{ "TOSERVER_INIT2", 1, true }, // 0x11
|
||||
null_command_factory, // 0x12
|
||||
null_command_factory, // 0x13
|
||||
null_command_factory, // 0x14
|
||||
null_command_factory, // 0x15
|
||||
null_command_factory, // 0x16
|
||||
{ "TOSERVER_MODCHANNEL_JOIN", 0, true }, // 0x17
|
||||
{ "TOSERVER_MODCHANNEL_LEAVE", 0, true }, // 0x18
|
||||
{ "TOSERVER_MODCHANNEL_MSG", 0, true }, // 0x19
|
||||
null_command_factory, // 0x1a
|
||||
null_command_factory, // 0x1b
|
||||
null_command_factory, // 0x1c
|
||||
null_command_factory, // 0x1d
|
||||
null_command_factory, // 0x1e
|
||||
null_command_factory, // 0x1f
|
||||
null_command_factory, // 0x20
|
||||
null_command_factory, // 0x21
|
||||
null_command_factory, // 0x22
|
||||
{ "TOSERVER_PLAYERPOS", 0, false }, // 0x23
|
||||
{ "TOSERVER_GOTBLOCKS", 2, true }, // 0x24
|
||||
{ "TOSERVER_DELETEDBLOCKS", 2, true }, // 0x25
|
||||
null_command_factory, // 0x26
|
||||
null_command_factory, // 0x27
|
||||
null_command_factory, // 0x28
|
||||
null_command_factory, // 0x29
|
||||
null_command_factory, // 0x2a
|
||||
null_command_factory, // 0x2b
|
||||
null_command_factory, // 0x2c
|
||||
null_command_factory, // 0x2d
|
||||
null_command_factory, // 0x2e
|
||||
null_command_factory, // 0x2f
|
||||
null_command_factory, // 0x30
|
||||
{ "TOSERVER_INVENTORY_ACTION", 0, true }, // 0x31
|
||||
{ "TOSERVER_CHAT_MESSAGE", 0, true }, // 0x32
|
||||
null_command_factory, // 0x33
|
||||
null_command_factory, // 0x34
|
||||
{ "TOSERVER_DAMAGE", 0, true }, // 0x35
|
||||
null_command_factory, // 0x36
|
||||
{ "TOSERVER_PLAYERITEM", 0, true }, // 0x37
|
||||
{ "TOSERVER_RESPAWN", 0, true }, // 0x38
|
||||
{ "TOSERVER_INTERACT", 0, true }, // 0x39
|
||||
{ "TOSERVER_REMOVED_SOUNDS", 2, true }, // 0x3a
|
||||
{ "TOSERVER_NODEMETA_FIELDS", 0, true }, // 0x3b
|
||||
{ "TOSERVER_INVENTORY_FIELDS", 0, true }, // 0x3c
|
||||
null_command_factory, // 0x3d
|
||||
null_command_factory, // 0x3e
|
||||
null_command_factory, // 0x3f
|
||||
{ "TOSERVER_REQUEST_MEDIA", 1, true }, // 0x40
|
||||
{ "TOSERVER_HAVE_MEDIA", 2, true }, // 0x41
|
||||
null_command_factory, // 0x42
|
||||
{ "TOSERVER_CLIENT_READY", 1, true }, // 0x43
|
||||
null_command_factory, // 0x44
|
||||
null_command_factory, // 0x45
|
||||
null_command_factory, // 0x46
|
||||
null_command_factory, // 0x47
|
||||
null_command_factory, // 0x48
|
||||
null_command_factory, // 0x49
|
||||
null_command_factory, // 0x4a
|
||||
null_command_factory, // 0x4b
|
||||
null_command_factory, // 0x4c
|
||||
null_command_factory, // 0x4d
|
||||
null_command_factory, // 0x4e
|
||||
null_command_factory, // 0x4f
|
||||
{ "TOSERVER_FIRST_SRP", 1, true }, // 0x50
|
||||
{ "TOSERVER_SRP_BYTES_A", 1, true }, // 0x51
|
||||
{ "TOSERVER_SRP_BYTES_M", 1, true }, // 0x52
|
||||
};
|
||||
50
src/network/clientopcodes.h
Normal file
50
src/network/clientopcodes.h
Normal file
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
Minetest
|
||||
Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
|
||||
Copyright (C) 2015 nerzhul, Loic Blot <loic.blot@unix-experience.fr>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2.1 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "client/client.h"
|
||||
#include "networkprotocol.h"
|
||||
|
||||
class NetworkPacket;
|
||||
|
||||
enum ToClientConnectionState {
|
||||
TOCLIENT_STATE_NOT_CONNECTED,
|
||||
TOCLIENT_STATE_CONNECTED,
|
||||
TOCLIENT_STATE_ALL,
|
||||
};
|
||||
|
||||
struct ToClientCommandHandler
|
||||
{
|
||||
const char* name;
|
||||
ToClientConnectionState state;
|
||||
void (Client::*handler)(NetworkPacket* pkt);
|
||||
};
|
||||
|
||||
struct ServerCommandFactory
|
||||
{
|
||||
const char* name;
|
||||
u8 channel;
|
||||
bool reliable;
|
||||
};
|
||||
|
||||
extern const ToClientCommandHandler toClientCommandTable[TOCLIENT_NUM_MSG_TYPES];
|
||||
|
||||
extern const ServerCommandFactory serverCommandFactoryTable[TOSERVER_NUM_MSG_TYPES];
|
||||
1767
src/network/clientpackethandler.cpp
Normal file
1767
src/network/clientpackethandler.cpp
Normal file
File diff suppressed because it is too large
Load Diff
1647
src/network/connection.cpp
Normal file
1647
src/network/connection.cpp
Normal file
File diff suppressed because it is too large
Load Diff
785
src/network/connection.h
Normal file
785
src/network/connection.h
Normal file
@@ -0,0 +1,785 @@
|
||||
/*
|
||||
Minetest
|
||||
Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2.1 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "irrlichttypes.h"
|
||||
#include "peerhandler.h"
|
||||
#include "socket.h"
|
||||
#include "constants.h"
|
||||
#include "util/pointer.h"
|
||||
#include "util/container.h"
|
||||
#include "util/thread.h"
|
||||
#include "util/numeric.h"
|
||||
#include "networkprotocol.h"
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
#define MAX_UDP_PEERS 65535
|
||||
|
||||
/*
|
||||
=== NOTES ===
|
||||
|
||||
A packet is sent through a channel to a peer with a basic header:
|
||||
Header (7 bytes):
|
||||
[0] u32 protocol_id
|
||||
[4] session_t sender_peer_id
|
||||
[6] u8 channel
|
||||
sender_peer_id:
|
||||
Unique to each peer.
|
||||
value 0 (PEER_ID_INEXISTENT) is reserved for making new connections
|
||||
value 1 (PEER_ID_SERVER) is reserved for server
|
||||
these constants are defined in constants.h
|
||||
channel:
|
||||
Channel numbers have no intrinsic meaning. Currently only 0, 1, 2 exist.
|
||||
*/
|
||||
#define BASE_HEADER_SIZE 7
|
||||
#define CHANNEL_COUNT 3
|
||||
|
||||
/*
|
||||
Packet types:
|
||||
|
||||
CONTROL: This is a packet used by the protocol.
|
||||
- When this is processed, nothing is handed to the user.
|
||||
Header (2 byte):
|
||||
[0] u8 type
|
||||
[1] u8 controltype
|
||||
controltype and data description:
|
||||
CONTROLTYPE_ACK
|
||||
[2] u16 seqnum
|
||||
CONTROLTYPE_SET_PEER_ID
|
||||
[2] session_t peer_id_new
|
||||
CONTROLTYPE_PING
|
||||
- There is no actual reply, but this can be sent in a reliable
|
||||
packet to get a reply
|
||||
CONTROLTYPE_DISCO
|
||||
*/
|
||||
enum ControlType : u8 {
|
||||
CONTROLTYPE_ACK = 0,
|
||||
CONTROLTYPE_SET_PEER_ID = 1,
|
||||
CONTROLTYPE_PING = 2,
|
||||
CONTROLTYPE_DISCO = 3,
|
||||
};
|
||||
|
||||
/*
|
||||
ORIGINAL: This is a plain packet with no control and no error
|
||||
checking at all.
|
||||
- When this is processed, it is directly handed to the user.
|
||||
Header (1 byte):
|
||||
[0] u8 type
|
||||
*/
|
||||
//#define TYPE_ORIGINAL 1
|
||||
#define ORIGINAL_HEADER_SIZE 1
|
||||
|
||||
/*
|
||||
SPLIT: These are sequences of packets forming one bigger piece of
|
||||
data.
|
||||
- When processed and all the packet_nums 0...packet_count-1 are
|
||||
present (this should be buffered), the resulting data shall be
|
||||
directly handed to the user.
|
||||
- If the data fails to come up in a reasonable time, the buffer shall
|
||||
be silently discarded.
|
||||
- These can be sent as-is or atop of a RELIABLE packet stream.
|
||||
Header (7 bytes):
|
||||
[0] u8 type
|
||||
[1] u16 seqnum
|
||||
[3] u16 chunk_count
|
||||
[5] u16 chunk_num
|
||||
*/
|
||||
//#define TYPE_SPLIT 2
|
||||
|
||||
/*
|
||||
RELIABLE: Delivery of all RELIABLE packets shall be forced by ACKs,
|
||||
and they shall be delivered in the same order as sent. This is done
|
||||
with a buffer in the receiving and transmitting end.
|
||||
- When this is processed, the contents of each packet is recursively
|
||||
processed as packets.
|
||||
Header (3 bytes):
|
||||
[0] u8 type
|
||||
[1] u16 seqnum
|
||||
|
||||
*/
|
||||
//#define TYPE_RELIABLE 3
|
||||
#define RELIABLE_HEADER_SIZE 3
|
||||
#define SEQNUM_INITIAL 65500
|
||||
#define SEQNUM_MAX 65535
|
||||
|
||||
class NetworkPacket;
|
||||
|
||||
namespace con
|
||||
{
|
||||
|
||||
class ConnectionReceiveThread;
|
||||
class ConnectionSendThread;
|
||||
|
||||
typedef enum MTProtocols {
|
||||
MTP_PRIMARY,
|
||||
MTP_UDP,
|
||||
MTP_MINETEST_RELIABLE_UDP
|
||||
} MTProtocols;
|
||||
|
||||
enum PacketType : u8 {
|
||||
PACKET_TYPE_CONTROL = 0,
|
||||
PACKET_TYPE_ORIGINAL = 1,
|
||||
PACKET_TYPE_SPLIT = 2,
|
||||
PACKET_TYPE_RELIABLE = 3,
|
||||
PACKET_TYPE_MAX
|
||||
};
|
||||
|
||||
inline bool seqnum_higher(u16 totest, u16 base)
|
||||
{
|
||||
if (totest > base)
|
||||
{
|
||||
if ((totest - base) > (SEQNUM_MAX/2))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if ((base - totest) > (SEQNUM_MAX/2))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
inline bool seqnum_in_window(u16 seqnum, u16 next,u16 window_size)
|
||||
{
|
||||
u16 window_start = next;
|
||||
u16 window_end = ( next + window_size ) % (SEQNUM_MAX+1);
|
||||
|
||||
if (window_start < window_end) {
|
||||
return ((seqnum >= window_start) && (seqnum < window_end));
|
||||
}
|
||||
|
||||
|
||||
return ((seqnum < window_end) || (seqnum >= window_start));
|
||||
}
|
||||
|
||||
static inline float CALC_DTIME(u64 lasttime, u64 curtime)
|
||||
{
|
||||
float value = ( curtime - lasttime) / 1000.0;
|
||||
return MYMAX(MYMIN(value,0.1),0.0);
|
||||
}
|
||||
|
||||
/*
|
||||
Struct for all kinds of packets. Includes following data:
|
||||
BASE_HEADER
|
||||
u8[] packet data (usually copied from SharedBuffer<u8>)
|
||||
*/
|
||||
struct BufferedPacket {
|
||||
BufferedPacket(u32 a_size)
|
||||
{
|
||||
m_data.resize(a_size);
|
||||
data = &m_data[0];
|
||||
}
|
||||
|
||||
DISABLE_CLASS_COPY(BufferedPacket)
|
||||
|
||||
u16 getSeqnum() const;
|
||||
|
||||
inline size_t size() const { return m_data.size(); }
|
||||
|
||||
u8 *data; // Direct memory access
|
||||
float time = 0.0f; // Seconds from buffering the packet or re-sending
|
||||
float totaltime = 0.0f; // Seconds from buffering the packet
|
||||
u64 absolute_send_time = -1;
|
||||
Address address; // Sender or destination
|
||||
unsigned int resend_count = 0;
|
||||
|
||||
private:
|
||||
std::vector<u8> m_data; // Data of the packet, including headers
|
||||
};
|
||||
|
||||
typedef std::shared_ptr<BufferedPacket> BufferedPacketPtr;
|
||||
|
||||
|
||||
// This adds the base headers to the data and makes a packet out of it
|
||||
BufferedPacketPtr makePacket(Address &address, const SharedBuffer<u8> &data,
|
||||
u32 protocol_id, session_t sender_peer_id, u8 channel);
|
||||
|
||||
// Depending on size, make a TYPE_ORIGINAL or TYPE_SPLIT packet
|
||||
// Increments split_seqnum if a split packet is made
|
||||
void makeAutoSplitPacket(const SharedBuffer<u8> &data, u32 chunksize_max,
|
||||
u16 &split_seqnum, std::list<SharedBuffer<u8>> *list);
|
||||
|
||||
// Add the TYPE_RELIABLE header to the data
|
||||
SharedBuffer<u8> makeReliablePacket(const SharedBuffer<u8> &data, u16 seqnum);
|
||||
|
||||
struct IncomingSplitPacket
|
||||
{
|
||||
IncomingSplitPacket(u32 cc, bool r):
|
||||
chunk_count(cc), reliable(r) {}
|
||||
|
||||
IncomingSplitPacket() = delete;
|
||||
|
||||
float time = 0.0f; // Seconds from adding
|
||||
u32 chunk_count;
|
||||
bool reliable; // If true, isn't deleted on timeout
|
||||
|
||||
bool allReceived() const
|
||||
{
|
||||
return (chunks.size() == chunk_count);
|
||||
}
|
||||
bool insert(u32 chunk_num, SharedBuffer<u8> &chunkdata);
|
||||
SharedBuffer<u8> reassemble();
|
||||
|
||||
private:
|
||||
// Key is chunk number, value is data without headers
|
||||
std::map<u16, SharedBuffer<u8>> chunks;
|
||||
};
|
||||
|
||||
/*
|
||||
A buffer which stores reliable packets and sorts them internally
|
||||
for fast access to the smallest one.
|
||||
*/
|
||||
|
||||
typedef std::list<BufferedPacketPtr>::iterator RPBSearchResult;
|
||||
|
||||
class ReliablePacketBuffer
|
||||
{
|
||||
public:
|
||||
ReliablePacketBuffer() = default;
|
||||
|
||||
bool getFirstSeqnum(u16& result);
|
||||
|
||||
BufferedPacketPtr popFirst();
|
||||
BufferedPacketPtr popSeqnum(u16 seqnum);
|
||||
void insert(BufferedPacketPtr &p_ptr, u16 next_expected);
|
||||
|
||||
void incrementTimeouts(float dtime);
|
||||
std::list<ConstSharedPtr<BufferedPacket>> getTimedOuts(float timeout, u32 max_packets);
|
||||
|
||||
void print();
|
||||
bool empty();
|
||||
u32 size();
|
||||
|
||||
|
||||
private:
|
||||
RPBSearchResult findPacketNoLock(u16 seqnum);
|
||||
|
||||
std::list<BufferedPacketPtr> m_list;
|
||||
|
||||
u16 m_oldest_non_answered_ack;
|
||||
|
||||
std::mutex m_list_mutex;
|
||||
};
|
||||
|
||||
/*
|
||||
A buffer for reconstructing split packets
|
||||
*/
|
||||
|
||||
class IncomingSplitBuffer
|
||||
{
|
||||
public:
|
||||
~IncomingSplitBuffer();
|
||||
/*
|
||||
Returns a reference counted buffer of length != 0 when a full split
|
||||
packet is constructed. If not, returns one of length 0.
|
||||
*/
|
||||
SharedBuffer<u8> insert(BufferedPacketPtr &p_ptr, bool reliable);
|
||||
|
||||
void removeUnreliableTimedOuts(float dtime, float timeout);
|
||||
|
||||
private:
|
||||
// Key is seqnum
|
||||
std::map<u16, IncomingSplitPacket*> m_buf;
|
||||
|
||||
std::mutex m_map_mutex;
|
||||
};
|
||||
|
||||
enum ConnectionCommandType{
|
||||
CONNCMD_NONE,
|
||||
CONNCMD_SERVE,
|
||||
CONNCMD_CONNECT,
|
||||
CONNCMD_DISCONNECT,
|
||||
CONNCMD_DISCONNECT_PEER,
|
||||
CONNCMD_SEND,
|
||||
CONNCMD_SEND_TO_ALL,
|
||||
CONCMD_ACK,
|
||||
CONCMD_CREATE_PEER
|
||||
};
|
||||
|
||||
struct ConnectionCommand;
|
||||
typedef std::shared_ptr<ConnectionCommand> ConnectionCommandPtr;
|
||||
|
||||
// This is very similar to ConnectionEvent
|
||||
struct ConnectionCommand
|
||||
{
|
||||
const ConnectionCommandType type;
|
||||
Address address;
|
||||
session_t peer_id = PEER_ID_INEXISTENT;
|
||||
u8 channelnum = 0;
|
||||
Buffer<u8> data;
|
||||
bool reliable = false;
|
||||
bool raw = false;
|
||||
|
||||
DISABLE_CLASS_COPY(ConnectionCommand);
|
||||
|
||||
static ConnectionCommandPtr serve(Address address);
|
||||
static ConnectionCommandPtr connect(Address address);
|
||||
static ConnectionCommandPtr disconnect();
|
||||
static ConnectionCommandPtr disconnect_peer(session_t peer_id);
|
||||
static ConnectionCommandPtr send(session_t peer_id, u8 channelnum, NetworkPacket *pkt, bool reliable);
|
||||
static ConnectionCommandPtr ack(session_t peer_id, u8 channelnum, const Buffer<u8> &data);
|
||||
static ConnectionCommandPtr createPeer(session_t peer_id, const Buffer<u8> &data);
|
||||
|
||||
private:
|
||||
ConnectionCommand(ConnectionCommandType type_) :
|
||||
type(type_) {}
|
||||
|
||||
static ConnectionCommandPtr create(ConnectionCommandType type);
|
||||
};
|
||||
|
||||
/* maximum window size to use, 0xFFFF is theoretical maximum. don't think about
|
||||
* touching it, the less you're away from it the more likely data corruption
|
||||
* will occur
|
||||
*/
|
||||
#define MAX_RELIABLE_WINDOW_SIZE 0x8000
|
||||
/* starting value for window size */
|
||||
#define START_RELIABLE_WINDOW_SIZE 0x400
|
||||
/* minimum value for window size */
|
||||
#define MIN_RELIABLE_WINDOW_SIZE 0x40
|
||||
|
||||
class Channel
|
||||
{
|
||||
|
||||
public:
|
||||
u16 readNextIncomingSeqNum();
|
||||
u16 incNextIncomingSeqNum();
|
||||
|
||||
u16 getOutgoingSequenceNumber(bool& successfull);
|
||||
u16 readOutgoingSequenceNumber();
|
||||
bool putBackSequenceNumber(u16);
|
||||
|
||||
u16 readNextSplitSeqNum();
|
||||
void setNextSplitSeqNum(u16 seqnum);
|
||||
|
||||
// This is for buffering the incoming packets that are coming in
|
||||
// the wrong order
|
||||
ReliablePacketBuffer incoming_reliables;
|
||||
// This is for buffering the sent packets so that the sender can
|
||||
// re-send them if no ACK is received
|
||||
ReliablePacketBuffer outgoing_reliables_sent;
|
||||
|
||||
//queued reliable packets
|
||||
std::queue<BufferedPacketPtr> queued_reliables;
|
||||
|
||||
//queue commands prior splitting to packets
|
||||
std::deque<ConnectionCommandPtr> queued_commands;
|
||||
|
||||
IncomingSplitBuffer incoming_splits;
|
||||
|
||||
Channel() = default;
|
||||
~Channel() = default;
|
||||
|
||||
void UpdatePacketLossCounter(unsigned int count);
|
||||
void UpdatePacketTooLateCounter();
|
||||
void UpdateBytesSent(unsigned int bytes,unsigned int packages=1);
|
||||
void UpdateBytesLost(unsigned int bytes);
|
||||
void UpdateBytesReceived(unsigned int bytes);
|
||||
|
||||
void UpdateTimers(float dtime);
|
||||
|
||||
float getCurrentDownloadRateKB()
|
||||
{ MutexAutoLock lock(m_internal_mutex); return cur_kbps; };
|
||||
float getMaxDownloadRateKB()
|
||||
{ MutexAutoLock lock(m_internal_mutex); return max_kbps; };
|
||||
|
||||
float getCurrentLossRateKB()
|
||||
{ MutexAutoLock lock(m_internal_mutex); return cur_kbps_lost; };
|
||||
float getMaxLossRateKB()
|
||||
{ MutexAutoLock lock(m_internal_mutex); return max_kbps_lost; };
|
||||
|
||||
float getCurrentIncomingRateKB()
|
||||
{ MutexAutoLock lock(m_internal_mutex); return cur_incoming_kbps; };
|
||||
float getMaxIncomingRateKB()
|
||||
{ MutexAutoLock lock(m_internal_mutex); return max_incoming_kbps; };
|
||||
|
||||
float getAvgDownloadRateKB()
|
||||
{ MutexAutoLock lock(m_internal_mutex); return avg_kbps; };
|
||||
float getAvgLossRateKB()
|
||||
{ MutexAutoLock lock(m_internal_mutex); return avg_kbps_lost; };
|
||||
float getAvgIncomingRateKB()
|
||||
{ MutexAutoLock lock(m_internal_mutex); return avg_incoming_kbps; };
|
||||
|
||||
u16 getWindowSize() const { return m_window_size; };
|
||||
|
||||
void setWindowSize(long size)
|
||||
{
|
||||
m_window_size = (u16)rangelim(size, MIN_RELIABLE_WINDOW_SIZE, MAX_RELIABLE_WINDOW_SIZE);
|
||||
}
|
||||
|
||||
private:
|
||||
std::mutex m_internal_mutex;
|
||||
u16 m_window_size = MIN_RELIABLE_WINDOW_SIZE;
|
||||
|
||||
u16 next_incoming_seqnum = SEQNUM_INITIAL;
|
||||
|
||||
u16 next_outgoing_seqnum = SEQNUM_INITIAL;
|
||||
u16 next_outgoing_split_seqnum = SEQNUM_INITIAL;
|
||||
|
||||
unsigned int current_packet_loss = 0;
|
||||
unsigned int current_packet_too_late = 0;
|
||||
unsigned int current_packet_successful = 0;
|
||||
float packet_loss_counter = 0.0f;
|
||||
|
||||
unsigned int current_bytes_transfered = 0;
|
||||
unsigned int current_bytes_received = 0;
|
||||
unsigned int current_bytes_lost = 0;
|
||||
float max_kbps = 0.0f;
|
||||
float cur_kbps = 0.0f;
|
||||
float avg_kbps = 0.0f;
|
||||
float max_incoming_kbps = 0.0f;
|
||||
float cur_incoming_kbps = 0.0f;
|
||||
float avg_incoming_kbps = 0.0f;
|
||||
float max_kbps_lost = 0.0f;
|
||||
float cur_kbps_lost = 0.0f;
|
||||
float avg_kbps_lost = 0.0f;
|
||||
float bpm_counter = 0.0f;
|
||||
|
||||
unsigned int rate_samples = 0;
|
||||
};
|
||||
|
||||
class Peer;
|
||||
|
||||
class PeerHelper
|
||||
{
|
||||
public:
|
||||
PeerHelper() = default;
|
||||
PeerHelper(Peer* peer);
|
||||
~PeerHelper();
|
||||
|
||||
PeerHelper& operator=(Peer* peer);
|
||||
Peer* operator->() const;
|
||||
bool operator!();
|
||||
Peer* operator&() const;
|
||||
bool operator!=(void* ptr);
|
||||
|
||||
private:
|
||||
Peer *m_peer = nullptr;
|
||||
};
|
||||
|
||||
class Connection;
|
||||
|
||||
typedef enum {
|
||||
CUR_DL_RATE,
|
||||
AVG_DL_RATE,
|
||||
CUR_INC_RATE,
|
||||
AVG_INC_RATE,
|
||||
CUR_LOSS_RATE,
|
||||
AVG_LOSS_RATE,
|
||||
} rate_stat_type;
|
||||
|
||||
class Peer {
|
||||
public:
|
||||
friend class PeerHelper;
|
||||
|
||||
Peer(Address address_,session_t id_,Connection* connection) :
|
||||
id(id_),
|
||||
m_connection(connection),
|
||||
address(address_),
|
||||
m_last_timeout_check(porting::getTimeMs())
|
||||
{
|
||||
};
|
||||
|
||||
virtual ~Peer() {
|
||||
MutexAutoLock usage_lock(m_exclusive_access_mutex);
|
||||
FATAL_ERROR_IF(m_usage != 0, "Reference counting failure");
|
||||
};
|
||||
|
||||
// Unique id of the peer
|
||||
const session_t id;
|
||||
|
||||
void Drop();
|
||||
|
||||
virtual void PutReliableSendCommand(ConnectionCommandPtr &c,
|
||||
unsigned int max_packet_size) {};
|
||||
|
||||
virtual bool getAddress(MTProtocols type, Address& toset) = 0;
|
||||
|
||||
bool isPendingDeletion()
|
||||
{ MutexAutoLock lock(m_exclusive_access_mutex); return m_pending_deletion; };
|
||||
|
||||
void ResetTimeout()
|
||||
{MutexAutoLock lock(m_exclusive_access_mutex); m_timeout_counter = 0.0; };
|
||||
|
||||
bool isTimedOut(float timeout);
|
||||
|
||||
unsigned int m_increment_packets_remaining = 0;
|
||||
|
||||
virtual u16 getNextSplitSequenceNumber(u8 channel) { return 0; };
|
||||
virtual void setNextSplitSequenceNumber(u8 channel, u16 seqnum) {};
|
||||
virtual SharedBuffer<u8> addSplitPacket(u8 channel, BufferedPacketPtr &toadd,
|
||||
bool reliable)
|
||||
{
|
||||
errorstream << "Peer::addSplitPacket called,"
|
||||
<< " this is supposed to be never called!" << std::endl;
|
||||
return SharedBuffer<u8>(0);
|
||||
};
|
||||
|
||||
virtual bool Ping(float dtime, SharedBuffer<u8>& data) { return false; };
|
||||
|
||||
virtual float getStat(rtt_stat_type type) const {
|
||||
switch (type) {
|
||||
case MIN_RTT:
|
||||
return m_rtt.min_rtt;
|
||||
case MAX_RTT:
|
||||
return m_rtt.max_rtt;
|
||||
case AVG_RTT:
|
||||
return m_rtt.avg_rtt;
|
||||
case MIN_JITTER:
|
||||
return m_rtt.jitter_min;
|
||||
case MAX_JITTER:
|
||||
return m_rtt.jitter_max;
|
||||
case AVG_JITTER:
|
||||
return m_rtt.jitter_avg;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
protected:
|
||||
virtual void reportRTT(float rtt) {};
|
||||
|
||||
void RTTStatistics(float rtt,
|
||||
const std::string &profiler_id = "",
|
||||
unsigned int num_samples = 1000);
|
||||
|
||||
bool IncUseCount();
|
||||
void DecUseCount();
|
||||
|
||||
mutable std::mutex m_exclusive_access_mutex;
|
||||
|
||||
bool m_pending_deletion = false;
|
||||
|
||||
Connection* m_connection;
|
||||
|
||||
// Address of the peer
|
||||
Address address;
|
||||
|
||||
// Ping timer
|
||||
float m_ping_timer = 0.0f;
|
||||
private:
|
||||
|
||||
struct rttstats {
|
||||
float jitter_min = FLT_MAX;
|
||||
float jitter_max = 0.0f;
|
||||
float jitter_avg = -1.0f;
|
||||
float min_rtt = FLT_MAX;
|
||||
float max_rtt = 0.0f;
|
||||
float avg_rtt = -1.0f;
|
||||
|
||||
rttstats() = default;
|
||||
};
|
||||
|
||||
rttstats m_rtt;
|
||||
float m_last_rtt = -1.0f;
|
||||
|
||||
// current usage count
|
||||
unsigned int m_usage = 0;
|
||||
|
||||
// Seconds from last receive
|
||||
float m_timeout_counter = 0.0f;
|
||||
|
||||
u64 m_last_timeout_check;
|
||||
};
|
||||
|
||||
class UDPPeer : public Peer
|
||||
{
|
||||
public:
|
||||
|
||||
friend class PeerHelper;
|
||||
friend class ConnectionReceiveThread;
|
||||
friend class ConnectionSendThread;
|
||||
friend class Connection;
|
||||
|
||||
UDPPeer(u16 a_id, Address a_address, Connection* connection);
|
||||
virtual ~UDPPeer() = default;
|
||||
|
||||
void PutReliableSendCommand(ConnectionCommandPtr &c,
|
||||
unsigned int max_packet_size);
|
||||
|
||||
bool getAddress(MTProtocols type, Address& toset);
|
||||
|
||||
u16 getNextSplitSequenceNumber(u8 channel);
|
||||
void setNextSplitSequenceNumber(u8 channel, u16 seqnum);
|
||||
|
||||
SharedBuffer<u8> addSplitPacket(u8 channel, BufferedPacketPtr &toadd,
|
||||
bool reliable);
|
||||
|
||||
protected:
|
||||
/*
|
||||
Calculates avg_rtt and resend_timeout.
|
||||
rtt=-1 only recalculates resend_timeout
|
||||
*/
|
||||
void reportRTT(float rtt);
|
||||
|
||||
void RunCommandQueues(
|
||||
unsigned int max_packet_size,
|
||||
unsigned int maxcommands,
|
||||
unsigned int maxtransfer);
|
||||
|
||||
float getResendTimeout()
|
||||
{ MutexAutoLock lock(m_exclusive_access_mutex); return resend_timeout; }
|
||||
|
||||
void setResendTimeout(float timeout)
|
||||
{ MutexAutoLock lock(m_exclusive_access_mutex); resend_timeout = timeout; }
|
||||
bool Ping(float dtime,SharedBuffer<u8>& data);
|
||||
|
||||
Channel channels[CHANNEL_COUNT];
|
||||
bool m_pending_disconnect = false;
|
||||
private:
|
||||
// This is changed dynamically
|
||||
float resend_timeout = 0.5;
|
||||
|
||||
bool processReliableSendCommand(
|
||||
ConnectionCommandPtr &c_ptr,
|
||||
unsigned int max_packet_size);
|
||||
};
|
||||
|
||||
/*
|
||||
Connection
|
||||
*/
|
||||
|
||||
enum ConnectionEventType {
|
||||
CONNEVENT_NONE,
|
||||
CONNEVENT_DATA_RECEIVED,
|
||||
CONNEVENT_PEER_ADDED,
|
||||
CONNEVENT_PEER_REMOVED,
|
||||
CONNEVENT_BIND_FAILED,
|
||||
};
|
||||
|
||||
struct ConnectionEvent;
|
||||
typedef std::shared_ptr<ConnectionEvent> ConnectionEventPtr;
|
||||
|
||||
// This is very similar to ConnectionCommand
|
||||
struct ConnectionEvent
|
||||
{
|
||||
const ConnectionEventType type;
|
||||
session_t peer_id = 0;
|
||||
Buffer<u8> data;
|
||||
bool timeout = false;
|
||||
Address address;
|
||||
|
||||
// We don't want to copy "data"
|
||||
DISABLE_CLASS_COPY(ConnectionEvent);
|
||||
|
||||
static ConnectionEventPtr create(ConnectionEventType type);
|
||||
static ConnectionEventPtr dataReceived(session_t peer_id, const Buffer<u8> &data);
|
||||
static ConnectionEventPtr peerAdded(session_t peer_id, Address address);
|
||||
static ConnectionEventPtr peerRemoved(session_t peer_id, bool is_timeout, Address address);
|
||||
static ConnectionEventPtr bindFailed();
|
||||
|
||||
const char *describe() const;
|
||||
|
||||
private:
|
||||
ConnectionEvent(ConnectionEventType type_) :
|
||||
type(type_) {}
|
||||
};
|
||||
|
||||
class PeerHandler;
|
||||
|
||||
class Connection
|
||||
{
|
||||
public:
|
||||
friend class ConnectionSendThread;
|
||||
friend class ConnectionReceiveThread;
|
||||
|
||||
Connection(u32 protocol_id, u32 max_packet_size, float timeout, bool ipv6,
|
||||
PeerHandler *peerhandler);
|
||||
~Connection();
|
||||
|
||||
/* Interface */
|
||||
ConnectionEventPtr waitEvent(u32 timeout_ms);
|
||||
|
||||
void putCommand(ConnectionCommandPtr c);
|
||||
|
||||
void SetTimeoutMs(u32 timeout) { m_bc_receive_timeout = timeout; }
|
||||
void Serve(Address bind_addr);
|
||||
void Connect(Address address);
|
||||
bool Connected();
|
||||
void Disconnect();
|
||||
void Receive(NetworkPacket* pkt);
|
||||
bool TryReceive(NetworkPacket *pkt);
|
||||
void Send(session_t peer_id, u8 channelnum, NetworkPacket *pkt, bool reliable);
|
||||
session_t GetPeerID() const { return m_peer_id; }
|
||||
Address GetPeerAddress(session_t peer_id);
|
||||
float getPeerStat(session_t peer_id, rtt_stat_type type);
|
||||
float getLocalStat(rate_stat_type type);
|
||||
u32 GetProtocolID() const { return m_protocol_id; };
|
||||
const std::string getDesc();
|
||||
void DisconnectPeer(session_t peer_id);
|
||||
|
||||
protected:
|
||||
PeerHelper getPeerNoEx(session_t peer_id);
|
||||
u16 lookupPeer(Address& sender);
|
||||
|
||||
u16 createPeer(Address& sender, MTProtocols protocol, int fd);
|
||||
UDPPeer* createServerPeer(Address& sender);
|
||||
bool deletePeer(session_t peer_id, bool timeout);
|
||||
|
||||
void SetPeerID(session_t id) { m_peer_id = id; }
|
||||
|
||||
void sendAck(session_t peer_id, u8 channelnum, u16 seqnum);
|
||||
|
||||
std::vector<session_t> getPeerIDs()
|
||||
{
|
||||
MutexAutoLock peerlock(m_peers_mutex);
|
||||
return m_peer_ids;
|
||||
}
|
||||
|
||||
UDPSocket m_udpSocket;
|
||||
// Command queue: user -> SendThread
|
||||
MutexedQueue<ConnectionCommandPtr> m_command_queue;
|
||||
|
||||
bool Receive(NetworkPacket *pkt, u32 timeout);
|
||||
|
||||
void putEvent(ConnectionEventPtr e);
|
||||
|
||||
void TriggerSend();
|
||||
|
||||
bool ConnectedToServer()
|
||||
{
|
||||
return getPeerNoEx(PEER_ID_SERVER) != nullptr;
|
||||
}
|
||||
private:
|
||||
// Event queue: ReceiveThread -> user
|
||||
MutexedQueue<ConnectionEventPtr> m_event_queue;
|
||||
|
||||
session_t m_peer_id = 0;
|
||||
u32 m_protocol_id;
|
||||
|
||||
std::map<session_t, Peer *> m_peers;
|
||||
std::vector<session_t> m_peer_ids;
|
||||
std::mutex m_peers_mutex;
|
||||
|
||||
std::unique_ptr<ConnectionSendThread> m_sendThread;
|
||||
std::unique_ptr<ConnectionReceiveThread> m_receiveThread;
|
||||
|
||||
mutable std::mutex m_info_mutex;
|
||||
|
||||
// Backwards compatibility
|
||||
PeerHandler *m_bc_peerhandler;
|
||||
u32 m_bc_receive_timeout = 0;
|
||||
|
||||
bool m_shutting_down = false;
|
||||
|
||||
session_t m_next_remote_peer_id = 2;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
1358
src/network/connectionthreads.cpp
Normal file
1358
src/network/connectionthreads.cpp
Normal file
File diff suppressed because it is too large
Load Diff
169
src/network/connectionthreads.h
Normal file
169
src/network/connectionthreads.h
Normal file
@@ -0,0 +1,169 @@
|
||||
/*
|
||||
Minetest
|
||||
Copyright (C) 2013-2017 celeron55, Perttu Ahola <celeron55@gmail.com>
|
||||
Copyright (C) 2017 celeron55, Loic Blot <loic.blot@unix-experience.fr>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2.1 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cassert>
|
||||
#include "threading/thread.h"
|
||||
#include "connection.h"
|
||||
|
||||
namespace con
|
||||
{
|
||||
|
||||
class Connection;
|
||||
|
||||
struct OutgoingPacket
|
||||
{
|
||||
session_t peer_id;
|
||||
u8 channelnum;
|
||||
SharedBuffer<u8> data;
|
||||
bool reliable;
|
||||
bool ack;
|
||||
|
||||
OutgoingPacket(session_t peer_id_, u8 channelnum_, const SharedBuffer<u8> &data_,
|
||||
bool reliable_,bool ack_=false):
|
||||
peer_id(peer_id_),
|
||||
channelnum(channelnum_),
|
||||
data(data_),
|
||||
reliable(reliable_),
|
||||
ack(ack_)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
class ConnectionSendThread : public Thread
|
||||
{
|
||||
|
||||
public:
|
||||
friend class UDPPeer;
|
||||
|
||||
ConnectionSendThread(unsigned int max_packet_size, float timeout);
|
||||
|
||||
void *run();
|
||||
|
||||
void Trigger();
|
||||
|
||||
void setParent(Connection *parent)
|
||||
{
|
||||
assert(parent != NULL); // Pre-condition
|
||||
m_connection = parent;
|
||||
}
|
||||
|
||||
void setPeerTimeout(float peer_timeout) { m_timeout = peer_timeout; }
|
||||
|
||||
private:
|
||||
void runTimeouts(float dtime);
|
||||
void rawSend(const BufferedPacket *p);
|
||||
bool rawSendAsPacket(session_t peer_id, u8 channelnum,
|
||||
const SharedBuffer<u8> &data, bool reliable);
|
||||
|
||||
void processReliableCommand(ConnectionCommandPtr &c);
|
||||
void processNonReliableCommand(ConnectionCommandPtr &c);
|
||||
void serve(Address bind_address);
|
||||
void connect(Address address);
|
||||
void disconnect();
|
||||
void disconnect_peer(session_t peer_id);
|
||||
void send(session_t peer_id, u8 channelnum, const SharedBuffer<u8> &data);
|
||||
void sendReliable(ConnectionCommandPtr &c);
|
||||
void sendToAll(u8 channelnum, const SharedBuffer<u8> &data);
|
||||
void sendToAllReliable(ConnectionCommandPtr &c);
|
||||
|
||||
void sendPackets(float dtime);
|
||||
|
||||
void sendAsPacket(session_t peer_id, u8 channelnum, const SharedBuffer<u8> &data,
|
||||
bool ack = false);
|
||||
|
||||
void sendAsPacketReliable(BufferedPacketPtr &p, Channel *channel);
|
||||
|
||||
bool packetsQueued();
|
||||
|
||||
Connection *m_connection = nullptr;
|
||||
unsigned int m_max_packet_size;
|
||||
float m_timeout;
|
||||
std::queue<OutgoingPacket> m_outgoing_queue;
|
||||
Semaphore m_send_sleep_semaphore;
|
||||
|
||||
unsigned int m_iteration_packets_avaialble;
|
||||
unsigned int m_max_commands_per_iteration = 1;
|
||||
unsigned int m_max_data_packets_per_iteration;
|
||||
unsigned int m_max_packets_requeued = 256;
|
||||
};
|
||||
|
||||
class ConnectionReceiveThread : public Thread
|
||||
{
|
||||
public:
|
||||
ConnectionReceiveThread(unsigned int max_packet_size);
|
||||
|
||||
void *run();
|
||||
|
||||
void setParent(Connection *parent)
|
||||
{
|
||||
assert(parent); // Pre-condition
|
||||
m_connection = parent;
|
||||
}
|
||||
|
||||
private:
|
||||
void receive(SharedBuffer<u8> &packetdata, bool &packet_queued);
|
||||
|
||||
// Returns next data from a buffer if possible
|
||||
// If found, returns true; if not, false.
|
||||
// If found, sets peer_id and dst
|
||||
bool getFromBuffers(session_t &peer_id, SharedBuffer<u8> &dst);
|
||||
|
||||
bool checkIncomingBuffers(
|
||||
Channel *channel, session_t &peer_id, SharedBuffer<u8> &dst);
|
||||
|
||||
/*
|
||||
Processes a packet with the basic header stripped out.
|
||||
Parameters:
|
||||
packetdata: Data in packet (with no base headers)
|
||||
peer_id: peer id of the sender of the packet in question
|
||||
channelnum: channel on which the packet was sent
|
||||
reliable: true if recursing into a reliable packet
|
||||
*/
|
||||
SharedBuffer<u8> processPacket(Channel *channel,
|
||||
const SharedBuffer<u8> &packetdata, session_t peer_id,
|
||||
u8 channelnum, bool reliable);
|
||||
|
||||
SharedBuffer<u8> handlePacketType_Control(Channel *channel,
|
||||
const SharedBuffer<u8> &packetdata, Peer *peer, u8 channelnum,
|
||||
bool reliable);
|
||||
SharedBuffer<u8> handlePacketType_Original(Channel *channel,
|
||||
const SharedBuffer<u8> &packetdata, Peer *peer, u8 channelnum,
|
||||
bool reliable);
|
||||
SharedBuffer<u8> handlePacketType_Split(Channel *channel,
|
||||
const SharedBuffer<u8> &packetdata, Peer *peer, u8 channelnum,
|
||||
bool reliable);
|
||||
SharedBuffer<u8> handlePacketType_Reliable(Channel *channel,
|
||||
const SharedBuffer<u8> &packetdata, Peer *peer, u8 channelnum,
|
||||
bool reliable);
|
||||
|
||||
struct PacketTypeHandler
|
||||
{
|
||||
SharedBuffer<u8> (ConnectionReceiveThread::*handler)(Channel *channel,
|
||||
const SharedBuffer<u8> &packet, Peer *peer, u8 channelnum,
|
||||
bool reliable);
|
||||
};
|
||||
|
||||
static const PacketTypeHandler packetTypeRouter[PACKET_TYPE_MAX];
|
||||
|
||||
Connection *m_connection = nullptr;
|
||||
};
|
||||
}
|
||||
100
src/network/networkexceptions.h
Normal file
100
src/network/networkexceptions.h
Normal file
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
Minetest
|
||||
Copyright (C) 2013-2017 celeron55, Perttu Ahola <celeron55@gmail.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2.1 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "exceptions.h"
|
||||
|
||||
namespace con
|
||||
{
|
||||
/*
|
||||
Exceptions
|
||||
*/
|
||||
class NotFoundException : public BaseException
|
||||
{
|
||||
public:
|
||||
NotFoundException(const char *s) : BaseException(s) {}
|
||||
};
|
||||
|
||||
class PeerNotFoundException : public BaseException
|
||||
{
|
||||
public:
|
||||
PeerNotFoundException(const char *s) : BaseException(s) {}
|
||||
};
|
||||
|
||||
class ConnectionException : public BaseException
|
||||
{
|
||||
public:
|
||||
ConnectionException(const char *s) : BaseException(s) {}
|
||||
};
|
||||
|
||||
class ConnectionBindFailed : public BaseException
|
||||
{
|
||||
public:
|
||||
ConnectionBindFailed(const char *s) : BaseException(s) {}
|
||||
};
|
||||
|
||||
class InvalidIncomingDataException : public BaseException
|
||||
{
|
||||
public:
|
||||
InvalidIncomingDataException(const char *s) : BaseException(s) {}
|
||||
};
|
||||
|
||||
class NoIncomingDataException : public BaseException
|
||||
{
|
||||
public:
|
||||
NoIncomingDataException(const char *s) : BaseException(s) {}
|
||||
};
|
||||
|
||||
class ProcessedSilentlyException : public BaseException
|
||||
{
|
||||
public:
|
||||
ProcessedSilentlyException(const char *s) : BaseException(s) {}
|
||||
};
|
||||
|
||||
class ProcessedQueued : public BaseException
|
||||
{
|
||||
public:
|
||||
ProcessedQueued(const char *s) : BaseException(s) {}
|
||||
};
|
||||
|
||||
class IncomingDataCorruption : public BaseException
|
||||
{
|
||||
public:
|
||||
IncomingDataCorruption(const char *s) : BaseException(s) {}
|
||||
};
|
||||
}
|
||||
|
||||
class SocketException : public BaseException
|
||||
{
|
||||
public:
|
||||
SocketException(const std::string &s) : BaseException(s) {}
|
||||
};
|
||||
|
||||
class ResolveError : public BaseException
|
||||
{
|
||||
public:
|
||||
ResolveError(const std::string &s) : BaseException(s) {}
|
||||
};
|
||||
|
||||
class SendFailedException : public BaseException
|
||||
{
|
||||
public:
|
||||
SendFailedException(const std::string &s) : BaseException(s) {}
|
||||
};
|
||||
559
src/network/networkpacket.cpp
Normal file
559
src/network/networkpacket.cpp
Normal file
@@ -0,0 +1,559 @@
|
||||
/*
|
||||
Minetest
|
||||
Copyright (C) 2015 nerzhul, Loic Blot <loic.blot@unix-experience.fr>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2.1 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "networkpacket.h"
|
||||
#include <sstream>
|
||||
#include "networkexceptions.h"
|
||||
#include "util/serialize.h"
|
||||
#include "networkprotocol.h"
|
||||
|
||||
NetworkPacket::NetworkPacket(u16 command, u32 datasize, session_t peer_id):
|
||||
m_datasize(datasize), m_command(command), m_peer_id(peer_id)
|
||||
{
|
||||
m_data.resize(m_datasize);
|
||||
}
|
||||
|
||||
NetworkPacket::NetworkPacket(u16 command, u32 datasize):
|
||||
m_datasize(datasize), m_command(command)
|
||||
{
|
||||
m_data.resize(m_datasize);
|
||||
}
|
||||
|
||||
NetworkPacket::~NetworkPacket()
|
||||
{
|
||||
m_data.clear();
|
||||
}
|
||||
|
||||
void NetworkPacket::checkReadOffset(u32 from_offset, u32 field_size)
|
||||
{
|
||||
if (from_offset + field_size > m_datasize) {
|
||||
std::stringstream ss;
|
||||
ss << "Reading outside packet (offset: " <<
|
||||
from_offset << ", packet size: " << getSize() << ")";
|
||||
throw PacketError(ss.str());
|
||||
}
|
||||
}
|
||||
|
||||
void NetworkPacket::putRawPacket(const u8 *data, u32 datasize, session_t peer_id)
|
||||
{
|
||||
// If a m_command is already set, we are rewriting on same packet
|
||||
// This is not permitted
|
||||
assert(m_command == 0);
|
||||
|
||||
m_datasize = datasize - 2;
|
||||
m_peer_id = peer_id;
|
||||
|
||||
m_data.resize(m_datasize);
|
||||
|
||||
// split command and datas
|
||||
m_command = readU16(&data[0]);
|
||||
memcpy(m_data.data(), &data[2], m_datasize);
|
||||
}
|
||||
|
||||
void NetworkPacket::clear()
|
||||
{
|
||||
m_data.clear();
|
||||
m_datasize = 0;
|
||||
m_read_offset = 0;
|
||||
m_command = 0;
|
||||
m_peer_id = 0;
|
||||
}
|
||||
|
||||
const char* NetworkPacket::getString(u32 from_offset)
|
||||
{
|
||||
checkReadOffset(from_offset, 0);
|
||||
|
||||
return (char*)&m_data[from_offset];
|
||||
}
|
||||
|
||||
void NetworkPacket::putRawString(const char* src, u32 len)
|
||||
{
|
||||
if (m_read_offset + len > m_datasize) {
|
||||
m_datasize = m_read_offset + len;
|
||||
m_data.resize(m_datasize);
|
||||
}
|
||||
|
||||
if (len == 0)
|
||||
return;
|
||||
|
||||
memcpy(&m_data[m_read_offset], src, len);
|
||||
m_read_offset += len;
|
||||
}
|
||||
|
||||
NetworkPacket& NetworkPacket::operator>>(std::string& dst)
|
||||
{
|
||||
checkReadOffset(m_read_offset, 2);
|
||||
u16 strLen = readU16(&m_data[m_read_offset]);
|
||||
m_read_offset += 2;
|
||||
|
||||
dst.clear();
|
||||
|
||||
if (strLen == 0) {
|
||||
return *this;
|
||||
}
|
||||
|
||||
checkReadOffset(m_read_offset, strLen);
|
||||
|
||||
dst.reserve(strLen);
|
||||
dst.append((char*)&m_data[m_read_offset], strLen);
|
||||
|
||||
m_read_offset += strLen;
|
||||
return *this;
|
||||
}
|
||||
|
||||
NetworkPacket& NetworkPacket::operator<<(const std::string &src)
|
||||
{
|
||||
if (src.size() > STRING_MAX_LEN) {
|
||||
throw PacketError("String too long");
|
||||
}
|
||||
|
||||
u16 msgsize = src.size();
|
||||
|
||||
*this << msgsize;
|
||||
|
||||
putRawString(src.c_str(), (u32)msgsize);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
void NetworkPacket::putLongString(const std::string &src)
|
||||
{
|
||||
if (src.size() > LONG_STRING_MAX_LEN) {
|
||||
throw PacketError("String too long");
|
||||
}
|
||||
|
||||
u32 msgsize = src.size();
|
||||
|
||||
*this << msgsize;
|
||||
|
||||
putRawString(src.c_str(), msgsize);
|
||||
}
|
||||
|
||||
static constexpr bool NEED_SURROGATE_CODING = sizeof(wchar_t) > 2;
|
||||
|
||||
NetworkPacket& NetworkPacket::operator>>(std::wstring& dst)
|
||||
{
|
||||
checkReadOffset(m_read_offset, 2);
|
||||
u16 strLen = readU16(&m_data[m_read_offset]);
|
||||
m_read_offset += 2;
|
||||
|
||||
dst.clear();
|
||||
|
||||
if (strLen == 0) {
|
||||
return *this;
|
||||
}
|
||||
|
||||
checkReadOffset(m_read_offset, strLen * 2);
|
||||
|
||||
dst.reserve(strLen);
|
||||
for (u16 i = 0; i < strLen; i++) {
|
||||
wchar_t c = readU16(&m_data[m_read_offset]);
|
||||
if (NEED_SURROGATE_CODING && c >= 0xD800 && c < 0xDC00 && i+1 < strLen) {
|
||||
i++;
|
||||
m_read_offset += sizeof(u16);
|
||||
|
||||
wchar_t c2 = readU16(&m_data[m_read_offset]);
|
||||
c = 0x10000 + ( ((c & 0x3ff) << 10) | (c2 & 0x3ff) );
|
||||
}
|
||||
dst.push_back(c);
|
||||
m_read_offset += sizeof(u16);
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
NetworkPacket& NetworkPacket::operator<<(const std::wstring &src)
|
||||
{
|
||||
if (src.size() > WIDE_STRING_MAX_LEN) {
|
||||
throw PacketError("String too long");
|
||||
}
|
||||
|
||||
if (!NEED_SURROGATE_CODING || src.size() == 0) {
|
||||
*this << static_cast<u16>(src.size());
|
||||
for (u16 i = 0; i < src.size(); i++)
|
||||
*this << static_cast<u16>(src[i]);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
// write dummy value, to be overwritten later
|
||||
const u32 len_offset = m_read_offset;
|
||||
u32 written = 0;
|
||||
*this << static_cast<u16>(0xfff0);
|
||||
|
||||
for (u16 i = 0; i < src.size(); i++) {
|
||||
wchar_t c = src[i];
|
||||
if (c > 0xffff) {
|
||||
// Encode high code-points as surrogate pairs
|
||||
u32 n = c - 0x10000;
|
||||
*this << static_cast<u16>(0xD800 | (n >> 10))
|
||||
<< static_cast<u16>(0xDC00 | (n & 0x3ff));
|
||||
written += 2;
|
||||
} else {
|
||||
*this << static_cast<u16>(c);
|
||||
written++;
|
||||
}
|
||||
}
|
||||
|
||||
if (written > WIDE_STRING_MAX_LEN)
|
||||
throw PacketError("String too long");
|
||||
writeU16(&m_data[len_offset], written);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
std::string NetworkPacket::readLongString()
|
||||
{
|
||||
checkReadOffset(m_read_offset, 4);
|
||||
u32 strLen = readU32(&m_data[m_read_offset]);
|
||||
m_read_offset += 4;
|
||||
|
||||
if (strLen == 0) {
|
||||
return "";
|
||||
}
|
||||
|
||||
if (strLen > LONG_STRING_MAX_LEN) {
|
||||
throw PacketError("String too long");
|
||||
}
|
||||
|
||||
checkReadOffset(m_read_offset, strLen);
|
||||
|
||||
std::string dst;
|
||||
|
||||
dst.reserve(strLen);
|
||||
dst.append((char*)&m_data[m_read_offset], strLen);
|
||||
|
||||
m_read_offset += strLen;
|
||||
|
||||
return dst;
|
||||
}
|
||||
|
||||
NetworkPacket& NetworkPacket::operator>>(char& dst)
|
||||
{
|
||||
checkReadOffset(m_read_offset, 1);
|
||||
|
||||
dst = readU8(&m_data[m_read_offset]);
|
||||
|
||||
m_read_offset += 1;
|
||||
return *this;
|
||||
}
|
||||
|
||||
NetworkPacket& NetworkPacket::operator<<(char src)
|
||||
{
|
||||
checkDataSize(1);
|
||||
|
||||
writeU8(&m_data[m_read_offset], src);
|
||||
|
||||
m_read_offset += 1;
|
||||
return *this;
|
||||
}
|
||||
|
||||
NetworkPacket& NetworkPacket::operator<<(u8 src)
|
||||
{
|
||||
checkDataSize(1);
|
||||
|
||||
writeU8(&m_data[m_read_offset], src);
|
||||
|
||||
m_read_offset += 1;
|
||||
return *this;
|
||||
}
|
||||
|
||||
NetworkPacket& NetworkPacket::operator<<(bool src)
|
||||
{
|
||||
checkDataSize(1);
|
||||
|
||||
writeU8(&m_data[m_read_offset], src);
|
||||
|
||||
m_read_offset += 1;
|
||||
return *this;
|
||||
}
|
||||
|
||||
NetworkPacket& NetworkPacket::operator<<(u16 src)
|
||||
{
|
||||
checkDataSize(2);
|
||||
|
||||
writeU16(&m_data[m_read_offset], src);
|
||||
|
||||
m_read_offset += 2;
|
||||
return *this;
|
||||
}
|
||||
|
||||
NetworkPacket& NetworkPacket::operator<<(u32 src)
|
||||
{
|
||||
checkDataSize(4);
|
||||
|
||||
writeU32(&m_data[m_read_offset], src);
|
||||
|
||||
m_read_offset += 4;
|
||||
return *this;
|
||||
}
|
||||
|
||||
NetworkPacket& NetworkPacket::operator<<(u64 src)
|
||||
{
|
||||
checkDataSize(8);
|
||||
|
||||
writeU64(&m_data[m_read_offset], src);
|
||||
|
||||
m_read_offset += 8;
|
||||
return *this;
|
||||
}
|
||||
|
||||
NetworkPacket& NetworkPacket::operator<<(float src)
|
||||
{
|
||||
checkDataSize(4);
|
||||
|
||||
writeF32(&m_data[m_read_offset], src);
|
||||
|
||||
m_read_offset += 4;
|
||||
return *this;
|
||||
}
|
||||
|
||||
NetworkPacket& NetworkPacket::operator>>(bool& dst)
|
||||
{
|
||||
checkReadOffset(m_read_offset, 1);
|
||||
|
||||
dst = readU8(&m_data[m_read_offset]);
|
||||
|
||||
m_read_offset += 1;
|
||||
return *this;
|
||||
}
|
||||
|
||||
NetworkPacket& NetworkPacket::operator>>(u8& dst)
|
||||
{
|
||||
checkReadOffset(m_read_offset, 1);
|
||||
|
||||
dst = readU8(&m_data[m_read_offset]);
|
||||
|
||||
m_read_offset += 1;
|
||||
return *this;
|
||||
}
|
||||
|
||||
u8 NetworkPacket::getU8(u32 offset)
|
||||
{
|
||||
checkReadOffset(offset, 1);
|
||||
|
||||
return readU8(&m_data[offset]);
|
||||
}
|
||||
|
||||
u8* NetworkPacket::getU8Ptr(u32 from_offset)
|
||||
{
|
||||
if (m_datasize == 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
checkReadOffset(from_offset, 1);
|
||||
|
||||
return (u8*)&m_data[from_offset];
|
||||
}
|
||||
|
||||
NetworkPacket& NetworkPacket::operator>>(u16& dst)
|
||||
{
|
||||
checkReadOffset(m_read_offset, 2);
|
||||
|
||||
dst = readU16(&m_data[m_read_offset]);
|
||||
|
||||
m_read_offset += 2;
|
||||
return *this;
|
||||
}
|
||||
|
||||
u16 NetworkPacket::getU16(u32 from_offset)
|
||||
{
|
||||
checkReadOffset(from_offset, 2);
|
||||
|
||||
return readU16(&m_data[from_offset]);
|
||||
}
|
||||
|
||||
NetworkPacket& NetworkPacket::operator>>(u32& dst)
|
||||
{
|
||||
checkReadOffset(m_read_offset, 4);
|
||||
|
||||
dst = readU32(&m_data[m_read_offset]);
|
||||
|
||||
m_read_offset += 4;
|
||||
return *this;
|
||||
}
|
||||
|
||||
NetworkPacket& NetworkPacket::operator>>(u64& dst)
|
||||
{
|
||||
checkReadOffset(m_read_offset, 8);
|
||||
|
||||
dst = readU64(&m_data[m_read_offset]);
|
||||
|
||||
m_read_offset += 8;
|
||||
return *this;
|
||||
}
|
||||
|
||||
NetworkPacket& NetworkPacket::operator>>(float& dst)
|
||||
{
|
||||
checkReadOffset(m_read_offset, 4);
|
||||
|
||||
dst = readF32(&m_data[m_read_offset]);
|
||||
|
||||
m_read_offset += 4;
|
||||
return *this;
|
||||
}
|
||||
|
||||
NetworkPacket& NetworkPacket::operator>>(v2f& dst)
|
||||
{
|
||||
checkReadOffset(m_read_offset, 8);
|
||||
|
||||
dst = readV2F32(&m_data[m_read_offset]);
|
||||
|
||||
m_read_offset += 8;
|
||||
return *this;
|
||||
}
|
||||
|
||||
NetworkPacket& NetworkPacket::operator>>(v3f& dst)
|
||||
{
|
||||
checkReadOffset(m_read_offset, 12);
|
||||
|
||||
dst = readV3F32(&m_data[m_read_offset]);
|
||||
|
||||
m_read_offset += 12;
|
||||
return *this;
|
||||
}
|
||||
|
||||
NetworkPacket& NetworkPacket::operator>>(s16& dst)
|
||||
{
|
||||
checkReadOffset(m_read_offset, 2);
|
||||
|
||||
dst = readS16(&m_data[m_read_offset]);
|
||||
|
||||
m_read_offset += 2;
|
||||
return *this;
|
||||
}
|
||||
|
||||
NetworkPacket& NetworkPacket::operator<<(s16 src)
|
||||
{
|
||||
*this << (u16) src;
|
||||
return *this;
|
||||
}
|
||||
|
||||
NetworkPacket& NetworkPacket::operator>>(s32& dst)
|
||||
{
|
||||
checkReadOffset(m_read_offset, 4);
|
||||
|
||||
dst = readS32(&m_data[m_read_offset]);
|
||||
|
||||
m_read_offset += 4;
|
||||
return *this;
|
||||
}
|
||||
|
||||
NetworkPacket& NetworkPacket::operator<<(s32 src)
|
||||
{
|
||||
*this << (u32) src;
|
||||
return *this;
|
||||
}
|
||||
|
||||
NetworkPacket& NetworkPacket::operator>>(v3s16& dst)
|
||||
{
|
||||
checkReadOffset(m_read_offset, 6);
|
||||
|
||||
dst = readV3S16(&m_data[m_read_offset]);
|
||||
|
||||
m_read_offset += 6;
|
||||
return *this;
|
||||
}
|
||||
|
||||
NetworkPacket& NetworkPacket::operator>>(v2s32& dst)
|
||||
{
|
||||
checkReadOffset(m_read_offset, 8);
|
||||
|
||||
dst = readV2S32(&m_data[m_read_offset]);
|
||||
|
||||
m_read_offset += 8;
|
||||
return *this;
|
||||
}
|
||||
|
||||
NetworkPacket& NetworkPacket::operator>>(v3s32& dst)
|
||||
{
|
||||
checkReadOffset(m_read_offset, 12);
|
||||
|
||||
dst = readV3S32(&m_data[m_read_offset]);
|
||||
|
||||
m_read_offset += 12;
|
||||
return *this;
|
||||
}
|
||||
|
||||
NetworkPacket& NetworkPacket::operator<<(v2f src)
|
||||
{
|
||||
*this << (float) src.X;
|
||||
*this << (float) src.Y;
|
||||
return *this;
|
||||
}
|
||||
|
||||
NetworkPacket& NetworkPacket::operator<<(v3f src)
|
||||
{
|
||||
*this << (float) src.X;
|
||||
*this << (float) src.Y;
|
||||
*this << (float) src.Z;
|
||||
return *this;
|
||||
}
|
||||
|
||||
NetworkPacket& NetworkPacket::operator<<(v3s16 src)
|
||||
{
|
||||
*this << (s16) src.X;
|
||||
*this << (s16) src.Y;
|
||||
*this << (s16) src.Z;
|
||||
return *this;
|
||||
}
|
||||
|
||||
NetworkPacket& NetworkPacket::operator<<(v2s32 src)
|
||||
{
|
||||
*this << (s32) src.X;
|
||||
*this << (s32) src.Y;
|
||||
return *this;
|
||||
}
|
||||
|
||||
NetworkPacket& NetworkPacket::operator<<(v3s32 src)
|
||||
{
|
||||
*this << (s32) src.X;
|
||||
*this << (s32) src.Y;
|
||||
*this << (s32) src.Z;
|
||||
return *this;
|
||||
}
|
||||
|
||||
NetworkPacket& NetworkPacket::operator>>(video::SColor& dst)
|
||||
{
|
||||
checkReadOffset(m_read_offset, 4);
|
||||
|
||||
dst = readARGB8(&m_data[m_read_offset]);
|
||||
|
||||
m_read_offset += 4;
|
||||
return *this;
|
||||
}
|
||||
|
||||
NetworkPacket& NetworkPacket::operator<<(video::SColor src)
|
||||
{
|
||||
checkDataSize(4);
|
||||
|
||||
writeU32(&m_data[m_read_offset], src.color);
|
||||
|
||||
m_read_offset += 4;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Buffer<u8> NetworkPacket::oldForgePacket()
|
||||
{
|
||||
Buffer<u8> sb(m_datasize + 2);
|
||||
writeU16(&sb[0], m_command);
|
||||
memcpy(&sb[2], m_data.data(), m_datasize);
|
||||
|
||||
return sb;
|
||||
}
|
||||
137
src/network/networkpacket.h
Normal file
137
src/network/networkpacket.h
Normal file
@@ -0,0 +1,137 @@
|
||||
/*
|
||||
Minetest
|
||||
Copyright (C) 2015 nerzhul, Loic Blot <loic.blot@unix-experience.fr>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2.1 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "util/pointer.h"
|
||||
#include "util/numeric.h"
|
||||
#include "networkprotocol.h"
|
||||
#include <SColor.h>
|
||||
|
||||
class NetworkPacket
|
||||
{
|
||||
|
||||
public:
|
||||
NetworkPacket(u16 command, u32 datasize, session_t peer_id);
|
||||
NetworkPacket(u16 command, u32 datasize);
|
||||
NetworkPacket() = default;
|
||||
|
||||
~NetworkPacket();
|
||||
|
||||
void putRawPacket(const u8 *data, u32 datasize, session_t peer_id);
|
||||
void clear();
|
||||
|
||||
// Getters
|
||||
u32 getSize() const { return m_datasize; }
|
||||
session_t getPeerId() const { return m_peer_id; }
|
||||
u16 getCommand() { return m_command; }
|
||||
u32 getRemainingBytes() const { return m_datasize - m_read_offset; }
|
||||
const char *getRemainingString() { return getString(m_read_offset); }
|
||||
|
||||
// Returns a c-string without copying.
|
||||
// A better name for this would be getRawString()
|
||||
const char *getString(u32 from_offset);
|
||||
// major difference to putCString(): doesn't write len into the buffer
|
||||
void putRawString(const char *src, u32 len);
|
||||
void putRawString(const std::string &src)
|
||||
{
|
||||
putRawString(src.c_str(), src.size());
|
||||
}
|
||||
|
||||
NetworkPacket &operator>>(std::string &dst);
|
||||
NetworkPacket &operator<<(const std::string &src);
|
||||
|
||||
void putLongString(const std::string &src);
|
||||
|
||||
NetworkPacket &operator>>(std::wstring &dst);
|
||||
NetworkPacket &operator<<(const std::wstring &src);
|
||||
|
||||
std::string readLongString();
|
||||
|
||||
NetworkPacket &operator>>(char &dst);
|
||||
NetworkPacket &operator<<(char src);
|
||||
|
||||
NetworkPacket &operator>>(bool &dst);
|
||||
NetworkPacket &operator<<(bool src);
|
||||
|
||||
u8 getU8(u32 offset);
|
||||
|
||||
NetworkPacket &operator>>(u8 &dst);
|
||||
NetworkPacket &operator<<(u8 src);
|
||||
|
||||
u8 *getU8Ptr(u32 offset);
|
||||
|
||||
u16 getU16(u32 from_offset);
|
||||
NetworkPacket &operator>>(u16 &dst);
|
||||
NetworkPacket &operator<<(u16 src);
|
||||
|
||||
NetworkPacket &operator>>(u32 &dst);
|
||||
NetworkPacket &operator<<(u32 src);
|
||||
|
||||
NetworkPacket &operator>>(u64 &dst);
|
||||
NetworkPacket &operator<<(u64 src);
|
||||
|
||||
NetworkPacket &operator>>(float &dst);
|
||||
NetworkPacket &operator<<(float src);
|
||||
|
||||
NetworkPacket &operator>>(v2f &dst);
|
||||
NetworkPacket &operator<<(v2f src);
|
||||
|
||||
NetworkPacket &operator>>(v3f &dst);
|
||||
NetworkPacket &operator<<(v3f src);
|
||||
|
||||
NetworkPacket &operator>>(s16 &dst);
|
||||
NetworkPacket &operator<<(s16 src);
|
||||
|
||||
NetworkPacket &operator>>(s32 &dst);
|
||||
NetworkPacket &operator<<(s32 src);
|
||||
|
||||
NetworkPacket &operator>>(v2s32 &dst);
|
||||
NetworkPacket &operator<<(v2s32 src);
|
||||
|
||||
NetworkPacket &operator>>(v3s16 &dst);
|
||||
NetworkPacket &operator<<(v3s16 src);
|
||||
|
||||
NetworkPacket &operator>>(v3s32 &dst);
|
||||
NetworkPacket &operator<<(v3s32 src);
|
||||
|
||||
NetworkPacket &operator>>(video::SColor &dst);
|
||||
NetworkPacket &operator<<(video::SColor src);
|
||||
|
||||
// Temp, we remove SharedBuffer when migration finished
|
||||
// ^ this comment has been here for 4 years
|
||||
Buffer<u8> oldForgePacket();
|
||||
|
||||
private:
|
||||
void checkReadOffset(u32 from_offset, u32 field_size);
|
||||
|
||||
inline void checkDataSize(u32 field_size)
|
||||
{
|
||||
if (m_read_offset + field_size > m_datasize) {
|
||||
m_datasize = m_read_offset + field_size;
|
||||
m_data.resize(m_datasize);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<u8> m_data;
|
||||
u32 m_datasize = 0;
|
||||
u32 m_read_offset = 0;
|
||||
u16 m_command = 0;
|
||||
session_t m_peer_id = 0;
|
||||
};
|
||||
1141
src/network/networkprotocol.h
Normal file
1141
src/network/networkprotocol.h
Normal file
File diff suppressed because it is too large
Load Diff
77
src/network/peerhandler.h
Normal file
77
src/network/peerhandler.h
Normal file
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
Minetest
|
||||
Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2.1 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "networkprotocol.h"
|
||||
|
||||
namespace con
|
||||
{
|
||||
|
||||
typedef enum
|
||||
{
|
||||
MIN_RTT,
|
||||
MAX_RTT,
|
||||
AVG_RTT,
|
||||
MIN_JITTER,
|
||||
MAX_JITTER,
|
||||
AVG_JITTER
|
||||
} rtt_stat_type;
|
||||
|
||||
class Peer;
|
||||
|
||||
class PeerHandler
|
||||
{
|
||||
public:
|
||||
PeerHandler() = default;
|
||||
|
||||
virtual ~PeerHandler() = default;
|
||||
|
||||
/*
|
||||
This is called after the Peer has been inserted into the
|
||||
Connection's peer container.
|
||||
*/
|
||||
virtual void peerAdded(Peer *peer) = 0;
|
||||
|
||||
/*
|
||||
This is called before the Peer has been removed from the
|
||||
Connection's peer container.
|
||||
*/
|
||||
virtual void deletingPeer(Peer *peer, bool timeout) = 0;
|
||||
};
|
||||
|
||||
enum PeerChangeType : u8
|
||||
{
|
||||
PEER_ADDED,
|
||||
PEER_REMOVED
|
||||
};
|
||||
|
||||
struct PeerChange
|
||||
{
|
||||
PeerChange(PeerChangeType t, session_t _peer_id, bool _timeout) :
|
||||
type(t), peer_id(_peer_id), timeout(_timeout)
|
||||
{
|
||||
}
|
||||
PeerChange() = delete;
|
||||
|
||||
PeerChangeType type;
|
||||
session_t peer_id;
|
||||
bool timeout;
|
||||
};
|
||||
}
|
||||
225
src/network/serveropcodes.cpp
Normal file
225
src/network/serveropcodes.cpp
Normal file
@@ -0,0 +1,225 @@
|
||||
/*
|
||||
Minetest
|
||||
Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
|
||||
Copyright (C) 2015 nerzhul, Loic Blot <loic.blot@unix-experience.fr>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2.1 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "serveropcodes.h"
|
||||
|
||||
const static ToServerCommandHandler null_command_handler = { "TOSERVER_NULL", TOSERVER_STATE_ALL, &Server::handleCommand_Null };
|
||||
|
||||
const ToServerCommandHandler toServerCommandTable[TOSERVER_NUM_MSG_TYPES] =
|
||||
{
|
||||
null_command_handler, // 0x00 (never use this)
|
||||
null_command_handler, // 0x01
|
||||
{ "TOSERVER_INIT", TOSERVER_STATE_NOT_CONNECTED, &Server::handleCommand_Init }, // 0x02
|
||||
null_command_handler, // 0x03
|
||||
null_command_handler, // 0x04
|
||||
null_command_handler, // 0x05
|
||||
null_command_handler, // 0x06
|
||||
null_command_handler, // 0x07
|
||||
null_command_handler, // 0x08
|
||||
null_command_handler, // 0x09
|
||||
null_command_handler, // 0x0a
|
||||
null_command_handler, // 0x0b
|
||||
null_command_handler, // 0x0c
|
||||
null_command_handler, // 0x0d
|
||||
null_command_handler, // 0x0e
|
||||
null_command_handler, // 0x0f
|
||||
null_command_handler, // 0x10
|
||||
{ "TOSERVER_INIT2", TOSERVER_STATE_NOT_CONNECTED, &Server::handleCommand_Init2 }, // 0x11
|
||||
null_command_handler, // 0x12
|
||||
null_command_handler, // 0x13
|
||||
null_command_handler, // 0x14
|
||||
null_command_handler, // 0x15
|
||||
null_command_handler, // 0x16
|
||||
{ "TOSERVER_MODCHANNEL_JOIN", TOSERVER_STATE_INGAME, &Server::handleCommand_ModChannelJoin }, // 0x17
|
||||
{ "TOSERVER_MODCHANNEL_LEAVE", TOSERVER_STATE_INGAME, &Server::handleCommand_ModChannelLeave }, // 0x18
|
||||
{ "TOSERVER_MODCHANNEL_MSG", TOSERVER_STATE_INGAME, &Server::handleCommand_ModChannelMsg }, // 0x19
|
||||
null_command_handler, // 0x1a
|
||||
null_command_handler, // 0x1b
|
||||
null_command_handler, // 0x1c
|
||||
null_command_handler, // 0x1d
|
||||
null_command_handler, // 0x1e
|
||||
null_command_handler, // 0x1f
|
||||
null_command_handler, // 0x20
|
||||
null_command_handler, // 0x21
|
||||
null_command_handler, // 0x22
|
||||
{ "TOSERVER_PLAYERPOS", TOSERVER_STATE_INGAME, &Server::handleCommand_PlayerPos }, // 0x23
|
||||
{ "TOSERVER_GOTBLOCKS", TOSERVER_STATE_STARTUP, &Server::handleCommand_GotBlocks }, // 0x24
|
||||
{ "TOSERVER_DELETEDBLOCKS", TOSERVER_STATE_INGAME, &Server::handleCommand_DeletedBlocks }, // 0x25
|
||||
null_command_handler, // 0x26
|
||||
null_command_handler, // 0x27
|
||||
null_command_handler, // 0x28
|
||||
null_command_handler, // 0x29
|
||||
null_command_handler, // 0x2a
|
||||
null_command_handler, // 0x2b
|
||||
null_command_handler, // 0x2c
|
||||
null_command_handler, // 0x2d
|
||||
null_command_handler, // 0x2e
|
||||
null_command_handler, // 0x2f
|
||||
null_command_handler, // 0x30
|
||||
{ "TOSERVER_INVENTORY_ACTION", TOSERVER_STATE_INGAME, &Server::handleCommand_InventoryAction }, // 0x31
|
||||
{ "TOSERVER_CHAT_MESSAGE", TOSERVER_STATE_INGAME, &Server::handleCommand_ChatMessage }, // 0x32
|
||||
null_command_handler, // 0x33
|
||||
null_command_handler, // 0x34
|
||||
{ "TOSERVER_DAMAGE", TOSERVER_STATE_INGAME, &Server::handleCommand_Damage }, // 0x35
|
||||
null_command_handler, // 0x36
|
||||
{ "TOSERVER_PLAYERITEM", TOSERVER_STATE_INGAME, &Server::handleCommand_PlayerItem }, // 0x37
|
||||
{ "TOSERVER_RESPAWN", TOSERVER_STATE_INGAME, &Server::handleCommand_Respawn }, // 0x38
|
||||
{ "TOSERVER_INTERACT", TOSERVER_STATE_INGAME, &Server::handleCommand_Interact }, // 0x39
|
||||
{ "TOSERVER_REMOVED_SOUNDS", TOSERVER_STATE_INGAME, &Server::handleCommand_RemovedSounds }, // 0x3a
|
||||
{ "TOSERVER_NODEMETA_FIELDS", TOSERVER_STATE_INGAME, &Server::handleCommand_NodeMetaFields }, // 0x3b
|
||||
{ "TOSERVER_INVENTORY_FIELDS", TOSERVER_STATE_INGAME, &Server::handleCommand_InventoryFields }, // 0x3c
|
||||
null_command_handler, // 0x3d
|
||||
null_command_handler, // 0x3e
|
||||
null_command_handler, // 0x3f
|
||||
{ "TOSERVER_REQUEST_MEDIA", TOSERVER_STATE_STARTUP, &Server::handleCommand_RequestMedia }, // 0x40
|
||||
{ "TOSERVER_HAVE_MEDIA", TOSERVER_STATE_INGAME, &Server::handleCommand_HaveMedia }, // 0x41
|
||||
null_command_handler, // 0x42
|
||||
{ "TOSERVER_CLIENT_READY", TOSERVER_STATE_STARTUP, &Server::handleCommand_ClientReady }, // 0x43
|
||||
null_command_handler, // 0x44
|
||||
null_command_handler, // 0x45
|
||||
null_command_handler, // 0x46
|
||||
null_command_handler, // 0x47
|
||||
null_command_handler, // 0x48
|
||||
null_command_handler, // 0x49
|
||||
null_command_handler, // 0x4a
|
||||
null_command_handler, // 0x4b
|
||||
null_command_handler, // 0x4c
|
||||
null_command_handler, // 0x4d
|
||||
null_command_handler, // 0x4e
|
||||
null_command_handler, // 0x4f
|
||||
{ "TOSERVER_FIRST_SRP", TOSERVER_STATE_NOT_CONNECTED, &Server::handleCommand_FirstSrp }, // 0x50
|
||||
{ "TOSERVER_SRP_BYTES_A", TOSERVER_STATE_NOT_CONNECTED, &Server::handleCommand_SrpBytesA }, // 0x51
|
||||
{ "TOSERVER_SRP_BYTES_M", TOSERVER_STATE_NOT_CONNECTED, &Server::handleCommand_SrpBytesM }, // 0x52
|
||||
};
|
||||
|
||||
const static ClientCommandFactory null_command_factory = { "TOCLIENT_NULL", 0, false };
|
||||
|
||||
/*
|
||||
Channels used for Server -> Client communication
|
||||
2: Bulk data (mapblocks, media, ...)
|
||||
1: HUD packets
|
||||
0: everything else
|
||||
|
||||
Packet order is only guaranteed inside a channel, so packets that operate on
|
||||
the same objects are *required* to be in the same channel.
|
||||
*/
|
||||
|
||||
const ClientCommandFactory clientCommandFactoryTable[TOCLIENT_NUM_MSG_TYPES] =
|
||||
{
|
||||
null_command_factory, // 0x00
|
||||
null_command_factory, // 0x01
|
||||
{ "TOCLIENT_HELLO", 0, true }, // 0x02
|
||||
{ "TOCLIENT_AUTH_ACCEPT", 0, true }, // 0x03
|
||||
{ "TOCLIENT_ACCEPT_SUDO_MODE", 0, true }, // 0x04
|
||||
{ "TOCLIENT_DENY_SUDO_MODE", 0, true }, // 0x05
|
||||
null_command_factory, // 0x06
|
||||
null_command_factory, // 0x07
|
||||
null_command_factory, // 0x08
|
||||
null_command_factory, // 0x09
|
||||
{ "TOCLIENT_ACCESS_DENIED", 0, true }, // 0x0A
|
||||
null_command_factory, // 0x0B
|
||||
null_command_factory, // 0x0C
|
||||
null_command_factory, // 0x0D
|
||||
null_command_factory, // 0x0E
|
||||
null_command_factory, // 0x0F
|
||||
{ "TOCLIENT_INIT", 0, true }, // 0x10
|
||||
null_command_factory, // 0x11
|
||||
null_command_factory, // 0x12
|
||||
null_command_factory, // 0x13
|
||||
null_command_factory, // 0x14
|
||||
null_command_factory, // 0x15
|
||||
null_command_factory, // 0x16
|
||||
null_command_factory, // 0x17
|
||||
null_command_factory, // 0x18
|
||||
null_command_factory, // 0x19
|
||||
null_command_factory, // 0x1A
|
||||
null_command_factory, // 0x1B
|
||||
null_command_factory, // 0x1C
|
||||
null_command_factory, // 0x1D
|
||||
null_command_factory, // 0x1E
|
||||
null_command_factory, // 0x1F
|
||||
{ "TOCLIENT_BLOCKDATA", 2, true }, // 0x20
|
||||
{ "TOCLIENT_ADDNODE", 0, true }, // 0x21
|
||||
{ "TOCLIENT_REMOVENODE", 0, true }, // 0x22
|
||||
null_command_factory, // 0x23
|
||||
null_command_factory, // 0x24
|
||||
null_command_factory, // 0x25
|
||||
null_command_factory, // 0x26
|
||||
{ "TOCLIENT_INVENTORY", 0, true }, // 0x27
|
||||
null_command_factory, // 0x28
|
||||
{ "TOCLIENT_TIME_OF_DAY", 0, true }, // 0x29
|
||||
{ "TOCLIENT_CSM_RESTRICTION_FLAGS", 0, true }, // 0x2A
|
||||
{ "TOCLIENT_PLAYER_SPEED", 0, true }, // 0x2B
|
||||
{ "TOCLIENT_MEDIA_PUSH", 0, true }, // 0x2C (sent over channel 1 too if legacy)
|
||||
null_command_factory, // 0x2D
|
||||
null_command_factory, // 0x2E
|
||||
{ "TOCLIENT_CHAT_MESSAGE", 0, true }, // 0x2F
|
||||
null_command_factory, // 0x30
|
||||
{ "TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD", 0, true }, // 0x31
|
||||
{ "TOCLIENT_ACTIVE_OBJECT_MESSAGES", 0, true }, // 0x32 (may be sent as unrel over channel 1 too)
|
||||
{ "TOCLIENT_HP", 0, true }, // 0x33
|
||||
{ "TOCLIENT_MOVE_PLAYER", 0, true }, // 0x34
|
||||
null_command_factory, // 0x35
|
||||
{ "TOCLIENT_FOV", 0, true }, // 0x36
|
||||
{ "TOCLIENT_DEATHSCREEN", 0, true }, // 0x37
|
||||
{ "TOCLIENT_MEDIA", 2, true }, // 0x38
|
||||
null_command_factory, // 0x39
|
||||
{ "TOCLIENT_NODEDEF", 0, true }, // 0x3A
|
||||
null_command_factory, // 0x3B
|
||||
{ "TOCLIENT_ANNOUNCE_MEDIA", 0, true }, // 0x3C
|
||||
{ "TOCLIENT_ITEMDEF", 0, true }, // 0x3D
|
||||
null_command_factory, // 0x3E
|
||||
{ "TOCLIENT_PLAY_SOUND", 0, true }, // 0x3f (may be sent as unrel too)
|
||||
{ "TOCLIENT_STOP_SOUND", 0, true }, // 0x40
|
||||
{ "TOCLIENT_PRIVILEGES", 0, true }, // 0x41
|
||||
{ "TOCLIENT_INVENTORY_FORMSPEC", 0, true }, // 0x42
|
||||
{ "TOCLIENT_DETACHED_INVENTORY", 0, true }, // 0x43
|
||||
{ "TOCLIENT_SHOW_FORMSPEC", 0, true }, // 0x44
|
||||
{ "TOCLIENT_MOVEMENT", 0, true }, // 0x45
|
||||
{ "TOCLIENT_SPAWN_PARTICLE", 0, true }, // 0x46
|
||||
{ "TOCLIENT_ADD_PARTICLESPAWNER", 0, true }, // 0x47
|
||||
null_command_factory, // 0x48
|
||||
{ "TOCLIENT_HUDADD", 1, true }, // 0x49
|
||||
{ "TOCLIENT_HUDRM", 1, true }, // 0x4a
|
||||
{ "TOCLIENT_HUDCHANGE", 1, true }, // 0x4b
|
||||
{ "TOCLIENT_HUD_SET_FLAGS", 1, true }, // 0x4c
|
||||
{ "TOCLIENT_HUD_SET_PARAM", 1, true }, // 0x4d
|
||||
{ "TOCLIENT_BREATH", 0, true }, // 0x4e
|
||||
{ "TOCLIENT_SET_SKY", 0, true }, // 0x4f
|
||||
{ "TOCLIENT_OVERRIDE_DAY_NIGHT_RATIO", 0, true }, // 0x50
|
||||
{ "TOCLIENT_LOCAL_PLAYER_ANIMATIONS", 0, true }, // 0x51
|
||||
{ "TOCLIENT_EYE_OFFSET", 0, true }, // 0x52
|
||||
{ "TOCLIENT_DELETE_PARTICLESPAWNER", 0, true }, // 0x53
|
||||
{ "TOCLIENT_CLOUD_PARAMS", 0, true }, // 0x54
|
||||
{ "TOCLIENT_FADE_SOUND", 0, true }, // 0x55
|
||||
{ "TOCLIENT_UPDATE_PLAYER_LIST", 0, true }, // 0x56
|
||||
{ "TOCLIENT_MODCHANNEL_MSG", 0, true }, // 0x57
|
||||
{ "TOCLIENT_MODCHANNEL_SIGNAL", 0, true }, // 0x58
|
||||
{ "TOCLIENT_NODEMETA_CHANGED", 0, true }, // 0x59
|
||||
{ "TOCLIENT_SET_SUN", 0, true }, // 0x5a
|
||||
{ "TOCLIENT_SET_MOON", 0, true }, // 0x5b
|
||||
{ "TOCLIENT_SET_STARS", 0, true }, // 0x5c
|
||||
null_command_factory, // 0x5d
|
||||
null_command_factory, // 0x5e
|
||||
null_command_factory, // 0x5f
|
||||
{ "TOSERVER_SRP_BYTES_S_B", 0, true }, // 0x60
|
||||
{ "TOCLIENT_FORMSPEC_PREPEND", 0, true }, // 0x61
|
||||
{ "TOCLIENT_MINIMAP_MODES", 0, true }, // 0x62
|
||||
};
|
||||
50
src/network/serveropcodes.h
Normal file
50
src/network/serveropcodes.h
Normal file
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
Minetest
|
||||
Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
|
||||
Copyright (C) 2015 nerzhul, Loic Blot <loic.blot@unix-experience.fr>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2.1 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "server.h"
|
||||
#include "networkprotocol.h"
|
||||
|
||||
class NetworkPacket;
|
||||
|
||||
enum ToServerConnectionState {
|
||||
TOSERVER_STATE_NOT_CONNECTED,
|
||||
TOSERVER_STATE_STARTUP,
|
||||
TOSERVER_STATE_INGAME,
|
||||
TOSERVER_STATE_ALL,
|
||||
};
|
||||
struct ToServerCommandHandler
|
||||
{
|
||||
const std::string name;
|
||||
ToServerConnectionState state;
|
||||
void (Server::*handler)(NetworkPacket* pkt);
|
||||
};
|
||||
|
||||
struct ClientCommandFactory
|
||||
{
|
||||
const char* name;
|
||||
u8 channel;
|
||||
bool reliable;
|
||||
};
|
||||
|
||||
extern const ToServerCommandHandler toServerCommandTable[TOSERVER_NUM_MSG_TYPES];
|
||||
|
||||
extern const ClientCommandFactory clientCommandFactoryTable[TOCLIENT_NUM_MSG_TYPES];
|
||||
1843
src/network/serverpackethandler.cpp
Normal file
1843
src/network/serverpackethandler.cpp
Normal file
File diff suppressed because it is too large
Load Diff
372
src/network/socket.cpp
Normal file
372
src/network/socket.cpp
Normal file
@@ -0,0 +1,372 @@
|
||||
/*
|
||||
Minetest
|
||||
Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2.1 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "socket.h"
|
||||
|
||||
#include <cstdio>
|
||||
#include <iostream>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <iomanip>
|
||||
#include "util/string.h"
|
||||
#include "util/numeric.h"
|
||||
#include "constants.h"
|
||||
#include "debug.h"
|
||||
#include "log.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
// Without this some of the network functions are not found on mingw
|
||||
#ifndef _WIN32_WINNT
|
||||
#define _WIN32_WINNT 0x0501
|
||||
#endif
|
||||
#include <windows.h>
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
#define LAST_SOCKET_ERR() WSAGetLastError()
|
||||
#define SOCKET_ERR_STR(e) itos(e)
|
||||
typedef int socklen_t;
|
||||
#else
|
||||
#include <cerrno>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <fcntl.h>
|
||||
#include <netdb.h>
|
||||
#include <unistd.h>
|
||||
#include <arpa/inet.h>
|
||||
#define LAST_SOCKET_ERR() (errno)
|
||||
#define SOCKET_ERR_STR(e) strerror(e)
|
||||
#endif
|
||||
|
||||
// Set to true to enable verbose debug output
|
||||
bool socket_enable_debug_output = false; // yuck
|
||||
|
||||
static bool g_sockets_initialized = false;
|
||||
|
||||
// Initialize sockets
|
||||
void sockets_init()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
// Windows needs sockets to be initialized before use
|
||||
WSADATA WsaData;
|
||||
if (WSAStartup(MAKEWORD(2, 2), &WsaData) != NO_ERROR)
|
||||
throw SocketException("WSAStartup failed");
|
||||
#endif
|
||||
g_sockets_initialized = true;
|
||||
}
|
||||
|
||||
void sockets_cleanup()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
// On Windows, cleanup sockets after use
|
||||
WSACleanup();
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
UDPSocket
|
||||
*/
|
||||
|
||||
UDPSocket::UDPSocket(bool ipv6)
|
||||
{
|
||||
init(ipv6, false);
|
||||
}
|
||||
|
||||
bool UDPSocket::init(bool ipv6, bool noExceptions)
|
||||
{
|
||||
if (!g_sockets_initialized) {
|
||||
dstream << "Sockets not initialized" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Use IPv6 if specified
|
||||
m_addr_family = ipv6 ? AF_INET6 : AF_INET;
|
||||
m_handle = socket(m_addr_family, SOCK_DGRAM, IPPROTO_UDP);
|
||||
|
||||
if (socket_enable_debug_output) {
|
||||
dstream << "UDPSocket(" << (int)m_handle
|
||||
<< ")::UDPSocket(): ipv6 = " << (ipv6 ? "true" : "false")
|
||||
<< std::endl;
|
||||
}
|
||||
|
||||
if (m_handle <= 0) {
|
||||
if (noExceptions) {
|
||||
return false;
|
||||
}
|
||||
|
||||
throw SocketException(std::string("Failed to create socket: error ") +
|
||||
SOCKET_ERR_STR(LAST_SOCKET_ERR()));
|
||||
}
|
||||
|
||||
setTimeoutMs(0);
|
||||
|
||||
if (m_addr_family == AF_INET6) {
|
||||
// Allow our socket to accept both IPv4 and IPv6 connections
|
||||
// required on Windows:
|
||||
// https://msdn.microsoft.com/en-us/library/windows/desktop/bb513665(v=vs.85).aspx
|
||||
int value = 0;
|
||||
setsockopt(m_handle, IPPROTO_IPV6, IPV6_V6ONLY,
|
||||
reinterpret_cast<char *>(&value), sizeof(value));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
UDPSocket::~UDPSocket()
|
||||
{
|
||||
if (socket_enable_debug_output) {
|
||||
dstream << "UDPSocket( " << (int)m_handle << ")::~UDPSocket()"
|
||||
<< std::endl;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
closesocket(m_handle);
|
||||
#else
|
||||
close(m_handle);
|
||||
#endif
|
||||
}
|
||||
|
||||
void UDPSocket::Bind(Address addr)
|
||||
{
|
||||
if (socket_enable_debug_output) {
|
||||
dstream << "UDPSocket(" << (int)m_handle
|
||||
<< ")::Bind(): " << addr.serializeString() << ":"
|
||||
<< addr.getPort() << std::endl;
|
||||
}
|
||||
|
||||
if (addr.getFamily() != m_addr_family) {
|
||||
const char *errmsg =
|
||||
"Socket and bind address families do not match";
|
||||
errorstream << "Bind failed: " << errmsg << std::endl;
|
||||
throw SocketException(errmsg);
|
||||
}
|
||||
|
||||
int ret = 0;
|
||||
|
||||
if (m_addr_family == AF_INET6) {
|
||||
struct sockaddr_in6 address;
|
||||
memset(&address, 0, sizeof(address));
|
||||
|
||||
address.sin6_family = AF_INET6;
|
||||
address.sin6_addr = addr.getAddress6();
|
||||
address.sin6_port = htons(addr.getPort());
|
||||
|
||||
ret = bind(m_handle, (const struct sockaddr *) &address,
|
||||
sizeof(struct sockaddr_in6));
|
||||
} else {
|
||||
struct sockaddr_in address;
|
||||
memset(&address, 0, sizeof(address));
|
||||
|
||||
address.sin_family = AF_INET;
|
||||
address.sin_addr = addr.getAddress();
|
||||
address.sin_port = htons(addr.getPort());
|
||||
|
||||
ret = bind(m_handle, (const struct sockaddr *) &address,
|
||||
sizeof(struct sockaddr_in));
|
||||
}
|
||||
|
||||
if (ret < 0) {
|
||||
dstream << (int)m_handle << ": Bind failed: "
|
||||
<< SOCKET_ERR_STR(LAST_SOCKET_ERR()) << std::endl;
|
||||
throw SocketException("Failed to bind socket");
|
||||
}
|
||||
}
|
||||
|
||||
void UDPSocket::Send(const Address &destination, const void *data, int size)
|
||||
{
|
||||
bool dumping_packet = false; // for INTERNET_SIMULATOR
|
||||
|
||||
if (INTERNET_SIMULATOR)
|
||||
dumping_packet = myrand() % INTERNET_SIMULATOR_PACKET_LOSS == 0;
|
||||
|
||||
if (socket_enable_debug_output) {
|
||||
// Print packet destination and size
|
||||
dstream << (int)m_handle << " -> ";
|
||||
destination.print(dstream);
|
||||
dstream << ", size=" << size;
|
||||
|
||||
// Print packet contents
|
||||
dstream << ", data=";
|
||||
for (int i = 0; i < size && i < 20; i++) {
|
||||
if (i % 2 == 0)
|
||||
dstream << " ";
|
||||
unsigned int a = ((const unsigned char *)data)[i];
|
||||
dstream << std::hex << std::setw(2) << std::setfill('0') << a;
|
||||
}
|
||||
|
||||
if (size > 20)
|
||||
dstream << "...";
|
||||
|
||||
if (dumping_packet)
|
||||
dstream << " (DUMPED BY INTERNET_SIMULATOR)";
|
||||
|
||||
dstream << std::endl;
|
||||
}
|
||||
|
||||
if (dumping_packet) {
|
||||
// Lol let's forget it
|
||||
dstream << "UDPSocket::Send(): INTERNET_SIMULATOR: dumping packet."
|
||||
<< std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
if (destination.getFamily() != m_addr_family)
|
||||
throw SendFailedException("Address family mismatch");
|
||||
|
||||
int sent;
|
||||
if (m_addr_family == AF_INET6) {
|
||||
struct sockaddr_in6 address = {};
|
||||
address.sin6_family = AF_INET6;
|
||||
address.sin6_addr = destination.getAddress6();
|
||||
address.sin6_port = htons(destination.getPort());
|
||||
|
||||
sent = sendto(m_handle, (const char *)data, size, 0,
|
||||
(struct sockaddr *)&address, sizeof(struct sockaddr_in6));
|
||||
} else {
|
||||
struct sockaddr_in address = {};
|
||||
address.sin_family = AF_INET;
|
||||
address.sin_addr = destination.getAddress();
|
||||
address.sin_port = htons(destination.getPort());
|
||||
|
||||
sent = sendto(m_handle, (const char *)data, size, 0,
|
||||
(struct sockaddr *)&address, sizeof(struct sockaddr_in));
|
||||
}
|
||||
|
||||
if (sent != size)
|
||||
throw SendFailedException("Failed to send packet");
|
||||
}
|
||||
|
||||
int UDPSocket::Receive(Address &sender, void *data, int size)
|
||||
{
|
||||
// Return on timeout
|
||||
if (!WaitData(m_timeout_ms))
|
||||
return -1;
|
||||
|
||||
int received;
|
||||
if (m_addr_family == AF_INET6) {
|
||||
struct sockaddr_in6 address;
|
||||
memset(&address, 0, sizeof(address));
|
||||
socklen_t address_len = sizeof(address);
|
||||
|
||||
received = recvfrom(m_handle, (char *)data, size, 0,
|
||||
(struct sockaddr *)&address, &address_len);
|
||||
|
||||
if (received < 0)
|
||||
return -1;
|
||||
|
||||
u16 address_port = ntohs(address.sin6_port);
|
||||
const auto *bytes = reinterpret_cast<IPv6AddressBytes*>
|
||||
(address.sin6_addr.s6_addr);
|
||||
sender = Address(bytes, address_port);
|
||||
} else {
|
||||
struct sockaddr_in address;
|
||||
memset(&address, 0, sizeof(address));
|
||||
|
||||
socklen_t address_len = sizeof(address);
|
||||
|
||||
received = recvfrom(m_handle, (char *)data, size, 0,
|
||||
(struct sockaddr *)&address, &address_len);
|
||||
|
||||
if (received < 0)
|
||||
return -1;
|
||||
|
||||
u32 address_ip = ntohl(address.sin_addr.s_addr);
|
||||
u16 address_port = ntohs(address.sin_port);
|
||||
|
||||
sender = Address(address_ip, address_port);
|
||||
}
|
||||
|
||||
if (socket_enable_debug_output) {
|
||||
// Print packet sender and size
|
||||
dstream << (int)m_handle << " <- ";
|
||||
sender.print(dstream);
|
||||
dstream << ", size=" << received;
|
||||
|
||||
// Print packet contents
|
||||
dstream << ", data=";
|
||||
for (int i = 0; i < received && i < 20; i++) {
|
||||
if (i % 2 == 0)
|
||||
dstream << " ";
|
||||
unsigned int a = ((const unsigned char *)data)[i];
|
||||
dstream << std::hex << std::setw(2) << std::setfill('0') << a;
|
||||
}
|
||||
if (received > 20)
|
||||
dstream << "...";
|
||||
|
||||
dstream << std::endl;
|
||||
}
|
||||
|
||||
return received;
|
||||
}
|
||||
|
||||
int UDPSocket::GetHandle()
|
||||
{
|
||||
return m_handle;
|
||||
}
|
||||
|
||||
void UDPSocket::setTimeoutMs(int timeout_ms)
|
||||
{
|
||||
m_timeout_ms = timeout_ms;
|
||||
}
|
||||
|
||||
bool UDPSocket::WaitData(int timeout_ms)
|
||||
{
|
||||
fd_set readset;
|
||||
int result;
|
||||
|
||||
// Initialize the set
|
||||
FD_ZERO(&readset);
|
||||
FD_SET(m_handle, &readset);
|
||||
|
||||
// Initialize time out struct
|
||||
struct timeval tv;
|
||||
tv.tv_sec = 0;
|
||||
tv.tv_usec = timeout_ms * 1000;
|
||||
|
||||
// select()
|
||||
result = select(m_handle + 1, &readset, NULL, NULL, &tv);
|
||||
|
||||
if (result == 0)
|
||||
return false;
|
||||
|
||||
int e = LAST_SOCKET_ERR();
|
||||
#ifdef _WIN32
|
||||
if (result < 0 && (e == WSAEINTR || e == WSAEBADF)) {
|
||||
#else
|
||||
if (result < 0 && (e == EINTR || e == EBADF)) {
|
||||
#endif
|
||||
// N.B. select() fails when sockets are destroyed on Connection's dtor
|
||||
// with EBADF. Instead of doing tricky synchronization, allow this
|
||||
// thread to exit but don't throw an exception.
|
||||
return false;
|
||||
}
|
||||
|
||||
if (result < 0) {
|
||||
dstream << (int)m_handle << ": Select failed: " << SOCKET_ERR_STR(e)
|
||||
<< std::endl;
|
||||
|
||||
throw SocketException("Select failed");
|
||||
} else if (!FD_ISSET(m_handle, &readset)) {
|
||||
// No data
|
||||
return false;
|
||||
}
|
||||
|
||||
// There is data
|
||||
return true;
|
||||
}
|
||||
56
src/network/socket.h
Normal file
56
src/network/socket.h
Normal file
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
Minetest
|
||||
Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2.1 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ostream>
|
||||
#include <cstring>
|
||||
#include "address.h"
|
||||
#include "irrlichttypes.h"
|
||||
#include "networkexceptions.h"
|
||||
|
||||
extern bool socket_enable_debug_output;
|
||||
|
||||
void sockets_init();
|
||||
void sockets_cleanup();
|
||||
|
||||
class UDPSocket
|
||||
{
|
||||
public:
|
||||
UDPSocket() = default;
|
||||
|
||||
UDPSocket(bool ipv6);
|
||||
~UDPSocket();
|
||||
void Bind(Address addr);
|
||||
|
||||
bool init(bool ipv6, bool noExceptions = false);
|
||||
|
||||
void Send(const Address &destination, const void *data, int size);
|
||||
// Returns -1 if there is no data
|
||||
int Receive(Address &sender, void *data, int size);
|
||||
int GetHandle(); // For debugging purposes only
|
||||
void setTimeoutMs(int timeout_ms);
|
||||
// Returns true if there is data, false if timeout occurred
|
||||
bool WaitData(int timeout_ms);
|
||||
|
||||
private:
|
||||
int m_handle;
|
||||
int m_timeout_ms;
|
||||
int m_addr_family;
|
||||
};
|
||||
Reference in New Issue
Block a user