//////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// // UDP Communication Engine #include "gsUdpEngine.h" //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// // Internal Structures // Internal representation of a connection // Also includes a ip and port for mapping to a connection typedef struct { unsigned int mAddr; unsigned short mPort; GT2Connection mConnection; } GSUdpRemotePeer; // Message handler used filter traffic to a specific SDK or part of application typedef struct { unsigned char mInitialMsg[GS_UDP_MSG_HEADER_LEN]; unsigned char mHeader[GS_UDP_MSG_HEADER_LEN]; DArray mPendingConnections; gsUdpConnClosedCallback mClosed; gsUdpConnReceivedDataCallback mReceived; gsUdpConnConnectedCallback mConnected; gsUdpConnPingCallback mPingReply; gsUdpErrorCallback mNetworkError; void *mUserData; } GSUdpMsgHandler; // The internal representation of UDP Communication Engine typedef struct { GT2Socket mSocket; DArray mRemotePeers; DArray mMsgHandlers; gsi_bool mInitialized; // Application callbacks for connection that gets // un-handled messages gsUdpConnConnectedCallback mAppConnected; gsUdpConnClosedCallback mAppClosed; gsUdpConnPingCallback mAppPingReply; gsUdpConnReceivedDataCallback mAppRecvData; gsUdpAppConnectAttemptCallback mAppConnAttempt; // Error callback ? gsUdpErrorCallback mAppNetworkError; // Unknown Message gsUdpUnknownMsgCallback mAppUnknownMessage; void *mAppUserData; // This was any easier way to keep track of pending connections // It does not rely on an address since it only keeps track of pending // connections an app makes. int mAppPendingConnections; unsigned int mLocalAddr; unsigned short mLocalPort; }GSUdpEngineObject; //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// // Acts as the function to return the singleton // This function is not exposed. It will only be // used internally to do any modifications to the // GSUdpEngineObject GSUdpEngineObject * gsUdpEngineGetEngine() { static GSUdpEngineObject aUdpObject; return &aUdpObject; } //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// // Message Handler DArray functions void gsUdpMsgHandlerFree(void *theMsgHandler) { GSUdpMsgHandler *aHandler= (GSUdpMsgHandler *)theMsgHandler; ArrayFree(aHandler->mPendingConnections); } // Used to find a message handler based on the initial message. int gsUdpMsgHandlerCompare(const void *theFirstHandler, const void *theSecondHandler) { GSUdpMsgHandler *msgHandler1 = (GSUdpMsgHandler *)theFirstHandler, *msgHandler2 = (GSUdpMsgHandler *)theSecondHandler; int initCmp; initCmp = memcmp(msgHandler1->mInitialMsg, msgHandler2->mInitialMsg, GS_UDP_MSG_HEADER_LEN); return initCmp; } // Used to find a message handler based on the header. int gsUdpMsgHandlerCompare2(const void *theFirstHandler, const void *theSecondHandler) { GSUdpMsgHandler *msgHandler1 = (GSUdpMsgHandler *)theFirstHandler, *msgHandler2 = (GSUdpMsgHandler *)theSecondHandler; int headerCmp; headerCmp = memcmp(msgHandler1->mHeader, msgHandler2->mHeader, GS_UDP_MSG_HEADER_LEN); return headerCmp; } //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// // Remote Peer DArray compare functions // Finds a remote peer based on IP and Port int gsUdpRemotePeerCompare(const void *theFirstPeer, const void *theSecondPeer) { GSUdpRemotePeer *aPeer1 = (GSUdpRemotePeer *)theFirstPeer, *aPeer2 = (GSUdpRemotePeer *)theSecondPeer; if (aPeer1->mAddr != aPeer2->mAddr) return 1; if (aPeer1->mPort != aPeer2->mPort) return 1; return 0; } // Finds a remote peer based on a GT2Connection int gsUdpRemotePeerCompare2(const void *theFirstPeer, const void *theSecondPeer) { GSUdpRemotePeer *aPeer1 = (GSUdpRemotePeer *)theFirstPeer, *aPeer2 = (GSUdpRemotePeer *)theSecondPeer; if (aPeer1->mConnection != aPeer2->mConnection) return 1; return 0; } //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// // Lets the Message Handler and App know about network errors void gsUdpSocketError(GT2Socket theSocket) { int i, len; GSUdpEngineObject *aUdp = gsUdpEngineGetEngine(); gsDebugFormat(GSIDebugCat_Common, GSIDebugType_Network, GSIDebugLevel_Comment, "[Udp Engine] Socket error, passing to app and message handlers\n"); if (aUdp->mAppNetworkError) aUdp->mAppNetworkError(GS_UDP_NETWORK_ERROR, aUdp->mAppUserData); len = ArrayLength(aUdp->mMsgHandlers); for (i = 0; i < len; i++) { GSUdpMsgHandler *aMsgHandler = (GSUdpMsgHandler *)ArrayNth(aUdp->mMsgHandlers, i); if (aMsgHandler->mNetworkError) aMsgHandler->mNetworkError(GS_UDP_NETWORK_ERROR, aMsgHandler->mUserData); } GSI_UNUSED(theSocket); } //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// // Lets the App and Message Handlers know a peer left void gsUdpClosedRoutingCB(GT2Connection theConnection, GT2CloseReason reason) { GSUdpEngineObject *aUdp = gsUdpEngineGetEngine(); GSUdpRemotePeer aRemotePeer; int index, len; GSUdpCloseReason aReason; char anAddr[GS_IP_ADDR_AND_PORT]; if (reason == GT2CommunicationError || reason == GT2SocketError) aReason = GS_UDP_CLOSED_BY_COMM_ERROR; else if (reason == GT2NotEnoughMemory) aReason = GS_UDP_CLOSED_BY_LOW_MEM; else aReason = (GSUdpCloseReason)reason; gsDebugFormat(GSIDebugCat_Common, GSIDebugType_Network, GSIDebugLevel_Comment, "[Udp Engine] Connection closed to %s\n", gt2AddressToString(gt2GetRemoteIP(theConnection), gt2GetRemotePort(theConnection), anAddr)); len = ArrayLength(aUdp->mMsgHandlers); for (index = 0; index < len; index++) { GSUdpMsgHandler *aHandler = (GSUdpMsgHandler *)ArrayNth(aUdp->mMsgHandlers, index); if (aHandler->mClosed) { gsDebugFormat(GSIDebugCat_Common, GSIDebugType_Network, GSIDebugLevel_Comment, "[Udp Engine] Connection closed: passed to message handler\n"); aHandler->mClosed(gt2GetRemoteIP(theConnection), gt2GetRemotePort(theConnection), aReason, aHandler->mUserData); } } if (aUdp->mAppClosed) { gsDebugFormat(GSIDebugCat_Common, GSIDebugType_Network, GSIDebugLevel_Comment, "[Udp Engine] Connection closed: passed to app\n"); aUdp->mAppClosed(gt2GetRemoteIP(theConnection), gt2GetRemotePort(theConnection), aReason, aUdp->mAppUserData); } aRemotePeer.mConnection = theConnection; index = ArraySearch(aUdp->mRemotePeers, &aRemotePeer, gsUdpRemotePeerCompare2, 0, 0); if (index != NOT_FOUND) { ArrayDeleteAt(aUdp->mRemotePeers, index); } GSI_UNUSED(anAddr); } //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// // When a peer has accepted a GT2Connection the UDP layer needs to let // higher level app or message handler know that it accepted the request to // to message a peer. void gsUdpConnectedRoutingCB(GT2Connection theConnection, GT2Result theResult, GT2Byte *theMessage, int theMessageLen) { GSUdpEngineObject *aUdp = gsUdpEngineGetEngine(); int aIndex, len; GSUdpErrorCode aCode; char anAddr[GS_IP_ADDR_AND_PORT]; switch(theResult) { case GT2NegotiationError: aCode = GS_UDP_REMOTE_ERROR; break; case GT2Rejected: aCode = GS_UDP_REJECTED; break; case GT2TimedOut: aCode = GS_UDP_TIMED_OUT; break; case GT2Success: aCode = GS_UDP_NO_ERROR; break; default: aCode = GS_UDP_UNKNOWN_ERROR; break; } if (theResult == GT2Rejected) { int aRemotePeerIdx; GSUdpRemotePeer aRemotePeer; aRemotePeer.mAddr = gt2GetRemoteIP(theConnection); aRemotePeer.mPort = gt2GetRemotePort(theConnection); aRemotePeerIdx = ArraySearch(aUdp->mRemotePeers, &aRemotePeer, gsUdpRemotePeerCompare, 0, 0); if (aRemotePeerIdx != NOT_FOUND) { gsDebugFormat(GSIDebugCat_Common, GSIDebugType_Network, GSIDebugLevel_Comment, "[Udp Engine] Connect rejected by %s\n", gt2AddressToString(gt2GetRemoteIP(theConnection), gt2GetRemotePort(theConnection), anAddr)); ArrayDeleteAt(aUdp->mRemotePeers, aRemotePeerIdx); } } len = ArrayLength(aUdp->mMsgHandlers); for (aIndex = 0; aIndex < len; aIndex++) { int aRemotePeerIdx; GSUdpRemotePeer aRemotePeer; GSUdpMsgHandler *aTempHandler = (GSUdpMsgHandler *)ArrayNth(aUdp->mMsgHandlers, aIndex); aRemotePeer.mAddr = gt2GetRemoteIP(theConnection); aRemotePeer.mPort = gt2GetRemotePort(theConnection); aRemotePeerIdx = ArraySearch(aTempHandler->mPendingConnections, &aRemotePeer, gsUdpRemotePeerCompare, 0, 0); if (aRemotePeerIdx != NOT_FOUND) { if (aTempHandler->mConnected) { gsDebugFormat(GSIDebugCat_Common, GSIDebugType_Network, GSIDebugLevel_Comment, "[Udp Engine] Passing connect result to message handler\n"); aTempHandler->mConnected(gt2GetRemoteIP(theConnection), gt2GetRemotePort(theConnection), aCode, theResult == GT2Rejected ? gsi_true : gsi_false, aTempHandler->mUserData); } ArrayDeleteAt(aTempHandler->mPendingConnections, aRemotePeerIdx); return; } } if (aUdp->mAppPendingConnections > 0) { if (aUdp->mAppConnected) { gsDebugFormat(GSIDebugCat_Common, GSIDebugType_Network, GSIDebugLevel_Comment, "[Udp Engine] Passing connect result to app\n"); aUdp->mAppConnected(gt2GetRemoteIP(theConnection),gt2GetRemotePort(theConnection), aCode, theResult == GT2Rejected ? gsi_true : gsi_false, aUdp->mAppUserData); } aUdp->mAppPendingConnections--; } GSI_UNUSED(theMessage); GSI_UNUSED(theMessageLen); GSI_UNUSED(anAddr); } //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// // Pings are passed on to higher level app or message handlers void gsUdpPingRoutingCB(GT2Connection theConnection, int theLatency) { GSUdpEngineObject *aUdp = gsUdpEngineGetEngine(); int index, len; char anAddr[GS_IP_ADDR_AND_PORT]; gsDebugFormat(GSIDebugCat_Common, GSIDebugType_Network, GSIDebugLevel_Comment, "[Udp Engine] Received ping from %s\n", gt2AddressToString(gt2GetRemoteIP(theConnection), gt2GetRemotePort(theConnection), anAddr)); len = ArrayLength(aUdp->mMsgHandlers); for (index = 0; index < len; index++) { GSUdpMsgHandler *aHandler = (GSUdpMsgHandler *)ArrayNth(aUdp->mMsgHandlers, index); if (aHandler->mPingReply) { gsDebugFormat(GSIDebugCat_Common, GSIDebugType_Network, GSIDebugLevel_Comment, "[Udp Engine] Passed to message handler\n"); aHandler->mPingReply(gt2GetRemoteIP(theConnection), gt2GetRemotePort(theConnection), (unsigned int)theLatency, aHandler->mUserData); return; } } if (aUdp->mAppPingReply) { gsDebugFormat(GSIDebugCat_Common, GSIDebugType_Network, GSIDebugLevel_Comment, "[Udp Engine] Passed to app\n"); aUdp->mAppPingReply(gt2GetRemoteIP(theConnection), gt2GetRemotePort(theConnection), (unsigned int)theLatency, aUdp->mAppUserData); } GSI_UNUSED(anAddr); } //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// // Any data received prompts the UDP layer to first find a higher level // message handler to handle the data. If there was no message handler // found, the data is passed to the higher level app. void gsUdpReceivedRoutingCB(GT2Connection theConnection, GT2Byte *theMessage, int theMessageLen, GT2Bool reliable) { GSUdpEngineObject *aUdp = gsUdpEngineGetEngine(); GSUdpMsgHandler aHandler; int index; char anAddr[GS_IP_ADDR_AND_PORT]; gsDebugFormat(GSIDebugCat_Common, GSIDebugType_Network, GSIDebugLevel_Comment, "[Udp Engine] Received data from %s\n", gt2AddressToString(gt2GetRemoteIP(theConnection), gt2GetRemotePort(theConnection), anAddr)); //If there is a handler, pass it to the handler //The header should not be stripped off if (theMessageLen >= GS_UDP_MSG_HEADER_LEN) { memcpy(aHandler.mHeader, theMessage, GS_UDP_MSG_HEADER_LEN); index = ArraySearch(aUdp->mMsgHandlers, &aHandler, gsUdpMsgHandlerCompare2, 0, 0); if (index != NOT_FOUND) { GSUdpMsgHandler *aHandlerFound = (GSUdpMsgHandler *)ArrayNth(aUdp->mMsgHandlers, index); if (aHandlerFound->mReceived) { gsDebugFormat(GSIDebugCat_Common, GSIDebugType_Network, GSIDebugLevel_Comment, "[Udp Engine] Passed to message handler\n"); aHandlerFound->mReceived(gt2GetRemoteIP(theConnection), gt2GetRemotePort(theConnection), theMessage + GS_UDP_MSG_HEADER_LEN, (unsigned int)(theMessageLen - GS_UDP_MSG_HEADER_LEN), reliable, aHandlerFound->mUserData); return; } } } if (aUdp->mAppRecvData) { gsDebugFormat(GSIDebugCat_Common, GSIDebugType_Network, GSIDebugLevel_Comment, "[Udp Engine] Passed to app\n"); aUdp->mAppRecvData(gt2GetRemoteIP(theConnection), gt2GetRemotePort(theConnection), theMessage, (unsigned int)theMessageLen, reliable, aUdp->mAppUserData); } GSI_UNUSED(anAddr); } //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// // Messages that are not recognized are passed only to the higher level app // since message handlers always request to talk to peers GT2Bool gsUdpUnrecognizedMsgCB(GT2Socket theSocket, unsigned int theIp, unsigned short thePort, GT2Byte * theMessage, int theMsgLen) { GSUdpEngineObject *aUdp = gsUdpEngineGetEngine(); char anAddr[GS_IP_ADDR_AND_PORT]; if (aUdp->mAppUnknownMessage) { gsi_bool aRet; gsDebugFormat(GSIDebugCat_Common, GSIDebugType_Network, GSIDebugLevel_Comment, "[Udp Engine] Unknown message from %s, passing to app\n", gt2AddressToString(theIp, thePort, anAddr)); aRet = aUdp->mAppUnknownMessage(theIp, thePort, (unsigned char *)theMessage, (unsigned int)theMsgLen, aUdp->mAppUserData); return aRet ? GT2True : GT2False; } GSI_UNUSED(theSocket); GSI_UNUSED(anAddr); return GT2False; } //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// // Requests for communication from a peer is handled by first checking if the // initial message has a message handler registered for it. Otherwise // the message is passed onto the app. void gsUdpConnAttemptCB(GT2Socket socket, GT2Connection connection, unsigned int ip, unsigned short port, int latency, GT2Byte * message, int len) { // Get the message handler for the connection int index; GSUdpMsgHandler aHandler; GSUdpRemotePeer aRemotePeer; GSUdpEngineObject *aUdp = gsUdpEngineGetEngine(); char anAddr[GS_IP_ADDR_AND_PORT]; gsDebugFormat(GSIDebugCat_Common, GSIDebugType_Network, GSIDebugLevel_Comment, "[Udp Engine] Connection attempt from %s\n", gt2AddressToString(ip, port, anAddr)); //If there is a handler, automatically accept a connection if the initial message is //the same as the handler's registered initial message if (len >= GS_UDP_MSG_HEADER_LEN) { memcpy(aHandler.mInitialMsg, message, GS_UDP_MSG_HEADER_LEN); aRemotePeer.mAddr = ip; aRemotePeer.mPort = port; aRemotePeer.mConnection = connection; ArrayAppend(aUdp->mRemotePeers, &aRemotePeer); index = ArraySearch(aUdp->mMsgHandlers, &aHandler, gsUdpMsgHandlerCompare, 0, 0); if (index != NOT_FOUND) { GT2ConnectionCallbacks aCallbacks; aCallbacks.closed = gsUdpClosedRoutingCB; aCallbacks.connected = gsUdpConnectedRoutingCB; aCallbacks.ping = gsUdpPingRoutingCB; aCallbacks.received = gsUdpReceivedRoutingCB; // Automatically accept connections for Message Handlers gt2Accept(aRemotePeer.mConnection, &aCallbacks); gsDebugFormat(GSIDebugCat_Common, GSIDebugType_Network, GSIDebugLevel_Comment, "[Udp Engine] Connection attempt auto-accepted for message handler\n"); return; } } // all other messages go to the app if (aUdp->mAppConnAttempt) { gsDebugFormat(GSIDebugCat_Common, GSIDebugType_Network, GSIDebugLevel_Comment, "[Udp Engine] Connection attempt from %s, asking app to accept/reject\n", gt2AddressToString(ip, port, anAddr)); aUdp->mAppConnAttempt(ip, port, latency, (unsigned char *)message, (unsigned int)len, aUdp->mAppUserData); } else { // Reject any un-handled connections or unknown connections gt2Reject(connection, NULL, 0); ArrayRemoveAt(aUdp->mRemotePeers, ArrayLength(aUdp->mRemotePeers) -1); } GSI_UNUSED(socket); GSI_UNUSED(anAddr); } //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// // Public functions //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// // Used to check if the UDP layer needs to be initialized gsi_bool gsUdpEngineIsInitialized() { GSUdpEngineObject *aUdp = gsUdpEngineGetEngine(); return aUdp->mInitialized; } //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// // Initializes the UDP layer // A specific port can be used if needed. // An app must register their callbacks here. // An App must also call this before starting SDKs since they might start // the UDP layer without the app having a chance to register its callbacks. // Any message handler can get the port the UDP layer is using if it did not // initialize the UDP Layer. // Use of the UDP Layer requires it to be initialized as is the case with most // functions below. GSUdpErrorCode gsUdpEngineInitialize(unsigned short thePort, int theIncomingBufSize, int theOutgoingBufSize, gsUdpErrorCallback theAppNetworkError, gsUdpConnConnectedCallback theAppConnected, gsUdpConnClosedCallback theAppClosed, gsUdpConnPingCallback theAppPing, gsUdpConnReceivedDataCallback theAppReceive, gsUdpUnknownMsgCallback theAppUnownMsg, gsUdpAppConnectAttemptCallback theAppConnectAttempt, void *theAppUserData) { int incomingBufferSize, outgoingBufferSize; char anAddr[GS_IP_ADDR_AND_PORT]; GT2Result aGt2Result; // Grab the single instance of the UDP Communication Engine GSUdpEngineObject *aUdp = gsUdpEngineGetEngine(); // Setup our gt2 buffer sizes for reliable messages incomingBufferSize = theIncomingBufSize != 0 ? theIncomingBufSize : GS_UDP_DEFAULT_IN_BUFFSIZE; outgoingBufferSize = theOutgoingBufSize != 0 ? theOutgoingBufSize : GS_UDP_DEFAULT_OUT_BUFFSIZE; // Setup our internal socket that will be shared among more than one application aUdp->mAppNetworkError = theAppNetworkError; aUdp->mAppUnknownMessage = theAppUnownMsg; aUdp->mAppConnected = theAppConnected; aUdp->mAppClosed = theAppClosed; aUdp->mAppPingReply = theAppPing; aUdp->mAppRecvData = theAppReceive; aUdp->mAppConnAttempt = theAppConnectAttempt; // Any port can be used gt2AddressToString(0, thePort, anAddr); aGt2Result = gt2CreateSocket(&aUdp->mSocket, anAddr, outgoingBufferSize, incomingBufferSize, gsUdpSocketError); if (aGt2Result != GT2Success) { gsDebugFormat(GSIDebugCat_Common, GSIDebugType_Network, GSIDebugLevel_WarmError, "[Udp Engine] error creating gt2 socket, error code: %d\n", aGt2Result); return GS_UDP_NETWORK_ERROR; } // We'll need to keep track of connections with an array of GPconnection to address mapping aUdp->mRemotePeers = ArrayNew(sizeof(GSUdpRemotePeer), 1, NULL); if (aUdp->mRemotePeers == NULL) { gsDebugFormat(GSIDebugCat_Common, GSIDebugType_Memory, GSIDebugLevel_HotError, "[Udp Engine] No more memory!!!\n"); return GS_UDP_NO_MEMORY; } aUdp->mMsgHandlers = ArrayNew(sizeof(GSUdpMsgHandler), 1, gsUdpMsgHandlerFree); if (aUdp->mMsgHandlers == NULL) { gsDebugFormat(GSIDebugCat_Common, GSIDebugType_Memory, GSIDebugLevel_HotError, "[Udp Engine] No more memory!!!\n"); return GS_UDP_NO_MEMORY; } // Used by the manager to receive messages from backend services or other clients // that may not be using gt2 gt2SetUnrecognizedMessageCallback(aUdp->mSocket, gsUdpUnrecognizedMsgCB); // Automatically start listening for all SDKS and the game if game uses UDP Engine gt2Listen(aUdp->mSocket, gsUdpConnAttemptCB); aUdp->mLocalAddr = gt2GetLocalIP(aUdp->mSocket); aUdp->mLocalPort = gt2GetLocalPort(aUdp->mSocket); aUdp->mAppPendingConnections = 0; aUdp->mInitialized = gsi_true; if (theAppUserData) { aUdp->mAppUserData = theAppUserData; } else aUdp->mAppUserData = NULL; return GS_UDP_NO_ERROR; } //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// // UDP Layer must be initialized // Used obtain the peer's state // theIp and thePort cannot be 0 (Zero) GSUdpErrorCode gsUdpEngineGetPeerState(unsigned int theIp, unsigned short thePort, GSUdpPeerState *thePeerState) { GSUdpEngineObject *aUdp = gsUdpEngineGetEngine(); GSUdpRemotePeer aPeer, *aPeerFound; int index; GS_ASSERT(aUdp->mInitialized); GS_ASSERT(theIp); GS_ASSERT(thePort); GS_ASSERT(thePeerState != NULL); if (!aUdp->mInitialized) { gsDebugFormat(GSIDebugCat_Common, GSIDebugType_State, GSIDebugLevel_Debug, "[Udp Engine] Engine not initialized\n"); *thePeerState = GS_UDP_PEER_CLOSED; return GS_UDP_NOT_INITIALIZED; } aPeer.mAddr = theIp; aPeer.mPort = thePort; index = ArraySearch(aUdp->mRemotePeers, &aPeer, gsUdpRemotePeerCompare, 0, 0); if (index == NOT_FOUND) { *thePeerState = GS_UDP_PEER_CLOSED; return GS_UDP_NO_ERROR; } aPeerFound = (GSUdpRemotePeer *)ArrayNth(aUdp->mRemotePeers, index); *thePeerState = (GSUdpPeerState)gt2GetConnectionState(aPeerFound->mConnection); return GS_UDP_NO_ERROR; } //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// // UDP Layer must be initialized // theIp and thePort cannot be 0 (Zero) // Starts a request to open a communication channel with another peer based on // IP and port. GSUdpErrorCode gsUdpEngineStartTalkingToPeer(unsigned int theIp, unsigned short thePort, char theInitMsg[GS_UDP_MSG_HEADER_LEN], int timeOut) { char anAddr[GS_IP_ADDR_AND_PORT]; GSUdpRemotePeer aRemotePeer; GSUdpMsgHandler aHandler; GSUdpEngineObject *aUdp = gsUdpEngineGetEngine(); GT2ConnectionCallbacks aCallbacks; int index; GS_ASSERT(aUdp->mInitialized); GS_ASSERT(theIp); GS_ASSERT(thePort); if (!aUdp->mInitialized) { gsDebugFormat(GSIDebugCat_Common, GSIDebugType_Network, GSIDebugLevel_Debug, "[Udp Engine] Engine not initialized\n"); return GS_UDP_NETWORK_ERROR; } if (theIp == 0 || thePort == 0) { gsDebugFormat(GSIDebugCat_Common, GSIDebugType_Network, GSIDebugLevel_Debug, "[Udp Engine] Invalid parameter(s), check ip, port"); return GS_UDP_PARAMETER_ERROR; } aRemotePeer.mAddr = theIp; // In Network Byte Order for GT2 aRemotePeer.mPort = thePort; // In Host Byte Order for GT2 index = ArraySearch(aUdp->mRemotePeers, &aRemotePeer, gsUdpRemotePeerCompare, 0, 0); if (index != NOT_FOUND) { GSUdpRemotePeer *aPeerFound = (GSUdpRemotePeer *)ArrayNth(aUdp->mRemotePeers, index); GT2ConnectionState aState = gt2GetConnectionState(aPeerFound->mConnection); if (aState == GT2Connected) { gsDebugFormat(GSIDebugCat_Common, GSIDebugType_Network, GSIDebugLevel_Debug, "[Udp Engine] Engine is already talking to remote address\n"); return GS_UDP_ADDRESS_ALREADY_IN_USE; } else if (aState == GT2Connecting) { memcpy(aHandler.mInitialMsg, theInitMsg, GS_UDP_MSG_HEADER_LEN); index = ArraySearch(aUdp->mMsgHandlers, &aHandler, gsUdpMsgHandlerCompare, 0, 0); if (index != NOT_FOUND) { GSUdpMsgHandler *aHandlerFound = (GSUdpMsgHandler *)ArrayNth(aUdp->mMsgHandlers, index); ArrayAppend(aHandlerFound->mPendingConnections, aPeerFound); } } } else { gt2AddressToString(theIp, thePort, anAddr); aCallbacks.closed = gsUdpClosedRoutingCB; aCallbacks.connected = gsUdpConnectedRoutingCB; aCallbacks.ping = gsUdpPingRoutingCB; aCallbacks.received = gsUdpReceivedRoutingCB; // start the connect without blocking since we want the engine to be as asynchronous as possible gt2Connect(aUdp->mSocket, &aRemotePeer.mConnection, anAddr, (unsigned char *)theInitMsg, GS_UDP_MSG_HEADER_LEN, timeOut, &aCallbacks, GT2False); ArrayAppend(aUdp->mRemotePeers, &aRemotePeer); memcpy(aHandler.mInitialMsg, theInitMsg, GS_UDP_MSG_HEADER_LEN); index = ArraySearch(aUdp->mMsgHandlers, &aHandler, gsUdpMsgHandlerCompare, 0, 0); if (index != NOT_FOUND) { GSUdpRemotePeer *aRemotePeerPtr = (GSUdpRemotePeer *)ArrayNth(aUdp->mRemotePeers, ArrayLength(aUdp->mRemotePeers) - 1); GSUdpMsgHandler *aHandlerFound = (GSUdpMsgHandler *)ArrayNth(aUdp->mMsgHandlers, index); ArrayAppend(aHandlerFound->mPendingConnections, &aRemotePeerPtr); } else { aUdp->mAppPendingConnections++; } } return GS_UDP_NO_ERROR; } //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// // UDP Layer must be initialized // theIp and thePort cannot be 0 (Zero) // Accepts a Peer's request for communication // Should only be used by App GSUdpErrorCode gsUdpEngineAcceptPeer(unsigned int theIp, unsigned short thePort) { GSUdpRemotePeer aRemotePeer; GSUdpEngineObject *aUdp = gsUdpEngineGetEngine(); int index; GS_ASSERT(aUdp->mInitialized); GS_ASSERT(theIp); GS_ASSERT(thePort); if (!aUdp->mInitialized) { gsDebugFormat(GSIDebugCat_Common, GSIDebugType_Network, GSIDebugLevel_Debug, "[Udp Engine] Engine not initialized\n"); return GS_UDP_NETWORK_ERROR; } if (theIp == 0 || thePort == 0) { gsDebugFormat(GSIDebugCat_Common, GSIDebugType_Network, GSIDebugLevel_Debug, "[Udp Engine] Invalid parameter(s), check ip, port\n"); return GS_UDP_PARAMETER_ERROR; } aRemotePeer.mAddr = theIp; aRemotePeer.mPort = thePort; index = ArraySearch(aUdp->mRemotePeers, &aRemotePeer, gsUdpRemotePeerCompare, 0, 0); if (index != NOT_FOUND) { GT2ConnectionCallbacks aCallbacks; GSUdpRemotePeer *aPeerFound = (GSUdpRemotePeer *)ArrayNth(aUdp->mRemotePeers, index); aCallbacks.closed = gsUdpClosedRoutingCB; aCallbacks.connected = gsUdpConnectedRoutingCB; aCallbacks.ping = gsUdpPingRoutingCB; aCallbacks.received = gsUdpReceivedRoutingCB; gt2Accept(aPeerFound->mConnection, &aCallbacks); } return GS_UDP_NO_ERROR; } //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// // UDP Layer must be initialized // theIp and thePort cannot be 0 (Zero) // Rejects a Peer's request for communication // Should only be used by App GSUdpErrorCode gsUdpEngineRejectPeer(unsigned int theIp, unsigned short thePort) { GSUdpRemotePeer aRemotePeer; GSUdpEngineObject *aUdp = gsUdpEngineGetEngine(); int index; GS_ASSERT(aUdp->mInitialized); GS_ASSERT(theIp); GS_ASSERT(thePort); if (!aUdp->mInitialized) { gsDebugFormat(GSIDebugCat_Common, GSIDebugType_Network, GSIDebugLevel_Debug, "[Udp Engine] Engine not initialized\n"); return GS_UDP_NETWORK_ERROR; } if (theIp == 0 || thePort == 0) { gsDebugFormat(GSIDebugCat_Common, GSIDebugType_Network, GSIDebugLevel_Debug, "[Udp Engine] Invalid parameter(s), check ip, port\n"); return GS_UDP_PARAMETER_ERROR; } // Find the connection to reject in our array of peers aRemotePeer.mAddr = theIp; aRemotePeer.mPort = thePort; index = ArraySearch(aUdp->mRemotePeers, &aRemotePeer, gsUdpRemotePeerCompare, 0, 0); if (index != NOT_FOUND) { GSUdpRemotePeer *aPeerFound = (GSUdpRemotePeer *)ArrayNth(aUdp->mRemotePeers, index); gt2Reject(aPeerFound->mConnection, NULL, 0); ArrayDeleteAt(aUdp->mRemotePeers, index); } return GS_UDP_NO_ERROR; } //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// // UDP Layer must be initialized // Sends a message to a peer using IP and Port // An empty header constitutes the app sending this message. // theIp and thePort cannot be 0 (Zero) // // WARNING: Messages should not be greater than the outgoing buffer size minus the header // and the 7 byte header for reliable messages (used for internal gt2 operations). Most // UDP fragmentation occurs if messages are bigger than 1500 bytes. Also, some routers are // known to drop those packets that are larger than 1500 bytes. The recommended outgoing // buffer size is the default (1460). So take that, and subtract 16 for message handler header // and reliable message header (if sending data reliably). // freeSpace = 1460 - 16 - 7 GSUdpErrorCode gsUdpEngineSendMessage(unsigned int theIp, unsigned short thePort, char theHeader[GS_UDP_MSG_HEADER_LEN], unsigned char *theMsg, unsigned int theMsgLen, gsi_bool theReliable) { GSUdpEngineObject *aUdp = gsUdpEngineGetEngine(); int aTotalMessageLen, index; GSUdpRemotePeer aRemotePeer, *aRemotePeerFound; GT2Byte *fullMessage; GT2Result aResult; GS_ASSERT(aUdp->mInitialized); GS_ASSERT(theIp); GS_ASSERT(thePort); if (!aUdp->mInitialized) { gsDebugFormat(GSIDebugCat_Common, GSIDebugType_Network, GSIDebugLevel_Debug, "[Udp Engine] Engine not initialized\n"); return GS_UDP_NETWORK_ERROR; } // Messages being sent with an empty header are treated as app messages if (!theHeader[0]) aTotalMessageLen = (int)theMsgLen; else aTotalMessageLen = (int)(GS_UDP_MSG_HEADER_LEN + theMsgLen); aRemotePeer.mAddr = theIp; aRemotePeer.mPort = thePort; index = ArraySearch(aUdp->mRemotePeers, &aRemotePeer, gsUdpRemotePeerCompare, 0, 0); if (index == NOT_FOUND) { char anAddr[GS_IP_ADDR_AND_PORT]; gt2AddressToString(theIp, thePort, anAddr); gsDebugFormat(GSIDebugCat_Common, GSIDebugType_Network, GSIDebugLevel_WarmError, "[Udp Engine] address not found for sending message\n", anAddr); return GS_UDP_ADDRESS_ERROR; } else { aRemotePeerFound = (GSUdpRemotePeer *)ArrayNth(aUdp->mRemotePeers, index); } if (aTotalMessageLen > gt2GetOutgoingBufferSize(aRemotePeerFound->mConnection) && theReliable) { gsDebugFormat(GSIDebugCat_Common, GSIDebugType_Network, GSIDebugLevel_WarmError, "[Udp Engine] Message Size too large, dropping message\n"); return GS_UDP_MSG_TOO_BIG; } fullMessage = (GT2Byte *)gsimalloc((unsigned long)aTotalMessageLen); memcpy(fullMessage, theHeader, GS_UDP_MSG_HEADER_LEN); memcpy(fullMessage + GS_UDP_MSG_HEADER_LEN, theMsg, theMsgLen); // Send the message // reliable messages will be kept in the outgoing buffers till they are sent aResult = gt2Send(aRemotePeerFound->mConnection, fullMessage, aTotalMessageLen, theReliable); gsifree(fullMessage); if (aResult != GT2Success) return GS_UDP_SEND_FAILED; return GS_UDP_NO_ERROR; } //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// // UDP Layer must be initialized // One the UDP Layer is initialized, this function // should be called every 10-100 ms // Message handlers already call this which means // that the app may not have to call this function GSUdpErrorCode gsUdpEngineThink() { GSUdpEngineObject *aUdp = gsUdpEngineGetEngine(); GS_ASSERT(aUdp->mInitialized); if (!aUdp->mInitialized) { gsDebugFormat(GSIDebugCat_Common, GSIDebugType_Network, GSIDebugLevel_Debug, "[Udp Engine] Engine not initialized\n"); return GS_UDP_NETWORK_ERROR; } gt2Think(aUdp->mSocket); return GS_UDP_NO_ERROR; } //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// // UDP Layer must be initialized // Shutdown should only happen if following is met: // Apps can call this function after they have shutdown message handlers (SDKs), // and verified there are no message handlers remaining. Calling the function // gsUdpEngineNoMoreMsgHandlers will do this. // Message handlers cannot call this function without checking if there no more // message handlers remaining and if there is no app (gsUdpEngineNoApp). GSUdpErrorCode gsUdpEngineShutdown() { GSUdpEngineObject *aUdp = gsUdpEngineGetEngine(); GS_ASSERT(aUdp->mInitialized); if (!aUdp->mInitialized) { gsDebugFormat(GSIDebugCat_Common, GSIDebugType_Network, GSIDebugLevel_Debug, "[Udp Engine] Engine not initialized\n"); return GS_UDP_NETWORK_ERROR; } gt2CloseSocket(aUdp->mSocket); ArrayFree(aUdp->mMsgHandlers); ArrayFree(aUdp->mRemotePeers); aUdp->mInitialized = gsi_false; return GS_UDP_NO_ERROR; } //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// // UDP Layer must be initialized // Obtains the lower level socket that can be used to perform other operations // or pass to other SDKs. SOCKET gsUdpEngineGetSocket() { GSUdpEngineObject *aUdp = gsUdpEngineGetEngine(); GS_ASSERT(aUdp->mInitialized); if (!aUdp->mInitialized) { gsDebugFormat(GSIDebugCat_Common, GSIDebugType_Network, GSIDebugLevel_HotError, "[Udp Engine] Engine not initialized\n"); return INVALID_SOCKET; } return gt2GetSocketSOCKET(aUdp->mSocket); } //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// // UDP Layer must be initialized // Gets the local IP the UDP layer is bound to. unsigned int gsUdpEngineGetLocalAddr() { GSUdpEngineObject *aUdp = gsUdpEngineGetEngine(); GS_ASSERT(aUdp->mInitialized); if (!aUdp->mInitialized) { gsDebugFormat(GSIDebugCat_Common, GSIDebugType_Network, GSIDebugLevel_HotError, "[Udp Engine] Engine not initialized\n"); return (unsigned int)-1; } return aUdp->mLocalAddr; } //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// // UDP Layer must be initialized // Gets the local port the UDP layer is bound to. unsigned short gsUdpEngineGetLocalPort() { GSUdpEngineObject *aUdp = gsUdpEngineGetEngine(); GS_ASSERT(aUdp->mInitialized); if (!aUdp->mInitialized) { gsDebugFormat(GSIDebugCat_Common, GSIDebugType_Network, GSIDebugLevel_HotError, "[Udp Engine] Engine not initialized\n"); return 0; } return aUdp->mLocalPort; } //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// // UDP Layer must be initialized // Message handlers are added using this function // The initial message and header of a message handler cannot be empty // However, they can be the same. // User data can be useful for keeping track of Message Handler GSUdpErrorCode gsUdpEngineAddMsgHandler(char theInitMsg[GS_UDP_MSG_HEADER_LEN], char theHeader[GS_UDP_MSG_HEADER_LEN], gsUdpErrorCallback theMsgHandlerError, gsUdpConnConnectedCallback theMsgHandlerConnected, gsUdpConnClosedCallback theMsgHandlerClosed, gsUdpConnPingCallback theMsgHandlerPing, gsUdpConnReceivedDataCallback theMsgHandlerRecv, void *theUserData) { GSUdpEngineObject *aUdp = gsUdpEngineGetEngine(); GSUdpMsgHandler aMsgHandler; GS_ASSERT(aUdp->mInitialized); GS_ASSERT(theInitMsg || theInitMsg[0]); GS_ASSERT(theHeader || theHeader[0]); if (!aUdp->mInitialized) { gsDebugFormat(GSIDebugCat_Common, GSIDebugType_Network, GSIDebugLevel_Debug, "[Udp Engine] Engine not initialized\n"); return GS_UDP_NETWORK_ERROR; } // setup a message handler that the UDP engine will use to pass connection attempts to //check for valid input if (!theInitMsg[0]) { gsDebugFormat(GSIDebugCat_Common, GSIDebugType_Network, GSIDebugLevel_Debug, "[Udp Engine] Invalid init message\n"); return GS_UDP_PARAMETER_ERROR; } if (!theHeader[0]) { gsDebugFormat(GSIDebugCat_Common, GSIDebugType_Network, GSIDebugLevel_Debug, "[Udp Engine] Invalid header\n"); return GS_UDP_PARAMETER_ERROR; } // This check is not necessary. Some SDKs may not use all callbacks /*if (!theMsgHandlerError || !theMsgHandlerConnected || !theMsgHandlerClosed || !theMsgHandlerPing || !theMsgHandlerRecv) { gsDebugFormat(GSIDebugCat_Common, GSIDebugType_Network, GSIDebugLevel_Debug, "[Udp Engine] Invalid callback(s)"); return GS_UDP_PARAMETER_ERROR; } */ aMsgHandler.mClosed = theMsgHandlerClosed; aMsgHandler.mConnected = theMsgHandlerConnected; aMsgHandler.mPingReply = theMsgHandlerPing; aMsgHandler.mReceived = theMsgHandlerRecv; aMsgHandler.mNetworkError = theMsgHandlerError; memcpy(aMsgHandler.mInitialMsg, theInitMsg, GS_UDP_MSG_HEADER_LEN); memcpy(aMsgHandler.mHeader, theHeader, GS_UDP_MSG_HEADER_LEN); aMsgHandler.mPendingConnections = ArrayNew(sizeof(GSUdpRemotePeer *), 1, NULL); if (aMsgHandler.mPendingConnections == NULL) { gsDebugFormat(GSIDebugCat_Common, GSIDebugType_Memory, GSIDebugLevel_HotError, "[Udp Engine] No more memory!!!\n"); return GS_UDP_NO_MEMORY; } aMsgHandler.mUserData = theUserData; ArrayAppend(aUdp->mMsgHandlers, &aMsgHandler); return GS_UDP_NO_ERROR; } //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// // UDP Layer must be initialized // When a message handler is done or shutting down, the message handler should remove // itself from the UDP Layer // The header cannot be empty GSUdpErrorCode gsUdpEngineRemoveMsgHandler(char theHeader[GS_UDP_MSG_HEADER_LEN]) { GSUdpEngineObject *aUdp = gsUdpEngineGetEngine(); GSUdpMsgHandler aHandler; int index; GS_ASSERT(aUdp->mInitialized); GS_ASSERT(theHeader); if (!aUdp->mInitialized) { gsDebugFormat(GSIDebugCat_Common, GSIDebugType_Network, GSIDebugLevel_Debug, "[Udp Engine] Engine not initialized\n"); return GS_UDP_NETWORK_ERROR; } if (!theHeader || !theHeader[0]) { gsDebugFormat(GSIDebugCat_Common, GSIDebugType_Network, GSIDebugLevel_Debug, "[Udp Engine] invalid or empty header\n"); return GS_UDP_PARAMETER_ERROR; } memcpy(aHandler.mHeader, theHeader, GS_UDP_MSG_HEADER_LEN); index = ArraySearch(aUdp->mMsgHandlers, &aHandler, gsUdpMsgHandlerCompare2, 0, 0); if (index != NOT_FOUND) { ArrayDeleteAt(aUdp->mMsgHandlers, index); } return GS_UDP_NO_ERROR; } //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// // UDP Layer must be initialized // Checks to see if there any remaining message handlers. // returns gsi_false if there are none. gsi_bool gsUdpEngineNoMoreMsgHandlers() { GSUdpEngineObject *aUdp = gsUdpEngineGetEngine(); GS_ASSERT(aUdp->mInitialized); if (!aUdp->mInitialized) { gsDebugFormat(GSIDebugCat_Common, GSIDebugType_Network, GSIDebugLevel_Debug, "[Udp Engine] Engine not initialized\n"); return gsi_true; } return ArrayLength(aUdp->mMsgHandlers) == 0 ? gsi_true : gsi_false; } //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// // UDP Layer must be initialized // returns gsi_false if there is no app using the UDP Layer gsi_bool gsUdpEngineNoApp() { GSUdpEngineObject *aUdp = gsUdpEngineGetEngine(); GS_ASSERT(aUdp->mInitialized); if (!aUdp->mInitialized) { gsDebugFormat(GSIDebugCat_Common, GSIDebugType_Network, GSIDebugLevel_Debug, "[Udp Engine] Engine not initialized\n"); return gsi_true; } if (aUdp->mAppClosed || aUdp->mAppConnAttempt || aUdp->mAppConnected || aUdp->mAppNetworkError || aUdp->mAppPingReply || aUdp->mAppRecvData || aUdp->mAppUnknownMessage) { return gsi_false; } return gsi_true; } //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// // UDP Layer must be initialized // theIp and thePort cannot be 0 (Zero) // Based on an IP and port, the function will return the amount of free space // of a peer's buffer. int gsUdpEngineGetPeerOutBufferFreeSpace(unsigned int theIp, unsigned short thePort) { GSUdpEngineObject *aUdp = gsUdpEngineGetEngine(); GSUdpRemotePeer aRemotePeer, *aRemotePeerFound; int index; GS_ASSERT(aUdp->mInitialized); GS_ASSERT(theIp); GS_ASSERT(thePort); if (!aUdp->mInitialized) { gsDebugFormat(GSIDebugCat_Common, GSIDebugType_Network, GSIDebugLevel_Debug, "[Udp Engine] Engine not initialized\n"); return 0; } aRemotePeer.mAddr = theIp; aRemotePeer.mPort = thePort; index = ArraySearch(aUdp->mRemotePeers, &aRemotePeer, gsUdpRemotePeerCompare, 0, 0); if (index != NOT_FOUND) { aRemotePeerFound = (GSUdpRemotePeer *)ArrayNth(aUdp->mRemotePeers, index); return gt2GetOutgoingBufferFreeSpace(aRemotePeerFound->mConnection); } return 0; } //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// // Creates a string address given an IP and port // IP and port can be 0. void gsUdpEngineAddrToString(unsigned int theIp, unsigned short thePort, char addrstring[GS_IP_ADDR_AND_PORT]) { gt2AddressToString(theIp, thePort, addrstring); }