Online games and standalone games
When it comes to online games, we have to think of Single-host games. In fact, the essence of online games cannot be separated from the production idea of Single-host games. The difference between online games and single-host games can be directly thought: is it possible to connect multiple users? Yes, but how to implement these functions and reasonably integrate network connections into a single-host game is what we will discuss below. Before learning about the specific implementation of network interconnection, let's take a look at the processes of standalone and online games. Only by understanding these processes can you go deep into the core of online game development. Now let's take a look at the Simplified execution process of a common standalone Game: Initialize () // initialization module {initializing Game data;} Game () // game loop part {draw game scenarios, characters, and other elements;
Obtains user operation input;
Switch (user input data ){
Move case :{
Handling the movement of characters;
}
Break;
Case attack :{
Attack processing logic:
}
Break;
...
Other processing responses;
...
Default:
Break;
}
Logic AI processing such as the game's NPC;
} Exit ()
// The game ends {
Releases game data;
Leave the game ;}
Let's explain the process of the single-host game above. First, Initialization is essential for both game software and other applications. Game data needs to be initialized here, including images, sounds, and necessary data. Next, our games draw the scenes, characters, and other elements cyclically, present the game world to players, receive input operations from players, and respond accordingly, the game also needs to process the NPC and some logic AI. Finally, the game data is released and the game ends. There is a significant difference between online games and stand-alone games, that is, in addition to a user interface platform (such as a stand-alone game) for game operations, online games also need to be used to connect all users, it also provides data service servers for all users. In some aspects, the game server is like a large database, providing data and data logic interaction functions. Let's take a look at a simple online game model execution process:
Client: Login () // Login module {
Initialize game data;
Obtain the user and password entered by the user, create a network connection with the server, and send it to the server for user verification ;... wait for the server to confirm the message ;... obtain the login message from the server. if (true)
Enter the game;
Else
Prompt the user to log on incorrectly and accept the user to log on again;
} Game ()
// Game loop part {
Draw game scenarios, figures, and other elements;
Obtains user operation input and sends user operations to the server;
... Waiting for a message from the server;
... Receive feedback from the server;
Switch (server feedback message data ){
Case: messages of local gamer movement:
{
If (allow local players to move)
The client processes the movement of characters;
Else
The client remains in the original state;
}
Break;
Case other player/NPC mobile messages:
{
Based on the feedback from the server, move other players or NPC players;} break; case new players join the game: {Add this player to the client;} break; case players leave the game: {destroy player data in the client;} break ;... other message type processing ;... default: break;} Exit () // the end of the game {send the Exit message to the server ;... waiting for confirmation from the server ;... get the server confirmation message; disconnect from the server; release game data; leave the game;} server: Listen () // The game server waits for the player connection module {... wait for the user's login information ;... receive user login information; analyze whether the user name and password match; if (yes) {send confirmation to allow access to the game message to the client; publish the message about the player entering the Game to all players in the Game scenario; Add the player to the server scenario;} else {disconnect from the client;} Game () // game server loop part {... wait for the player operation input in the scenario ;... receives the mobile input of a player or the mobile logic input of the NPC. // here, only mobile is used as an example to determine whether the player/NPC can be moved in the map scenario. if (mobile) {This player/NPC performs server mobile processing; sends a mobile message to the client; sends a mobile message to all players in the scenario;} else sends a non-mobile message to the client ;} exit () // game service = server end {receives the player Exit message; sends the message to all players in the scenario; sends the information that can be left; stores the player data to the database; log off the player's data in the server memory ;}}
Let's explain the operating mechanism of the above simple online game model. Let's talk about the server. Here, the server is divided into three parts (in fact, a complete online game is far more than this): the login module, the game module, and the logout module. The logon module is used to listen to the network connection messages sent from the online game client and verify its validity. Then, the player is created on the server and lead the player to the game module; the game module provides the actual application services for gamers. We will introduce this part in detail later. After receiving the message that the players are about to leave the game, the logout module deletes players from the server and saves the gamer attribute data to the server database, such as experience values, levels, and lifecycles.
Next let's take a look at the online game client. At this time, the client no longer enters the game directly after initializing the data, just like a single-host game. Instead, it enters the game only after creating a connection with the server and obtaining a license. In addition, the client game processes of online games need to constantly communicate with the server and exchange data with the server to determine the current game status, such as location changes and item drops of other players. Similarly, when you leave the game, the client will tell the server that the player user has left, so that the server can handle the problem accordingly. The above uses a simple pseudocode to explain the execution process of standalone and online games. You should be able to clearly see the differences between the two and their relationships. From another angle, we can consider that online games are to move the logic operation of single-host games to the game server for processing, and then process the results (including other player data) return to connected players through the game server. Network Interconnection
After learning about the basic form of online games, let us enter the real practical application section. First of all, as an online game, in addition to what is essential for conventional standalone games, we also need to add a network communication module. Of course, this is also the main part of online games, let's discuss how to implement the network communication module.
A complete network communication module involves a wide range of fields. This article only discusses the basic processing methods. Online Games are composed of clients and servers. They also need two different network communication processing methods. However, they also have similarities. Let's first introduce what they have in common. Microsoft Windows 2000 [2000 Server] is used as the development platform and Winsock is used as the network interface. (Some friends may consider using DirectPlay for network communication. However, for online games, directPlay is not suitable. The reason is not discussed here ).
After determining the platform and interface, we start some necessary initialization work before the network connection is created, which is required for both the client and server. Let's take a look at the following code snippet: WORD wVersionRequested; WSADATAwsaData; wVersionRequested MAKEWORD (1, 1); if (WSAStartup (wVersionRequested, & wsaData )! 0) {Failed (WinSock Version Error! ");}
The above code initializes the network device by calling the socket API function of Windows, and then creates the network Socket. The code snippet is as follows: SOCKET sSocket socket (AF_INET, m_lProtocol, 0 ); if (sSocket = INVALID_SOCKET) {Failed ("WinSocket Create Error! ");}
The number of Socket connections required by the client and the server is different. The client only needs one Socket connection to meet the needs of the game, the server must create a Socket connection for communication for each player user. Of course, it doesn't mean that if there are no players on the server, you don't need to create a Socket connection. The server will generate a special Socket at startup to respond to the player's request for creating a connection to the server, and so on.
There must be release and deletion for initialization and creation. Let's take a look at the release section below: if (sSocket! = INVALID_SOCKET) {closesocket (sSocket);} if (WSACleanup ()! = 0) {Warning ("Can't release Winsocket ");}
The two steps are respectively released for the previous creation initialization.
Next, let's take a look at a network on the server for processing. Here we assume that the server has already created a Socket for use. What we need to do is to turn this Socket into a dedicated interface for listening to network connection requests, take a look at the following code snippet: SOCKADDR_IN addr; memset (& addr, 0, sizeof (addr); addr. sin_family = AF_INET; addr. sin_addr.s_addr = htonl (INADDR_ANY); addr. sin_port = htons (Port); // Port is the Port number to be monitored // bind socketif (bind (sSocket, (SOCKADDR *) & addr, sizeof (addr) = SOCKET_ERROR) {Failed ("WinSocket Bind Error! ");} // Listen if (Listen (sSocket, SOMAXCONN) = SOCKET_ERROR) {Failed (" WinSocket listen Error! ");}
Blocking communication processing is used here. At this time, the program will be in the waiting state for gamer user connection. If a client is connected at this time, the accept () to create a Socket connection for this player user. The code snippet is as follows: sockaddraddrServer; int nLen sizeof (addrServer); SOCKET sPlayerSocket accept (sSocket, & addrServer, & nLen ); if (sPlayerSocket = INVALID_SOCKET) {Failed (WinSocket Accept Error! ");}
Here we have created a sPlayerSocket connection. Since then, all the communication between the game server and the player user has been carried out through this Socket. So far, our server has the function of accepting the connection from the player user, now let's take a look at how the game client connects to the game server. The code snippets are as follows: SOCKADDR_IN addr; memset (& addr, 0, sizeof (addr); addr. sin_family = AF_INET; // Number of the game server to be connected to addr. sin_addr.s_addr = inet_addr (IP); // IP address of the game server to be connected, addr. sin_port = htons (Port); // at this point, the client and server already have a communication bridge. // The next step is to send and receive data: connect (sSocket, (SOCKADDR *) & addr, sizeof (addr); if (send (sSocket, PBuffer, lLength, 0) = SOCKET_ERROR) {Failed ("WinSocket Send Error! ");}
Here, pBuffer is the data buffer pointer to be sent, and lLength is the length of the data to be sent. Through this Socket API function, we can send data on both the client and the server, at the same time, we can use the recv () Socket API function to receive data: if (recv (sSocket, pBuffer, lLength, 0) = SOCKET_ERROR) {Failed ("WinSocket Recv Error! ");}
PBuffer is used to store the obtained network data buffer, while lLength is the length of the data to be obtained.
Now, we have learned some basic knowledge about network interconnection. However, as an online game, such a simple connection method cannot meet the needs of hundreds of people online at the same time, we need more reasonable and fault-tolerant network communication processing methods. Of course, we need to first understand what network gaming needs for network communication.
As we all know, games need to constantly process the logic in the game and draw the game world. The Winsock processing method described above is blocked, which violates the essence of game execution, as you can imagine, when the client is connected to the server, your game cannot be controlled. If the player wants to cancel the connection or perform other operations, even a basic dynamic connection prompt is not displayed.
Therefore, we need to use other methods to process network communication so that it will not conflict with the main line of the game. We may think that it is okay to create a network thread to process it? Yes, we can create a subthread dedicated for network communication to solve this problem. Of course, if we have another thread in our game, we need to consider more. Let's take a look at how to create a network communication thread.
In Windows, we can use the CreateThread () function to create threads. Let's see the following code snippet: DWORD dwThreadID; HANDLE hThread = CreateThread (NULL, 0, netThread/* network Thread function */, sSocket, 0, & dwThreadID); if (hThread = NULL) {Failed ("WinSocket Thread Create Error! ");}
Here we have created a thread and passed our Socket into the thread function: DWORD WINAPINetThread (LPVOID lParam ){
SOCKET sSocket (SOCKET) lParam;
... Return 0 ;}
NetThread is the network thread that we will use to process network communication in the future. Then, how can we introduce Socket processing into the thread?
Take a look at the following code snippet: HANDLE hEvent; hEvent = CreateEvent (NULL, 0); // set asynchronous communication if (WSAEventSelect (sSocket, hEvent, FD_ACCEPT | FD_CONNECT | FD_READ | FD_WRITE | FD_CLOSE) = SOCKET_ERROR) {Failed ("WinSocket EventSelect Error! ");}
After the preceding settings, the WinSock API functions run in a non-blocking manner, that is, the function will be returned immediately after execution. network communication is stored in hEvent as an event, instead of stopping the entire program.
After completing the above steps, we need to respond to and process the event. Let's see how to obtain the event message generated by network communication in the network thread: WSAEnumNetworkEvents (sSocket, hEvent, & SocketEvents); if (SocketEvents. lNetworkEvents! = 0 ){
Switch (SocketEvents. lNetworkEvents ){
Case FD_ACCEPT:
WSANETWORKEVENTS SocketEvents;
Break;
Case FD_CONNECT:
{
If (SocketEvents. iErrorCode [FD_CONNECT_BIT] = 0)
// Connection successful
{
// Notify the main thread (game thread) for processing after the connection is successful
}
}
Break;
Case FD_READ:
// Obtain Network Data
{
If (recv (sSocket, pBuffer, lLength, 0) = SOCKET_ERROR)
{Failed ("WinSocket Recv Error! ");
}
}
Break;
Case FD_WRITE:
Break;
Case FD_CLOSE:
// Notify the main thread (game thread) that the network has been disconnected
Break;
Default:
Break;
}}
Here, only the network connection (FD_CONNECT) and Data Reading (FD_READ) are simulated. However, after the network thread receives the event message, it organizes and organizes the data, then the data will be sent back to our main game thread for use, and the main game thread will then send the processed data, so that a round trip constitutes the data communication in our online game, is the most basic element for online games to move.
Finally, let's talk about the organization of network packets (data packets). The data packets of online games are the most basic unit of game Data Communication. Online Games generally do not use Word throttling for data transmission, A data packet can also be considered as a message command. During the game, servers and clients will continuously send and receive these message packets, then, the message packet Parsing is converted into the meaning of the command to be expressed and executed. Interaction and Management
When it comes to interaction, it is for gamers to communicate with other players, but for computers, InterAction means mutual transmission of data messages. We have already understood the basic concepts of network communication, which constitute the most basic conditions for interaction. Next we need to communicate data at this network layer. Unfortunately, computers do not know how to express the communication between players. Therefore, we need to provide a set of command organization and resolution mechanisms that allow computers to understand, that is, the processing mechanism of network packets (data packets) that we mentioned above. In order to give you a more simple explanation of the network packet organization form, we will discuss it with a chat processing module and look at the following code structure:
Struct tagMessage {long lType; long lPlayerID ;};
// Message command
// Instruction-related player ID
Char strTalk [256];
// Message content
The preceding is an extremely simple structure of the message package. Let's talk about the usage of each data domain:
First, lType is the type of message instruction, which is the most basic message identifier. This identifier is used to tell the server or client the specific purpose of this instruction, so that the server or client can handle it accordingly. The lPlayerID is used as the player identifier. As we all know, a player is actually a pile of data inside the machine, especially in the game server, there may be thousands of players. At this time, we need a tag to differentiate players, in this way, you can quickly find a specific player and apply the communication data to it.
StrTalk is the chat data we want to transmit. This part is the real data entity. The preceding parameters are only limited by the application scope of the data entity.
After the data is organized, the structured data is sent and received through a Socket connection. Here, we need to know that the network does not care about the data structure used during data transmission. Therefore, we need to convert the data structure into a binary data code and send it to the receiver, we then convert these binary data codes back to the corresponding data structure used by the program. Let's take a look at how to implement: tagMessageMsg; Msg. lTypeMSG_CHAT; Msg. lPlayerID 1000; strcpy (& Msg. strTalk, "chat information ");
First, let us assume that a data packet has been organized. Here, MSG_CHAT is our custom identifier. Of course, this identifier must be consistent on the server and client. The player ID is set based on the game's needs. Here, 1000 is used as the assumption. Continue: char * p = (char *) & Msg; long lLength = sizeof (tagMessage ); send (sSocket, p, lLength); // obtain the length of the Data Structure
We convert the struct type into a char type Data Pointer through forced conversion, so that we can use this pointer for stream data processing. Here we get the struct length through sizeof, then, use the Send () function of WinSock to Send data.
Next let's take a look at how to receive data: long lLength = sizeof (tagMessage); char * Buffer = new char [lLength]; recv (sSocket, Buffer, lLength ); tagMessage * p = (tagMessage *) Buffer; // get data
After obtaining network data through the WinSock recv () function, we also forcibly convert the obtained buffer data into the corresponding struct to facilitate data access. (Note: forced conversion is only used as a means of data conversion. There are more optional methods in actual application. Here we only describe the logic in a simple way.) Here, I have to mention how the server/client filters and processes various messages and manages communication packets. When a server or client receives a network message, after the above data is parsed, the message type must also be filtered and sent. in simple words, it is similar to a Windows message loop, different messages are processed differently. In this case, you can use a switch statement (a friend familiar with Windows message loops may already understand this) To differentiate messages based on the lType information in the message packets. Consider the following code snippet: switch (p-> lType) // here, p-> lType is the parsed message type identifier {case MSG_CHAT: // chat message break; case MSG_MOVE: // move the gamer message
Break; case MSG_EXIT: // The player leaves the message break; default: break ;}
Both MSG_MOVE and MSG_EXIT in the above snippet are our virtual message Identifiers (There may be hundreds of identifiers in a real game, so we need to consider optimization and priority message processing issues ). In addition, an online game server faces hundreds of thousands of connected users. We also need some reasonable data organization and management methods for relevant processing. A common single game server may paralyze the entire game network because of the large number of machines or users. This introduces the group server mechanism, which allows us to split servers for Distributed Data Processing.
We extract each module into a dedicated server system and establish a data center that connects all servers for Data Interaction. Here, each module creates a connection with the data center, this ensures the relevance of each module and enables gamers to communicate with the servers currently providing services. This reduces the burden on a single server, the pressure is distributed across multiple servers and data consistency is ensured. Even if a server is running abnormally, it will not affect gamers of other modules, thus improving the overall stability.
Grouping servers relieve the pressure on servers, but also bring about server scheduling problems. Grouping servers need to handle server redirection, so the game scenario jump of a player is used as the basis for discussion: assume that A player is in game scenario A and wants to jump from scenario A to scenario B. In the game, we call this scenario switching. Then, the player will trigger the jump request, for example, the server deletes the player data from "game scenario A server" and establishes players in "game scenario B server.
Here we describe the simple model of scenario switching, and there are still many steps to process it. However, through such thinking, we believe that you can derive a lot of application skills. However, when switching between scenarios or switching between modules, you must take into account the data transmission security and Logic Rationality, otherwise, switching may be a bridge for players to copy items in the future.