#pragma once // p2p.h - peer to peer UDP networking for voice // // This header is *very* toxic. #include #include #include #include #include #include #include #include #include //#include #include #include #include using namespace Microsoft::Xbox::Services::Multiplayer; using namespace Windows::Xbox::Networking; using namespace Windows::Foundation; namespace Xp2p { struct PacketHeader; class Peer; typedef boost::shared_ptr PeerPtr; enum { kNetworkPort = 8700, // NOTE: check the manifest! kMaxUdpSize = 1264, // what MS says in the XDK help }; enum NetResult { Net_Ok, Net_Fail = -1, // operation failed Net_Timeout = -2, // operation timed out (you'll be getting quite a few of these) Net_Peer = -3, // bad peer Net_Retry = -4, // operation aborted because the peer was busy, the calling code should try again Net_NotReady = -5, // operation aborted because the peer is not ready, Net_Packet = -6, // bad incoming packet }; enum PacketFlags { Packet_Default = 0, Packet_Reliable = 1, Packet_Ordered = 2, }; struct Address { SOCKADDR_STORAGE rawdata; Address(); Address(in6_addr addr, unsigned port); sockaddr* toSockaddr() const { return (sockaddr*)&rawdata; } int size() const { return sizeof(rawdata); } in6_addr addr6() const { return ((sockaddr_in6&)rawdata).sin6_addr; } unsigned port() const { return ntohs( ((sockaddr_in6&)rawdata).sin6_port ); } char* str(char* to) const; }; class Sock : public boost::noncopyable { int fd; public: Sock(unsigned port); ~Sock(); int send( const Address& to, const void* data, unsigned size, unsigned timeoutMs ); int recv( Address* from, void* data, unsigned size, unsigned timeoutMs ); }; class Peer : public boost::enable_shared_from_this, boost::noncopyable { friend class Network; friend void dbgPrintTx(PeerPtr p); enum State { State_New, State_Connecting, State_Waiting, State_Ready, State_Busy, State_Busy2, State_Error, State_Dead }; volatile long state; // mutex and state indicator SecureDeviceAssociation^ devAssoc; // device association (kinda like VPN transport layer between two consoles) Address remoteAddr; // remote address as reported by devass uint32 pingTime; // last ping time uint32 id; // peer id (MultiplayerSessionMember::MemberId) // transmission control, see comment below uint32 txseq; uint32 rxseq; uint32 txseqR; uint32 rxseqR; uint32 rxackR; std::vector reliable; // retransmit queue for reliable packets Peer(); int setstate( int expect, int replace ); public: void* userData; // whatever you want to put in here Platform::Object^ userObject; // or here ~Peer() {} uint32 getPeerId() const { return id; } }; /* * TODO: write an essay * */ ////////////////////////////////////////////////////////////////////////// // Packets #include struct PacketHeader { // fill these out: uint16 size; // entire packet size (incl. this header) uint8 type; // type 0 is reserved, do not use! // don't bother with these: uint8 flags; // a combination of Packet_XXX uint32 seq; // sequence # (all) uint32 seqR; // sequence # (last reliable) uint32 id; // peer id (MultiplayerSessionMember::MemberId) uint32 timestamp; uint8 reserved[32]; // reserved for now }; // defines a packet of maximum length // used to recv() an unknown packet struct PacketStorage: PacketHeader { enum { MaxPayloadSize = kMaxUdpSize - sizeof(PacketHeader) }; uint8 payload[MaxPayloadSize]; }; #include ////////////////////////////////////////////////////////////////////////// // Network class Network: public boost::noncopyable { typedef std::lock_guard lock; Sock sock; uint32 myMemberId; // (MultiplayerSessionMember::MemberId) SecureDeviceAssociationTemplate^ assTemplate; Windows::Foundation::EventRegistrationToken incomingHandler; volatile long controlRunFlag; // control thread run/exit flag std::mutex peerMutex; // only used to manipulate these arrays; use Peer::setstate() to lock individual peers std::vector< PeerPtr > peers; std::vector< SecureDeviceAssociation^ > pendingList; boost::thread controlThreadObject; public: Network( MultiplayerSessionMember^ currentUser ); ~Network(); PeerPtr createPeer( MultiplayerSessionMember^ member, boost::function onConnected ); void destroyPeer( PeerPtr peer ); // sends a packet to that peer // returns number of bytes sent or -1 on error NetResult sendPacket( PeerPtr peer, PacketHeader* packet, unsigned timeoutMs, unsigned flags ); // receives a packet NetResult recvPacket( PacketStorage* stor, unsigned timeoutMs ); // MUST be called after each recvPacket // figures our who sent this packet NetResult processPacket( PeerPtr& who, PacketHeader* header ); private: void connectPeer( PeerPtr peer, MultiplayerSessionMember^ member, boost::function onConnected ); // call on a separate thread! PeerPtr getPeerByAssoc( SecureDeviceAssociation^ incoming ); void controlThread(); }; }