• Advanced WinSock Multiplayer Game Programming: Multicasting

Networking and Multiplayer

Combating lag is a major problem in multiplayer network game development. As multiplayer game developers, we always strive to make things faster, leaner and meaner to reduce lag and free up bandwidth. This is why we often forsake the reliability of TCP for the speed that UDP provides. Multicasting is yet another step in the fight against latency, carrying many promises, including the transmission of very high quality streaming digital TV over networks and in the future, the Internet. What is the magic behind multicasting and how can it be used in our games? In short, it can not only reduce server workload but is also a solution to the age old problem of players finding each other on networks without the game developer having to put up dedicated master servers, but more on that later.

Oh, and if DirectPlay uses multicasting extensively, then it's all more the reason for us to use it .

[size="5"]The Idea Behind Multicasting

The theory goes something like this. In the most commonly used networking client-server model, when a client sends input to the server, this input updates the game state and then the server tells all the other clients about what has happened by sending the same information to all the clients:

As you can see there is a traffic problem on the server's network connection. If, say, there were 32 players connected to the server at the time, then the same information would be sent 32 times (once to each player). If there were 20 bytes of data to be sent to each of the 32 players then 640 bytes would have to be sent through the server's network connection. If that were to happen every time any of the 32 players pressed a key or moved the mouse, a huge amount of traffic is generated. Naturally, there is no replacement for good coding practice and sending only the data that is needed, but multicasting can seriously help.

So how can multicasting help? Well, Multicasting can dramatically reduce the amount of data that needs to be sent by taking the task of packet replication away from the game server to the actual network infrastructure. In multicasting, packets can be sent to groups of network addresses, instead of individual addresses.

This is similar to the way email works - when we want to send the same email message to multiple email addresses, we don't send the message to every address from our computer. Instead we send the message once, telling the server to replicate the message to all the other addresses.

[size="5"]The Darker Side

Of course, there are reasons why multicasting is not commonly used:
• Some ISPs and networks don't support multicasting yet. Bastards. So if you want to implement multicasting in a game, you're better off adding it as an option. Internet multicasting is rarely supported, but hopefully it will be in the future.
• Multicasting only makes a worthwhile gain in performance when network data is replicated, realistically only worth bothering when there is support for more than four players.
• Multicasting requires some more coding and programmers are lazy to even look into it. As you will see, in fact it requires very little additional code. The corporate "Quality Digital TV via Multicasting" idea seems to put game programmers off the subject altogether, I suspect it has something to do with hacker ethics, so long live the .org's!
• The openness of multicast groups may make your packets easier to sniff. Usually UDP packets can only be intercepted between their source and destination, but now they can be captured anywhere on the network; by joining the right group, anyone can get a carbon-copy!
[size="5"]How Multicasting Works

You may have heard of broadcasting. Broadcasting forwards data to every address on the network. Unlike broadcasting, multicasting only forwards to those addresses who have explicitly registered interest in the data.

On an IP network supporting multicasting there are such things as multicast groups. If you want to receive multicast data packets, you must join a multicast group. Although it should be possible to send data packets to a multicast group regardless of membership, it is often better to join a group before sending to it for reasons I won't venture into. If you are a member of a group to which you are sending multicast data packets, you will receive a copy of the data packets. Also, a client will not receive all data packets from a multicast group, but only those which are sent to the port that the socket is bound to.

So a sensible idea would be for all the game clients to join a multicast group and wait for data on the same port. Then the server, by sending a single packet of data to that multicast group, would be sending to all the clients as the packets are replicated somewhere along the way.

We've seen the light, we've seen the darkness, so let us onto the code...

[size="5"]Joining a Multicast Group and Receiving Multicast Data Packets

To receive multicast packets sent to a multicast group, your game will need to join or become a member of that multicast group. To request becoming a member of a multicast group is a lot simpler than you may at first imagine. You need to first bind() your UDP socket to a local port (elementary, my dear friend):

SOCKADDR_IN addrLocal; // We want to use the Internet address family addrLocal.sin_family = AF_INET; // Use any local address addrLocal.sin_addr.s_addr = INADDR_ANY; // Use arbitrary port - but the same as on other clients/servers addrLocal.sin_port = htons(uiPort); // Bind socket to our address if(SOCKET_ERROR == bind(hUDPSocket, (LPSOCKADDR)&addrLocal, sizeof(struct sockaddr))) {cout << "Euston, we have a problem";} // Ready to switch to multicasting modeAnd then just make a call to setsockopt(), and here's a prototype for your convenience *grin*: int WSAAPI setsockopt(SOCKET s, int level, int optname, const char FAR * optval, int optlen);If you thought you were getting away with just 1 new line of code to learn, you were wrong... you're only getting away with 4 new lines =). There are special parameters to prepare for this call: s is your socket handle, level should be set to IPPROTO_IP, optname should be set to IP_ADD_MEMBERSHIP and a pointer to the p_mreq structure passed as optval, with its length in optlen. This is what the p_mreq structure looks like: struct ip_mreq { struct in_addr imr_multiaddr; /* multicast group to join */ struct in_addr imr_interface; /* interface to join on */ } It has 2 fields, both of them are in_add r structures: imr_multiaddr specifies the address of the multicast group to join and imr_interface specifies the local address INADDR_ANY. There are special (Class 'D') addresses allocated for multicast groups. These are in the range from 224.0.1.0 to 239.255.255.255. You can choose an address from the range as the target multicast group to join, and set the imr_multiaddr to this address. The full setsockopt() call would look something like this: struct ip_mreq mreq; mreq.imr_multiaddr.s_addr = inet_addr("234.5.6.7"); mreq.imr_interface.s_addr = INADDR_ANY; nRet = setsockopt(hUDPSocket, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char*)&mreq, sizeof(mreq)); And that's all there is to it, apart from a lot of error checking which I've decided to leave out for clarity (aka Laziness). The socket will now receive data packets sent to the multicast group on the specified port with calls to recvfrom(): SOCKADDR_IN addrSrc; nRet = recvfrom(hUDPSocket, (char *)&Data, sizeof(Data), 0, (struct sockaddr*)&addrSrc, sizeof(addrSrc)); When you're finished with the group and want to leave, just repeat the call with identical parameters apart from IP_ADD_MEMBERSHIP which should be replaced with IP_DROP_MEMBERSHIP. nRet = setsockopt(hUDPSocket, IPPROTO_IP, IP_DROP_MEMBERSHIP, (char*)&mreq, sizeof(mreq)); Now that we can join a multicast group and receive packets sent to it, the logical thing to do is to learn how to send packets to a multicast group. [size="5"]Sending Multicast Data Packets Sending multicast data packets is accomplished with a call to sendto(), specifying a multicast group address as the destination IP address and the wanted port (on which your clients are tuned to listen for data). So there really a lot to learn apart from using the TTL (Time To Live) socket option. All IP packets carry a TTL value to make sure that they are discarded if they don't reach a destination so they don't clog up the network. In a multicast data packet, TTL specifies how far a multicast data packet can travel: [size="3"]TTL Threshold[size="3"]DescriptionTTL equal to 0Restricted to the same hostTTL equal to 1Restricted to the same subnetTTL equal to 32Restricted to the same siteTTL equal to 64Restricted to the same regionTTL equal to 128Restricted to the same continentTTL equal to 255Unrestricted in scope [From MSDN, this is very rough and should not be taken literally] Multicasting is nowhere as dangerous as broadcasting in terms of unwanted traffic that it can produce but caution is advised when using some of the higher TTL values. To set a socket's multicast TTL value, setsockopt() can be used with IPPROTO_IP as the protocol level and IP_MULTICAST_TTL as the socket option. char TTL = 32 ; // Restrict to our school network, for example setsockopt(hUDPSocket, IPPROTO_IP, IP_MULTICAST_TTL, (char *)&TTL, sizeof(TTL)); We must also tell the system exactly which local network interface we would like to multicast on. // Set the local interface from which multicast is to be transmitted unsigned long addr = inet_addr(YOUR_IP_ADDRESS_STRING); setsockopt(sSocket, IPPROTO_IP, IP_MULTICAST_IF, (char *)&addr, sizeof(addr)); Once the TTL and multicast interface are set, just sendto() away: SOCKADDR_IN addrDest; szHi[50]; addrDest.sin_family = AF_INET; // Target multicast group address addrDest.sin_addr.s_addr = inet_addr("234.5.6.7"); // Port on which client is set to receive data packets addrDest.sin_port = htons(uiPort); // Something unoriginal to send strcpy(szHi,"Hello Multicast Group!"); nRet = sendto(hUDPSocket, (char *)szHi, strlen(szHi), 0, (struct sockaddr*)&addrDest, sizeof(addrDest)); We can now join multicast groups, send and receive data from them, but how do we implement multicasting as an option in our game and what would we use it for? [size="5"]Uses of Multicasting in Games I can think of two ways straight away - one is to use it for reducing (maybe even eliminating) the amount of repeated data that a server has to send out, but another interesting use is a global server-less interface for finding other players on the network. The scenario: there are 2 people on a large network running the same game that want to play together, but they don't know each other's IP addresses let alone the fact that the potential opponent exists. The common ways for connecting the 2 players: The players send out a broadcast message to the entire network, however this would create huge traffic and will probably be restricted to sub networks. Broadcasting on the Internet would create an enormous amount of traffic, so it is not allowed.The players connect to an intermediate, "known" master server IP, which tells them of each other's existence. These servers are costly to run and their uptime is often undependable.The players go to a chatroom hoping to find other players and play together. This will not connect all the players as some may be in different chatrooms. And the process of finding someone may take a while. So here we are with an age old problem (how A finds B) on one hand and multicasting on the other. Multicasting groups always have the same address - a "known" address as in the case of a dedicated server, they are online 100% of the time - unlimited uptime, they don't cost anything to connect to or send information across. All game clients simply connects to a multicast group, multicast an "I want to play" message and the servers can then advertise their availability directly (instead of broadcasting, to save bandwidth) to the clients who are members of the multicast group. Sure, there are itsy-bitsy technical problems to sort out, but the idea is cool enough. And the TTL control allows us to query within a certain range of routers (see TTL table) so we can specifically only ask to send to our LAN, or a university network, or all servers within our country to respond. Don't you think that is COOL? I sure do. The only problem (see "The Dark Side") remains multicast support by ISP's and networks. So the best way to add multicasting to a game is still as an option (although I hope and pray that multicasting will be 100% supported in the future). But how do we integrate multicasting into our game as an option? [size="5"]Integrating Multicasting into Games Ok then, where do we start? There are so many different types of multiplayer games that I won't even try to explain how to integrate multicasting into different types of games. Instead I'll just give a few possible ideas of solutions in a client-server relationship. First of all, all the current network code should be kept as it is, when you add multicast support make sure you do not remove any existing code unless you really think it is necessary. When adding multicast support, you can either do a parallel integration where multicasting runs along with existing code, or you could write two separate sets of network code and add a 'multicast on/off switch' for the user. The on/off switch would isolate servers using the other network code and add one more daunting and mysterious switch for the average newbie to get wrong. Parallel integration (bah, the things I learned in school last year) is my favorite as it will use multicasting only if it is supported and should be transparent to the user. So let's stick with parallel integration - in this case the normal network code runs always, but the multicasting code only runs if multicasting is supported. How do we determine if multicasting is supported? Just read the error setsockopt() gives us when trying to join a group: nRet = setsockopt(hUDPSocket, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char*)&mreq, sizeof(mreq)); if(WSAESOCKTNOSUPPORT == nRet) { // Multicasting not supported. Damn. } The client-server relationship is a game of two halves. So what if the server supports multicasting while the client does not? How does the server know which clients are covered with a single send to the multicast group and which are not? The client first determines that it does not support multicasting, then connects to the server and tells the server whether it supports multicasting. The server usually keeps a list or array of clients, to which it is easy to add an extra boolean flag: struct Client { SOCKADDR_IN addrRemote; /* ... Game specific info here ...*/ BOOL bSupportMulticast; } The server's function to send data sends a multicast to clients who support multicasting and normal UDP datagrams to those that do not. If, however, the server itself does not support multicasting then we must use the old method. Here's a useful code snippet for a server with multicasting as an option: int SendToAll(char *Data) { if(bServerSupportMulticast) { // First send multicast, then send individually // to those who don't support it for(int index = 0; index < MAX_CLIENTS; index++) { if(Clients[index].Exist && Clients[index].bSupportMulticast) { // At least one client supports multicasting, so use it SendMulticast(Data, addrMulticast); break; } } for(int index = 0; index < MAX_CLIENTS; index++) { if(Clients[index].Exist && !Clients[index].bSupportMulticast) { OldSendToClient(Data, Clients[index].addrRemote); } } } else { // Use the old method all the way regardless of support // as we ourselves don't support it for(int index = 0; index < MAX_CLIENTS; index++) { if(Clients[index].Exist) { OldSendToClient(Data, Clients[index].addrRemote); } } } } I hope I've shed some light on multicasting and its possible uses in games. If you've found this article the least bit interesting or have a problem, drop an email to [email="denis@voxelsoft.com"]denis@voxelsoft.com[/email]. I hope to write another article soon, but for now, Happy Multicasting! - Denis "Voxel" Lukianov Thanks to Jan "Riva" Halfar for the wonderful diagrams.
 
 0   Report Article 
 Sign in to follow this   Followers 0 Go to articles Networking and Multiplayer 
 User Feedback 2 Comments 0 Reviews Nick of ZA 177 Posted December 17, 2011 So, ten years since this article was written. What's the status of UDP multicast support? Still not something that its possible to take for granted, I guess. Share this comment Link to comment Share on other sites Mussi 4434 Posted December 20, 2011 I wonder as well, does anyone know? Share this comment Link to comment Share on other sites Create an account or sign in to comment You need to be a member in order to leave a comment Create an account Sign up for a new account in our community. It's easy! Register a new account Sign in Already have an account? Sign in here. Sign In Now 
•  
 
 Advertisement 
 Advertisement Categories 29Audio 29Music and Sound FX 200Business 125Business and Law 36Career Development 39Production and Management 191Game Design 185Game Design and Theory 3Writing for Games 3UX for Games 233Industry 172Interviews 61Event Coverage 1165Programming 55Artificial Intelligence 616General and Gameplay Programming 322Graphics and GPU Programming 56Engines and Middleware (and 2 more) 65Visual Arts 34Archive Show all categories   Advertisement GameDev.net GameDev.net Articles GameDev.net Event Coverage GameDev.net Forums GameDev.net Blogs GameDev.net Gallery GameDev.net News GameDev.net Projects GDNet Chat All Activity Search In Everywhere This Category This Article More options... Find results that contain... All of my search term words Any of my search term words Find results in... Content titles and body Content titles only Home Articles Programming Networking and Multiplayer Advanced WinSock Multiplayer Game Programming: Multicasting 
 
 
 × Existing user? Sign In Sign Up Browse Back Articles & Tutorials Back All Categories Audio Business Game Design Industry Programming Visual Arts Columns Back GameDev Unboxed Event Coverage Back All Events Game Developers Conference Power Up Digital Games Conference GameDev.Market Links News Podcasts Back All Podcasts Game Dev Loadout Archive Community Back Beginners Back Beginners Group Beginners Forum Beginners Resources Blogs Calendar Chat Forums Back All Forums Audio Business Game Design Programming Visual Arts Community GameDev Challenges Affiliates Topical Workshops Gallery Groups Back For Beginners GameDev Challenges All Groups Projects Back All Projects Games Game Assets Game Mods Developer Tools Store Careers Back Contractors Hobby Projects Game Jobs Back Browse on GameDev.Jobs Post a Job Members Back Chat GDNet+ Membership Guidelines Leaderboard Online Users Awards Search Back All Activity My Activity Streams Back Latest Topics Featured Blogs Search var ipsDebug=false;var CKEDITOR_BASEPATH='//www.gamedev.net/applications/core/interface/ckeditor/ckeditor/';var ipsSettings={cookie_path:"/",cookie_prefix:"ips4_",cookie_ssl:true,upload_imgURL:"",message_imgURL:"",notification_imgURL:"",baseURL:"//www.gamedev.net/",jsURL:"//www.gamedev.net/applications/core/interface/js/js.php",csrfKey:"f530558a2d613613dbce8174120a9ea7",antiCache:"2a361a8a02",disableNotificationSounds:false,useCompiledFiles:true,links_external:true,memberID:0,analyticsProvider:"ga",viewProfiles:true,mapProvider:'google',mapApiKey:"AIzaSyAeT7tk3vnWWmbgVISkLpbhkQvekG19rHM",}; ips.setSetting('date_format',jQuery.parseJSON('"mm\/dd\/yy"'));ips.setSetting('date_first_day',jQuery.parseJSON('0'));ips.setSetting('remote_image_proxy',jQuery.parseJSON('1'));ips.setSetting('ipb_url_filter_option',jQuery.parseJSON('"none"'));ips.setSetting('url_filter_any_action',jQuery.parseJSON('"allow"'));ips.setSetting('bypass_profanity',jQuery.parseJSON('0'));ips.setSetting('emoji_style',jQuery.parseJSON('"native"'));ips.setSetting('emoji_shortcodes',jQuery.parseJSON('"1"'));ips.setSetting('emoji_ascii',jQuery.parseJSON('"1"'));ips.setSetting('emoji_cache',jQuery.parseJSON('"1"'));ips.setSetting('quickSearchDefault',jQuery.parseJSON('"all"'));ips.setSetting('quickSearchMinimum',jQuery.parseJSON('3'));ips.setSetting('quickSearchShowAdv',jQuery.parseJSON('true'));ips.setSetting('quickSearchIn',jQuery.parseJSON('"title"')); { "@context": "http://schema.org", "@type": "Article", "url": "https://www.gamedev.net/articles/programming/networking-and-multiplayer/advanced-winsock-multiplayer-game-programming-r1587/", "discussionUrl": "https://www.gamedev.net/articles/programming/networking-and-multiplayer/advanced-winsock-multiplayer-game-programming-r1587/", "mainEntityOfPage": "https://www.gamedev.net/articles/programming/networking-and-multiplayer/advanced-winsock-multiplayer-game-programming-r1587/", "name": "Advanced WinSock Multiplayer Game Programming: Multicasting", "headline": "Advanced WinSock Multiplayer Game Programming: Multicasting", "text": "Combating lag is a major problem in multiplayer network game development. As multiplayer game developers, we always strive to make things faster, leaner and meaner to reduce lag and free up bandwidth. This is why we often forsake the reliability of TCP for the speed that UDP provides. Multicasting is yet another step in the fight against latency, carrying many promises, including the transmission of very high quality streaming digital TV over networks and in the future, the Internet. What is the magic behind multicasting and how can it be used in our games? In short, it can not only reduce server workload but is also a solution to the age old problem of players finding each other on networks without the game developer having to put up dedicated master servers, but more on that later.\n Oh, and if DirectPlay uses multicasting extensively, then it\u0027s all more the reason for us to use it .\n[size=\"5\"]The Idea Behind Multicasting\nThe theory goes something like this. In the most commonly used networking client-server model, when a client sends input to the server, this input updates the game state and then the server tells all the other clients about what has happened by sending the same information to all the clients:\n \n\n As you can see there is a traffic problem on the server\u0027s network connection. If, say, there were 32 players connected to the server at the time, then the same information would be sent 32 times (once to each player). If there were 20 bytes of data to be sent to each of the 32 players then 640 bytes would have to be sent through the server\u0027s network connection. If that were to happen every time any of the 32 players pressed a key or moved the mouse, a huge amount of traffic is generated. Naturally, there is no replacement for good coding practice and sending only the data that is needed, but multicasting can seriously help.\n So how can multicasting help? Well, Multicasting can dramatically reduce the amount of data that needs to be sent by taking the task of packet replication away from the game server to the actual network infrastructure. In multicasting, packets can be sent to groups of network addresses, instead of individual addresses.\n This is similar to the way email works - when we want to send the same email message to multiple email addresses, we don\u0027t send the message to every address from our computer. Instead we send the message once, telling the server to replicate the message to all the other addresses. \n\n[size=\"5\"]The Darker Side\nOf course, there are reasons why multicasting is not commonly used:\nSome ISPs and networks don\u0027t support multicasting yet. Bastards. So if you want to implement multicasting in a game, you\u0027re better off adding it as an option. Internet multicasting is rarely supported, but hopefully it will be in the future. \nMulticasting only makes a worthwhile gain in performance when network data is replicated, realistically only worth bothering when there is support for more than four players. \nMulticasting requires some more coding and programmers are lazy to even look into it. As you will see, in fact it requires very little additional code. The corporate \"Quality Digital TV via Multicasting\" idea seems to put game programmers off the subject altogether, I suspect it has something to do with hacker ethics, so long live the .org\u0027s! \nThe openness of multicast groups may make your packets easier to sniff. Usually UDP packets can only be intercepted between their source and destination, but now they can be captured anywhere on the network; by joining the right group, anyone can get a carbon-copy! \n[size=\"5\"]How Multicasting Works\nYou may have heard of broadcasting. Broadcasting forwards data to every address on the network. Unlike broadcasting, multicasting only forwards to those addresses who have explicitly registered interest in the data.\n On an IP network supporting multicasting there are such things as multicast groups. If you want to receive multicast data packets, you must join a multicast group. Although it should be possible to send data packets to a multicast group regardless of membership, it is often better to join a group before sending to it for reasons I won\u0027t venture into. If you are a member of a group to which you are sending multicast data packets, you will receive a copy of the data packets. Also, a client will not receive all data packets from a multicast group, but only those which are sent to the port that the socket is bound to.\n So a sensible idea would be for all the game clients to join a multicast group and wait for data on the same port. Then the server, by sending a single packet of data to that multicast group, would be sending to all the clients as the packets are replicated somewhere along the way.\n\n We\u0027ve seen the light, we\u0027ve seen the darkness, so let us onto the code...\n[size=\"5\"]Joining a Multicast Group and Receiving Multicast Data Packets\nTo receive multicast packets sent to a multicast group, your game will need to join or become a member of that multicast group. To request becoming a member of a multicast group is a lot simpler than you may at first imagine. You need to first bind() your UDP socket to a local port (elementary, my dear friend):SOCKADDR_IN addrLocal;\n// We want to use the Internet address family\naddrLocal.sin_family = AF_INET;\n// Use any local address\naddrLocal.sin_addr.s_addr = INADDR_ANY;\n// Use arbitrary port - but the same as on other clients/servers\naddrLocal.sin_port = htons(uiPort); \n// Bind socket to our address\nif(SOCKET_ERROR == bind(hUDPSocket, (LPSOCKADDR)\u0026amp;addrLocal, \n sizeof(struct sockaddr)))\n {cout \n// Ready to switch to multicasting modeAnd then just make a call to setsockopt(), and here\u0027s a prototype for your convenience *grin*:int WSAAPI setsockopt(SOCKET s, int level, int optname,\n const char FAR * optval, int optlen);If you thought you were getting away with just 1 new line of code to learn, you were wrong... you\u0027re only getting away with 4 new lines =). There are special parameters to prepare for this call: s is your socket handle, level should be set to IPPROTO_IP, optname should be set to IP_ADD_MEMBERSHIP and a pointer to the p_mreq structure passed as optval, with its length in optlen. This is what the p_mreq structure looks like:struct ip_mreq {\n struct in_addr imr_multiaddr; /* multicast group to join */\n struct in_addr imr_interface; /* interface to join on */\n} It has 2 fields, both of them are in_add r structures: imr_multiaddr specifies the address of the multicast group to join and imr_interface specifies the local address INADDR_ANY.\n There are special (Class \u0027D\u0027) addresses allocated for multicast groups. These are in the range from 224.0.1.0 to 239.255.255.255. You can choose an address from the range as the target multicast group to join, and set the imr_multiaddr to this address. The full setsockopt() call would look something like this:struct ip_mreq mreq;\nmreq.imr_multiaddr.s_addr = inet_addr(\"234.5.6.7\");\nmreq.imr_interface.s_addr = INADDR_ANY;\nnRet = setsockopt(hUDPSocket, IPPROTO_IP, IP_ADD_MEMBERSHIP,\n (char*)\u0026amp;mreq, sizeof(mreq)); And that\u0027s all there is to it, apart from a lot of error checking which I\u0027ve decided to leave out for clarity (aka Laziness). The socket will now receive data packets sent to the multicast group on the specified port with calls to recvfrom():SOCKADDR_IN addrSrc;\nnRet = recvfrom(hUDPSocket, (char *)\u0026amp;Data, sizeof(Data), 0,\n (struct sockaddr*)\u0026amp;addrSrc, sizeof(addrSrc)); When you\u0027re finished with the group and want to leave, just repeat the call with identical parameters apart from IP_ADD_MEMBERSHIP which should be replaced with IP_DROP_MEMBERSHIP.nRet = setsockopt(hUDPSocket, IPPROTO_IP, IP_DROP_MEMBERSHIP,\n (char*)\u0026amp;mreq, sizeof(mreq)); Now that we can join a multicast group and receive packets sent to it, the logical thing to do is to learn how to send packets to a multicast group.\n[size=\"5\"]Sending Multicast Data Packets\nSending multicast data packets is accomplished with a call to sendto(), specifying a multicast group address as the destination IP address and the wanted port (on which your clients are tuned to listen for data). So there really a lot to learn apart from using the TTL (Time To Live) socket option.\n All IP packets carry a TTL value to make sure that they are discarded if they don\u0027t reach a destination so they don\u0027t clog up the network. In a multicast data packet, TTL specifies how far a multicast data packet can travel:\n\n[size=\"3\"]TTL Threshold\n\n[size=\"3\"]Description\n\n\nTTL equal to 0\nRestricted to the same host\n\n\nTTL equal to 1\nRestricted to the same subnet\n\n\nTTL equal to 32\nRestricted to the same site\n\n\nTTL equal to 64\nRestricted to the same region\n\n\nTTL equal to 128\nRestricted to the same continent\n\n\nTTL equal to 255\nUnrestricted in scope\n\n \t\t\t\t\t[From MSDN, this is very rough and should not be taken literally] \nMulticasting is nowhere as dangerous as broadcasting in terms of unwanted traffic that it can produce but caution is advised when using some of the higher TTL values.\n To set a socket\u0027s multicast TTL value, setsockopt() can be used with IPPROTO_IP as the protocol level and IP_MULTICAST_TTL as the socket option.char TTL = 32 ; // Restrict to our school network, for example\nsetsockopt(hUDPSocket, IPPROTO_IP, IP_MULTICAST_TTL,\n \t\t(char *)\u0026amp;TTL, sizeof(TTL)); We must also tell the system exactly which local network interface we would like to multicast on.// Set the local interface from which multicast is to be transmitted\nunsigned long addr = inet_addr(YOUR_IP_ADDRESS_STRING);\nsetsockopt(sSocket, IPPROTO_IP, IP_MULTICAST_IF, (char *)\u0026amp;addr,\nsizeof(addr)); Once the TTL and multicast interface are set, just sendto() away:SOCKADDR_IN addrDest;\nszHi[50];\naddrDest.sin_family = AF_INET;\n// Target multicast group address\naddrDest.sin_addr.s_addr = inet_addr(\"234.5.6.7\");\n// Port on which client is set to receive data packets\naddrDest.sin_port = htons(uiPort);\n// Something unoriginal to send\nstrcpy(szHi,\"Hello Multicast Group!\");\nnRet = sendto(hUDPSocket, (char *)szHi, strlen(szHi), 0,\n (struct sockaddr*)\u0026amp;addrDest, sizeof(addrDest)); We can now join multicast groups, send and receive data from them, but how do we implement multicasting as an option in our game and what would we use it for?\n[size=\"5\"]Uses of Multicasting in Games\nI can think of two ways straight away - one is to use it for reducing (maybe even eliminating) the amount of repeated data that a server has to send out, but another interesting use is a global server-less interface for finding other players on the network.\n The scenario: there are 2 people on a large network running the same game that want to play together, but they don\u0027t know each other\u0027s IP addresses let alone the fact that the potential opponent exists. The common ways for connecting the 2 players:\nThe players send out a broadcast message to the entire network, however this would create huge traffic and will probably be restricted to sub networks. Broadcasting on the Internet would create an enormous amount of traffic, so it is not allowed. \nThe players connect to an intermediate, \"known\" master server IP, which tells them of each other\u0027s existence. These servers are costly to run and their uptime is often undependable. \nThe players go to a chatroom hoping to find other players and play together. This will not connect all the players as some may be in different chatrooms. And the process of finding someone may take a while. So here we are with an age old problem (how A finds B) on one hand and multicasting on the other. Multicasting groups always have the same address - a \"known\" address as in the case of a dedicated server, they are online 100% of the time - unlimited uptime, they don\u0027t cost anything to connect to or send information across. All game clients simply connects to a multicast group, multicast an \"I want to play\" message and the servers can then advertise their availability directly (instead of broadcasting, to save bandwidth) to the clients who are members of the multicast group.\n Sure, there are itsy-bitsy technical problems to sort out, but the idea is cool enough. And the TTL control allows us to query within a certain range of routers (see TTL table) so we can specifically only ask to send to our LAN, or a university network, or all servers within our country to respond. Don\u0027t you think that is COOL? I sure do.\n The only problem (see \"The Dark Side\") remains multicast support by ISP\u0027s and networks. So the best way to add multicasting to a game is still as an option (although I hope and pray that multicasting will be 100% supported in the future). But how do we integrate multicasting into our game as an option?\n[size=\"5\"]Integrating Multicasting into Games\nOk then, where do we start? There are so many different types of multiplayer games that I won\u0027t even try to explain how to integrate multicasting into different types of games. Instead I\u0027ll just give a few possible ideas of solutions in a client-server relationship.\n First of all, all the current network code should be kept as it is, when you add multicast support make sure you do not remove any existing code unless you really think it is necessary.\n When adding multicast support, you can either do a parallel integration where multicasting runs along with existing code, or you could write two separate sets of network code and add a \u0027multicast on/off switch\u0027 for the user. The on/off switch would isolate servers using the other network code and add one more daunting and mysterious switch for the average newbie to get wrong. Parallel integration (bah, the things I learned in school last year) is my favorite as it will use multicasting only if it is supported and should be transparent to the user.\n So let\u0027s stick with parallel integration - in this case the normal network code runs always, but the multicasting code only runs if multicasting is supported. How do we determine if multicasting is supported? Just read the error setsockopt() gives us when trying to join a group:nRet = setsockopt(hUDPSocket, IPPROTO_IP, IP_ADD_MEMBERSHIP,\n (char*)\u0026amp;mreq, sizeof(mreq));\nif(WSAESOCKTNOSUPPORT == nRet)\n {\n // Multicasting not supported. Damn.\n } The client-server relationship is a game of two halves. So what if the server supports multicasting while the client does not? How does the server know which clients are covered with a single send to the multicast group and which are not? The client first determines that it does not support multicasting, then connects to the server and tells the server whether it supports multicasting. The server usually keeps a list or array of clients, to which it is easy to add an extra boolean flag:struct Client\n {\n SOCKADDR_IN addrRemote;\n /* ... Game specific info here ...*/\n BOOL bSupportMulticast;\n } The server\u0027s function to send data sends a multicast to clients who support multicasting and normal UDP datagrams to those that do not. If, however, the server itself does not support multicasting then we must use the old method. Here\u0027s a useful code snippet for a server with multicasting as an option:int SendToAll(char *Data)\n {\n if(bServerSupportMulticast)\n {\n // First send multicast, then send individually\n // to those who don\u0027t support it\n for(int index = 0; index \n {\n if(Clients[index].Exist \u0026amp;\u0026amp; Clients[index].bSupportMulticast)\n {\n // At least one client supports multicasting, so use it\n SendMulticast(Data, addrMulticast);\n break;\n }\n }\n for(int index = 0; index \n {\n if(Clients[index].Exist \u0026amp;\u0026amp; !Clients[index].bSupportMulticast)\n {\n OldSendToClient(Data, Clients[index].addrRemote);\n }\n }\n }\n else\n {\n // Use the old method all the way regardless of support\n // as we ourselves don\u0027t support it\n for(int index = 0; index \n {\n if(Clients[index].Exist)\n {\n OldSendToClient(Data, Clients[index].addrRemote);\n }\n }\n }\n } I hope I\u0027ve shed some light on multicasting and its possible uses in games. If you\u0027ve found this article the least bit interesting or have a problem, drop an email to [email=\"denis@voxelsoft.com\"]denis@voxelsoft.com[/email]. I hope to write another article soon, but for now, Happy Multicasting!\n - Denis \"Voxel\" Lukianov\nThanks to Jan \"Riva\" Halfar for the wonderful diagrams. \n\n \n\n", "articleBody": "Combating lag is a major problem in multiplayer network game development. As multiplayer game developers, we always strive to make things faster, leaner and meaner to reduce lag and free up bandwidth. This is why we often forsake the reliability of TCP for the speed that UDP provides. Multicasting is yet another step in the fight against latency, carrying many promises, including the transmission of very high quality streaming digital TV over networks and in the future, the Internet. What is the magic behind multicasting and how can it be used in our games? In short, it can not only reduce server workload but is also a solution to the age old problem of players finding each other on networks without the game developer having to put up dedicated master servers, but more on that later.\n Oh, and if DirectPlay uses multicasting extensively, then it\u0027s all more the reason for us to use it .\n[size=\"5\"]The Idea Behind Multicasting\nThe theory goes something like this. In the most commonly used networking client-server model, when a client sends input to the server, this input updates the game state and then the server tells all the other clients about what has happened by sending the same information to all the clients:\n \n\n As you can see there is a traffic problem on the server\u0027s network connection. If, say, there were 32 players connected to the server at the time, then the same information would be sent 32 times (once to each player). If there were 20 bytes of data to be sent to each of the 32 players then 640 bytes would have to be sent through the server\u0027s network connection. If that were to happen every time any of the 32 players pressed a key or moved the mouse, a huge amount of traffic is generated. Naturally, there is no replacement for good coding practice and sending only the data that is needed, but multicasting can seriously help.\n So how can multicasting help? Well, Multicasting can dramatically reduce the amount of data that needs to be sent by taking the task of packet replication away from the game server to the actual network infrastructure. In multicasting, packets can be sent to groups of network addresses, instead of individual addresses.\n This is similar to the way email works - when we want to send the same email message to multiple email addresses, we don\u0027t send the message to every address from our computer. Instead we send the message once, telling the server to replicate the message to all the other addresses. \n\n[size=\"5\"]The Darker Side\nOf course, there are reasons why multicasting is not commonly used:\nSome ISPs and networks don\u0027t support multicasting yet. Bastards. So if you want to implement multicasting in a game, you\u0027re better off adding it as an option. Internet multicasting is rarely supported, but hopefully it will be in the future. \nMulticasting only makes a worthwhile gain in performance when network data is replicated, realistically only worth bothering when there is support for more than four players. \nMulticasting requires some more coding and programmers are lazy to even look into it. As you will see, in fact it requires very little additional code. The corporate \"Quality Digital TV via Multicasting\" idea seems to put game programmers off the subject altogether, I suspect it has something to do with hacker ethics, so long live the .org\u0027s! \nThe openness of multicast groups may make your packets easier to sniff. Usually UDP packets can only be intercepted between their source and destination, but now they can be captured anywhere on the network; by joining the right group, anyone can get a carbon-copy! \n[size=\"5\"]How Multicasting Works\nYou may have heard of broadcasting. Broadcasting forwards data to every address on the network. Unlike broadcasting, multicasting only forwards to those addresses who have explicitly registered interest in the data.\n On an IP network supporting multicasting there are such things as multicast groups. If you want to receive multicast data packets, you must join a multicast group. Although it should be possible to send data packets to a multicast group regardless of membership, it is often better to join a group before sending to it for reasons I won\u0027t venture into. If you are a member of a group to which you are sending multicast data packets, you will receive a copy of the data packets. Also, a client will not receive all data packets from a multicast group, but only those which are sent to the port that the socket is bound to.\n So a sensible idea would be for all the game clients to join a multicast group and wait for data on the same port. Then the server, by sending a single packet of data to that multicast group, would be sending to all the clients as the packets are replicated somewhere along the way.\n\n We\u0027ve seen the light, we\u0027ve seen the darkness, so let us onto the code...\n[size=\"5\"]Joining a Multicast Group and Receiving Multicast Data Packets\nTo receive multicast packets sent to a multicast group, your game will need to join or become a member of that multicast group. To request becoming a member of a multicast group is a lot simpler than you may at first imagine. You need to first bind() your UDP socket to a local port (elementary, my dear friend):SOCKADDR_IN addrLocal;\n// We want to use the Internet address family\naddrLocal.sin_family = AF_INET;\n// Use any local address\naddrLocal.sin_addr.s_addr = INADDR_ANY;\n// Use arbitrary port - but the same as on other clients/servers\naddrLocal.sin_port = htons(uiPort); \n// Bind socket to our address\nif(SOCKET_ERROR == bind(hUDPSocket, (LPSOCKADDR)\u0026amp;addrLocal, \n sizeof(struct sockaddr)))\n {cout \n// Ready to switch to multicasting modeAnd then just make a call to setsockopt(), and here\u0027s a prototype for your convenience *grin*:int WSAAPI setsockopt(SOCKET s, int level, int optname,\n const char FAR * optval, int optlen);If you thought you were getting away with just 1 new line of code to learn, you were wrong... you\u0027re only getting away with 4 new lines =). There are special parameters to prepare for this call: s is your socket handle, level should be set to IPPROTO_IP, optname should be set to IP_ADD_MEMBERSHIP and a pointer to the p_mreq structure passed as optval, with its length in optlen. This is what the p_mreq structure looks like:struct ip_mreq {\n struct in_addr imr_multiaddr; /* multicast group to join */\n struct in_addr imr_interface; /* interface to join on */\n} It has 2 fields, both of them are in_add r structures: imr_multiaddr specifies the address of the multicast group to join and imr_interface specifies the local address INADDR_ANY.\n There are special (Class \u0027D\u0027) addresses allocated for multicast groups. These are in the range from 224.0.1.0 to 239.255.255.255. You can choose an address from the range as the target multicast group to join, and set the imr_multiaddr to this address. The full setsockopt() call would look something like this:struct ip_mreq mreq;\nmreq.imr_multiaddr.s_addr = inet_addr(\"234.5.6.7\");\nmreq.imr_interface.s_addr = INADDR_ANY;\nnRet = setsockopt(hUDPSocket, IPPROTO_IP, IP_ADD_MEMBERSHIP,\n (char*)\u0026amp;mreq, sizeof(mreq)); And that\u0027s all there is to it, apart from a lot of error checking which I\u0027ve decided to leave out for clarity (aka Laziness). The socket will now receive data packets sent to the multicast group on the specified port with calls to recvfrom():SOCKADDR_IN addrSrc;\nnRet = recvfrom(hUDPSocket, (char *)\u0026amp;Data, sizeof(Data), 0,\n (struct sockaddr*)\u0026amp;addrSrc, sizeof(addrSrc)); When you\u0027re finished with the group and want to leave, just repeat the call with identical parameters apart from IP_ADD_MEMBERSHIP which should be replaced with IP_DROP_MEMBERSHIP.nRet = setsockopt(hUDPSocket, IPPROTO_IP, IP_DROP_MEMBERSHIP,\n (char*)\u0026amp;mreq, sizeof(mreq)); Now that we can join a multicast group and receive packets sent to it, the logical thing to do is to learn how to send packets to a multicast group.\n[size=\"5\"]Sending Multicast Data Packets\nSending multicast data packets is accomplished with a call to sendto(), specifying a multicast group address as the destination IP address and the wanted port (on which your clients are tuned to listen for data). So there really a lot to learn apart from using the TTL (Time To Live) socket option.\n All IP packets carry a TTL value to make sure that they are discarded if they don\u0027t reach a destination so they don\u0027t clog up the network. In a multicast data packet, TTL specifies how far a multicast data packet can travel:\n\n[size=\"3\"]TTL Threshold\n\n[size=\"3\"]Description\n\n\nTTL equal to 0\nRestricted to the same host\n\n\nTTL equal to 1\nRestricted to the same subnet\n\n\nTTL equal to 32\nRestricted to the same site\n\n\nTTL equal to 64\nRestricted to the same region\n\n\nTTL equal to 128\nRestricted to the same continent\n\n\nTTL equal to 255\nUnrestricted in scope\n\n \t\t\t\t\t[From MSDN, this is very rough and should not be taken literally] \nMulticasting is nowhere as dangerous as broadcasting in terms of unwanted traffic that it can produce but caution is advised when using some of the higher TTL values.\n To set a socket\u0027s multicast TTL value, setsockopt() can be used with IPPROTO_IP as the protocol level and IP_MULTICAST_TTL as the socket option.char TTL = 32 ; // Restrict to our school network, for example\nsetsockopt(hUDPSocket, IPPROTO_IP, IP_MULTICAST_TTL,\n \t\t(char *)\u0026amp;TTL, sizeof(TTL)); We must also tell the system exactly which local network interface we would like to multicast on.// Set the local interface from which multicast is to be transmitted\nunsigned long addr = inet_addr(YOUR_IP_ADDRESS_STRING);\nsetsockopt(sSocket, IPPROTO_IP, IP_MULTICAST_IF, (char *)\u0026amp;addr,\nsizeof(addr)); Once the TTL and multicast interface are set, just sendto() away:SOCKADDR_IN addrDest;\nszHi[50];\naddrDest.sin_family = AF_INET;\n// Target multicast group address\naddrDest.sin_addr.s_addr = inet_addr(\"234.5.6.7\");\n// Port on which client is set to receive data packets\naddrDest.sin_port = htons(uiPort);\n// Something unoriginal to send\nstrcpy(szHi,\"Hello Multicast Group!\");\nnRet = sendto(hUDPSocket, (char *)szHi, strlen(szHi), 0,\n (struct sockaddr*)\u0026amp;addrDest, sizeof(addrDest)); We can now join multicast groups, send and receive data from them, but how do we implement multicasting as an option in our game and what would we use it for?\n[size=\"5\"]Uses of Multicasting in Games\nI can think of two ways straight away - one is to use it for reducing (maybe even eliminating) the amount of repeated data that a server has to send out, but another interesting use is a global server-less interface for finding other players on the network.\n The scenario: there are 2 people on a large network running the same game that want to play together, but they don\u0027t know each other\u0027s IP addresses let alone the fact that the potential opponent exists. The common ways for connecting the 2 players:\nThe players send out a broadcast message to the entire network, however this would create huge traffic and will probably be restricted to sub networks. Broadcasting on the Internet would create an enormous amount of traffic, so it is not allowed. \nThe players connect to an intermediate, \"known\" master server IP, which tells them of each other\u0027s existence. These servers are costly to run and their uptime is often undependable. \nThe players go to a chatroom hoping to find other players and play together. This will not connect all the players as some may be in different chatrooms. And the process of finding someone may take a while. So here we are with an age old problem (how A finds B) on one hand and multicasting on the other. Multicasting groups always have the same address - a \"known\" address as in the case of a dedicated server, they are online 100% of the time - unlimited uptime, they don\u0027t cost anything to connect to or send information across. All game clients simply connects to a multicast group, multicast an \"I want to play\" message and the servers can then advertise their availability directly (instead of broadcasting, to save bandwidth) to the clients who are members of the multicast group.\n Sure, there are itsy-bitsy technical problems to sort out, but the idea is cool enough. And the TTL control allows us to query within a certain range of routers (see TTL table) so we can specifically only ask to send to our LAN, or a university network, or all servers within our country to respond. Don\u0027t you think that is COOL? I sure do.\n The only problem (see \"The Dark Side\") remains multicast support by ISP\u0027s and networks. So the best way to add multicasting to a game is still as an option (although I hope and pray that multicasting will be 100% supported in the future). But how do we integrate multicasting into our game as an option?\n[size=\"5\"]Integrating Multicasting into Games\nOk then, where do we start? There are so many different types of multiplayer games that I won\u0027t even try to explain how to integrate multicasting into different types of games. Instead I\u0027ll just give a few possible ideas of solutions in a client-server relationship.\n First of all, all the current network code should be kept as it is, when you add multicast support make sure you do not remove any existing code unless you really think it is necessary.\n When adding multicast support, you can either do a parallel integration where multicasting runs along with existing code, or you could write two separate sets of network code and add a \u0027multicast on/off switch\u0027 for the user. The on/off switch would isolate servers using the other network code and add one more daunting and mysterious switch for the average newbie to get wrong. Parallel integration (bah, the things I learned in school last year) is my favorite as it will use multicasting only if it is supported and should be transparent to the user.\n So let\u0027s stick with parallel integration - in this case the normal network code runs always, but the multicasting code only runs if multicasting is supported. How do we determine if multicasting is supported? Just read the error setsockopt() gives us when trying to join a group:nRet = setsockopt(hUDPSocket, IPPROTO_IP, IP_ADD_MEMBERSHIP,\n (char*)\u0026amp;mreq, sizeof(mreq));\nif(WSAESOCKTNOSUPPORT == nRet)\n {\n // Multicasting not supported. Damn.\n } The client-server relationship is a game of two halves. So what if the server supports multicasting while the client does not? How does the server know which clients are covered with a single send to the multicast group and which are not? The client first determines that it does not support multicasting, then connects to the server and tells the server whether it supports multicasting. The server usually keeps a list or array of clients, to which it is easy to add an extra boolean flag:struct Client\n {\n SOCKADDR_IN addrRemote;\n /* ... Game specific info here ...*/\n BOOL bSupportMulticast;\n } The server\u0027s function to send data sends a multicast to clients who support multicasting and normal UDP datagrams to those that do not. If, however, the server itself does not support multicasting then we must use the old method. Here\u0027s a useful code snippet for a server with multicasting as an option:int SendToAll(char *Data)\n {\n if(bServerSupportMulticast)\n {\n // First send multicast, then send individually\n // to those who don\u0027t support it\n for(int index = 0; index \n {\n if(Clients[index].Exist \u0026amp;\u0026amp; Clients[index].bSupportMulticast)\n {\n // At least one client supports multicasting, so use it\n SendMulticast(Data, addrMulticast);\n break;\n }\n }\n for(int index = 0; index \n {\n if(Clients[index].Exist \u0026amp;\u0026amp; !Clients[index].bSupportMulticast)\n {\n OldSendToClient(Data, Clients[index].addrRemote);\n }\n }\n }\n else\n {\n // Use the old method all the way regardless of support\n // as we ourselves don\u0027t support it\n for(int index = 0; index \n {\n if(Clients[index].Exist)\n {\n OldSendToClient(Data, Clients[index].addrRemote);\n }\n }\n }\n } I hope I\u0027ve shed some light on multicasting and its possible uses in games. If you\u0027ve found this article the least bit interesting or have a problem, drop an email to [email=\"denis@voxelsoft.com\"]denis@voxelsoft.com[/email]. I hope to write another article soon, but for now, Happy Multicasting!\n - Denis \"Voxel\" Lukianov\nThanks to Jan \"Riva\" Halfar for the wonderful diagrams. \n\n \n\n", "dateCreated": "2001-11-09T01:10:37+0000", "datePublished": "2001-11-09T01:10:00+0000", "dateModified": "2001-11-09T01:10:00+0000", "pageStart": 1, "pageEnd": 1, "author": { "@type": "Person", "name": "Myopic Rhino", "image": "https://secure.gravatar.com/avatar/16f4f23aa8088c2c27c829623874b42e?d=https://www.gamedev.net/uploads/monthly_2017_08/M.png.3d2e4bcade48bd80a1d6554f9c036e99.png", "url": "https://www.gamedev.net/profile/6-dave-astle/" }, "publisher": { "@type": "Organization", "name": "Myopic Rhino", "image": "https://secure.gravatar.com/avatar/16f4f23aa8088c2c27c829623874b42e?d=https://www.gamedev.net/uploads/monthly_2017_08/M.png.3d2e4bcade48bd80a1d6554f9c036e99.png", "logo": { "@type": "ImageObject", "url": "https://secure.gravatar.com/avatar/16f4f23aa8088c2c27c829623874b42e?d=https://www.gamedev.net/uploads/monthly_2017_08/M.png.3d2e4bcade48bd80a1d6554f9c036e99.png" }, "url": "https://www.gamedev.net/profile/6-dave-astle/" }, "interactionStatistic": [ { "@type": "InteractionCounter", "interactionType": "http://schema.org/ViewAction", "userInteractionCount": 19887 }, { "@type": "InteractionCounter", "interactionType": "http://schema.org/FollowAction", "userInteractionCount": 1 }, { "@type": "InteractionCounter", "interactionType": "http://schema.org/ReviewAction", "userInteractionCount": 0 }, { "@type": "InteractionCounter", "interactionType": "http://schema.org/CommentAction", "userInteractionCount": 2 } ], "image": { "@type": "ImageObject", "url": "https://secure.gravatar.com/avatar/16f4f23aa8088c2c27c829623874b42e?d=https://www.gamedev.net/uploads/monthly_2017_08/M.png.3d2e4bcade48bd80a1d6554f9c036e99.png", "width": "500", "height": "500" }, "aggregateRating": { "@type": "AggregateRating", "ratingValue": 0, "ratingCount": 0 }, "commentCount": 2 } { "@context": "http://www.schema.org", "@type": "WebSite", "name": "GameDev.net", "url": "https://www.gamedev.net/", "potentialAction": { "type": "SearchAction", "query-input": "required name=query", "target": "https://www.gamedev.net/search/?q={query}" }, "inLanguage": [ { "@type": "Language", "name": "English (USA)", "alternateName": "en-US" } ] } { "@context": "http://www.schema.org", "@type": "Organization", "name": "GameDev.net", "url": "https://www.gamedev.net/", "logo": "https://www.gamedev.net/uploads/themes/monthly_2017_04/gamedev-logo-2017-368x76.png.e986e41d566ff6c90485a5304985ed5f.png", "address": { "@type": "PostalAddress", "streetAddress": "", "addressLocality": null, "addressRegion": null, "postalCode": null, "addressCountry": null } } { "@context": "http://schema.org", "@type": "BreadcrumbList", "itemListElement": [ { "@type": "ListItem", "position": 1, "item": { "@id": "https://www.gamedev.net/articles/", "name": "Articles" } }, { "@type": "ListItem", "position": 2, "item": { "@id": "https://www.gamedev.net/articles/programming/", "name": "Programming" } }, { "@type": "ListItem", "position": 3, "item": { "@id": "https://www.gamedev.net/articles/programming/networking-and-multiplayer/", "name": "Networking and Multiplayer" } } ] } { "@context": "http://schema.org", "@type": "ContactPage", "url": "https://www.gamedev.net/contact/" } Important Information By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.   I accept We are the game development community. Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up! Sign me up!