1
0
Fork 0
mirror of https://github.com/luanti-org/luanti.git synced 2025-07-27 17:28:41 +00:00

Merge remote-tracking branch 'upstream/master' into Visuals-Vol-2

This commit is contained in:
Gefüllte Taubenbrust 2025-04-13 11:33:37 +02:00
commit fa212d19f7
572 changed files with 71629 additions and 67352 deletions

View file

@ -83,7 +83,7 @@ const ToClientCommandHandler toClientCommandTable[TOCLIENT_NUM_MSG_TYPES] =
{ "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_CAMERA", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_Camera }, // 0x48
{ "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

View file

@ -132,17 +132,10 @@ void Client::handleCommand_AuthAccept(NetworkPacket* pkt)
{
deleteAuthData();
v3f playerpos;
*pkt >> playerpos >> m_map_seed >> m_recommended_send_interval
v3f unused;
*pkt >> unused >> m_map_seed >> m_recommended_send_interval
>> m_sudo_auth_methods;
playerpos -= v3f(0, BS / 2, 0);
// Set player position
LocalPlayer *player = m_env.getLocalPlayer();
assert(player != NULL);
player->setPosition(playerpos);
infostream << "Client: received map seed: " << m_map_seed << std::endl;
infostream << "Client: received recommended send interval "
<< m_recommended_send_interval<<std::endl;
@ -176,6 +169,7 @@ void Client::handleCommand_AcceptSudoMode(NetworkPacket* pkt)
// reset again
m_chosen_auth_mech = AUTH_MECHANISM_NONE;
}
void Client::handleCommand_DenySudoMode(NetworkPacket* pkt)
{
ChatMessage *chatMessage = new ChatMessage(CHATMESSAGE_TYPE_SYSTEM,
@ -193,8 +187,8 @@ void Client::handleCommand_AccessDenied(NetworkPacket* pkt)
m_access_denied = true;
if (pkt->getCommand() != TOCLIENT_ACCESS_DENIED) {
// Legacy code from 0.4.12 and older but is still used
// in some places of the server code
// Servers older than 5.6 still send TOCLIENT_ACCESS_DENIED_LEGACY sometimes.
// see commit a65f6f07f3a5601207b790edcc8cc945133112f7
if (pkt->getSize() >= 2) {
std::wstring wide_reason;
*pkt >> wide_reason;
@ -231,9 +225,6 @@ void Client::handleCommand_AccessDenied(NetworkPacket* pkt)
void Client::handleCommand_RemoveNode(NetworkPacket* pkt)
{
if (pkt->getSize() < 6)
return;
v3s16 p;
*pkt >> p;
removeNode(p);
@ -241,22 +232,19 @@ void Client::handleCommand_RemoveNode(NetworkPacket* pkt)
void Client::handleCommand_AddNode(NetworkPacket* pkt)
{
if (pkt->getSize() < 6 + MapNode::serializedLength(m_server_ser_ver))
return;
v3s16 p;
*pkt >> p;
auto *ptr = reinterpret_cast<const u8*>(pkt->getRemainingString());
pkt->skip(MapNode::serializedLength(m_server_ser_ver)); // performs length check
MapNode n;
n.deSerialize(pkt->getU8Ptr(6), m_server_ser_ver);
n.deSerialize(ptr, m_server_ser_ver);
bool remove_metadata = true;
u32 index = 6 + MapNode::serializedLength(m_server_ser_ver);
if ((pkt->getSize() >= index + 1) && pkt->getU8(index)) {
remove_metadata = false;
}
bool keep_metadata;
*pkt >> keep_metadata;
addNode(p, n, remove_metadata);
addNode(p, n, !keep_metadata);
}
void Client::handleCommand_NodemetaChanged(NetworkPacket *pkt)
@ -272,7 +260,7 @@ void Client::handleCommand_NodemetaChanged(NetworkPacket *pkt)
meta_updates_list.deSerialize(sstr, m_itemdef, true);
Map &map = m_env.getMap();
for (NodeMetadataMap::const_iterator i = meta_updates_list.begin();
for (auto i = meta_updates_list.begin();
i != meta_updates_list.end(); ++i) {
v3s16 pos = i->first;
@ -294,7 +282,7 @@ void Client::handleCommand_BlockData(NetworkPacket* pkt)
v3s16 p;
*pkt >> p;
std::string datastring(pkt->getString(6), pkt->getSize() - 6);
std::string datastring(pkt->getRemainingString(), pkt->getRemainingBytes());
std::istringstream istr(datastring, std::ios_base::binary);
MapSector *sector;
@ -358,46 +346,15 @@ void Client::handleCommand_TimeOfDay(NetworkPacket* pkt)
return;
u16 time_of_day;
*pkt >> time_of_day;
time_of_day = time_of_day % 24000;
float time_speed = 0;
if (pkt->getSize() >= 2 + 4) {
*pkt >> time_speed;
}
else {
// Old message; try to approximate speed of time by ourselves
float time_of_day_f = (float)time_of_day / 24000.0f;
float tod_diff_f = 0;
if (time_of_day_f < 0.2 && m_last_time_of_day_f > 0.8)
tod_diff_f = time_of_day_f - m_last_time_of_day_f + 1.0f;
else
tod_diff_f = time_of_day_f - m_last_time_of_day_f;
m_last_time_of_day_f = time_of_day_f;
float time_diff = m_time_of_day_update_timer;
m_time_of_day_update_timer = 0;
if (m_time_of_day_set) {
time_speed = (3600.0f * 24.0f) * tod_diff_f / time_diff;
infostream << "Client: Measured time_of_day speed (old format): "
<< time_speed << " tod_diff_f=" << tod_diff_f
<< " time_diff=" << time_diff << std::endl;
}
}
float time_speed;
*pkt >> time_speed;
// Update environment
m_env.setTimeOfDay(time_of_day);
m_env.setTimeOfDaySpeed(time_speed);
m_time_of_day_set = true;
//u32 dr = m_env.getDayNightRatio();
//infostream << "Client: time_of_day=" << time_of_day
// << " time_speed=" << time_speed
// << " dr=" << dr << std::endl;
}
void Client::handleCommand_ChatMessage(NetworkPacket *pkt)
@ -611,7 +568,7 @@ void Client::handleCommand_MovePlayer(NetworkPacket* pkt)
player->setPosition(pos);
infostream << "Client got TOCLIENT_MOVE_PLAYER"
<< " pos=(" << pos.X << "," << pos.Y << "," << pos.Z << ")"
<< " pos=" << pos
<< " pitch=" << pitch
<< " yaw=" << yaw
<< std::endl;
@ -649,10 +606,6 @@ void Client::handleCommand_DeathScreenLegacy(NetworkPacket* pkt)
void Client::handleCommand_AnnounceMedia(NetworkPacket* pkt)
{
u16 num_files;
*pkt >> num_files;
infostream << "Client: Received media announcement: packet size: "
<< pkt->getSize() << std::endl;
@ -662,9 +615,7 @@ void Client::handleCommand_AnnounceMedia(NetworkPacket* pkt)
"we already saw another announcement" :
"all media has been received already";
errorstream << "Client: Received media announcement but "
<< problem << "! "
<< " files=" << num_files
<< " size=" << pkt->getSize() << std::endl;
<< problem << "!" << std::endl;
return;
}
@ -672,16 +623,36 @@ void Client::handleCommand_AnnounceMedia(NetworkPacket* pkt)
// updating content definitions
sanity_check(!m_mesh_update_manager->isRunning());
for (u16 i = 0; i < num_files; i++) {
if (m_proto_ver >= 48) {
// compressed table of media names
std::vector<std::string> names;
{
std::istringstream iss(pkt->readLongString(), std::ios::binary);
std::stringstream ss(std::ios::in | std::ios::out | std::ios::binary);
decompressZstd(iss, ss);
names = deserializeString16Array(ss);
}
// raw hash for each media file
for (auto &name : names) {
auto sha1_raw = pkt->readRawString(20);
m_media_downloader->addFile(name, sha1_raw);
}
} else {
u16 num_files;
*pkt >> num_files;
std::string name, sha1_base64;
for (u16 i = 0; i < num_files; i++) {
*pkt >> name >> sha1_base64;
*pkt >> name >> sha1_base64;
std::string sha1_raw = base64_decode(sha1_base64);
m_media_downloader->addFile(name, sha1_raw);
std::string sha1_raw = base64_decode(sha1_base64);
m_media_downloader->addFile(name, sha1_raw);
}
}
{
// Remote media servers
std::string str;
*pkt >> str;
@ -700,18 +671,6 @@ void Client::handleCommand_AnnounceMedia(NetworkPacket* pkt)
void Client::handleCommand_Media(NetworkPacket* pkt)
{
/*
u16 command
u16 total number of file bunches
u16 index of this bunch
u32 number of files in this bunch
for each file {
u16 length of name
string name
u32 length of data
data
}
*/
u16 num_bunches;
u16 bunch_i;
u32 num_files;
@ -738,6 +697,12 @@ void Client::handleCommand_Media(NetworkPacket* pkt)
*pkt >> name;
data = pkt->readLongString();
if (m_proto_ver >= 48) {
std::istringstream iss(data, std::ios::binary);
std::ostringstream oss(std::ios::binary);
decompressZstd(iss, oss);
data = oss.str();
}
bool ok = false;
if (init_phase) {
@ -772,7 +737,10 @@ void Client::handleCommand_NodeDef(NetworkPacket* pkt)
// Decompress node definitions
std::istringstream tmp_is(pkt->readLongString(), std::ios::binary);
std::stringstream tmp_os(std::ios::binary | std::ios::in | std::ios::out);
decompressZlib(tmp_is, tmp_os);
if (m_proto_ver >= 48)
decompressZstd(tmp_is, tmp_os);
else
decompressZlib(tmp_is, tmp_os);
// Deserialize node definitions
m_nodedef->deSerialize(tmp_os, m_proto_ver);
@ -791,7 +759,10 @@ void Client::handleCommand_ItemDef(NetworkPacket* pkt)
// Decompress item definitions
std::istringstream tmp_is(pkt->readLongString(), std::ios::binary);
std::stringstream tmp_os(std::ios::binary | std::ios::in | std::ios::out);
decompressZlib(tmp_is, tmp_os);
if (m_proto_ver >= 48)
decompressZstd(tmp_is, tmp_os);
else
decompressZlib(tmp_is, tmp_os);
// Deserialize node definitions
m_itemdef->deSerialize(tmp_os, m_proto_ver);
@ -851,6 +822,8 @@ void Client::handleCommand_PlaySound(NetworkPacket* pkt)
pos = cao->getPosition() * (1.0f/BS);
vel = cao->getVelocity() * (1.0f/BS);
}
// Note that the server sends 'pos' correctly even for attached sounds,
// so this fallback path is not a mistake.
m_sound->playSoundAt(client_id, spec, pos, vel);
break;
}
@ -883,7 +856,7 @@ void Client::handleCommand_StopSound(NetworkPacket* pkt)
*pkt >> server_id;
std::unordered_map<s32, int>::iterator i = m_sounds_server_to_client.find(server_id);
auto i = m_sounds_server_to_client.find(server_id);
if (i != m_sounds_server_to_client.end()) {
int client_id = i->second;
m_sound->stopSound(client_id);
@ -898,9 +871,7 @@ void Client::handleCommand_FadeSound(NetworkPacket *pkt)
*pkt >> sound_id >> step >> gain;
std::unordered_map<s32, int>::const_iterator i =
m_sounds_server_to_client.find(sound_id);
auto i = m_sounds_server_to_client.find(sound_id);
if (i != m_sounds_server_to_client.end())
m_sound->fadeSound(i->second, step, gain);
}
@ -958,8 +929,8 @@ void Client::handleCommand_DetachedInventory(NetworkPacket* pkt)
inv = inv_it->second;
}
u16 ignore;
*pkt >> ignore; // this used to be the length of the following string, ignore it
// this used to be the length of the following string, ignore it
pkt->skip(2);
std::string contents(pkt->getRemainingString(), pkt->getRemainingBytes());
std::istringstream is(contents, std::ios::binary);
@ -1009,7 +980,7 @@ void Client::handleCommand_AddParticleSpawner(NetworkPacket* pkt)
p.amount = readU16(is);
p.time = readF32(is);
if (p.time < 0)
throw SerializationError("particle spawner time < 0");
throw PacketError("particle spawner time < 0");
bool missing_end_values = false;
if (m_proto_ver >= 42) {
@ -1324,10 +1295,7 @@ void Client::handleCommand_HudSetSky(NetworkPacket* pkt)
for (size_t i = 0; i < count; i++)
skybox.textures.emplace_back(deSerializeString16(is));
skybox.clouds = true;
try {
skybox.clouds = readU8(is);
} catch (...) {}
skybox.clouds = readU8(is) != 0;
// Use default skybox settings:
SunParams sun = SkyboxDefaults::getSunDefaults();
@ -1530,7 +1498,19 @@ void Client::handleCommand_EyeOffset(NetworkPacket* pkt)
*pkt >> player->eye_offset_third_front;
} catch (PacketError &e) {
player->eye_offset_third_front = player->eye_offset_third;
};
}
}
void Client::handleCommand_Camera(NetworkPacket* pkt)
{
LocalPlayer *player = m_env.getLocalPlayer();
assert(player);
u8 tmp;
*pkt >> tmp;
player->allowed_camera_mode = static_cast<CameraMode>(tmp);
m_client_event_queue.push(new ClientEvent(CE_UPDATE_CAMERA));
}
void Client::handleCommand_UpdatePlayerList(NetworkPacket* pkt)

View file

@ -53,6 +53,15 @@ u16 BufferedPacket::getSeqnum() const
return readU16(&data[BASE_HEADER_SIZE + 1]);
}
void BufferedPacket::setSenderPeerId(session_t id)
{
if (size() < BASE_HEADER_SIZE) {
assert(false); // should never happen
return;
}
writeU16(&data[4], id);
}
BufferedPacketPtr makePacket(const Address &address, const SharedBuffer<u8> &data,
u32 protocol_id, session_t sender_peer_id, u8 channel)
{
@ -337,6 +346,13 @@ void ReliablePacketBuffer::insert(BufferedPacketPtr &p_ptr, u16 next_expected)
m_oldest_non_answered_ack = m_list.front()->getSeqnum();
}
void ReliablePacketBuffer::fixPeerId(session_t new_id)
{
MutexAutoLock listlock(m_list_mutex);
for (auto &packet : m_list)
packet->setSenderPeerId(new_id);
}
void ReliablePacketBuffer::incrementTimeouts(float dtime)
{
MutexAutoLock listlock(m_list_mutex);
@ -569,6 +585,13 @@ ConnectionCommandPtr ConnectionCommand::resend_one(session_t peer_id)
return c;
}
ConnectionCommandPtr ConnectionCommand::peer_id_set(session_t own_peer_id)
{
auto c = create(CONNCMD_PEER_ID_SET);
c->peer_id = own_peer_id;
return c;
}
ConnectionCommandPtr ConnectionCommand::send(session_t peer_id, u8 channelnum,
NetworkPacket *pkt, bool reliable)
{
@ -1615,6 +1638,14 @@ void Connection::DisconnectPeer(session_t peer_id)
putCommand(ConnectionCommand::disconnect_peer(peer_id));
}
void Connection::SetPeerID(session_t id)
{
m_peer_id = id;
// fix peer id in existing queued reliable packets
if (id != PEER_ID_INEXISTENT)
putCommand(ConnectionCommand::peer_id_set(id));
}
void Connection::doResendOne(session_t peer_id)
{
assert(peer_id != PEER_ID_INEXISTENT);

View file

@ -14,6 +14,7 @@
#include "network/networkprotocol.h"
#include <iostream>
#include <vector>
#include <memory>
#include <map>
namespace con
@ -24,7 +25,6 @@ class ConnectionSendThread;
class Peer;
// FIXME: Peer refcounting should generally be replaced by std::shared_ptr
class PeerHelper
{
public:
@ -256,7 +256,7 @@ protected:
UDPPeer* createServerPeer(const Address& sender);
bool deletePeer(session_t peer_id, bool timeout);
void SetPeerID(session_t id) { m_peer_id = id; }
void SetPeerID(session_t id);
void doResendOne(session_t peer_id);

View file

@ -33,7 +33,7 @@ channel:
/*
Packet types:
CONTROL: This is a packet used by the protocol.
PACKET_TYPE_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
@ -48,25 +48,18 @@ controltype and data description:
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
PACKET_TYPE_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
PACKET_TYPE_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
@ -80,10 +73,9 @@ data.
[3] u16 chunk_count
[5] u16 chunk_num
*/
//#define TYPE_SPLIT 2
/*
RELIABLE: Delivery of all RELIABLE packets shall be forced by ACKs,
PACKET_TYPE_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
@ -93,15 +85,29 @@ with a buffer in the receiving and transmitting end.
[1] u16 seqnum
*/
//#define TYPE_RELIABLE 3
#define RELIABLE_HEADER_SIZE 3
#define SEQNUM_INITIAL 65500
#define SEQNUM_MAX 65535
/****/
template<typename T>
class ConstSharedPtr {
public:
ConstSharedPtr(T *ptr) : ptr(ptr) {}
ConstSharedPtr(const std::shared_ptr<T> &ptr) : ptr(ptr) {}
const T* get() const noexcept { return ptr.get(); }
const T& operator*() const noexcept { return *ptr.get(); }
const T* operator->() const noexcept { return ptr.get(); }
private:
std::shared_ptr<T> ptr;
};
namespace con
{
enum PacketType : u8 {
PACKET_TYPE_CONTROL = 0,
PACKET_TYPE_ORIGINAL = 1,
@ -110,6 +116,13 @@ enum PacketType : u8 {
PACKET_TYPE_MAX
};
enum ControlType : u8 {
CONTROLTYPE_ACK = 0,
CONTROLTYPE_SET_PEER_ID = 1,
CONTROLTYPE_PING = 2,
CONTROLTYPE_DISCO = 3,
};
inline bool seqnum_higher(u16 totest, u16 base)
{
if (totest > base)
@ -187,6 +200,7 @@ struct BufferedPacket {
DISABLE_CLASS_COPY(BufferedPacket)
u16 getSeqnum() const;
void setSenderPeerId(session_t id);
inline size_t size() const { return m_data.size(); }
@ -250,6 +264,8 @@ public:
BufferedPacketPtr popFirst();
BufferedPacketPtr popSeqnum(u16 seqnum);
void insert(BufferedPacketPtr &p_ptr, u16 next_expected);
/// Adjusts the sender peer ID for all packets
void fixPeerId(session_t id);
void incrementTimeouts(float dtime);
u32 getTimedOuts(float timeout);
@ -307,7 +323,8 @@ enum ConnectionCommandType{
CONNCMD_SEND_TO_ALL,
CONCMD_ACK,
CONCMD_CREATE_PEER,
CONNCMD_RESEND_ONE
CONNCMD_RESEND_ONE,
CONNCMD_PEER_ID_SET
};
// This is very similar to ConnectionEvent
@ -328,6 +345,7 @@ struct ConnectionCommand
static ConnectionCommandPtr disconnect();
static ConnectionCommandPtr disconnect_peer(session_t peer_id);
static ConnectionCommandPtr resend_one(session_t peer_id);
static ConnectionCommandPtr peer_id_set(session_t own_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);

View file

@ -476,6 +476,11 @@ void ConnectionSendThread::processNonReliableCommand(ConnectionCommandPtr &c_ptr
<< " UDP processing CONNCMD_DISCONNECT_PEER" << std::endl);
disconnect_peer(c.peer_id);
return;
case CONNCMD_PEER_ID_SET:
LOG(dout_con << m_connection->getDesc()
<< " UDP processing CONNCMD_PEER_ID_SET" << std::endl);
fix_peer_id(c.peer_id);
return;
case CONNCMD_SEND:
LOG(dout_con << m_connection->getDesc()
<< " UDP processing CONNCMD_SEND" << std::endl);
@ -579,6 +584,26 @@ void ConnectionSendThread::disconnect_peer(session_t peer_id)
dynamic_cast<UDPPeer *>(&peer)->m_pending_disconnect = true;
}
void ConnectionSendThread::fix_peer_id(session_t own_peer_id)
{
auto peer_ids = m_connection->getPeerIDs();
for (const session_t peer_id : peer_ids) {
PeerHelper peer = m_connection->getPeerNoEx(peer_id);
if (!peer)
continue;
auto *udp_peer = dynamic_cast<UDPPeer*>(&peer);
if (!udp_peer)
continue;
for (int ch = 0; ch < CHANNEL_COUNT; ch++) {
auto &channel = udp_peer->channels[ch];
channel.outgoing_reliables_sent.fixPeerId(own_peer_id);
}
}
}
void ConnectionSendThread::send(session_t peer_id, u8 channelnum,
const SharedBuffer<u8> &data)
{

View file

@ -70,6 +70,7 @@ private:
void connect(Address address);
void disconnect();
void disconnect_peer(session_t peer_id);
void fix_peer_id(session_t own_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);

View file

@ -49,7 +49,13 @@ const char* NetworkPacket::getString(u32 from_offset) const
{
checkReadOffset(from_offset, 0);
return (char*)&m_data[from_offset];
return reinterpret_cast<const char*>(&m_data[from_offset]);
}
void NetworkPacket::skip(u32 count)
{
checkReadOffset(m_read_offset, count);
m_read_offset += count;
}
void NetworkPacket::putRawString(const char* src, u32 len)
@ -63,6 +69,18 @@ void NetworkPacket::putRawString(const char* src, u32 len)
m_read_offset += len;
}
void NetworkPacket::readRawString(char *dst, u32 len)
{
checkReadOffset(m_read_offset, len);
if (len == 0)
return;
memcpy(dst, &m_data[m_read_offset], len);
m_read_offset += len;
}
NetworkPacket& NetworkPacket::operator>>(std::string& dst)
{
checkReadOffset(m_read_offset, 2);
@ -311,24 +329,6 @@ NetworkPacket& NetworkPacket::operator>>(u8& dst)
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 &m_data[from_offset];
}
NetworkPacket& NetworkPacket::operator>>(u16& dst)
{
checkReadOffset(m_read_offset, 2);
@ -339,13 +339,6 @@ NetworkPacket& NetworkPacket::operator>>(u16& dst)
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);

View file

@ -8,6 +8,8 @@
#include "irrlichttypes_bloated.h"
#include "networkprotocol.h"
#include <SColor.h>
#include <string>
#include <string_view>
#include <vector>
class NetworkPacket
@ -35,18 +37,32 @@ public:
session_t getPeerId() const { return m_peer_id; }
u16 getCommand() const { 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.
// Returns a pointer to buffer data.
// A better name for this would be getRawString()
const char *getString(u32 from_offset) const;
// major difference to putCString(): doesn't write len into the buffer
const char *getRemainingString() const { return getString(m_read_offset); }
// Perform length check and skip ahead by `count` bytes.
void skip(u32 count);
// Appends bytes from string buffer to packet
void putRawString(const char *src, u32 len);
void putRawString(std::string_view src)
{
putRawString(src.data(), src.size());
}
// Reads bytes from packet into string buffer
void readRawString(char *dst, u32 len);
std::string readRawString(u32 len)
{
std::string s;
s.resize(len);
readRawString(&s[0], len);
return s;
}
NetworkPacket &operator>>(std::string &dst);
NetworkPacket &operator<<(std::string_view src);
@ -63,14 +79,9 @@ public:
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);
@ -114,6 +125,7 @@ public:
private:
void checkReadOffset(u32 from_offset, u32 field_size) const;
// resize data buffer for writing
inline void checkDataSize(u32 field_size)
{
if (m_read_offset + field_size > m_datasize) {
@ -124,7 +136,7 @@ private:
std::vector<u8> m_data;
u32 m_datasize = 0;
u32 m_read_offset = 0;
u32 m_read_offset = 0; // read and write offset
u16 m_command = 0;
session_t m_peer_id = 0;
};

View file

@ -63,9 +63,13 @@
Add particle blend mode "clip"
Add artificial light color, beta_r0, vignette, specular intensity, foliage translucency and cdl parameters to Lighting packets
[scheduled bump for 5.11.0]
PROTOCOL VERSION 48
Add compression to some existing packets
[scheduled bump for 5.12.0]
*/
const u16 LATEST_PROTOCOL_VERSION = 47;
// Note: Also update core.protocol_versions in builtin when bumping
const u16 LATEST_PROTOCOL_VERSION = 48;
// See also formspec [Version History] in doc/lua_api.md
const u16 FORMSPEC_API_VERSION = 8;

View file

@ -4,8 +4,7 @@
#pragma once
#include "irrTypes.h"
using namespace irr;
#include "irrlichttypes.h"
extern const u16 LATEST_PROTOCOL_VERSION;
@ -33,24 +32,28 @@ enum ToClientCommand : u16
u32 supported auth methods
std::string unused (used to be username)
*/
TOCLIENT_AUTH_ACCEPT = 0x03,
/*
Message from server to accept auth.
v3s16 player's position + v3f(0,BS/2,0) floatToInt'd
v3f unused
u64 map seed
f1000 recommended send interval
u32 : supported auth methods for sudo mode
(where the user can change their password)
*/
TOCLIENT_ACCEPT_SUDO_MODE = 0x04,
/*
Sent to client to show it is in sudo mode now.
*/
TOCLIENT_DENY_SUDO_MODE = 0x05,
/*
Signals client that sudo mode auth failed.
*/
TOCLIENT_ACCESS_DENIED = 0x0A,
/*
u8 reason
@ -59,18 +62,26 @@ enum ToClientCommand : u16
*/
TOCLIENT_BLOCKDATA = 0x20,
/*
v3s16 position
serialized MapBlock
*/
TOCLIENT_ADDNODE = 0x21,
/*
v3s16 position
serialized mapnode
u8 keep_metadata // Added in protocol version 22
u8 keep_metadata
*/
TOCLIENT_REMOVENODE = 0x22,
/*
v3s16 position
*/
TOCLIENT_INVENTORY = 0x27,
/*
[0] u16 command
[2] serialized inventory
serialized inventory
*/
TOCLIENT_TIME_OF_DAY = 0x29,
@ -167,40 +178,38 @@ enum ToClientCommand : u16
TOCLIENT_MEDIA = 0x38,
/*
u16 total number of texture bunches
u16 total number of bunches
u16 index of this bunch
u32 number of files in this bunch
for each file {
u16 length of name
string name
u32 length of data
data
data (zstd-compressed)
}
u16 length of remote media server url (if applicable)
string url
*/
TOCLIENT_NODEDEF = 0x3a,
/*
u32 length of the next item
serialized NodeDefManager
u32 length of buffer
serialized NodeDefManager (zstd-compressed)
*/
TOCLIENT_ANNOUNCE_MEDIA = 0x3c,
/*
u32 number of files
for each texture {
u16 length of name
string name
u16 length of sha1_digest
string sha1_digest
u32 length of compressed name array
string16array names (zstd-compressed)
for each file {
char[20] sha1_digest
}
u16 length of remote media server url
string url
*/
TOCLIENT_ITEMDEF = 0x3d,
/*
u32 length of next item
serialized ItemDefManager
u32 length of buffer
serialized ItemDefManager (zstd-compressed)
*/
TOCLIENT_PLAY_SOUND = 0x3f,
@ -442,6 +451,11 @@ enum ToClientCommand : u16
*/
TOCLIENT_CAMERA = 0x48,
/*
u8 allowed_camera_mode
*/
TOCLIENT_HUDADD = 0x49,
/*
u32 id
@ -716,18 +730,16 @@ enum ToServerCommand : u16
TOSERVER_PLAYERPOS = 0x23,
/*
[0] u16 command
[2] v3s32 position*100
[2+12] v3s32 speed*100
[2+12+12] s32 pitch*100
[2+12+12+4] s32 yaw*100
[2+12+12+4+4] u32 keyPressed
[2+12+12+4+4+4] u8 fov*80
[2+12+12+4+4+4+1] u8 ceil(wanted_range / MAP_BLOCKSIZE)
[2+12+12+4+4+4+1+1] u8 camera_inverted (bool)
[2+12+12+4+4+4+1+1+1] f32 movement_speed
[2+12+12+4+4+4+1+1+1+4] f32 movement_direction
v3s32 position*100
v3s32 speed*100
s32 pitch*100
s32 yaw*100
u32 keyPressed
u8 fov*80
u8 ceil(wanted_range / MAP_BLOCKSIZE)
u8 camera_inverted (bool)
f32 movement_speed
f32 movement_direction
*/
TOSERVER_GOTBLOCKS = 0x24,

View file

@ -183,7 +183,7 @@ const ClientCommandFactory clientCommandFactoryTable[TOCLIENT_NUM_MSG_TYPES] =
{ "TOCLIENT_MOVEMENT", 0, true }, // 0x45
{ "TOCLIENT_SPAWN_PARTICLE", 0, true }, // 0x46
{ "TOCLIENT_ADD_PARTICLESPAWNER", 0, true }, // 0x47
null_command_factory, // 0x48
{ "TOCLIENT_CAMERA", 0, true }, // 0x48
{ "TOCLIENT_HUDADD", 1, true }, // 0x49
{ "TOCLIENT_HUDRM", 1, true }, // 0x4a
{ "TOCLIENT_HUDCHANGE", 1, true }, // 0x4b

View file

@ -30,6 +30,8 @@
#include "util/srp.h"
#include "clientdynamicinfo.h"
#include <algorithm>
void Server::handleCommand_Deprecated(NetworkPacket* pkt)
{
infostream << "Server: " << toServerCommandTable[pkt->getCommand()].name
@ -323,13 +325,6 @@ void Server::handleCommand_Init2(NetworkPacket* pkt)
SendTimeOfDay(peer_id, time, time_speed);
SendCSMRestrictionFlags(peer_id);
// Warnings about protocol version can be issued here
if (client->net_proto_version < LATEST_PROTOCOL_VERSION) {
SendChatMessage(peer_id, ChatMessage(CHATMESSAGE_TYPE_SYSTEM,
L"# Server: WARNING: YOUR CLIENT'S VERSION MAY NOT BE FULLY COMPATIBLE "
L"WITH THIS SERVER!"));
}
}
void Server::handleCommand_RequestMedia(NetworkPacket* pkt)
@ -419,13 +414,10 @@ void Server::handleCommand_GotBlocks(NetworkPacket* pkt)
u8 count;
*pkt >> count;
if ((s16)pkt->getSize() < 1 + (int)count * 6) {
throw con::InvalidIncomingDataException
("GOTBLOCKS length is too short");
}
ClientInterface::AutoLock lock(m_clients);
RemoteClient *client = m_clients.lockedGetClientNoEx(pkt->getPeerId());
if (!client)
return;
for (u16 i = 0; i < count; i++) {
v3s16 p;
@ -468,7 +460,11 @@ void Server::process_PlayerPos(RemotePlayer *player, PlayerSAO *playersao,
*pkt >> bits;
if (pkt->getRemainingBytes() >= 8) {
*pkt >> player->control.movement_speed;
f32 movement_speed;
*pkt >> movement_speed;
if (movement_speed != movement_speed) // NaN
movement_speed = 0.0f;
player->control.movement_speed = std::clamp(movement_speed, 0.0f, 1.0f);
*pkt >> player->control.movement_direction;
} else {
player->control.movement_speed = 0.0f;
@ -505,20 +501,14 @@ void Server::handleCommand_PlayerPos(NetworkPacket* pkt)
{
session_t peer_id = pkt->getPeerId();
RemotePlayer *player = m_env->getPlayer(peer_id);
if (player == NULL) {
errorstream <<
"Server::ProcessData(): Canceling: No player for peer_id=" <<
peer_id << " disconnecting peer!" << std::endl;
DisconnectPeer(peer_id);
if (!player) {
warningstream << FUNCTION_NAME << ": player is null" << std::endl;
return;
}
PlayerSAO *playersao = player->getPlayerSAO();
if (playersao == NULL) {
errorstream <<
"Server::ProcessData(): Canceling: No player object for peer_id=" <<
peer_id << " disconnecting peer!" << std::endl;
DisconnectPeer(peer_id);
if (!playersao) {
warningstream << FUNCTION_NAME << ": player SAO is null" << std::endl;
return;
}
@ -548,12 +538,10 @@ void Server::handleCommand_DeletedBlocks(NetworkPacket* pkt)
u8 count;
*pkt >> count;
RemoteClient *client = getClient(pkt->getPeerId());
if ((s16)pkt->getSize() < 1 + (int)count * 6) {
throw con::InvalidIncomingDataException
("DELETEDBLOCKS length is too short");
}
ClientInterface::AutoLock lock(m_clients);
RemoteClient *client = m_clients.lockedGetClientNoEx(pkt->getPeerId());
if (!client)
return;
for (u16 i = 0; i < count; i++) {
v3s16 p;
@ -566,28 +554,19 @@ void Server::handleCommand_InventoryAction(NetworkPacket* pkt)
{
session_t peer_id = pkt->getPeerId();
RemotePlayer *player = m_env->getPlayer(peer_id);
if (player == NULL) {
errorstream <<
"Server::ProcessData(): Canceling: No player for peer_id=" <<
peer_id << " disconnecting peer!" << std::endl;
DisconnectPeer(peer_id);
if (!player) {
warningstream << FUNCTION_NAME << ": player is null" << std::endl;
return;
}
PlayerSAO *playersao = player->getPlayerSAO();
if (playersao == NULL) {
errorstream <<
"Server::ProcessData(): Canceling: No player object for peer_id=" <<
peer_id << " disconnecting peer!" << std::endl;
DisconnectPeer(peer_id);
if (!playersao) {
warningstream << FUNCTION_NAME << ": player SAO is null" << std::endl;
return;
}
// Strip command and create a stream
std::string datastring(pkt->getString(0), pkt->getSize());
verbosestream << "TOSERVER_INVENTORY_ACTION: data=" << datastring
<< std::endl;
std::istringstream is(datastring, std::ios_base::binary);
// Create an action
std::unique_ptr<InventoryAction> a(InventoryAction::deSerialize(is));
@ -607,6 +586,24 @@ void Server::handleCommand_InventoryAction(NetworkPacket* pkt)
where the client made a bad prediction.
*/
auto mark_player_inv_list_dirty = [this](const InventoryLocation &loc,
const std::string &list_name) {
// Undo the client prediction of the affected list. See `clientApply`.
if (loc.type != InventoryLocation::PLAYER)
return;
Inventory *inv = m_inventory_mgr->getInventory(loc);
if (!inv)
return;
InventoryList *list = inv->getList(list_name);
if (!list)
return;
list->setModified(true);
};
const bool player_has_interact = checkPriv(player->getName(), "interact");
auto check_inv_access = [player, player_has_interact, this] (
@ -651,8 +648,12 @@ void Server::handleCommand_InventoryAction(NetworkPacket* pkt)
ma->to_inv.applyCurrentPlayer(player->getName());
m_inventory_mgr->setInventoryModified(ma->from_inv);
if (ma->from_inv != ma->to_inv)
mark_player_inv_list_dirty(ma->from_inv, ma->from_list);
bool inv_different = ma->from_inv != ma->to_inv;
if (inv_different)
m_inventory_mgr->setInventoryModified(ma->to_inv);
if (inv_different || ma->from_list != ma->to_list)
mark_player_inv_list_dirty(ma->to_inv, ma->to_list);
if (!check_inv_access(ma->from_inv) ||
!check_inv_access(ma->to_inv))
@ -689,6 +690,7 @@ void Server::handleCommand_InventoryAction(NetworkPacket* pkt)
da->from_inv.applyCurrentPlayer(player->getName());
m_inventory_mgr->setInventoryModified(da->from_inv);
mark_player_inv_list_dirty(da->from_inv, da->from_list);
/*
Disable dropping items out of craftpreview
@ -721,6 +723,7 @@ void Server::handleCommand_InventoryAction(NetworkPacket* pkt)
ca->craft_inv.applyCurrentPlayer(player->getName());
m_inventory_mgr->setInventoryModified(ca->craft_inv);
// Note: `ICraftAction::clientApply` is empty, thus nothing to revert.
// Disallow crafting if not allowed to interact
if (!player_has_interact) {
@ -747,15 +750,12 @@ void Server::handleCommand_ChatMessage(NetworkPacket* pkt)
session_t peer_id = pkt->getPeerId();
RemotePlayer *player = m_env->getPlayer(peer_id);
if (player == NULL) {
errorstream <<
"Server::ProcessData(): Canceling: No player for peer_id=" <<
peer_id << " disconnecting peer!" << std::endl;
DisconnectPeer(peer_id);
if (!player) {
warningstream << FUNCTION_NAME << ": player is null" << std::endl;
return;
}
std::string name = player->getName();
const auto &name = player->getName();
std::wstring answer_to_sender = handleChat(name, message, true, player);
if (!answer_to_sender.empty()) {
@ -773,29 +773,22 @@ void Server::handleCommand_Damage(NetworkPacket* pkt)
session_t peer_id = pkt->getPeerId();
RemotePlayer *player = m_env->getPlayer(peer_id);
if (player == NULL) {
errorstream <<
"Server::ProcessData(): Canceling: No player for peer_id=" <<
peer_id << " disconnecting peer!" << std::endl;
DisconnectPeer(peer_id);
if (!player) {
warningstream << FUNCTION_NAME << ": player is null" << std::endl;
return;
}
PlayerSAO *playersao = player->getPlayerSAO();
if (playersao == NULL) {
errorstream <<
"Server::ProcessData(): Canceling: No player object for peer_id=" <<
peer_id << " disconnecting peer!" << std::endl;
DisconnectPeer(peer_id);
if (!playersao) {
warningstream << FUNCTION_NAME << ": player SAO is null" << std::endl;
return;
}
if (!playersao->isImmortal()) {
if (playersao->isDead()) {
verbosestream << "Server::ProcessData(): Info: "
verbosestream << "Server: "
"Ignoring damage as player " << player->getName()
<< " is already dead." << std::endl;
<< " is already dead" << std::endl;
return;
}
@ -815,21 +808,14 @@ void Server::handleCommand_PlayerItem(NetworkPacket* pkt)
session_t peer_id = pkt->getPeerId();
RemotePlayer *player = m_env->getPlayer(peer_id);
if (player == NULL) {
errorstream <<
"Server::ProcessData(): Canceling: No player for peer_id=" <<
peer_id << " disconnecting peer!" << std::endl;
DisconnectPeer(peer_id);
if (!player) {
warningstream << FUNCTION_NAME << ": player is null" << std::endl;
return;
}
PlayerSAO *playersao = player->getPlayerSAO();
if (playersao == NULL) {
errorstream <<
"Server::ProcessData(): Canceling: No player object for peer_id=" <<
peer_id << " disconnecting peer!" << std::endl;
DisconnectPeer(peer_id);
if (!playersao) {
warningstream << FUNCTION_NAME << ": player SAO is null" << std::endl;
return;
}
@ -838,7 +824,7 @@ void Server::handleCommand_PlayerItem(NetworkPacket* pkt)
*pkt >> item;
if (item >= player->getMaxHotbarItemcount()) {
actionstream << "Player: " << player->getName()
actionstream << "Player " << player->getName()
<< " tried to access item=" << item
<< " out of hotbar_itemcount="
<< player->getMaxHotbarItemcount()
@ -852,8 +838,8 @@ void Server::handleCommand_PlayerItem(NetworkPacket* pkt)
bool Server::checkInteractDistance(RemotePlayer *player, const f32 d, const std::string &what)
{
ItemStack selected_item, hand_item;
player->getWieldedItem(&selected_item, &hand_item);
f32 max_d = BS * getToolRange(selected_item, hand_item, m_itemdef);
const ItemStack &tool_item = player->getWieldedItem(&selected_item, &hand_item);
f32 max_d = BS * getToolRange(tool_item, hand_item, m_itemdef);
// Cube diagonal * 1.5 for maximal supported node extents:
// sqrt(3) * 1.5 ≅ 2.6
@ -903,21 +889,14 @@ void Server::handleCommand_Interact(NetworkPacket *pkt)
session_t peer_id = pkt->getPeerId();
RemotePlayer *player = m_env->getPlayer(peer_id);
if (player == NULL) {
errorstream <<
"Server::ProcessData(): Canceling: No player for peer_id=" <<
peer_id << " disconnecting peer!" << std::endl;
DisconnectPeer(peer_id);
if (!player) {
warningstream << FUNCTION_NAME << ": player is null" << std::endl;
return;
}
PlayerSAO *playersao = player->getPlayerSAO();
if (playersao == NULL) {
errorstream <<
"Server::ProcessData(): Canceling: No player object for peer_id=" <<
peer_id << " disconnecting peer!" << std::endl;
DisconnectPeer(peer_id);
if (!playersao) {
warningstream << FUNCTION_NAME << ": player SAO is null" << std::endl;
return;
}
@ -942,7 +921,7 @@ void Server::handleCommand_Interact(NetworkPacket *pkt)
// Update wielded item
if (item_i >= player->getMaxHotbarItemcount()) {
actionstream << "Player: " << player->getName()
actionstream << "Player " << player->getName()
<< " tried to access item=" << item_i
<< " out of hotbar_itemcount="
<< player->getMaxHotbarItemcount()
@ -1061,7 +1040,7 @@ void Server::handleCommand_Interact(NetworkPacket *pkt)
ItemStack selected_item, hand_item;
ItemStack tool_item = playersao->getWieldedItem(&selected_item, &hand_item);
ToolCapabilities toolcap =
tool_item.getToolCapabilities(m_itemdef);
tool_item.getToolCapabilities(m_itemdef, &hand_item);
v3f dir = (pointed_object->getBasePosition() -
(playersao->getBasePosition() + playersao->getEyeOffset())
).normalize();
@ -1118,12 +1097,12 @@ void Server::handleCommand_Interact(NetworkPacket *pkt)
// Get player's wielded item
// See also: Game::handleDigging
ItemStack selected_item, hand_item;
player->getWieldedItem(&selected_item, &hand_item);
ItemStack &tool_item = player->getWieldedItem(&selected_item, &hand_item);
// Get diggability and expected digging time
DigParams params = getDigParams(m_nodedef->get(n).groups,
&selected_item.getToolCapabilities(m_itemdef),
selected_item.wear);
&tool_item.getToolCapabilities(m_itemdef, &hand_item),
tool_item.wear);
// If can't dig, try hand
if (!params.diggable) {
params = getDigParams(m_nodedef->get(n).groups,
@ -1333,21 +1312,14 @@ void Server::handleCommand_NodeMetaFields(NetworkPacket* pkt)
{
session_t peer_id = pkt->getPeerId();
RemotePlayer *player = m_env->getPlayer(peer_id);
if (player == NULL) {
errorstream <<
"Server::ProcessData(): Canceling: No player for peer_id=" <<
peer_id << " disconnecting peer!" << std::endl;
DisconnectPeer(peer_id);
if (!player) {
warningstream << FUNCTION_NAME << ": player is null" << std::endl;
return;
}
PlayerSAO *playersao = player->getPlayerSAO();
if (playersao == NULL) {
errorstream <<
"Server::ProcessData(): Canceling: No player object for peer_id=" <<
peer_id << " disconnecting peer!" << std::endl;
DisconnectPeer(peer_id);
if (!playersao) {
warningstream << FUNCTION_NAME << ": player SAO is null" << std::endl;
return;
}
@ -1420,25 +1392,26 @@ void Server::handleCommand_InventoryFields(NetworkPacket* pkt)
}
// verify that we displayed the formspec to the user
const auto peer_state_iterator = m_formspec_state_data.find(peer_id);
if (peer_state_iterator != m_formspec_state_data.end()) {
const std::string &server_formspec_name = peer_state_iterator->second;
const auto it = m_formspec_state_data.find(peer_id);
if (it != m_formspec_state_data.end()) {
const auto &server_formspec_name = it->second;
if (client_formspec_name == server_formspec_name) {
auto it = fields.find("quit");
if (it != fields.end() && it->second == "true")
m_formspec_state_data.erase(peer_state_iterator);
// delete state if formspec was closed
auto it2 = fields.find("quit");
if (it2 != fields.end() && it2->second == "true")
m_formspec_state_data.erase(it);
m_script->on_playerReceiveFields(playersao, client_formspec_name, fields);
return;
}
actionstream << "'" << player->getName()
<< "' submitted formspec ('" << client_formspec_name
actionstream << player->getName()
<< " submitted formspec ('" << client_formspec_name
<< "') but the name of the formspec doesn't match the"
" expected name ('" << server_formspec_name << "')";
} else {
actionstream << "'" << player->getName()
<< "' submitted formspec ('" << client_formspec_name
actionstream << player->getName()
<< " submitted formspec ('" << client_formspec_name
<< "') but server hasn't sent formspec to client";
}
actionstream << ", possible exploitation attempt" << std::endl;

View file

@ -87,25 +87,16 @@ bool UDPSocket::init(bool ipv6, bool noExceptions)
m_handle = socket(m_addr_family, SOCK_DGRAM, IPPROTO_UDP);
if (m_handle < 0) {
if (noExceptions) {
auto msg = std::string("Failed to create socket: ") +
SOCKET_ERR_STR(LAST_SOCKET_ERR());
verbosestream << msg << std::endl;
if (noExceptions)
return false;
}
throw SocketException(std::string("Failed to create socket: error ") +
SOCKET_ERR_STR(LAST_SOCKET_ERR()));
throw SocketException(msg);
}
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;
}
@ -129,6 +120,20 @@ void UDPSocket::Bind(Address addr)
throw SocketException(errmsg);
}
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;
if (setsockopt(m_handle, IPPROTO_IPV6, IPV6_V6ONLY,
reinterpret_cast<char *>(&value), sizeof(value)) != 0) {
auto errmsg = SOCKET_ERR_STR(LAST_SOCKET_ERR());
errorstream << "Failed to disable V6ONLY: " << errmsg
<< "\nTry disabling ipv6_server to fix this." << std::endl;
throw SocketException(errmsg);
}
}
int ret = 0;
if (m_addr_family == AF_INET6) {