aboutsummaryrefslogtreecommitdiff
path: root/src/network/networkprotocol.h
Commit message (Expand)AuthorAge
* Connected Nodeboxes: Add `disconnected` boxesThomas--S2018-01-03
* CSM fixes: load mods after flavours & add flavour to block mod loading (#6738)Loïc Blot2017-12-11
* Zoom: Set zoom FOV per-player using a player object propertyparamat2017-12-04
* Real global textures (#6105)Vitaliy2017-10-15
* Add session_t typedef + remove unused functions (#6470)Loïc Blot2017-09-27
* Implement mod communication channels (#6351)Loïc Blot2017-09-26
* Object properties: Add 'glow', disables light's effect if negativeRob Blanckaert2017-09-14
* Network: Remove large parts of deprecated legacy code (#6404)SmallJoker2017-09-12
* Network protocol documentation: Add 'can_zoom' to version 36paramat2017-09-05
* Bump minimal protocol version to 36 (#6319)SmallJoker2017-08-29
* Step height: Add as a player object propertyparamat2017-08-09
* Network protocol: Document settable player collisionbox (#6168)Paramat2017-07-25
* [CSM] Add flavour limits controlled by server (#5930)Loïc Blot2017-07-18
* Chat protocol rewrite (#5117)Loïc Blot2017-07-16
* Fix the serialization error by ff73c7a (#5964)Rui2017-06-11
* Have the server send the player list to the client (#5924)red-0012017-06-08
* Set sky API: Add bool for clouds in front of custom skyboxparamat2017-05-02
* Sound API: Add fading soundsBrandon2017-05-03
* Add clouds APIBen Deutsch2017-04-30
* Fix #5655 (#5658)red-0012017-04-26
* Network:Remove old opcodes and fix documentation. (#5573)red-0012017-04-22
* Soft node overlay (#5186)Dániel Juhász2017-04-21
* Update server min protocol version to v24 (#5411)Loïc Blot2017-03-19
* Plantlike: Fix visual_scale being applied squaredparamat2017-02-10
* Revert "Plantlike visual scale: Send sqrt(visual_scale) to old clients"Craig Robbins2017-02-10
* Plantlike visual scale: Send sqrt(visual_scale) to old clientsparamat2017-01-30
* Add hardware node coloring. Includes:Dániel Juhász2017-01-23
* Remove client-side chat prediction. (#5055)red-0012017-01-17
* Move TileAnimation code to seperate filesfan52017-01-02
* Breath cheat fix: server sideLoic Blot2017-01-01
* Fix computation of viewing range (in blocks) sent to server (#4882)Rogier-52016-12-11
* Optimize/adjust blocks/ActiveObjects sent at the server based on client setti...lhofhansl2016-11-30
* Make plantlike drawtype more funAuke Kok2016-08-26
* Particles: Add option to remove particles on collisionAuke Kok2016-05-28
* Add option to not send pre v25 init packetest312016-03-15
* Nodebox: Allow nodeboxes to "connect"Auke Kok2016-03-12
* Backface culling: Ignore setting in tiledef from old servers.Auke Kok2016-01-23
* networkprotocol.h: remove "u16 command" from docest312015-09-10
* Bump protocol to 26est312015-07-24
* Optional reconnect functionalityest312015-07-23
* Fix relief mapping issuesRealBadAngel2015-07-16
* Generic CAO cleanups and renames for clarificationest312015-06-20
* Finalize init packets and enable protocol v25est312015-05-16
* Make early protocol auth mechanism generic, and add SRPest312015-05-11
* Make comments consistent with TOSERVER_INIT -> TOSERVER_INIT_LEGACY renameest312015-04-07
* Change error_message from wstring to stringShadowNinja2015-03-27
* Prepare Protocol v25 init & authentication.Loic Blot2015-03-13
* Handle the newly added TOCLIENT_ACCESS_DENIED and TOCLIENT_DELETE_PARTICLESPA...Loic Blot2015-03-13
* Rename some packet and handlers to <packet>_Legacy name for compat layer betw...Loic Blot2015-03-13
* Typo fix in networkprotocol.hNer'zhul2015-02-19
n479'>479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642
/*
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.
*/

#ifndef CONNECTION_HEADER
#define CONNECTION_HEADER

#include "irrlichttypes_bloated.h"
#include "socket.h"
#include "exceptions.h"
#include "constants.h"
#include "util/pointer.h"
#include "util/container.h"
#include "util/thread.h"
#include <iostream>
#include <fstream>
#include <list>
#include <map>

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 ThrottlingException : public BaseException
{
public:
	ThrottlingException(const char *s):
		BaseException(s)
	{}
};*/

class InvalidIncomingDataException : public BaseException
{
public:
	InvalidIncomingDataException(const char *s):
		BaseException(s)
	{}
};

class InvalidOutgoingDataException : public BaseException
{
public:
	InvalidOutgoingDataException(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)
	{}
};

#define SEQNUM_MAX 65535
inline bool seqnum_higher(u16 higher, u16 lower)
{
	if(lower > higher && lower - higher > SEQNUM_MAX/2){
		return true;
	}
	return (higher > lower);
}

struct BufferedPacket
{
	BufferedPacket(u8 *a_data, u32 a_size):
		data(a_data, a_size), time(0.0), totaltime(0.0)
	{}
	BufferedPacket(u32 a_size):
		data(a_size), time(0.0), totaltime(0.0)
	{}
	SharedBuffer<u8> data; // Data of the packet, including headers
	float time; // Seconds from buffering the packet or re-sending
	float totaltime; // Seconds from buffering the packet
	Address address; // Sender or destination
};

// This adds the base headers to the data and makes a packet out of it
BufferedPacket makePacket(Address &address, u8 *data, u32 datasize,
		u32 protocol_id, u16 sender_peer_id, u8 channel);
BufferedPacket makePacket(Address &address, SharedBuffer<u8> &data,
		u32 protocol_id, u16 sender_peer_id, u8 channel);

// Add the TYPE_ORIGINAL header to the data
SharedBuffer<u8> makeOriginalPacket(
		SharedBuffer<u8> data);

// Split data in chunks and add TYPE_SPLIT headers to them
std::list<SharedBuffer<u8> > makeSplitPacket(
		SharedBuffer<u8> data,
		u32 chunksize_max,
		u16 seqnum);

// Depending on size, make a TYPE_ORIGINAL or TYPE_SPLIT packet
// Increments split_seqnum if a split packet is made
std::list<SharedBuffer<u8> > makeAutoSplitPacket(
		SharedBuffer<u8> data,
		u32 chunksize_max,
		u16 &split_seqnum);

// Add the TYPE_RELIABLE header to the data
SharedBuffer<u8> makeReliablePacket(
		SharedBuffer<u8> data,
		u16 seqnum);

struct IncomingSplitPacket
{
	IncomingSplitPacket()
	{
		time = 0.0;
		reliable = false;
	}
	// Key is chunk number, value is data without headers
	std::map<u16, SharedBuffer<u8> > chunks;
	u32 chunk_count;
	float time; // Seconds from adding
	bool reliable; // If true, isn't deleted on timeout

	bool allReceived()
	{
		return (chunks.size() == chunk_count);
	}
};

/*
=== NOTES ===

A packet is sent through a channel to a peer with a basic header:
TODO: Should we have a receiver_peer_id also?
	Header (7 bytes):
	[0] u32 protocol_id
	[4] u16 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:
	The lower the number, the higher the priority is.
	Only channels 0, 1 and 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] u16 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
*/
#define TYPE_CONTROL 0
#define CONTROLTYPE_ACK 0
#define CONTROLTYPE_SET_PEER_ID 1
#define CONTROLTYPE_PING 2
#define 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 0x10
#define SEQNUM_INITIAL 65500

/*
	A buffer which stores reliable packets and sorts them internally
	for fast access to the smallest one.
*/

typedef std::list<BufferedPacket>::iterator RPBSearchResult;

class ReliablePacketBuffer
{
public:
	ReliablePacketBuffer();
	void print();
	bool empty();
	u32 size();
	RPBSearchResult findPacket(u16 seqnum);
	RPBSearchResult notFound();
	bool getFirstSeqnum(u16 *result);
	BufferedPacket popFirst();
	BufferedPacket popSeqnum(u16 seqnum);
	void insert(BufferedPacket &p);
	void incrementTimeouts(float dtime);
	void resetTimedOuts(float timeout);
	bool anyTotaltimeReached(float timeout);
	std::list<BufferedPacket> getTimedOuts(float timeout);

private:
	std::list<BufferedPacket> m_list;
	u16 m_list_size;
};

/*
	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(BufferedPacket &p, bool reliable);
	
	void removeUnreliableTimedOuts(float dtime, float timeout);
	
private:
	// Key is seqnum
	std::map<u16, IncomingSplitPacket*> m_buf;
};

class Connection;

struct Channel
{
	Channel();
	~Channel();

	u16 next_outgoing_seqnum;
	u16 next_incoming_seqnum;
	u16 next_outgoing_split_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;

	IncomingSplitBuffer incoming_splits;
};

class Peer;

class PeerHandler
{
public:
	PeerHandler()
	{
	}
	virtual ~PeerHandler()
	{
	}
	
	/*
		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;
};

class Peer
{
public:

	Peer(u16 a_id, Address a_address);
	virtual ~Peer();
	
	/*
		Calculates avg_rtt and resend_timeout.

		rtt=-1 only recalculates resend_timeout
	*/
	void reportRTT(float rtt);

	Channel channels[CHANNEL_COUNT];

	// Address of the peer
	Address address;
	// Unique id of the peer
	u16 id;
	// Seconds from last receive
	float timeout_counter;
	// Ping timer
	float ping_timer;
	// This is changed dynamically
	float resend_timeout;
	// Updated when an ACK is received
	float avg_rtt;
	// This is set to true when the peer has actually sent something
	// with the id we have given to it
	bool has_sent_with_id;
	
	float m_sendtime_accu;
	float m_max_packets_per_second;
	int m_num_sent;
	int m_max_num_sent;

	// Updated from configuration by Connection
	float congestion_control_aim_rtt;
	float congestion_control_max_rate;
	float congestion_control_min_rate;
private:
};

/*
	Connection
*/

struct OutgoingPacket
{
	u16 peer_id;
	u8 channelnum;
	SharedBuffer<u8> data;
	bool reliable;

	OutgoingPacket(u16 peer_id_, u8 channelnum_, SharedBuffer<u8> data_,
			bool reliable_):
		peer_id(peer_id_),
		channelnum(channelnum_),
		data(data_),
		reliable(reliable_)
	{
	}
};

enum ConnectionEventType{
	CONNEVENT_NONE,
	CONNEVENT_DATA_RECEIVED,
	CONNEVENT_PEER_ADDED,
	CONNEVENT_PEER_REMOVED,
	CONNEVENT_BIND_FAILED,
};

struct ConnectionEvent
{
	enum ConnectionEventType type;
	u16 peer_id;
	Buffer<u8> data;
	bool timeout;
	Address address;

	ConnectionEvent(): type(CONNEVENT_NONE) {}

	std::string describe()
	{
		switch(type){
		case CONNEVENT_NONE:
			return "CONNEVENT_NONE";
		case CONNEVENT_DATA_RECEIVED:
			return "CONNEVENT_DATA_RECEIVED";
		case CONNEVENT_PEER_ADDED: 
			return "CONNEVENT_PEER_ADDED";
		case CONNEVENT_PEER_REMOVED: 
			return "CONNEVENT_PEER_REMOVED";
		case CONNEVENT_BIND_FAILED: 
			return "CONNEVENT_BIND_FAILED";
		}
		return "Invalid ConnectionEvent";
	}
	
	void dataReceived(u16 peer_id_, SharedBuffer<u8> data_)
	{
		type = CONNEVENT_DATA_RECEIVED;
		peer_id = peer_id_;
		data = data_;
	}
	void peerAdded(u16 peer_id_, Address address_)
	{
		type = CONNEVENT_PEER_ADDED;
		peer_id = peer_id_;
		address = address_;
	}
	void peerRemoved(u16 peer_id_, bool timeout_, Address address_)
	{
		type = CONNEVENT_PEER_REMOVED;
		peer_id = peer_id_;
		timeout = timeout_;
		address = address_;
	}
	void bindFailed()
	{
		type = CONNEVENT_BIND_FAILED;
	}
};

enum ConnectionCommandType{
	CONNCMD_NONE,
	CONNCMD_SERVE,
	CONNCMD_CONNECT,
	CONNCMD_DISCONNECT,
	CONNCMD_SEND,
	CONNCMD_SEND_TO_ALL,
	CONNCMD_DELETE_PEER,
};

struct ConnectionCommand
{
	enum ConnectionCommandType type;
	u16 port;
	Address address;
	u16 peer_id;
	u8 channelnum;
	Buffer<u8> data;
	bool reliable;
	
	ConnectionCommand(): type(CONNCMD_NONE) {}

	void serve(u16 port_)
	{
		type = CONNCMD_SERVE;
		port = port_;
	}
	void connect(Address address_)
	{
		type = CONNCMD_CONNECT;
		address = address_;
	}
	void disconnect()
	{
		type = CONNCMD_DISCONNECT;
	}
	void send(u16 peer_id_, u8 channelnum_,
			SharedBuffer<u8> data_, bool reliable_)
	{
		type = CONNCMD_SEND;
		peer_id = peer_id_;
		channelnum = channelnum_;
		data = data_;
		reliable = reliable_;
	}
	void sendToAll(u8 channelnum_, SharedBuffer<u8> data_, bool reliable_)
	{
		type = CONNCMD_SEND_TO_ALL;
		channelnum = channelnum_;
		data = data_;
		reliable = reliable_;
	}
	void deletePeer(u16 peer_id_)
	{
		type = CONNCMD_DELETE_PEER;
		peer_id = peer_id_;
	}
};

class Connection: public SimpleThread
{
public:
	Connection(u32 protocol_id, u32 max_packet_size, float timeout, bool ipv6);
	Connection(u32 protocol_id, u32 max_packet_size, float timeout, bool ipv6,
			PeerHandler *peerhandler);
	~Connection();
	void * Thread();

	/* Interface */

	ConnectionEvent getEvent();
	ConnectionEvent waitEvent(u32 timeout_ms);
	void putCommand(ConnectionCommand &c);
	
	void SetTimeoutMs(int timeout){ m_bc_receive_timeout = timeout; }
	void Serve(unsigned short port);
	void Connect(Address address);
	bool Connected();
	void Disconnect();
	u32 Receive(u16 &peer_id, SharedBuffer<u8> &data);
	void SendToAll(u8 channelnum, SharedBuffer<u8> data, bool reliable);
	void Send(u16 peer_id, u8 channelnum, SharedBuffer<u8> data, bool reliable);
	void RunTimeouts(float dtime); // dummy
	u16 GetPeerID(){ return m_peer_id; }
	Address GetPeerAddress(u16 peer_id);
	float GetPeerAvgRTT(u16 peer_id);
	void DeletePeer(u16 peer_id);
	
private:
	void putEvent(ConnectionEvent &e);
	void processCommand(ConnectionCommand &c);
	void send(float dtime);
	void receive();
	void runTimeouts(float dtime);
	void serve(u16 port);
	void connect(Address address);
	void disconnect();
	void sendToAll(u8 channelnum, SharedBuffer<u8> data, bool reliable);
	void send(u16 peer_id, u8 channelnum, SharedBuffer<u8> data, bool reliable);
	void sendAsPacket(u16 peer_id, u8 channelnum,
			SharedBuffer<u8> data, bool reliable);
	void rawSendAsPacket(u16 peer_id, u8 channelnum,
			SharedBuffer<u8> data, bool reliable);
	void rawSend(const BufferedPacket &packet);
	Peer* getPeer(u16 peer_id);
	Peer* getPeerNoEx(u16 peer_id);
	std::list<Peer*> getPeers();
	bool getFromBuffers(u16 &peer_id, SharedBuffer<u8> &dst);
	// Returns next data from a buffer if possible
	// If found, returns true; if not, false.
	// If found, sets peer_id and dst
	bool checkIncomingBuffers(Channel *channel, u16 &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,
			SharedBuffer<u8> packetdata, u16 peer_id,
			u8 channelnum, bool reliable);
	bool deletePeer(u16 peer_id, bool timeout);
	
	Queue<OutgoingPacket> m_outgoing_queue;
	MutexedQueue<ConnectionEvent> m_event_queue;
	MutexedQueue<ConnectionCommand> m_command_queue;
	
	u32 m_protocol_id;
	u32 m_max_packet_size;
	float m_timeout;
	UDPSocket m_socket;
	u16 m_peer_id;
	
	std::map<u16, Peer*> m_peers;
	JMutex m_peers_mutex;

	// Backwards compatibility
	PeerHandler *m_bc_peerhandler;
	int m_bc_receive_timeout;
	
	void SetPeerID(u16 id){ m_peer_id = id; }
	u32 GetProtocolID(){ return m_protocol_id; }
	void PrintInfo(std::ostream &out);
	void PrintInfo();
	std::string getDesc();
	u16 m_indentation;
};

} // namespace

#endif