OSDN Git Service

0515d2edb9f4b669be655d34ac2359b006874153
[mikumikustudio/MikuMikuStudio.git] / sdk / jme3-documentation / src / com / jme3 / gde / docs / jme3 / advanced / networking.html
1
2 <h1><a>SpiderMonkey: Multi-Player Networking</a></h1>
3 <div>
4
5 <p>
6
7 This document introduces you to the SpiderMonkey networking <acronym title="Application Programming Interface">API</acronym>. You use this <acronym title="Application Programming Interface">API</acronym> when you develop games where several players compete with one another in real time. A multi-player game is made up of several clients connecting to a server:
8
9 </p>
10 <ul>
11 <li><div> The central server (one headless SimpleApplication) coordinates the game in the background.</div>
12 </li>
13 <li><div> Each player runs a game client (a standard SimpleApplication) and connects to the central server.</div>
14 </li>
15 </ul>
16
17 <p>
18
19 Each Client keeps the Server informed about its player&#039;s moves and actions. The Server centrally maintains the game state and broadcasts the state info back to all connected clients. This network synchronization allows all clients to share the same game world. Each client then displays the game state to one player from this player&#039;s perspective.
20 </p>
21
22 </div>
23 <!-- EDIT1 SECTION "SpiderMonkey: Multi-Player Networking" [1-841] -->
24 <h2><a>SpiderMonkey API Overview</a></h2>
25 <div>
26
27 <p>
28
29 The SpiderMonkey <acronym title="Application Programming Interface">API</acronym> is a set of interfaces and helper classes in the &#039;com.jme3.network&#039; package.  For most users, this package and the &#039;message&#039; package is all they need to worry about.  (The &#039;base&#039; and &#039;kernel&#039; packages only come into play when implementing custom network transports or alternate client/server protocols, which is now possible).
30 </p>
31
32 <p>
33 The SpiderMonkey <acronym title="Application Programming Interface">API</acronym> assists you in creating a Server, Clients, and Messages. Once a Server instance is created and started, the Server accepts remote connections from Clients, and you can send and receive Messages. Client objects represent the client-side of the client-server connection.  Within the Server, these Client objects are referred to as HostedConnections. HostedConnections can hold application-defined client-specific session attributes that the server-side listeners and services can use to track player information, etc.
34
35 </p>
36 <div><table>
37         <tr>
38                 <th> Seen from the Client </th><th> </th><th> Seen from the Server </th>
39         </tr>
40         <tr>
41                 <td> com.jme3.network.Client </td><td> == </td><td> com.jme3.network.HostedConnection </td>
42         </tr>
43 </table></div>
44 <!-- EDIT3 TABLE [1767-1885] -->
45 <p>
46
47 You can register several types of listeners to be notified of changes.
48 </p>
49 <ul>
50 <li><div> MessageListeners on both the Client and the Server are notified when new messages arrive.  You can use MessageListeners to be notified about only specific types of messages.</div>
51 </li>
52 <li><div> ClientStateListeners inform the Client of changes in its connection state, e.g. when the client gets kicked from the server.</div>
53 </li>
54 <li><div> ConnectionListeners inform the Server about HostedConnection arrivals and removals, e.g. if a client joins or quits.</div>
55 </li>
56 </ul>
57
58 </div>
59 <!-- EDIT2 SECTION "SpiderMonkey API Overview" [842-2386] -->
60 <h2><a>Client and Server</a></h2>
61 <div>
62
63 </div>
64 <!-- EDIT4 SECTION "Client and Server" [2387-2417] -->
65 <h3><a>Creating a Server</a></h3>
66 <div>
67
68 <p>
69
70 The game server is a &quot;headless&quot; com.jme3.app.SimpleApplication:
71 </p>
72 <pre>public class ServerMain extends SimpleApplication &#123;
73   public static void main&#40;String&#91;&#93; args&#41; &#123;
74     ServerMain app = new ServerMain&#40;&#41;;
75     app.start&#40;JmeContext.Type.Headless&#41;; // headless type for servers!
76   &#125;
77 &#125;</pre>
78
79 <p>
80 <p><div>A <code>Headless</code> SimpleApplication executes the simpleInitApp() method and runs the update loop normally. But the application does not open a window, and it does not listen to user input. This is the typical behavior for a server application.
81 </div></p>
82 </p>
83
84 <p>
85 Create a com.jme3.network.Server in the <code>simpleInitApp()</code> method and specify a communication port, for example 6143.
86 </p>
87 <pre>  public void simpleInitApp&#40;&#41; &#123;
88     ...
89     Server myServer = Network.createServer&#40;6143&#41;;
90     myServer.start&#40;&#41;;
91     ...
92   &#125;</pre>
93
94 <p>
95 When you run this app on a host, the server is ready to accept clients. Let&#039;s create a client next.
96 </p>
97
98 </div>
99 <!-- EDIT5 SECTION "Creating a Server" [2418-3368] -->
100 <h3><a>Creating a Client</a></h3>
101 <div>
102
103 <p>
104
105 A game client is a standard com.jme3.app.SimpleApplication.
106 </p>
107 <pre>public class ClientMain extends SimpleApplication &#123;
108   public static void main&#40;String&#91;&#93; args&#41; &#123;
109     ClientMain app = new ClientMain&#40;&#41;;
110     app.start&#40;JmeContext.Type.Display&#41;; // standard display type
111   &#125;
112 &#125;</pre>
113
114 <p>
115 <p><div>A standard SimpleApplication in <code>Display</code> mode executes the simpleInitApp() method, runs the update loop, opens a window for the rendered video output, and listens to user input. This is the typical behavior for a client application.
116 </div></p>
117 </p>
118
119 <p>
120
121 Create a com.jme3.network.Client in the <code>simpleInitApp()</code> method and specify the servers IP address, and the same communication port as for the server, here 6143.
122 </p>
123 <pre>public void simpleInitApp&#40;&#41; &#123;
124    ...
125    Client myClient = Network.connectToServer&#40;&quot;localhost&quot;, 6143&#41;;
126    myClient.start&#40;&#41;;
127    ...</pre>
128
129 <p>
130 The server address can be in the format &quot;localhost&quot; or &quot;127.0.0.1&quot; (for local testing), or an IP address of a remote host in the format ???123.456.78.9???. In this example, we assume the server is running on the localhost.
131 </p>
132
133 <p>
134 When you run this client, it connects to the server.
135 </p>
136
137 </div>
138 <!-- EDIT6 SECTION "Creating a Client" [3369-4534] -->
139 <h3><a>Getting Info About a Client</a></h3>
140 <div>
141
142 <p>
143
144 The server refers to a connected client as com.jme3.network.HostedConnection objects. The server can get info about clients as follows:
145
146 </p>
147 <div><table>
148         <tr>
149                 <th>Accessor</th><th>Purpose</th>
150         </tr>
151         <tr>
152                 <td>myServer.getConnections()</td><td>Server gets a collection of all connected HostedConnection objects (all connected clients).</td>
153         </tr>
154         <tr>
155                 <td>myServer.getConnections().size()</td><td>Server gets the number of all connected HostedConnection objects (number of clients).</td>
156         </tr>
157         <tr>
158                 <td>myServer.getConnection(0)</td><td>Server gets the first (0), second (1), etc, connected HostedConnection object (one client).</td>
159         </tr>
160 </table></div>
161 <!-- EDIT8 TABLE [4711-5090] -->
162 <p>
163
164 Your game can define its own game data based on whatever criteria you want, typically these include player ID and state. If the server needs to look up player/client-specific information, you can store this information directly on the HostedConnection object. The following examples read and write a custom Java object <code>MyState</code> in the HostedConnection object <code>conn</code>:
165
166 </p>
167 <div><table>
168         <tr>
169                 <th>Accessor</th><th>Purpose</th>
170         </tr>
171         <tr>
172                 <td> conn.setAttribute(&quot;MyState&quot;, new MyState()); </td><td> Server can change an attribute of the HostedConnection. </td>
173         </tr>
174         <tr>
175                 <td> MyState state = conn.getAttribute(&quot;MyState&quot;)</td><td> Server can read an attribute of the HostedConnection. </td>
176         </tr>
177 </table></div>
178 <!-- EDIT9 TABLE [5465-5694] -->
179 </div>
180 <!-- EDIT7 SECTION "Getting Info About a Client" [4535-5695] -->
181 <h2><a>Messaging</a></h2>
182 <div>
183
184 </div>
185 <!-- EDIT10 SECTION "Messaging" [5696-5718] -->
186 <h3><a>Creating Message Types</a></h3>
187 <div>
188
189 <p>
190
191 Each message represents data that you want to transmit between client and server. Common message examples include transformation updates or game actions. For each message type, create a message class that extends com.jme3.network.AbstractMessage. Use the @Serializable annotation from com.jme3.network.serializing.Serializable and create an empty default constructor. Custom constructors, fields, and methods are up to you and depend on the message data that you want to transmit.
192 </p>
193 <pre>@Serializable
194 public class HelloMessage extends AbstractMessage &#123;
195   private String hello;       // custom message data
196   public HelloMessage&#40;&#41; &#123;&#125;    // empty constructor
197   public HelloMessage&#40;String s&#41; &#123; hello = s; &#125; // custom constructor
198 &#125;</pre>
199
200 <p>
201 You must register each message type to the com.jme3.network.serializing.Serializer, in both server and client!
202 </p>
203 <pre>Serializer.registerClass&#40;HelloMessage.class&#41;;</pre>
204
205 </div>
206 <!-- EDIT11 SECTION "Creating Message Types" [5719-6671] -->
207 <h3><a>Responding to Messages</a></h3>
208 <div>
209
210 <p>
211
212 After a Message was received, a Listener responds to it. The listener can access fields of the message, and send messages back, start new threads, etc. There are two listeners, one on the server, one on the client. For each message type, you implement the responses in either Listeners??? <code>messageReceived()</code> method.
213 </p>
214
215 </div>
216
217 <h4><a>ClientListener.java</a></h4>
218 <div>
219
220 <p>
221
222 Create one ClientListener.java and make it extend <code>com.jme3.network.MessageListener</code>.
223
224 </p>
225 <pre>public class ClientListener implements MessageListener&lt;Client&gt; &#123;
226   public void messageReceived&#40;Client source, Message message&#41; &#123;
227     if &#40;message instanceof HelloMessage&#41; &#123;
228       // do something with the message
229       HelloMessage helloMessage = &#40;HelloMessage&#41; message;
230       System.out.println&#40;&quot;Client #&quot;+source.getId&#40;&#41;+&quot; received: '&quot;+helloMessage.getSomething&#40;&#41;+&quot;'&quot;&#41;;
231     &#125; // else...
232   &#125;</pre>
233
234 <p>
235 For each message type, register a client listener to the client.
236 </p>
237 <pre>myClient.addMessageListener&#40;new ClientListener&#40;&#41;, HelloMessage.class&#41;;</pre>
238
239 </div>
240
241 <h4><a>ServerListener.java</a></h4>
242 <div>
243
244 <p>
245
246 Create one ServerListener.java and make it extend <code>com.jme3.network.MessageListener</code>.
247 </p>
248 <pre>public class ServerListener implements MessageListener&lt;HostedConnection&gt; &#123;
249   public void messageReceived&#40;HostedConnection source, Message message&#41; &#123;
250     if &#40;message instanceof HelloMessage&#41; &#123;
251       // do something with the message
252       HelloMessage helloMessage = &#40;HelloMessage&#41; message;
253       System.out.println&#40;&quot;Server received '&quot; +helloMessage.getSomething&#40;&#41; +&quot;' from client #&quot;+source.getId&#40;&#41; &#41;;
254     &#125; // else....
255   &#125;</pre>
256
257 <p>
258 For each message type, register a server listener to the server:
259 </p>
260 <pre>myServer.addMessageListener&#40;new ServerListener&#40;&#41;, HelloMessage.class&#41;;</pre>
261
262 </div>
263 <!-- EDIT12 SECTION "Responding to Messages" [6672-8416] -->
264 <h3><a>Creating and Sending Messages</a></h3>
265 <div>
266
267 <p>
268
269 Let&#039;s create a new message of type HelloMessage:
270 </p>
271 <pre>Message message = new HelloMessage&#40;&quot;Hello World!&quot;&#41;;</pre>
272
273 <p>
274 Now the client can send this message to the server:
275 </p>
276 <pre>myClient.send&#40;message&#41;;</pre>
277
278 <p>
279 Or the server can broadcast this message to all HostedConnection (clients):
280 </p>
281 <pre>Message message = new HelloMessage&#40;&quot;Welcome!&quot;&#41;;
282 myServer.broadcast&#40;message&#41;;</pre>
283
284 <p>
285 Or the server can send the message to a specific subset of clients (e.g. to HostedConnection conn1, conn2, and conn3): 
286 </p>
287 <pre>myServer.broadcast&#40; Filters.in&#40; conn1, conn2, conn3 &#41;, message &#41;;</pre>
288
289 <p>
290 Or the server can send the message to all but a few selected clients (e.g. to all HostedConnections but conn4):
291
292 </p>
293 <pre>myServer.broadcast&#40; Filters.notEqualTo&#40; conn4 &#41;, message &#41;;</pre>
294
295 <p>
296 The last two broadcasting methods use com.jme3.network.Filters to select a subset of recipients. If you know the exact list of recipients, always send the messages directly to them using the Filters; avoid flooding the network with unnecessary broadcasts to all.
297 </p>
298
299 </div>
300 <!-- EDIT13 SECTION "Creating and Sending Messages" [8417-9506] -->
301 <h2><a>Identification and Rejection</a></h2>
302 <div>
303
304 <p>
305
306 The ID of the Client and HostedConnection are the same at both ends of a connection. The ID is given out authoritatively by the Server.
307 </p>
308 <pre>... myClient.getId&#40;&#41; ...</pre>
309
310 <p>
311 A server has a game version and game name property. Each client expects to communicate with a server with a certain game name and version. Test first whether the game name matches, and then whether game version matches, before sending any messages! If they do not match, you should refuse to connect, because unmatched clients and servers will likely miscommunicate.
312 </p>
313
314 <p>
315 <p><div>Typically, your networked game defines its own attributes (such as player ID) based on whatever criteria you want. If you want to look up player/client-specific information beyond the game version, you can set this information directly on the Client/HostedConnection object (see Getting Info About a Client).
316 </div></p>
317 </p>
318
319 </div>
320 <!-- EDIT14 SECTION "Identification and Rejection" [9507-10424] -->
321 <h2><a>Closing Clients and Server Cleanly</a></h2>
322 <div>
323
324 </div>
325 <!-- EDIT15 SECTION "Closing Clients and Server Cleanly" [10425-10471] -->
326 <h3><a>Closing a Client</a></h3>
327 <div>
328
329 <p>
330
331 You must override the client&#039;s destroy() method to close the connection cleanly when the player quits the client:
332 </p>
333 <pre>  @Override
334   public void destroy&#40;&#41; &#123;
335       ... // custom code
336       myClient.close&#40;&#41;;
337       super.destroy&#40;&#41;;
338   &#125;</pre>
339
340 </div>
341 <!-- EDIT16 SECTION "Closing a Client" [10472-10747] -->
342 <h3><a>Closing a Server</a></h3>
343 <div>
344
345 <p>
346
347 You must override the server&#039;s destroy() method to close the connection when the server quits:
348 </p>
349 <pre>  @Override
350   public void destroy&#40;&#41; &#123;
351       ... // custom code
352       myServer.close&#40;&#41;;
353       super.destroy&#40;&#41;;
354   &#125;</pre>
355
356 </div>
357 <!-- EDIT17 SECTION "Closing a Server" [10748-11004] -->
358 <h3><a>Kicking a Client</a></h3>
359 <div>
360
361 <p>
362
363 The server can kick a HostedConnection to make it disconnect. You should provide a String with further info (an explanation to the user what happened, e.g. &quot;Shutting down for maintenance&quot;) for the server to send along. This info message can be used (displayed to the user) by a ClientStateListener. (See below)
364 </p>
365 <pre>conn.close&#40;&quot;We kick cheaters.&quot;&#41;;</pre>
366
367 </div>
368 <!-- EDIT18 SECTION "Kicking a Client" [11005-11395] -->
369 <h2><a>Listening to Connection Notification</a></h2>
370 <div>
371
372 <p>
373
374 The server and clients are notified about connection changes.
375 </p>
376
377 </div>
378 <!-- EDIT19 SECTION "Listening to Connection Notification" [11396-11507] -->
379 <h3><a>ClientStateListener</a></h3>
380 <div>
381
382 <p>
383
384 The com.jme3.network.ClientStateListener notifies the Client when the Client has fully connected to the server (including any internal handshaking), and when the Client is kicked (disconnected) from the server.
385
386 </p>
387 <div><table>
388         <tr>
389                 <th> ClientStateListener interface method </th><th> Purpose </th>
390         </tr>
391         <tr>
392                 <td> public void clientConnected(Client c){} </td><td> Implement here what happens as soon as this client has fully connected to the server. </td>
393         </tr>
394         <tr>
395                 <td> public void clientDisconnected(Client c, DisconnectInfo info){} </td><td> Implement here what happens after the server kicks this client. For example, display the DisconnectInfo to the user. </td>
396         </tr>
397 </table></div>
398 <!-- EDIT21 TABLE [11750-12119] -->
399 <p>
400
401 First implement the ClientStateListener interface in the Client class. Then register it to myClient in MyGameClient&#039;s simpleInitApp() method:
402 </p>
403 <pre>myClient.addClientStateListener&#40;this&#41;;</pre>
404
405 </div>
406 <!-- EDIT20 SECTION "ClientStateListener" [11508-12321] -->
407 <h3><a>ConnectionListener</a></h3>
408 <div>
409
410 <p>
411
412 The com.jme3.network.ConnectionListener notifies the Server whenever new HostedConnections (clients) come and go.  The listener notifies the server after the Client connection is fully established (including any internal handshaking).
413
414 </p>
415 <div><table>
416         <tr>
417                 <th> ConnectionListener interface method </th><th> Purpose </th>
418         </tr>
419         <tr>
420                 <td> public void connectionAdded(Server s, HostedConnection c){} </td><td> Implemenent here what happens after a new HostedConnection has joined the Server. </td>
421         </tr>
422         <tr>
423                 <td> public void connectionRemoved(Server s, HostedConnection c){} </td><td> Implement here what happens after a HostedConnection has left. E.g. a player has quit the game and the server removes his character. </td>
424         </tr>
425 </table></div>
426 <!-- EDIT23 TABLE [12587-12985] -->
427 <p>
428
429 First implement the ConnectionListener interface in the Server class. Then register it to myServer in MyGameServer&#039;s simpleInitApp() method. 
430
431 </p>
432 <pre>myServer.addConnectionListener&#40;this&#41;;</pre>
433
434 </div>
435 <!-- EDIT22 SECTION "ConnectionListener" [12322-13185] -->
436 <h2><a>UDP versus TCP</a></h2>
437 <div>
438
439 <p>
440
441 SpiderMonkey supports both UDP (unreliable, fast) and TCP (reliable, slow) transport of messages.
442 </p>
443 <pre>message1.setReliable&#40;true&#41;; // TCP
444 message2.setReliable&#40;false&#41;; // UDP</pre>
445 <ul>
446 <li><div> Choose reliable and slow transport for messages, if you want to make certain the message is delivered (resent) when lost, and if the order of a series of messages is relevant. E.g. game actions such as &quot;1. wield weapon, 2. attack, 3. dodge&quot;.</div>
447 </li>
448 <li><div> Choose unreliable and fast transport for messages if the next message makes any previously delayed or lost message obsolete and synchronizes the state again. E.g. a series of new locations while walking.</div>
449 </li>
450 </ul>
451
452 </div>
453 <!-- EDIT24 SECTION "UDP versus TCP" [13186-13856] -->
454 <h2><a>Important: Use Multi-Threading</a></h2>
455 <div>
456
457 <p>
458
459 <p><div><strong>You cannot modify the scenegraph directly from the network thread.</strong> A common example for such a modification is when you synchronize the player&#039;s position in the scene. You have to use Java Multithreading.
460 </div></p>
461 </p>
462
463 <p>
464 Multithreading means that you create a Callable. A Callable is a Java class representing any (possibly time-intensive) self-contained task that has an impact on the scene graph (such as positioning the player). You enqueue the Callable in the Executor of the client&#039;s OpenGL thread. The Callable ensures to executes the modification in sync with the update loop.
465 </p>
466 <pre>app.enqueue&#40;callable&#41;;</pre>
467
468 <p>
469 Learn more about using <a href="/com/jme3/gde/docs/jme3/advanced/multithreading.html">multithreading</a> in jME3 here.
470 </p>
471
472 <p>
473 For general advice, see the articles <object classid="java:org.netbeans.modules.javahelp.BrowserDisplayer"><param name="content" value="https://developer.valvesoftware.com/wiki/Source_Multiplayer_Networking"><param name="text" value="<html><u>MultiPlayer Networking</u></html>"><param name="textColor" value="blue"></object> and <object classid="java:org.netbeans.modules.javahelp.BrowserDisplayer"><param name="content" value="https://developer.valvesoftware.com/wiki/Latency_Compensating_Methods_in_Client/Server_In-game_Protocol_Design_and_Optimization"><param name="text" value="<html><u>Latency Compensating Methods in Client/Server In-game Protocol Design and Optimization</u></html>"><param name="textColor" value="blue"></object> by the Valve Developer Community.
474 </p>
475
476 </div>
477 <!-- EDIT25 SECTION "Important: Use Multi-Threading" [13857-15003] -->
478 <h2><a>Troubleshooting</a></h2>
479 <div>
480
481 <p>
482
483 If you have set up a server in your home network, and the game clients cannot reach the server from the outside, it&#039;s time to learn about <object classid="java:org.netbeans.modules.javahelp.BrowserDisplayer"><param name="content" value="http://portforward.com/"><param name="text" value="<html><u>port forwarding</u></html>"><param name="textColor" value="blue"></object>.
484 </p>
485 <div><span>
486         <a href="/wiki/doku.php/tag:documentation?do=showtag&amp;tag=tag%3Adocumentation">documentation</a>,
487         <a href="/wiki/doku.php/tag:network?do=showtag&amp;tag=tag%3Anetwork">network</a>,
488         <a href="/wiki/doku.php/tag:spidermonkey?do=showtag&amp;tag=tag%3Aspidermonkey">spidermonkey</a>
489 </span></div>
490
491 </div>
492 <!-- EDIT26 SECTION "Troubleshooting" [15004-] -->
493 <p><em><a href="http://jmonkeyengine.org/wiki/doku.php/jme3:advanced:networking?do=export_xhtmlbody">view online version</a></em></p>