/* Minetest Copyright (C) 2013 celeron55, Perttu Ahola 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 #include #include #include 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 data; // Data of the packet, including headers float time; // Seconds from buffering the packet or re-sending float totaltime; // Seconds from buffering the packet unsigned int 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 &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 std::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 std::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 std::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 (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::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 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 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 insert(BufferedPacket &p, bool reliable); void removeUnreliableTimedOuts(float dtime, float timeout); private: // Key is seqnum std::map m_buf; Mutex m_map_mutex; }; struct OutgoingPacket { u16 peer_id; u8 channelnum; SharedBuffer data; bool reliable; bool ack; OutgoingPacket(u16 peer_id_, u8 channelnum_, SharedBuffer 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 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_, SharedBuffer data_) { type = CONCMD_ACK; peer_id = peer_id_; channelnum = channelnum_; data = data_; reliable = false; } void createPeer(u16 peer_id_, SharedBuffer data_) { type = CONCMD_CREATE_PEER; peer_id = peer_id_; data = data_; channelnum = 0; reliable = true; raw = true; } void disableLegacy(u16 peer_id_, SharedBuffer 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 o for (int k = 0; k < 3; ++k) { mat.TextureLayer[k].AnisotropicFilter = false; mat.TextureLayer[k].BilinearFilter = false; mat.TextureLayer[k].TrilinearFilter = false; mat.TextureLayer[k].TextureWrapU = video::ETC_CLAMP_TO_EDGE; mat.TextureLayer[k].TextureWrapV = video::ETC_CLAMP_TO_EDGE; } } void RenderingCoreInterlaced::initTextures() { v2u32 image_size{screensize.X, screensize.Y / 2}; left = driver->addRenderTargetTexture( image_size, "3d_render_left", video::ECF_A8R8G8B8); right = driver->addRenderTargetTexture( image_size, "3d_render_right", video::ECF_A8R8G8B8); mask = driver->addTexture(screensize, "3d_render_mask", video::ECF_A8R8G8B8); initMask(); mat.TextureLayer[0].Texture = left; mat.TextureLayer[1].Texture = right; mat.TextureLayer[2].Texture = mask; } void RenderingCoreInterlaced::clearTextures() { driver->removeTexture(left); driver->removeTexture(right); driver->removeTexture(mask); } void RenderingCoreInterlaced::initMask() { u8 *data = reinterpret_cast<u8 *>(mask->lock()); for (u32 j = 0; j < screensize.Y; j++) { u8 val = j % 2 ? 0xff : 0x00; memset(data, val, 4 * screensize.X); data += 4 * screensize.X; } mask->unlock(); } void RenderingCoreInterlaced::drawAll() { renderBothImages(); merge(); drawHUD(); } void RenderingCoreInterlaced::merge() { static const video::S3DVertex vertices[4] = { video::S3DVertex(1.0, -1.0, 0.0, 0.0, 0.0, -1.0, video::SColor(255, 0, 255, 255), 1.0, 0.0), video::S3DVertex(-1.0, -1.0, 0.0, 0.0, 0.0, -1.0, video::SColor(255, 255, 0, 255), 0.0, 0.0), video::S3DVertex(-1.0, 1.0, 0.0, 0.0, 0.0, -1.0, video::SColor(255, 255, 255, 0), 0.0, 1.0), video::S3DVertex(1.0, 1.0, 0.0, 0.0, 0.0, -1.0, video::SColor(255, 255, 255, 255), 1.0, 1.0), }; static const u16 indices[6] = {0, 1, 2, 2, 3, 0}; driver->setMaterial(mat); driver->drawVertexPrimitiveList(&vertices, 4, &indices, 2); } void RenderingCoreInterlaced::useEye(bool _right) { driver->setRenderTarget(_right ? right : left, true, true, skycolor); RenderingCoreStereo::useEye(_right); } void RenderingCoreInterlaced::resetEye() { driver->setRenderTarget(nullptr, false, false, skycolor); RenderingCoreStereo::resetEye(); } ity PeerHandler *m_bc_peerhandler; int m_bc_receive_timeout; bool m_shutting_down; u16 m_next_remote_peer_id; }; } // namespace #endif