code:
/*
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 "network/networkpacket.h"
#include "util/pointer.h"
#include "util/container.h"
#include "util/thread.h"
#include "util/numeric.h"
#include <iostream>
#include <fstream>
#include <list>
#include <map>

class NetworkPacket;

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 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)
	{}
};

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

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

typedef enum MTProtocols {
	MTP_PRIMARY,
	MTP_UDP,
	MTP_MINETEST_RELIABLE_UDP
} MTProtocols;

#define SEQNUM_MAX 65535
inline bool seqnum_higher(u16 totest, u16 base)
{
	if (totest > base)
	{
		if ((totest - base) > (SEQNUM_MAX/2))
			return false;
		else
			return true;
	}
	else
	{
		if ((base - totest) > (SEQNUM_MAX/2))
			return true;
		else
			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));
	}
	else
	{
		return ((seqnum < window_end) || (seqnum >= window_start));
	}
}

struct BufferedPacket
{
	BufferedPacket(u8 *a_data, u32 a_size):
		data(a_data, a_size), time(0.0), totaltime(0.0), absolute_send_time(-1),
		resend_count(0)
	{}
	BufferedPacket(u32 a_size):
		data(a_size), time(0.0), totaltime(0.0), absolute_send_time(-1),
		resend_count(0)
	{}
	Buffer<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
	u64 absolute_send_time;
	Address address; // Sender or destination
	unsigned int resend_count;
};

// 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
#define CONTROLTYPE_ENABLE_BIG_SEND_WINDOW 4

/*
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

/*
	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();

	bool getFirstSeqnum(u16& result);

	BufferedPacket popFirst();
	BufferedPacket popSeqnum(u16 seqnum);
	void insert(BufferedPacket &p,u16 next_expected);

	void incrementTimeouts(float dtime);
	std::list<BufferedPacket> getTimedOuts(float timeout,
			unsigned int max_packets);

	void print();
	bool empty();
	bool containsPacket(u16 seqnum);
	RPBSearchResult notFound();
	u32 size();

private:
	RPBSearchResult findPacket(u16 seqnum);

	std::list<BufferedPacket> m_list;
	u32 m_list_size;

	u16 m_oldest_non_answered_ack;

	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(BufferedPacket &p, bool reliable);

	void removeUnreliableTimedOuts(float dtime, float timeout);

private:
	// Key is seqnum
	std::map<u16, IncomingSplitPacket*> m_buf;

	Mutex m_map_mutex;
};

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

	OutgoingPacket(u16 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_)
	{
	}
};

enum ConnectionCommandType{
	CONNCMD_NONE,
	CONNCMD_SERVE,
	CONNCMD_CONNECT,
	CONNCMD_DISCONNECT,
	CONNCMD_DISCONNECT_PEER,
	CONNCMD_SEND,
	CONNCMD_SEND_TO_ALL,
	CONCMD_ACK,
	CONCMD_CREATE_PEER,
	CONCMD_DISABLE_LEGACY
};

struct ConnectionCommand
{
	enum ConnectionCommandType type;
	Address address;
	u16 peer_id;
	u8 channelnum;
	Buffer<u8> data;
	bool reliable;
	bool raw;

	ConnectionCommand(): type(CONNCMD_NONE), peer_id(PEER_ID_INEXISTENT), reliable(false), raw(false) {}

	void serve(Address address_)
	{
		type = CONNCMD_SERVE;
		address = address_;
	}
	void connect(Address address_)
	{
		type = CONNCMD_CONNECT;
		address = address_;
	}
	void disconnect()
	{
		type = CONNCMD_DISCONNECT;
	}
	void disconnect_peer(u16 peer_id_)
	{
		type = CONNCMD_DISCONNECT_PEER;
		peer_id = peer_id_;
	}
	void send(u16 peer_id_, u8 channelnum_,
			NetworkPacket* pkt, bool reliable_)
	{
		type = CONNCMD_SEND;
		peer_id = peer_id_;
		channelnum = channelnum_;
		data = pkt->oldForgePacket();
		reliable = reliable_;
	}

	void ack(u16 peer_id_, u8 channelnum_, const SharedBuffer<u8> &data_)
	{
		type = CONCMD_ACK;
		peer_id = peer_id_;
		channelnum = channelnum_;
		data = data_;
		reliable = false;
	}

	void createPeer(u16 peer_id_, const SharedBuffer<u8> &data_)
	{
		type = CONCMD_CREATE_PEER;
		peer_id = peer_id_;
		data = data_;
		channelnum = 0;
		reliable = true;
		raw = true;
	}

	void disableLegacy(u16 peer_id_, const SharedBuffer<u8> &data_)
	{
		type = CONCMD_DISABLE_LEGACY;
		peer_id = peer_id_;
		data = data_;
		channelnum = 0;
		reliable = true;
		raw = true;
	}
};

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<BufferedPacket> queued_reliables;

	//queue commands prior splitting to packets
	std::deque<ConnectionCommand> queued_commands;

	IncomingSplitBuffer incoming_splits;

	Channel();
	~Channel();

	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, bool legacy_peer);

	const float getCurrentDownloadRateKB()
		{ MutexAutoLock lock(m_internal_mutex); return cur_kbps; };
	const float getMaxDownloadRateKB()
		{ MutexAutoLock lock(m_internal_mutex); return max_kbps; };

	const float getCurrentLossRateKB()
		{ MutexAutoLock lock(m_internal_mutex); return cur_kbps_lost; };
	const float getMaxLossRateKB()
		{ MutexAutoLock lock(m_internal_mutex); return max_kbps_lost; };

	const float getCurrentIncomingRateKB()
		{ MutexAutoLock lock(m_internal_mutex); return cur_incoming_kbps; };
	const float getMaxIncomingRateKB()
		{ MutexAutoLock lock(m_internal_mutex); return max_incoming_kbps; };

	const float getAvgDownloadRateKB()
		{ MutexAutoLock lock(m_internal_mutex); return avg_kbps; };
	const float getAvgLossRateKB()
		{ MutexAutoLock lock(m_internal_mutex); return avg_kbps_lost; };
	const float getAvgIncomingRateKB()
		{ MutexAutoLock lock(m_internal_mutex); return avg_incoming_kbps; };

	const unsigned int getWindowSize() const { return window_size; };

	void setWindowSize(unsigned int size) { window_size = size; };
private:
	Mutex m_internal_mutex;
	int window_size;

	u16 next_incoming_seqnum;

	u16 next_outgoing_seqnum;
	u16 next_outgoing_split_seqnum;

	unsigned int current_packet_loss;
	unsigned int current_packet_too_late;
	unsigned int current_packet_successfull;
	float packet_loss_counter;

	unsigned int current_bytes_transfered;
	unsigned int current_bytes_received;
	unsigned int current_bytes_lost;
	float max_kbps;
	float cur_kbps;
	float avg_kbps;
	float max_incoming_kbps;
	float cur_incoming_kbps;
	float avg_incoming_kbps;
	float max_kbps_lost;
	float cur_kbps_lost;
	float avg_kbps_lost;
	float bpm_counter;

	unsigned int rate_samples;
};

class Peer;

enum PeerChangeType
{
	PEER_ADDED,
	PEER_REMOVED
};
struct PeerChange
{
	PeerChangeType type;
	u16 peer_id;
	bool timeout;
};

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 PeerHelper
{
public:
	PeerHelper();
	PeerHelper(Peer* peer);
	~PeerHelper();

	PeerHelper&   operator=(Peer* peer);
	Peer*         operator->() const;
	bool          operator!();
	Peer*         operator&() const;
	bool          operator!=(void* ptr);

private:
	Peer* m_peer;
};

class Connection;

typedef enum {
	MIN_RTT,
	MAX_RTT,
	AVG_RTT,
	MIN_JITTER,
	MAX_JITTER,
	AVG_JITTER
} rtt_stat_type;

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_,u16 id_,Connection* connection) :
			id(id_),
			m_increment_packets_remaining(9),
			m_increment_bytes_remaining(0),
			m_pending_deletion(false),
			m_connection(connection),
			address(address_),
			m_ping_timer(0.0),
			m_last_rtt(-1.0),
			m_usage(0),
			m_timeout_counter(0.0),
			m_last_timeout_check(porting::getTimeMs())
		{
			m_rtt.avg_rtt = -1.0;
			m_rtt.jitter_avg = -1.0;
			m_rtt.jitter_max = 0.0;
			m_rtt.max_rtt = 0.0;
			m_rtt.jitter_min = FLT_MAX;
			m_rtt.min_rtt = FLT_MAX;
		};

		virtual ~Peer() {
			MutexAutoLock usage_lock(m_exclusive_access_mutex);
			FATAL_ERROR_IF(m_usage != 0, "Reference counting failure");
		};

		// Unique id of the peer
		u16 id;

		void Drop();

		virtual void PutReliableSendCommand(ConnectionCommand &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;
		unsigned int m_increment_bytes_remaining;

		virtual u16 getNextSplitSequenceNumber(u8 channel) { return 0; };
		virtual void setNextSplitSequenceNumber(u8 channel, u16 seqnum) {};
		virtual SharedBuffer<u8> addSpiltPacket(u8 channel,
												BufferedPacket toadd,
												bool reliable)
				{
					fprintf(stderr,"Peer: addSplitPacket called, this is supposed to be never called!\n");
					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();

		Mutex m_exclusive_access_mutex;

		bool m_pending_deletion;

		Connection* m_connection;

		// Address of the peer
		Address address;

		// Ping timer
		float m_ping_timer;
	private:

		struct rttstats {
			float jitter_min;
			float jitter_max;
			float jitter_avg;
			float min_rtt;
			float max_rtt;
			float avg_rtt;
		};

		rttstats m_rtt;
		float    m_last_rtt;

		// current usage count
		unsigned int m_usage;

		// Seconds from last receive
		float m_timeout_counter;

		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() {};

	void PutReliableSendCommand(ConnectionCommand &c,
							unsigned int max_packet_size);

	bool getAddress(MTProtocols type, Address& toset);

	void setNonLegacyPeer();

	bool getLegacyPeer()
	{ return m_legacy_peer; }

	u16 getNextSplitSequenceNumber(u8 channel);
	void setNextSplitSequenceNumber(u8 channel, u16 seqnum);

	SharedBuffer<u8> addSpiltPacket(u8 channel,
									BufferedPacket 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;
private:
	// This is changed dynamically
	float resend_timeout;

	bool processReliableSendCommand(
					ConnectionCommand &c,
					unsigned int max_packet_size);

	bool m_legacy_peer;
};

/*
	Connection
*/

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), peer_id(0),
			timeout(false) {}

	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_, const 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;
	}
};

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 &packet);
	bool rawSendAsPacket(u16 peer_id, u8 channelnum,
							SharedBuffer<u8> data, bool reliable);

	void processReliableCommand (ConnectionCommand &c);
	void processNonReliableCommand (ConnectionCommand &c);
	void serve          (Address bind_address);
	void connect        (Address address);
	void disconnect     ();
	void disconnect_peer(u16 peer_id);
	void send           (u16 peer_id, u8 channelnum,
							SharedBuffer<u8> data);
	void sendReliable   (ConnectionCommand &c);
	void sendToAll      (u8 channelnum,
							SharedBuffer<u8> data);
	void sendToAllReliable(ConnectionCommand &c);

	void sendPackets    (float dtime);

	void sendAsPacket   (u16 peer_id, u8 channelnum,
							SharedBuffer<u8> data,bool ack=false);

	void sendAsPacketReliable(BufferedPacket& p, Channel* channel);

	bool packetsQueued();

	Connection*           m_connection;
	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;
	unsigned int          m_max_data_packets_per_iteration;
	unsigned int          m_max_packets_requeued;
};

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();

	// Returns next data from a buffer if possible
	// If found, returns true; if not, false.
	// If found, sets peer_id and dst
	bool getFromBuffers(u16 &peer_id, SharedBuffer<u8> &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);

	Connection*           m_connection;
};

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 */
	ConnectionEvent waitEvent(u32 timeout_ms);
	void putCommand(ConnectionCommand &c);

	void SetTimeoutMs(int timeout) { m_bc_receive_timeout = timeout; }
	void Serve(Address bind_addr);
	void Connect(Address address);
	bool Connected();
	void Disconnect();
	void Receive(NetworkPacket* pkt);
	void Send(u16 peer_id, u8 channelnum, NetworkPacket* pkt, bool reliable);
	u16 GetPeerID() { return m_peer_id; }
	Address GetPeerAddress(u16 peer_id);
	float getPeerStat(u16 peer_id, rtt_stat_type type);
	float getLocalStat(rate_stat_type type);
	const u32 GetProtocolID() const { return m_protocol_id; };
	const std::string getDesc();
	void DisconnectPeer(u16 peer_id);

protected:
	PeerHelper getPeer(u16 peer_id);
	PeerHelper getPeerNoEx(u16 peer_id);
	u16   lookupPeer(Address& sender);

	u16 createPeer(Address& sender, MTProtocols protocol, int fd);
	UDPPeer*  createServerPeer(Address& sender);
	bool deletePeer(u16 peer_id, bool timeout);

	void SetPeerID(u16 id) { m_peer_id = id; }

	void sendAck(u16 peer_id, u8 channelnum, u16 seqnum);

	void PrintInfo(std::ostream &out);
	void PrintInfo();

	std::list<u16> getPeerIDs()
	{
		MutexAutoLock peerlock(m_peers_mutex);
		return m_peer_ids;
	}

	UDPSocket m_udpSocket;
	MutexedQueue<ConnectionCommand> m_command_queue;

	void putEvent(ConnectionEvent &e);

	void TriggerSend()
		{ m_sendThread.Trigger(); }
private:
	std::list<Peer*> getPeers();

	MutexedQueue<ConnectionEvent> m_event_queue;

	u16 m_peer_id;
	u32 m_protocol_id;

	std::map<u16, Peer*> m_peers;
	std::list<u16> m_peer_ids;
	Mutex m_peers_mutex;

	ConnectionSendThread m_sendThread;
	ConnectionReceiveThread m_receiveThread;

	Mutex m_info_mutex;

	// Backwards compatibility
	PeerHandler *m_bc_peerhandler;
	int m_bc_receive_timeout;

	bool m_shutting_down;

	u16 m_next_remote_peer_id;
};

} // namespace

#endif

repo_name: pgimeno/minetest

license: unknown


code:
/*
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 "debug.h"
#include "exceptions.h"
#include "util/serialize.h"

NetworkPacket::NetworkPacket(u16 command, u32 datasize, u16 peer_id):
m_datasize(datasize), m_read_offset(0), m_command(command), m_peer_id(peer_id)
{
	m_data.resize(m_datasize);
}

NetworkPacket::NetworkPacket(u16 command, u32 datasize):
m_datasize(datasize), m_read_offset(0), m_command(command), m_peer_id(0)
{
	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(u8 *data, u32 datasize, u16 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[0], &data[2], m_datasize);
}

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)
{
	u16 msgsize = src.size();
	if (msgsize > STRING_MAX_LEN) {
		throw PacketError("String too long");
	}

	*this << msgsize;

	putRawString(src.c_str(), (u32)msgsize);

	return *this;
}

void NetworkPacket::putLongString(const std::string &src)
{
	u32 msgsize = src.size();
	if (msgsize > LONG_STRING_MAX_LEN) {
		throw PacketError("String too long");
	}

	*this << msgsize;

	putRawString(src.c_str(), msgsize);
}

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 c16 = readU16(&m_data[m_read_offset]);
		dst.append(&c16, 1);
		m_read_offset += sizeof(u16);
	}

	return *this;
}

NetworkPacket& NetworkPacket::operator<<(const std::wstring &src)
{
	u16 msgsize = src.size();
	if (msgsize > WIDE_STRING_MAX_LEN) {
		throw PacketError("String too long");
	}

	*this << msgsize;

	// Write string
	for (u16 i=0; i<msgsize; i++) {
		*this << (u16) src[i];
	}

	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;
}

char NetworkPacket::getChar(u32 offset)
{
	checkReadOffset(offset, 1);

	return readU8(&m_data[offset]);
}

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);

	writeF1000(&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 = readF1000(&m_data[m_read_offset]);

	m_read_offset += 4;
	return *this;
}

NetworkPacket& NetworkPacket::operator>>(v2f& dst)
{
	checkReadOffset(m_read_offset, 8);

	dst = readV2F1000(&m_data[m_read_offset]);

	m_read_offset += 8;
	return *this;
}

NetworkPacket& NetworkPacket::operator>>(v3f& dst)
{
	checkReadOffset(m_read_offset, 12);

	dst = readV3F1000(&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);

	u8* datas = getU8Ptr(0);

	if (datas != NULL)
		memcpy(&sb[2], datas, m_datasize);
	return sb;
}


code:
/*
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.
*/

#ifndef NETWORKPACKET_HEADER
#define NETWORKPACKET_HEADER

#include "util/pointer.h"
#include "util/numeric.h"
#include "networkprotocol.h"

class NetworkPacket
{

public:
		NetworkPacket(u16 command, u32 datasize, u16 peer_id);
		NetworkPacket(u16 command, u32 datasize);
		NetworkPacket(): m_datasize(0), m_read_offset(0), m_command(0),
				m_peer_id(0) {}
		~NetworkPacket();

		void putRawPacket(u8 *data, u32 datasize, u16 peer_id);

		// Getters
		u32 getSize() { return m_datasize; }
		u16 getPeerId() { return m_peer_id; }
		u16 getCommand() { return m_command; }
		const 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();

		char getChar(u32 offset);
		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
		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;
		u32 m_read_offset;
		u16 m_command;
		u16 m_peer_id;
};

#endif


code:
/*
Minetest
Copyright (C) 2010-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 NETWORKPROTOCOL_HEADER
#define NETWORKPROTOCOL_HEADER
#include "util/string.h"

/*
	changes by PROTOCOL_VERSION:

	PROTOCOL_VERSION 3:
		Base for writing changes here
	PROTOCOL_VERSION 4:
		Add TOCLIENT_MEDIA
		Add TOCLIENT_TOOLDEF
		Add TOCLIENT_NODEDEF
		Add TOCLIENT_CRAFTITEMDEF
		Add TOSERVER_INTERACT
		Obsolete TOSERVER_CLICK_ACTIVEOBJECT
		Obsolete TOSERVER_GROUND_ACTION
	PROTOCOL_VERSION 5:
		Make players to be handled mostly as ActiveObjects
	PROTOCOL_VERSION 6:
		Only non-cached textures are sent
	PROTOCOL_VERSION 7:
		Add TOCLIENT_ITEMDEF
		Obsolete TOCLIENT_TOOLDEF
		Obsolete TOCLIENT_CRAFTITEMDEF
		Compress the contents of TOCLIENT_ITEMDEF and TOCLIENT_NODEDEF
	PROTOCOL_VERSION 8:
		Digging based on item groups
		Many things
	PROTOCOL_VERSION 9:
		ContentFeatures and NodeDefManager use a different serialization
		    format; better for future version cross-compatibility
		Many things
		Obsolete TOCLIENT_PLAYERITEM
	PROTOCOL_VERSION 10:
		TOCLIENT_PRIVILEGES
		Version raised to force 'fly' and 'fast' privileges into effect.
		Node metadata change (came in later; somewhat incompatible)
	PROTOCOL_VERSION 11:
		TileDef in ContentFeatures
		Nodebox drawtype
		(some dev snapshot)
		TOCLIENT_INVENTORY_FORMSPEC
		(0.4.0, 0.4.1)
	PROTOCOL_VERSION 12:
		TOSERVER_INVENTORY_FIELDS
		16-bit node ids
		TOCLIENT_DETACHED_INVENTORY
	PROTOCOL_VERSION 13:
		InventoryList field "Width" (deserialization fails with old versions)
	PROTOCOL_VERSION 14:
		Added transfer of player pressed keys to the server
		Added new messages for mesh and bone animation, as well as attachments
		GENERIC_CMD_SET_ANIMATION
		GENERIC_CMD_SET_BONE_POSITION
		GENERIC_CMD_SET_ATTACHMENT
	PROTOCOL_VERSION 15:
		Serialization format changes
	PROTOCOL_VERSION 16:
		TOCLIENT_SHOW_FORMSPEC
	PROTOCOL_VERSION 17:
		Serialization format change: include backface_culling flag in TileDef
		Added rightclickable field in nodedef
		TOCLIENT_SPAWN_PARTICLE
		TOCLIENT_ADD_PARTICLESPAWNER
		TOCLIENT_DELETE_PARTICLESPAWNER
	PROTOCOL_VERSION 18:
		damageGroups added to ToolCapabilities
		sound_place added to ItemDefinition
	PROTOCOL_VERSION 19:
		GENERIC_CMD_SET_PHYSICS_OVERRIDE
	PROTOCOL_VERSION 20:
		TOCLIENT_HUDADD
		TOCLIENT_HUDRM
		TOCLIENT_HUDCHANGE
		TOCLIENT_HUD_SET_FLAGS
	PROTOCOL_VERSION 21:
		TOCLIENT_BREATH
		TOSERVER_BREATH
		range added to ItemDefinition
		drowning, leveled and liquid_range added to ContentFeatures
		stepheight and collideWithObjects added to object properties
		version, heat and humidity transfer in MapBock
		automatic_face_movement_dir and automatic_face_movement_dir_offset
			added to object properties
	PROTOCOL_VERSION 22:
		add swap_node
	PROTOCOL_VERSION 23:
		Obsolete TOSERVER_RECEIVED_MEDIA
		Server: Stop using TOSERVER_CLIENT_READY
	PROTOCOL_VERSION 24:
		ContentFeatures version 7
		ContentFeatures: change number of special tiles to 6 (CF_SPECIAL_COUNT)
	PROTOCOL_VERSION 25:
		Rename TOCLIENT_ACCESS_DENIED to TOCLIENT_ACCESS_DENIED_LEGAGY
		Rename TOCLIENT_DELETE_PARTICLESPAWNER to
			TOCLIENT_DELETE_PARTICLESPAWNER_LEGACY
		Rename TOSERVER_PASSWORD to TOSERVER_PASSWORD_LEGACY
		Rename TOSERVER_INIT to TOSERVER_INIT_LEGACY
		Rename TOCLIENT_INIT to TOCLIENT_INIT_LEGACY
		Add TOCLIENT_ACCESS_DENIED new opcode (0x0A), using error codes
			for standard error, keeping customisation possible. This
			permit translation
		Add TOCLIENT_DELETE_PARTICLESPAWNER (0x53), fixing the u16 read and
			reading u32
		Add new opcode TOSERVER_INIT for client presentation to server
		Add new opcodes TOSERVER_FIRST_SRP, TOSERVER_SRP_BYTES_A,
			TOSERVER_SRP_BYTES_M, TOCLIENT_SRP_BYTES_S_B
			for the three supported auth mechanisms around srp
		Add new opcodes TOCLIENT_ACCEPT_SUDO_MODE and TOCLIENT_DENY_SUDO_MODE
			for sudo mode handling (auth mech generic way of changing password).
		Add TOCLIENT_HELLO for presenting server to client after client
			presentation
		Add TOCLIENT_AUTH_ACCEPT to accept connection from client
		Rename GENERIC_CMD_SET_ATTACHMENT to GENERIC_CMD_ATTACH_TO
	PROTOCOL_VERSION 26:
		Add TileDef tileable_horizontal, tileable_vertical flags
	PROTOCOL_VERSION 27:
		backface_culling: backwards compatibility for playing with
		newer client on pre-27 servers.
		Add nodedef v3 - connected nodeboxes
	PROTOCOL_VERSION 28:
		CPT2_MESHOPTIONS
	PROTOCOL_VERSION 29:
		Server doesn't accept TOSERVER_BREATH anymore
		serialization of TileAnimation params changed
		TAT_SHEET_2D
		Removed client-sided chat perdiction
	PROTOCOL VERSION 30:
		New ContentFeatures serialization version
		Add node and tile color and palette
		Fix plantlike visual_scale being applied squared and add compatibility
			with pre-30 clients by sending sqrt(visual_scale)
	PROTOCOL VERSION 31:
		Add tile overlay
		Stop sending TOSERVER_CLIENT_READY
	PROTOCOL VERSION 32:
		Add fading sounds
*/

#define LATEST_PROTOCOL_VERSION 32

// Server's supported network protocol range
#define SERVER_PROTOCOL_VERSION_MIN 24
#define SERVER_PROTOCOL_VERSION_MAX LATEST_PROTOCOL_VERSION

// Client's supported network protocol range
// The minimal version depends on whether
// send_pre_v25_init is enabled or not
#define CLIENT_PROTOCOL_VERSION_MIN 25
#define CLIENT_PROTOCOL_VERSION_MIN_LEGACY 24
#define CLIENT_PROTOCOL_VERSION_MAX LATEST_PROTOCOL_VERSION

// Constant that differentiates the protocol from random data and other protocols
#define PROTOCOL_ID 0x4f457403

#define PASSWORD_SIZE 28       // Maximum password length. Allows for
                               // base64-encoded SHA-1 (27+\0).

#define FORMSPEC_API_VERSION 1
#define FORMSPEC_VERSION_STRING "formspec_version[" TOSTRING(FORMSPEC_API_VERSION) "]"

#define TEXTURENAME_ALLOWED_CHARS "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_.-"

enum ToClientCommand
{
	TOCLIENT_HELLO = 0x02,
	/*
		Sent after TOSERVER_INIT.

		u8 deployed serialisation version
		u16 deployed network compression mode
		u16 deployed protocol version
		u32 supported auth methods
		std::string username that should be used for legacy hash (for proper casing)
	*/
	TOCLIENT_AUTH_ACCEPT = 0x03,
	/*
		Message from server to accept auth.

		v3s16 player's position + v3f(0,BS/2,0) floatToInt'd
		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_INIT_LEGACY = 0x10,
	/*
		Server's reply to TOSERVER_INIT.
		Sent second after connected.

		[0] u16 TOSERVER_INIT
		[2] u8 deployed version
		[3] v3s16 player's position + v3f(0,BS/2,0) floatToInt'd
		[12] u64 map seed (new as of 2011-02-27)
		[20] f1000 recommended send interval (in seconds) (new as of 14)

		NOTE: The position in here is deprecated; position is
		      explicitly sent afterwards
	*/
	TOCLIENT_ACCESS_DENIED = 0x0A,
	/*
		u8 reason
		std::string custom reason (if needed, otherwise "")
		u8 (bool) reconnect
	*/
	TOCLIENT_BLOCKDATA = 0x20, //TODO: Multiple blocks
	TOCLIENT_ADDNODE = 0x21,
	/*
		v3s16 position
		serialized mapnode
		u8 keep_metadata // Added in protocol version 22
	*/
	TOCLIENT_REMOVENODE = 0x22,

	TOCLIENT_PLAYERPOS = 0x23, // Obsolete
	/*
		[0] u16 command
		// Followed by an arbitary number of these:
		// Number is determined from packet length.
		[N] u16 peer_id
		[N+2] v3s32 position*100
		[N+2+12] v3s32 speed*100
		[N+2+12+12] s32 pitch*100
		[N+2+12+12+4] s32 yaw*100
	*/

	TOCLIENT_PLAYERINFO = 0x24, // Obsolete
	/*
		[0] u16 command
		// Followed by an arbitary number of these:
		// Number is determined from packet length.
		[N] u16 peer_id
		[N] char[20] name
	*/

	TOCLIENT_OPT_BLOCK_NOT_FOUND = 0x25, // Obsolete

	TOCLIENT_SECTORMETA = 0x26, // Obsolete
	/*
		[0] u16 command
		[2] u8 sector count
		[3...] v2s16 pos + sector metadata
	*/

	TOCLIENT_INVENTORY = 0x27,
	/*
		[0] u16 command
		[2] serialized inventory
	*/

	TOCLIENT_OBJECTDATA = 0x28, // Obsolete
	/*
		Sent as unreliable.

		u16 number of player positions
		for each player:
			u16 peer_id
			v3s32 position*100
			v3s32 speed*100
			s32 pitch*100
			s32 yaw*100
		u16 count of blocks
		for each block:
			v3s16 blockpos
			block objects
	*/

	TOCLIENT_TIME_OF_DAY = 0x29,
	/*
		u16 time (0-23999)
		Added in a later version:
		f1000 time_speed
	*/

	// (oops, there is some gap here)

	TOCLIENT_CHAT_MESSAGE = 0x30,
	/*
		u16 length
		wstring message
	*/

	TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD = 0x31,
	/*
		u16 count of removed objects
		for all removed objects {
			u16 id
		}
		u16 count of added objects
		for all added objects {
			u16 id
			u8 type
			u32 initialization data length
			string initialization data
		}
	*/

	TOCLIENT_ACTIVE_OBJECT_MESSAGES = 0x32,
	/*
		for all objects
		{
			u16 id
			u16 message length
			string message
		}
	*/

	TOCLIENT_HP = 0x33,
	/*
		u8 hp
	*/

	TOCLIENT_MOVE_PLAYER = 0x34,
	/*
		v3f1000 player position
		f1000 player pitch
		f1000 player yaw
	*/

	TOCLIENT_ACCESS_DENIED_LEGACY = 0x35,
	/*
		u16 reason_length
		wstring reason
	*/

	TOCLIENT_PLAYERITEM = 0x36, // Obsolete
	/*
		u16 count of player items
		for all player items {
			u16 peer id
			u16 length of serialized item
			string serialized item
		}
	*/

	TOCLIENT_DEATHSCREEN = 0x37,
	/*
		u8 bool set camera point target
		v3f1000 camera point target (to point the death cause or whatever)
	*/

	TOCLIENT_MEDIA = 0x38,
	/*
		u16 total number of texture 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 length of remote media server url (if applicable)
		string url
	*/

	TOCLIENT_TOOLDEF = 0x39,
	/*
		u32 length of the next item
		serialized ToolDefManager
	*/

	TOCLIENT_NODEDEF = 0x3a,
	/*
		u32 length of the next item
		serialized NodeDefManager
	*/

	TOCLIENT_CRAFTITEMDEF = 0x3b,
	/*
		u32 length of the next item
		serialized CraftiItemDefManager
	*/

	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
		}
	*/

	TOCLIENT_ITEMDEF = 0x3d,
	/*
		u32 length of next item
		serialized ItemDefManager
	*/

	TOCLIENT_PLAY_SOUND = 0x3f,
	/*
		s32 sound_id
		u16 len
		u8[len] sound name
		s32 gain*1000
		u8 type (0=local, 1=positional, 2=object)
		s32[3] pos_nodes*10000
		u16 object_id
		u8 loop (bool)
	*/

	TOCLIENT_STOP_SOUND = 0x40,
	/*
		s32 sound_id
	*/

	TOCLIENT_PRIVILEGES = 0x41,
	/*
		u16 number of privileges
		for each privilege
			u16 len
			u8[len] privilege
	*/

	TOCLIENT_INVENTORY_FORMSPEC = 0x42,
	/*
		u32 len
		u8[len] formspec
	*/

	TOCLIENT_DETACHED_INVENTORY = 0x43,
	/*
		[0] u16 command
		u16 len
		u8[len] name
		[2] serialized inventory
	*/

	TOCLIENT_SHOW_FORMSPEC = 0x44,
	/*
		[0] u16 command
		u32 len
		u8[len] formspec
		u16 len
		u8[len] formname
	*/

	TOCLIENT_MOVEMENT = 0x45,
	/*
		f1000 movement_acceleration_default
		f1000 movement_acceleration_air
		f1000 movement_acceleration_fast
		f1000 movement_speed_walk
		f1000 movement_speed_crouch
		f1000 movement_speed_fast
		f1000 movement_speed_climb
		f1000 movement_speed_jump
		f1000 movement_liquid_fluidity
		f1000 movement_liquid_fluidity_smooth
		f1000 movement_liquid_sink
		f1000 movement_gravity
	*/

	TOCLIENT_SPAWN_PARTICLE = 0x46,
	/*
		v3f1000 pos
		v3f1000 velocity
		v3f1000 acceleration
		f1000 expirationtime
		f1000 size
		u8 bool collisiondetection
		u8 bool vertical
		u32 len
		u8[len] texture
		u8 collision_removal
	*/

	TOCLIENT_ADD_PARTICLESPAWNER = 0x47,
	/*
		u16 amount
		f1000 spawntime
		v3f1000 minpos
		v3f1000 maxpos
		v3f1000 minvel
		v3f1000 maxvel
		v3f1000 minacc
		v3f1000 maxacc
		f1000 minexptime
		f1000 maxexptime
		f1000 minsize
		f1000 maxsize
		u8 bool collisiondetection
		u8 bool vertical
		u32 len
		u8[len] texture
		u32 id
		u8 collision_removal
	*/

	TOCLIENT_DELETE_PARTICLESPAWNER_LEGACY = 0x48,
	/*
		u16 id
	*/

	TOCLIENT_HUDADD = 0x49,
	/*
		u32 id
		u8 type
		v2f1000 pos
		u32 len
		u8[len] name
		v2f1000 scale
		u32 len2
		u8[len2] text
		u32 number
		u32 item
		u32 dir
		v2f1000 align
		v2f1000 offset
		v3f1000 world_pos
		v2s32 size
	*/

	TOCLIENT_HUDRM = 0x4a,
	/*
		u32 id
	*/

	TOCLIENT_HUDCHANGE = 0x4b,
	/*
		u32 id
		u8 stat
		[v2f1000 data |
		 u32 len
		 u8[len] data |
		 u32 data]
	*/

	TOCLIENT_HUD_SET_FLAGS = 0x4c,
	/*
		u32 flags
		u32 mask
	*/

	TOCLIENT_HUD_SET_PARAM = 0x4d,
	/*
		u16 param
		u16 len
		u8[len] value
	*/

	TOCLIENT_BREATH = 0x4e,
	/*
		u16 breath
	*/

	TOCLIENT_SET_SKY = 0x4f,
	/*
		u8[4] color (ARGB)
		u8 len
		u8[len] type
		u16 count
		foreach count:
			u8 len
			u8[len] param
		u8 clouds (boolean)
	*/

	TOCLIENT_OVERRIDE_DAY_NIGHT_RATIO = 0x50,
	/*
		u8 do_override (boolean)
		u16 day-night ratio 0...65535
	*/

	TOCLIENT_LOCAL_PLAYER_ANIMATIONS = 0x51,
	/*
		v2s32 stand/idle
		v2s32 walk
		v2s32 dig
		v2s32 walk+dig
		f1000 frame_speed
	*/

	TOCLIENT_EYE_OFFSET = 0x52,
	/*
		v3f1000 first
		v3f1000 third
	*/

	TOCLIENT_DELETE_PARTICLESPAWNER = 0x53,
	/*
		u32 id
	*/

	TOCLIENT_CLOUD_PARAMS = 0x54,
	/*
		f1000 density
		u8[4] color_diffuse (ARGB)
		u8[4] color_ambient (ARGB)
		f1000 height
		f1000 thickness
		v2f1000 speed
	*/

	TOCLIENT_FADE_SOUND = 0x55,
	/*
		s32 sound_id
		float step
		float gain
	*/

	TOCLIENT_SRP_BYTES_S_B = 0x60,
	/*
		Belonging to AUTH_MECHANISM_LEGACY_PASSWORD and AUTH_MECHANISM_SRP.

		std::string bytes_s
		std::string bytes_B
	*/

	TOCLIENT_NUM_MSG_TYPES = 0x61,
};

enum ToServerCommand
{
	TOSERVER_INIT = 0x02,
	/*
		Sent first after connected.

		u8 serialisation version (=SER_FMT_VER_HIGHEST_READ)
		u16 supported network compression modes
		u16 minimum supported network protocol version
		u16 maximum supported network protocol version
		std::string player name
	*/

	TOSERVER_INIT_LEGACY = 0x10,
	/*
		Sent first after connected.

		[0] u16 TOSERVER_INIT_LEGACY
		[2] u8 SER_FMT_VER_HIGHEST_READ
		[3] u8[20] player_name
		[23] u8[28] password (new in some version)
		[51] u16 minimum supported network protocol version (added sometime)
		[53] u16 maximum supported network protocol version (added later than the previous one)
	*/

	TOSERVER_INIT2 = 0x11,
	/*
		Sent as an ACK for TOCLIENT_INIT.
		After this, the server can send data.

		[0] u16 TOSERVER_INIT2
	*/

	TOSERVER_GETBLOCK=0x20, // Obsolete
	TOSERVER_ADDNODE = 0x21, // Obsolete
	TOSERVER_REMOVENODE = 0x22, // Obsolete

	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+1] u8 fov*80
		[2+12+12+4+4+4+1] u8 ceil(wanted_range / MAP_BLOCKSIZE)
	*/

	TOSERVER_GOTBLOCKS = 0x24,
	/*
		[0] u16 command
		[2] u8 count
		[3] v3s16 pos_0
		[3+6] v3s16 pos_1
		...
	*/

	TOSERVER_DELETEDBLOCKS = 0x25,
	/*
		[0] u16 command
		[2] u8 count
		[3] v3s16 pos_0
		[3+6] v3s16 pos_1
		...
	*/

	TOSERVER_ADDNODE_FROM_INVENTORY = 0x26, // Obsolete
	/*
		[0] u16 command
		[2] v3s16 pos
		[8] u16 i
	*/

	TOSERVER_CLICK_OBJECT = 0x27, // Obsolete
	/*
		length: 13
		[0] u16 command
		[2] u8 button (0=left, 1=right)
		[3] v3s16 blockpos
		[9] s16 id
		[11] u16 item
	*/

	TOSERVER_GROUND_ACTION = 0x28, // Obsolete
	/*
		length: 17
		[0] u16 command
		[2] u8 action
		[3] v3s16 nodepos_undersurface
		[9] v3s16 nodepos_abovesurface
		[15] u16 item
		actions:
		0: start digging (from undersurface)
		1: place block (to abovesurface)
		2: stop digging (all parameters ignored)
		3: digging completed
	*/

	TOSERVER_RELEASE = 0x29, // Obsolete

	// (oops, there is some gap here)

	TOSERVER_SIGNTEXT = 0x30, // Old signs, obsolete
	/*
		v3s16 blockpos
		s16 id
		u16 textlen
		textdata
	*/

	TOSERVER_INVENTORY_ACTION = 0x31,
	/*
		See InventoryAction in inventorymanager.h
	*/

	TOSERVER_CHAT_MESSAGE = 0x32,
	/*
		u16 length
		wstring message
	*/

	TOSERVER_SIGNNODETEXT = 0x33, // obsolete
	/*
		v3s16 p
		u16 textlen
		textdata
	*/

	TOSERVER_CLICK_ACTIVEOBJECT = 0x34, // Obsolete
	/*
		length: 7
		[0] u16 command
		[2] u8 button (0=left, 1=right)
		[3] u16 id
		[5] u16 item
	*/

	TOSERVER_DAMAGE = 0x35,
	/*
		u8 amount
	*/

	TOSERVER_PASSWORD_LEGACY = 0x36,
	/*
		Sent to change password.

		[0] u16 TOSERVER_PASSWORD
		[2] u8[28] old password
		[30] u8[28] new password
	*/

	TOSERVER_PLAYERITEM = 0x37,
	/*
		Sent to change selected item.

		[0] u16 TOSERVER_PLAYERITEM
		[2] u16 item
	*/

	TOSERVER_RESPAWN = 0x38,
	/*
		u16 TOSERVER_RESPAWN
	*/

	TOSERVER_INTERACT = 0x39,
	/*
		[0] u16 command
		[2] u8 action
		[3] u16 item
		[5] u32 length of the next item
		[9] serialized PointedThing
		actions:
		0: start digging (from undersurface) or use
		1: stop digging (all parameters ignored)
		2: digging completed
		3: place block or item (to abovesurface)
		4: use item

		(Obsoletes TOSERVER_GROUND_ACTION and TOSERVER_CLICK_ACTIVEOBJECT.)
	*/

	TOSERVER_REMOVED_SOUNDS = 0x3a,
	/*
		u16 len
		s32[len] sound_id
	*/

	TOSERVER_NODEMETA_FIELDS = 0x3b,
	/*
		v3s16 p
		u16 len
		u8[len] form name (reserved for future use)
		u16 number of fields
		for each field:
			u16 len
			u8[len] field name
			u32 len
			u8[len] field value
	*/

	TOSERVER_INVENTORY_FIELDS = 0x3c,
	/*
		u16 len
		u8[len] form name (reserved for future use)
		u16 number of fields
		for each field:
			u16 len
			u8[len] field name
			u32 len
			u8[len] field value
	*/

	TOSERVER_REQUEST_MEDIA = 0x40,
	/*
		u16 number of files requested
		for each file {
			u16 length of name
			string name
		}
	 */

	TOSERVER_RECEIVED_MEDIA = 0x41,
	/*
		<no payload data>
	*/

	TOSERVER_BREATH = 0x42, // Obsolete
	/*
		u16 breath
	*/

	TOSERVER_CLIENT_READY = 0x43,
	/*
		u8 major
		u8 minor
		u8 patch
		u8 reserved
		u16 len
		u8[len] full_version_string
	*/

	TOSERVER_FIRST_SRP = 0x50,
	/*
		Belonging to AUTH_MECHANISM_FIRST_SRP.

		std::string srp salt
		std::string srp verification key
		u8 is_empty (=1 if password is empty, 0 otherwise)
	*/

	TOSERVER_SRP_BYTES_A = 0x51,
	/*
		Belonging to AUTH_MECHANISM_LEGACY_PASSWORD and AUTH_MECHANISM_SRP,
			depending on current_login_based_on.

		std::string bytes_A
		u8 current_login_based_on : on which version of the password's
		                            hash this login is based on (0 legacy hash,
		                            or 1 directly the password)
	*/

	TOSERVER_SRP_BYTES_M = 0x52,
	/*
		Belonging to AUTH_MECHANISM_LEGACY_PASSWORD and AUTH_MECHANISM_SRP.

		std::string bytes_M
	*/

	TOSERVER_NUM_MSG_TYPES = 0x53,
};

enum AuthMechanism
{
	// reserved
	AUTH_MECHANISM_NONE = 0,

	// SRP based on the legacy hash
	AUTH_MECHANISM_LEGACY_PASSWORD = 1 << 0,

	// SRP based on the srp verification key
	AUTH_MECHANISM_SRP = 1 << 1,

	// Establishes a srp verification key, for first login and password changing
	AUTH_MECHANISM_FIRST_SRP = 1 << 2,
};

enum AccessDeniedCode {
	SERVER_ACCESSDENIED_WRONG_PASSWORD,
	SERVER_ACCESSDENIED_UNEXPECTED_DATA,
	SERVER_ACCESSDENIED_SINGLEPLAYER,
	SERVER_ACCESSDENIED_WRONG_VERSION,
	SERVER_ACCESSDENIED_WRONG_CHARS_IN_NAME,
	SERVER_ACCESSDENIED_WRONG_NAME,
	SERVER_ACCESSDENIED_TOO_MANY_USERS,
	SERVER_ACCESSDENIED_EMPTY_PASSWORD,
	SERVER_ACCESSDENIED_ALREADY_CONNECTED,
	SERVER_ACCESSDENIED_SERVER_FAIL,
	SERVER_ACCESSDENIED_CUSTOM_STRING,
	SERVER_ACCESSDENIED_SHUTDOWN,
	SERVER_ACCESSDENIED_CRASH,
	SERVER_ACCESSDENIED_MAX,
};

enum NetProtoCompressionMode {
	NETPROTO_COMPRESSION_NONE = 0,
};

const static std::string accessDeniedStrings[SERVER_ACCESSDENIED_MAX] = {
	"Invalid password",
	"Your client sent something the server didn't expect.  Try reconnecting or updating your client",
	"The server is running in simple singleplayer mode.  You cannot connect.",
	"Your client's version is not supported.\nPlease contact server administrator.",
	"Player name contains disallowed characters.",
	"Player name not allowed.",
	"Too many users.",
	"Empty passwords are disallowed.  Set a password and try again.",
	"Another client is connected with this name.  If your client closed unexpectedly, try again in a minute.",
	"Server authentication failed.  This is likely a server error.",
	"",
	"Server shutting down.",
	"This server has experienced an internal error. You will now be disconnected."
};

#endif


code:
/*
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
	{ "TOSERVER_INIT_LEGACY",              TOSERVER_STATE_NOT_CONNECTED, &Server::handleCommand_Init_Legacy }, // 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
	null_command_handler, // 0x17
	null_command_handler, // 0x18
	null_command_handler, // 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
	{ "TOSERVER_CLICK_OBJECT",             TOSERVER_STATE_INGAME, &Server::handleCommand_Deprecated }, // 0x27
	{ "TOSERVER_GROUND_ACTION",            TOSERVER_STATE_INGAME, &Server::handleCommand_Deprecated }, // 0x28
	{ "TOSERVER_RELEASE",                  TOSERVER_STATE_INGAME, &Server::handleCommand_Deprecated }, // 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
	{ "TOSERVER_SIGNTEXT",                 TOSERVER_STATE_INGAME, &Server::handleCommand_Deprecated }, // 0x30
	{ "TOSERVER_INVENTORY_ACTION",         TOSERVER_STATE_INGAME, &Server::handleCommand_InventoryAction }, // 0x31
	{ "TOSERVER_CHAT_MESSAGE",             TOSERVER_STATE_INGAME, &Server::handleCommand_ChatMessage }, // 0x32
	{ "TOSERVER_SIGNNODETEXT",             TOSERVER_STATE_INGAME, &Server::handleCommand_Deprecated }, // 0x33
	{ "TOSERVER_CLICK_ACTIVEOBJECT",       TOSERVER_STATE_INGAME, &Server::handleCommand_Deprecated }, // 0x34
	{ "TOSERVER_DAMAGE",                   TOSERVER_STATE_INGAME, &Server::handleCommand_Damage }, // 0x35
	{ "TOSERVER_PASSWORD_LEGACY",          TOSERVER_STATE_INGAME, &Server::handleCommand_Password }, // 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_RECEIVED_MEDIA",           TOSERVER_STATE_STARTUP, &Server::handleCommand_Deprecated }, // 0x41 not used by the server since protocol version 23
	{ "TOSERVER_BREATH",                   TOSERVER_STATE_INGAME, &Server::handleCommand_Deprecated }, // 0x42 Old breath model which is now deprecated for anticheating
	{ "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 };

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,
	null_command_factory,
	null_command_factory,
	null_command_factory,
	null_command_factory,
	null_command_factory,
	null_command_factory,
	null_command_factory,
	null_command_factory,
	null_command_factory,
	null_command_factory,
	null_command_factory,
	null_command_factory,
	null_command_factory,
	null_command_factory,
	{ "TOCLIENT_BLOCKDATA",                2, true }, // 0x20
	{ "TOCLIENT_ADDNODE",                  0, true }, // 0x21
	{ "TOCLIENT_REMOVENODE",               0, true }, // 0x22
	null_command_factory,
	null_command_factory,
	null_command_factory,
	null_command_factory,
	{ "TOCLIENT_INVENTORY",                0, true }, // 0x27
	null_command_factory,
	{ "TOCLIENT_TIME_OF_DAY",              0, true }, // 0x29
	null_command_factory,
	null_command_factory,
	null_command_factory,
	null_command_factory,
	null_command_factory,
	null_command_factory,
	{ "TOCLIENT_CHAT_MESSAGE",             0, true }, // 0x30
	{ "TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD", 0, true }, // 0x31
	{ "TOCLIENT_ACTIVE_OBJECT_MESSAGES",   0, true }, // 0x32 Special packet, sent by 0 (rel) and 1 (unrel) channel
	{ "TOCLIENT_HP",                       0, true }, // 0x33
	{ "TOCLIENT_MOVE_PLAYER",              0, true }, // 0x34
	{ "TOCLIENT_ACCESS_DENIED_LEGACY",     0, true }, // 0x35
	{ "TOCLIENT_PLAYERITEM",               0, false }, // 0x36 obsolete
	{ "TOCLIENT_DEATHSCREEN",              0, true }, // 0x37
	{ "TOCLIENT_MEDIA",                    2, true }, // 0x38
	{ "TOCLIENT_TOOLDEF",                  0, false }, // 0x39 obsolete
	{ "TOCLIENT_NODEDEF",                  0, true }, // 0x3a
	{ "TOCLIENT_CRAFTITEMDEF",             0, false }, // 0x3b obsolete
	{ "TOCLIENT_ANNOUNCE_MEDIA",           0, true }, // 0x3c
	{ "TOCLIENT_ITEMDEF",                  0, true }, // 0x3d
	null_command_factory,
	{ "TOCLIENT_PLAY_SOUND",               0, true }, // 0x3f
	{ "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
	{ "TOCLIENT_DELETE_PARTICLESPAWNER_LEGACY",   0, true }, // 0x48
	{ "TOCLIENT_HUDADD",                   1, true }, // 0x49
	{ "TOCLIENT_HUDRM",                    1, true }, // 0x4a
	{ "TOCLIENT_HUDCHANGE",                0, true }, // 0x4b
	{ "TOCLIENT_HUD_SET_FLAGS",            0, true }, // 0x4c
	{ "TOCLIENT_HUD_SET_PARAM",            0, 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
	null_command_factory,
	null_command_factory,
	null_command_factory,
	null_command_factory,
	null_command_factory,
	null_command_factory,
	null_command_factory,
	null_command_factory,
	null_command_factory,
	null_command_factory,
	{ "TOSERVER_SRP_BYTES_S_B",            0, true }, // 0x60
};


code:
/*
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.
*/

#ifndef SERVEROPCODES_HEADER
#define SERVEROPCODES_HEADER

#include "server.h"
#include "networkprotocol.h"
#include "networkpacket.h"

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];

#endif