/* Minetest-c55 Copyright (C) 2010 celeron55, Perttu Ahola This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU 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 #include #include "debug.h" #include "common_irrlicht.h" #include "socket.h" #include "utility.h" #include "exceptions.h" #include "constants.h" namespace con { /* Exceptions */ class NotFoundException : public BaseException { public: NotFoundException(const char *s): BaseException(s) {} }; class PeerNotFoundException : public BaseException { public: PeerNotFoundException(const char *s): BaseException(s) {} }; class ConnectionException : public BaseException { public: ConnectionException(const char *s): BaseException(s) {} }; /*class 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) {} }; inline u16 readPeerId(u8 *packetdata) { return readU16(&packetdata[4]); } inline u8 readChannel(u8 *packetdata) { return readU8(&packetdata[6]); } #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 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 &data, u32 protocol_id, u16 sender_peer_id, u8 channel); // Add the TYPE_ORIGINAL header to the data SharedBuffer makeOriginalPacket( SharedBuffer data); // Split data in chunks and add TYPE_SPLIT headers to them core::list > makeSplitPacket( SharedBuffer 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 core::list > makeAutoSplitPacket( SharedBuffer data, u32 chunksize_max, u16 &split_seqnum); // Add the TYPE_RELIABLE header to the data SharedBuffer makeReliablePacket( SharedBuffer data, u16 seqnum); struct IncomingSplitPacket { IncomingSplitPacket() { time = 0.0; reliable = false; } // Key is chunk number, value is data without headers core::map > 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 is reserved for making new connections value 1 is reserved for server channel: The lower the number, the higher the priority is. Only channels 0, 1 and 2 exist. */ #define BASE_HEADER_SIZE 7 #define PEER_ID_INEXISTENT 0 #define PEER_ID_SERVER 1 #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 core::list::Iterator RPBSearchResult; class ReliablePacketBuffer { public: void print(); bool empty(); u32 size(); RPBSearchResult findPacket(u16 seqnum); RPBSearchResult notFound(); u16 getFirstSeqnum(); BufferedPacket popFirst(); BufferedPacket popSeqnum(u16 seqnum); void insert(BufferedPacket &p); void incrementTimeouts(float dtime); void resetTimedOuts(float timeout); bool anyTotaltimeReached(float timeout); core::list getTimedOuts(float timeout); private: core::list m_list; }; /* 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 insert(BufferedPacket &p, bool reliable); void removeUnreliableTimedOuts(float dtime, float timeout); private: // Key is seqnum core::map m_buf; }; class Connection; struct Channel { Channel(); ~Channel(); /* Processes a packet with the basic header stripped out. Parameters: packetdata: Data in packet (with no base headers) con: The connection to which the channel is associated (used for sending back stuff (ACKs)) 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 ProcessPacket( SharedBuffer packetdata, Connection *con, u16 peer_id, u8 channelnum, bool reliable=false); // Returns next data from a buffer if possible // throws a NoIncomingDataException if no data is available // If found, sets peer_id SharedBuffer CheckIncomingBuffers(Connection *con, u16 &peer_id); 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; private: }; class Connection { public: Connection( u32 protocol_id, u32 max_packet_size, float timeout, PeerHandler *peerhandler ); ~Connection(); void setTimeoutMs(int timeout){ m_socket.setTimeoutMs(timeout); } // Start being a server void Serve(unsigned short port); // Connect to a server void Connect(Address address); bool Connected(); void Disconnect(); // Sets peer_id SharedBuffer GetFromBuffers(u16 &peer_id); // The peer_id of sender is stored in peer_id // Return value: I guess this always throws an exception or // actually gets data // May call PeerHandler methods u32 Receive(u16 &peer_id, u8 *data, u32 datasize); // These will automatically package the data as an original or split void SendToAll(u8 channelnum, SharedBuffer data, bool reliable); void Send(u16 peer_id, u8 channelnum, SharedBuffer data, bool reliable); // Send data as a packet; it will be wrapped in base header and // optionally to a reliable packet. void SendAsPacket(u16 peer_id, u8 channelnum, SharedBuffer data, bool reliable); // Sends a raw packet void RawSend(const BufferedPacket &packet); // May call PeerHandler methods void RunTimeouts(float dtime); // Can throw a PeerNotFoundException Peer* GetPeer(u16 peer_id); // returns NULL if failed Peer* GetPeerNoEx(u16 peer_id); core::list GetPeers(); // Calls PeerHandler::deletingPeer // Returns false if peer was not found bool deletePeer(u16 peer_id, bool timeout); void SetPeerID(u16 id){ m_peer_id = id; } u16 GetPeerID(){ return m_peer_id; } u32 GetProtocolID(){ return m_protocol_id; } // For debug printing void PrintInfo(std::ostream &out); void PrintInfo(); u16 m_indentation; private: u32 m_protocol_id; float m_timeout; PeerHandler *m_peerhandler; core::map m_peers; u16 m_peer_id; //bool m_waiting_new_peer_id; u32 m_max_packet_size; UDPSocket m_socket; }; } // namespace #endif