ComprehensivePCGame.cpp 57 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508
  1. /*
  2. * Copyright (c) 2014, Oculus VR, Inc.
  3. * All rights reserved.
  4. *
  5. * This source code is licensed under the BSD-style license found in the
  6. * LICENSE file in the root directory of this source tree. An additional grant
  7. * of patent rights can be found in the PATENTS file in the same directory.
  8. *
  9. */
  10. #include <cstdio>
  11. #include <cstring>
  12. #include <stdlib.h>
  13. #include "GetTime.h"
  14. #include "Rand.h"
  15. #include "RakPeerInterface.h"
  16. #include "MessageIdentifiers.h"
  17. #include "FullyConnectedMesh2.h"
  18. #include "TeamManager.h"
  19. #include "Kbhit.h"
  20. #include "RakSleep.h"
  21. #include "RakNetTypes.h"
  22. #include "BitStream.h"
  23. #include "SocketLayer.h"
  24. #include "ReplicaManager3.h"
  25. #include "NetworkIDManager.h"
  26. #include "Gets.h"
  27. #include "Itoa.h"
  28. #include "NatPunchthroughClient.h"
  29. #include "NatTypeDetectionClient.h"
  30. #include "miniupnpc.h"
  31. #include "upnpcommands.h"
  32. #include "upnperrors.h"
  33. #include "TCPInterface.h"
  34. #include "ReadyEvent.h"
  35. #include "PacketLogger.h"
  36. #include "RPC4Plugin.h"
  37. #include "Kbhit.h"
  38. #include "HTTPConnection2.h"
  39. // See http://www.digip.org/jansson/doc/2.4/
  40. // This is used to make it easier to parse the JSON returned from the master server
  41. #include "jansson.h"
  42. #define DEFAULT_SERVER_PORT "61111"
  43. // Public test server
  44. #define DEFAULT_SERVER_ADDRESS "natpunch.jenkinssoftware.com"
  45. #define NAT_TYPE_DETECTION_SERVER 0
  46. #define USE_UPNP 1
  47. #define MASTER_SERVER_ADDRESS "masterserver2.raknet.com"
  48. //#define MASTER_SERVER_ADDRESS "localhost"
  49. #define MASTER_SERVER_PORT 80
  50. //#define MASTER_SERVER_PORT 8888
  51. using namespace RakNet;
  52. // ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
  53. // Plugins demonstrated by the sample ComphrensivePCGame
  54. // ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
  55. // Purpose: UDP network communication
  56. // Required?: Yes
  57. RakPeerInterface *rakPeer;
  58. // Purpose: Sophisticated team management
  59. // Required?: No, but provides a flexible and full-featured implementation that supports host migration, mid-game joins, and advanced team management.
  60. TeamManager *teamManager;
  61. // Purpose: Game object replication
  62. // Required?: No, but manages object replication automatically. Some form of this is required for almost every game
  63. ReplicaManager3 *replicaManager3;
  64. // Purpose: Lookup game objects given ID. Used by ReplicaManager3
  65. // Required?: Required to use ReplicaManager3, and some form of this is required for almost every game
  66. NetworkIDManager *networkIDManager;
  67. // Purpose: Connect to RakNet's hosted master server
  68. // Required?: Steam and consoles provide server solutions, so usually not if you are releasing only on those platforms.
  69. TCPInterface *tcp;
  70. // Purpose: If UPNP fails, used to connect across routers.
  71. // Required?: Steam and consoles provide server solutions that do this automatically. Otherwise, if UPNP fails you need this.
  72. // Note: See the project NAT Punchthrough / NATCompleteServer for a sample server implementation
  73. NatPunchthroughClient *natPunchthroughClient;
  74. #ifdef NAT_TYPE_DETECTION_SERVER
  75. // Purpose: Determine what type of router we are behind, if any
  76. // Required?: No, but game sessions that are impossible to join can be filtered out from game listings
  77. NatTypeDetectionClient *natTypeDetectionClient;
  78. #endif
  79. // Purpose: Used to call C functions on remote systems. Convenience function.
  80. // Required?: No
  81. RPC4 *rpc4;
  82. // Purpose: Used for players to ready / unready in the lobby in a way that is properly synchronized under peer to peer
  83. // Required?: No, but provides a more robust way to ready/unready in a peer to peer game
  84. ReadyEvent *readyEvent;
  85. // Purpose: Automatically determine and migrate the host of a peer to peer session. Avoid partial connection failures when joining peer-to-peer games.
  86. // Required?: Yes, for peer to peer games that need a session host. Not necessary for client/server
  87. FullyConnectedMesh2 *fullyConnectedMesh2;
  88. // Purpose: Helper class to parse http connection events
  89. // Required?: Technically no since you could parse the strings yourself. But it's a lot of work otherwise.
  90. HTTPConnection2 *httpConnection2;
  91. // ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
  92. // Sample game classes using ReplicaManager3
  93. // ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
  94. // Forward declarations
  95. class User;
  96. class Team;
  97. void PostRoomToMaster(void);
  98. // Game contains the list of objects, and serializes if the game is locked, the name of the game, and if it is in gameplay or in the lobby
  99. // Game is created on startup, rather than over the network, so is replicated as a static object.
  100. // see Help/replicamanager3.html under the topic Static Objects for more details about how this class is Replicated
  101. class Game : public Replica3
  102. {
  103. public:
  104. enum Phase
  105. {
  106. CONNECTING_TO_SERVER,
  107. DETERMINE_NAT_TYPE,
  108. SEARCH_FOR_GAMES,
  109. NAT_PUNCH_TO_GAME_HOST,
  110. CONNECTING_TO_GAME_HOST,
  111. VERIFIED_JOIN,
  112. IN_LOBBY_WAITING_FOR_HOST,
  113. IN_LOBBY_WITH_HOST,
  114. IN_GAME,
  115. EXIT_SAMPLE,
  116. };
  117. Game() {myNatType=NAT_TYPE_UNKNOWN; masterServerRow=-1; Reset(); whenToNextUpdateMasterServer=0; masterServerQueryResult=0;}
  118. virtual ~Game() {if (masterServerQueryResult) json_decref(masterServerQueryResult);}
  119. virtual void WriteAllocationID(RakNet::Connection_RM3 *destinationConnection, RakNet::BitStream *allocationIdBitstream) const {}
  120. virtual RM3ConstructionState QueryConstruction(RakNet::Connection_RM3 *destinationConnection, ReplicaManager3 *replicaManager3) {
  121. if (fullyConnectedMesh2->IsConnectedHost())
  122. return QueryConstruction_PeerToPeer(destinationConnection, R3P2PM_STATIC_OBJECT_CURRENTLY_AUTHORITATIVE);
  123. else
  124. return QueryConstruction_PeerToPeer(destinationConnection, R3P2PM_STATIC_OBJECT_NOT_CURRENTLY_AUTHORITATIVE);
  125. }
  126. virtual bool QueryRemoteConstruction(RakNet::Connection_RM3 *sourceConnection) {return true;}
  127. virtual void SerializeConstruction(RakNet::BitStream *constructionBitstream, RakNet::Connection_RM3 *destinationConnection) {}
  128. virtual bool DeserializeConstruction(RakNet::BitStream *constructionBitstream, RakNet::Connection_RM3 *sourceConnection) {return true;}
  129. virtual void SerializeConstructionExisting(RakNet::BitStream *constructionBitstream, RakNet::Connection_RM3 *destinationConnection)
  130. {
  131. constructionBitstream->Write(gameName);
  132. constructionBitstream->Write(lockGame);
  133. constructionBitstream->Write(gameInLobby);
  134. constructionBitstream->Write(masterServerRow);
  135. }
  136. virtual void DeserializeConstructionExisting(RakNet::BitStream *constructionBitstream, RakNet::Connection_RM3 *sourceConnection)
  137. {
  138. constructionBitstream->Read(gameName);
  139. constructionBitstream->Read(lockGame);
  140. constructionBitstream->Read(gameInLobby);
  141. constructionBitstream->Read(masterServerRow);
  142. printf("Downloaded game. locked=%i. inLobby=%i\n", lockGame, gameInLobby);
  143. }
  144. virtual void SerializeDestruction(RakNet::BitStream *destructionBitstream, RakNet::Connection_RM3 *destinationConnection) {}
  145. virtual bool DeserializeDestruction(RakNet::BitStream *destructionBitstream, RakNet::Connection_RM3 *sourceConnection) {return true;}
  146. virtual RakNet::RM3ActionOnPopConnection QueryActionOnPopConnection(RakNet::Connection_RM3 *droppedConnection) const {return RM3AOPC_DO_NOTHING;}
  147. virtual void DeallocReplica(RakNet::Connection_RM3 *sourceConnection) {delete this;}
  148. virtual RakNet::RM3QuerySerializationResult QuerySerialization(RakNet::Connection_RM3 *destinationConnection) {
  149. if (fullyConnectedMesh2->IsConnectedHost())
  150. return QuerySerialization_PeerToPeer(destinationConnection, R3P2PM_STATIC_OBJECT_CURRENTLY_AUTHORITATIVE);
  151. else
  152. return QuerySerialization_PeerToPeer(destinationConnection, R3P2PM_STATIC_OBJECT_NOT_CURRENTLY_AUTHORITATIVE);
  153. }
  154. virtual RM3SerializationResult Serialize(RakNet::SerializeParameters *serializeParameters)
  155. {
  156. serializeParameters->outputBitstream[0].Write(lockGame);
  157. serializeParameters->outputBitstream[0].Write(gameInLobby);
  158. serializeParameters->outputBitstream[0].Write(masterServerRow);
  159. return RM3SR_BROADCAST_IDENTICALLY;
  160. }
  161. virtual void Deserialize(RakNet::DeserializeParameters *deserializeParameters)
  162. {
  163. if (deserializeParameters->bitstreamWrittenTo[0])
  164. {
  165. bool b;
  166. deserializeParameters->serializationBitstream[0].Read(b);
  167. if (b!=lockGame)
  168. {
  169. lockGame=b;
  170. if (lockGame)
  171. printf("Game is no longer locked\n");
  172. else
  173. printf("Game is now locked\n");
  174. }
  175. deserializeParameters->serializationBitstream[0].Read(b);
  176. if (b!=gameInLobby)
  177. {
  178. gameInLobby=b;
  179. if (gameInLobby)
  180. {
  181. readyEvent->DeleteEvent(0);
  182. game->EnterPhase(Game::IN_LOBBY_WITH_HOST);
  183. printf("Game is now in the lobby\n");
  184. }
  185. else
  186. {
  187. readyEvent->ForceCompletion(0);
  188. game->EnterPhase(Game::IN_GAME);
  189. }
  190. }
  191. deserializeParameters->serializationBitstream[0].Read(masterServerRow);
  192. }
  193. }
  194. void EnterPhase(Phase newPhase)
  195. {
  196. phase = newPhase;
  197. switch (newPhase)
  198. {
  199. case CONNECTING_TO_SERVER:
  200. {
  201. char port[256];
  202. printf("Enter address of server running the NATCompleteServer project.\nEnter for default: ");
  203. Gets(game->serverIPAddr, 256);
  204. if (game->serverIPAddr[0]==0)
  205. strcpy(game->serverIPAddr, DEFAULT_SERVER_ADDRESS);
  206. printf("Enter server port, or enter for default: ");
  207. Gets(port, 256);
  208. if (port[0]==0)
  209. strcpy(port, DEFAULT_SERVER_PORT);
  210. ConnectionAttemptResult car = rakPeer->Connect(serverIPAddr, atoi(port), 0, 0);
  211. if (car!=RakNet::CONNECTION_ATTEMPT_STARTED)
  212. {
  213. printf("Failed connect call to %s. Code=%i\n", serverIPAddr, car);
  214. phase = EXIT_SAMPLE;
  215. }
  216. }
  217. break;
  218. #ifdef NAT_TYPE_DETECTION_SERVER
  219. case DETERMINE_NAT_TYPE:
  220. printf("Determining NAT type...\n");
  221. natTypeDetectionClient->DetectNATType(natPunchServerAddress);
  222. break;
  223. #endif
  224. case SEARCH_FOR_GAMES:
  225. SearchForGames();
  226. break;
  227. case NAT_PUNCH_TO_GAME_HOST:
  228. printf("Attempting NAT punch to host of game session...\n");
  229. break;
  230. case IN_LOBBY_WITH_HOST:
  231. printf("(1) to join team 1.\n");
  232. printf("(2) to join team 2.\n");
  233. printf("(R)eady to start\n");
  234. printf("(U)nready to start\n");
  235. break;
  236. case IN_GAME:
  237. printf("Game started.\n(C)hat in-game\n");
  238. break;
  239. }
  240. }
  241. void Reset(void) {lockGame=false; gameInLobby=true;}
  242. void SearchForGames(void)
  243. {
  244. printf("Downloading rooms...\n");
  245. RakString rsRequest = RakString::FormatForGET(
  246. MASTER_SERVER_ADDRESS "/testServer?__gameId=comprehensivePCGame");
  247. httpConnection2->TransmitRequest(rsRequest, MASTER_SERVER_ADDRESS, MASTER_SERVER_PORT);
  248. }
  249. // ---------------------------------------------------------------------------------
  250. // Serialized variables
  251. // ---------------------------------------------------------------------------------
  252. // Shows up in game listings
  253. RakString gameName;
  254. // If host locked the game, join queries are rejected
  255. bool lockGame;
  256. // Game is either in the lobby or in gameplay
  257. bool gameInLobby;
  258. // Which row of the master server our game was uploaded to
  259. // Returned from the POST request
  260. // This is serialized so if the host migrates, the new host takes over that row. Otherwise, a new row would be written for the same game.
  261. int masterServerRow;
  262. // ---------------------------------------------------------------------------------
  263. // Not serialized variables
  264. // ---------------------------------------------------------------------------------
  265. // Store what type of router I am behind
  266. RakNet::NATTypeDetectionResult myNatType;
  267. Phase phase;
  268. // NAT punchthrough server runs RakNet project NatCompleteServer with NAT_TYPE_DETECTION_SERVER, and NAT_PUNCHTHROUGH_SERVER
  269. RakNetGUID natPunchServerGuid;
  270. SystemAddress natPunchServerAddress;
  271. char serverIPAddr[256];
  272. // Just tracks what other objects have been created
  273. DataStructures::List<User*> users;
  274. DataStructures::List<Team*> teams;
  275. // Master server has to be refreshed periodically so it knows we didn't crash
  276. RakNet::Time whenToNextUpdateMasterServer;
  277. // Helper function to store and read the JSON from the GET request
  278. void SetMasterServerQueryResult(json_t *root)
  279. {
  280. if (masterServerQueryResult)
  281. json_decref(masterServerQueryResult);
  282. masterServerQueryResult = root;
  283. }
  284. json_t* GetMasterServerQueryResult(void)
  285. {
  286. if (masterServerQueryResult == 0)
  287. return 0;
  288. void *iter = json_object_iter(masterServerQueryResult);
  289. while (iter)
  290. {
  291. const char *firstKey = json_object_iter_key(iter);
  292. if (stricmp(firstKey, "GET")==0)
  293. {
  294. return json_object_iter_value(iter);
  295. }
  296. iter = json_object_iter_next(masterServerQueryResult, iter);
  297. RakAssert(iter != 0);
  298. }
  299. return 0;
  300. }
  301. // The GET request returns a string. I use http://www.digip.org/jansson/ to parse the string, and store the results.
  302. json_t *masterServerQueryResult;
  303. json_t *jsonArray;
  304. } *game;
  305. // Team represents a list of players
  306. // It uses TM_Team from the TeamManager plugin to do actual team functionality, store which players are on which teams, and networking
  307. // It derives from Replica3 in order to replicate the teams across the network
  308. class Team : public Replica3
  309. {
  310. public:
  311. Team() {
  312. game->teams.Push(this, _FILE_AND_LINE_); tmTeam.SetOwner(this);
  313. }
  314. virtual ~Team() {
  315. game->teams.RemoveAtIndex(game->teams.GetIndexOf(this));
  316. }
  317. virtual void WriteAllocationID(RakNet::Connection_RM3 *destinationConnection, RakNet::BitStream *allocationIdBitstream) const {allocationIdBitstream->Write("Team");}
  318. virtual RM3ConstructionState QueryConstruction(RakNet::Connection_RM3 *destinationConnection, ReplicaManager3 *replicaManager3) {
  319. // This implementation has the host create the Team instances initially
  320. // If the original host disconnects, the new host as determined by FullyConnectedMesh2 takes over replication duties
  321. if (fullyConnectedMesh2->IsConnectedHost())
  322. return QueryConstruction_PeerToPeer(destinationConnection, R3P2PM_MULTI_OWNER_CURRENTLY_AUTHORITATIVE);
  323. else
  324. return QueryConstruction_PeerToPeer(destinationConnection, R3P2PM_MULTI_OWNER_NOT_CURRENTLY_AUTHORITATIVE);
  325. }
  326. virtual bool QueryRemoteConstruction(RakNet::Connection_RM3 *sourceConnection) {return true;}
  327. virtual void SerializeConstruction(RakNet::BitStream *constructionBitstream, RakNet::Connection_RM3 *destinationConnection) {
  328. constructionBitstream->Write(teamName);
  329. }
  330. virtual bool DeserializeConstruction(RakNet::BitStream *constructionBitstream, RakNet::Connection_RM3 *sourceConnection) {
  331. constructionBitstream->Read(teamName);
  332. printf("Downloaded team. name=%s\n", teamName.C_String());
  333. // When ReplicaManager3 creates the team from a network command, the TeamManager class has to be informed of the new TM_Team instance
  334. teamManager->GetWorldAtIndex(0)->ReferenceTeam(&tmTeam, GetNetworkID(), false);
  335. return true;
  336. }
  337. virtual void PostSerializeConstruction(RakNet::BitStream *constructionBitstream, RakNet::Connection_RM3 *destinationConnection) {
  338. tmTeam.SerializeConstruction(constructionBitstream);
  339. }
  340. virtual void PostDeserializeConstruction(RakNet::BitStream *constructionBitstream, RakNet::Connection_RM3 *sourceConnection) {
  341. tmTeam.DeserializeConstruction(teamManager, constructionBitstream);
  342. }
  343. virtual void SerializeDestruction(RakNet::BitStream *destructionBitstream, RakNet::Connection_RM3 *destinationConnection) {}
  344. virtual bool DeserializeDestruction(RakNet::BitStream *destructionBitstream, RakNet::Connection_RM3 *sourceConnection) {return true;}
  345. virtual RakNet::RM3ActionOnPopConnection QueryActionOnPopConnection(RakNet::Connection_RM3 *droppedConnection) const {
  346. // Do not destroy the object when the connection that created it disconnects.
  347. return RM3AOPC_DO_NOTHING;
  348. }
  349. virtual void DeallocReplica(RakNet::Connection_RM3 *sourceConnection) {delete this;}
  350. virtual RakNet::RM3QuerySerializationResult QuerySerialization(RakNet::Connection_RM3 *destinationConnection) {
  351. // Whoever is currently the host serializes the class
  352. if (fullyConnectedMesh2->IsConnectedHost())
  353. return QuerySerialization_PeerToPeer(destinationConnection, R3P2PM_MULTI_OWNER_CURRENTLY_AUTHORITATIVE);
  354. else
  355. return QuerySerialization_PeerToPeer(destinationConnection, R3P2PM_MULTI_OWNER_NOT_CURRENTLY_AUTHORITATIVE);
  356. }
  357. virtual RM3SerializationResult Serialize(RakNet::SerializeParameters *serializeParameters) {return RM3SR_BROADCAST_IDENTICALLY;}
  358. virtual void Deserialize(RakNet::DeserializeParameters *deserializeParameters) {}
  359. // Team data managed by the TeamManager plugin
  360. TM_Team tmTeam;
  361. // Example of team data not managed by TeamManager
  362. RakString teamName;
  363. };
  364. // User represents a player in the game
  365. // Each system creates one user on startup. Other users are downloaded
  366. // User also contains a TM_TeamMember instance, since users join teams
  367. class User : public Replica3
  368. {
  369. public:
  370. User() {game->users.Push(this, _FILE_AND_LINE_); tmTeamMember.SetOwner(this); natType=NAT_TYPE_UNKNOWN;}
  371. virtual ~User() {
  372. game->users.RemoveAtIndex(game->users.GetIndexOf(this));
  373. }
  374. virtual void WriteAllocationID(RakNet::Connection_RM3 *destinationConnection, RakNet::BitStream *allocationIdBitstream) const {allocationIdBitstream->Write("User");}
  375. virtual RM3ConstructionState QueryConstruction(RakNet::Connection_RM3 *destinationConnection, ReplicaManager3 *replicaManager3)
  376. {
  377. // Whoever created the user replicates it.
  378. return QueryConstruction_PeerToPeer(destinationConnection);
  379. }
  380. virtual bool QueryRemoteConstruction(RakNet::Connection_RM3 *sourceConnection) {return true;}
  381. virtual void SerializeConstruction(RakNet::BitStream *constructionBitstream, RakNet::Connection_RM3 *destinationConnection) {
  382. constructionBitstream->Write(userName);
  383. constructionBitstream->WriteCasted<unsigned char>(natType);
  384. constructionBitstream->Write(playerGuid);
  385. constructionBitstream->Write(playerAddress);
  386. }
  387. virtual bool DeserializeConstruction(RakNet::BitStream *constructionBitstream, RakNet::Connection_RM3 *sourceConnection) {
  388. // The TeamManager plugin has to be informed of TM_TeamMember instances created over the network
  389. teamManager->GetWorldAtIndex(0)->ReferenceTeamMember(&tmTeamMember, GetNetworkID());
  390. constructionBitstream->Read(userName);
  391. constructionBitstream->ReadCasted<unsigned char>(natType);
  392. constructionBitstream->Read(playerGuid);
  393. constructionBitstream->Read(playerAddress);
  394. return true;
  395. }
  396. virtual void PostSerializeConstruction(RakNet::BitStream *constructionBitstream, RakNet::Connection_RM3 *destinationConnection) {
  397. // TeamManager requires that TM_Team was created before TM_TeamMember that uses it.
  398. // PostSerializeConstruction and PostDeserializeConstruction ensure that all objects have been created before serialization
  399. tmTeamMember.SerializeConstruction(constructionBitstream);
  400. }
  401. virtual void PostDeserializeConstruction(RakNet::BitStream *constructionBitstream, RakNet::Connection_RM3 *sourceConnection) {
  402. tmTeamMember.DeserializeConstruction(teamManager, constructionBitstream);
  403. printf("Downloaded user. name=%s", userName.C_String());
  404. if (tmTeamMember.GetCurrentTeam()==0)
  405. printf(" not on a team\n");
  406. else
  407. printf(" on team %s\n", ((Team*)(tmTeamMember.GetCurrentTeam()->GetOwner()))->teamName.C_String());
  408. // Update the user count on the master server as new users join
  409. if (fullyConnectedMesh2->IsConnectedHost())
  410. PostRoomToMaster();
  411. }
  412. virtual void SerializeDestruction(RakNet::BitStream *destructionBitstream, RakNet::Connection_RM3 *destinationConnection) {}
  413. virtual bool DeserializeDestruction(RakNet::BitStream *destructionBitstream, RakNet::Connection_RM3 *sourceConnection) {return true;}
  414. virtual RakNet::RM3ActionOnPopConnection QueryActionOnPopConnection(RakNet::Connection_RM3 *droppedConnection) const {return QueryActionOnPopConnection_PeerToPeer(droppedConnection);}
  415. virtual void DeallocReplica(RakNet::Connection_RM3 *sourceConnection) {delete this;}
  416. virtual RakNet::RM3QuerySerializationResult QuerySerialization(RakNet::Connection_RM3 *destinationConnection) {return QuerySerialization_PeerToPeer(destinationConnection);}
  417. virtual RM3SerializationResult Serialize(RakNet::SerializeParameters *serializeParameters) {return RM3SR_BROADCAST_IDENTICALLY;}
  418. virtual void Deserialize(RakNet::DeserializeParameters *deserializeParameters) {}
  419. // Team data managed by the TeamManager plugin
  420. TM_TeamMember tmTeamMember;
  421. RakString userName;
  422. NATTypeDetectionResult natType;
  423. RakNetGUID playerGuid;
  424. SystemAddress playerAddress;
  425. };
  426. // Required by ReplicaManager3. Acts as a class factory for Replica3 derived instances
  427. class SampleConnectionRM3 : public Connection_RM3
  428. {
  429. public:
  430. SampleConnectionRM3(const SystemAddress &_systemAddress, RakNetGUID _guid) : Connection_RM3(_systemAddress, _guid) {}
  431. virtual ~SampleConnectionRM3() {}
  432. virtual Replica3 *AllocReplica(RakNet::BitStream *allocationIdBitstream, ReplicaManager3 *replicaManager3)
  433. {
  434. RakString objectType;
  435. // Types are written by WriteAllocationID()
  436. allocationIdBitstream->Read(objectType);
  437. if (objectType=="User") return new User;
  438. if (objectType=="Team") return new Team;
  439. RakAssert("Unknown type in AllocReplica" && 0);
  440. return 0;
  441. }
  442. };
  443. // Required by ReplicaManager3. Acts as a class factory for Connection_RM3 derived instances
  444. class SampleRM3 : public ReplicaManager3
  445. {
  446. public:
  447. SampleRM3() {}
  448. virtual ~SampleRM3() {}
  449. virtual Connection_RM3* AllocConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID) const {return new SampleConnectionRM3(systemAddress,rakNetGUID);}
  450. virtual void DeallocConnection(Connection_RM3 *connection) const {delete connection;}
  451. };
  452. // ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
  453. // Helper functions
  454. // ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
  455. // Demonstrates how to use the RPC4 plugin
  456. void InGameChat(RakNet::BitStream *userData, Packet *packet)
  457. {
  458. RakString rs;
  459. userData->Read(rs);
  460. printf("%s\n", rs.C_String());
  461. }
  462. // Register the function where it is defined, which is easier than maintaining a bunch of RegisterSlot() calls in main()
  463. RPC4GlobalRegistration __InGameChat("InGameChat", InGameChat, 0);
  464. // Write roomName and a list of NATTypeDetectionResult to a bitStream
  465. void SerializeToJSON(RakString &outputString, RakString &roomName, DataStructures::List<NATTypeDetectionResult> &natTypes)
  466. {
  467. outputString.Set("'roomName': '%s', 'guid': '%s', 'natTypes' : [ ", roomName.C_String(), rakPeer->GetMyGUID().ToString());
  468. for (unsigned short i=0; i < natTypes.Size(); i++)
  469. {
  470. if (i!=0)
  471. outputString += ", ";
  472. RakString appendStr("{'type': %i}", natTypes[i]);
  473. outputString += appendStr;
  474. }
  475. outputString += " ] ";
  476. }
  477. // A system has connected and is ready to participate in the game
  478. // Register this system with the plugins that need to know about new participants
  479. // This operation happens after FullyConnectedMesh2 has told us about who the host is.
  480. void RegisterGameParticipant(RakNetGUID guid)
  481. {
  482. Connection_RM3 *connection = replicaManager3->AllocConnection(rakPeer->GetSystemAddressFromGuid(guid), guid);
  483. if (replicaManager3->PushConnection(connection)==false)
  484. replicaManager3->DeallocConnection(connection);
  485. teamManager->GetWorldAtIndex(0)->AddParticipant(guid);
  486. readyEvent->AddToWaitList(0, guid);
  487. }
  488. // Upload details about the current game state to the cloud
  489. // This is the responsibility of the system that initially created that room.
  490. // If that system disconnects, the new host, as determined by FullyConnectedMesh2 will reupload the room
  491. void PostRoomToMaster(void)
  492. {
  493. BitStream bsOut;
  494. RakString jsonSerializedRoom;
  495. DataStructures::List<NATTypeDetectionResult> natTypes;
  496. for (unsigned int i=0; i < game->users.Size(); i++)
  497. natTypes.Push(game->users[i]->natType, _FILE_AND_LINE_);
  498. SerializeToJSON(jsonSerializedRoom, game->gameName, natTypes);
  499. RakString rowStr;
  500. if (game->masterServerRow!=-1)
  501. rowStr.Set("\"__rowId\": %i,", game->masterServerRow);
  502. // See http://masterserver2.raknet.com/
  503. RakString rsRequest = RakString::FormatForPOST(
  504. (const char*) MASTER_SERVER_ADDRESS "/testServer",
  505. "text/plain; charset=UTF-8",
  506. RakString("{'__gameId': 'comprehensivePCGame', '__clientReqId': '0', %s '__timeoutSec': '30', %s }", rowStr.C_String(), jsonSerializedRoom.C_String()));
  507. // Refresh the room again slightly less than every 30 seconds
  508. game->whenToNextUpdateMasterServer = RakNet::GetTime() + 30000 - 1000;
  509. httpConnection2->TransmitRequest(rsRequest, MASTER_SERVER_ADDRESS, MASTER_SERVER_PORT);
  510. printf("Posted game session. In room.\n");
  511. }
  512. void ReleaseRoomFromCloud(void)
  513. {
  514. RakString rsRequest = RakString::FormatForDELETE(
  515. RakString(MASTER_SERVER_ADDRESS "/testServer?__gameId=comprehensivePCGame&__rowId=%i", game->masterServerRow));
  516. httpConnection2->TransmitRequest(rsRequest, MASTER_SERVER_ADDRESS, MASTER_SERVER_PORT);
  517. game->masterServerRow=-1;
  518. }
  519. void CreateRoom(void)
  520. {
  521. size_t arraySize;
  522. if (game->GetMasterServerQueryResult())
  523. arraySize = json_array_size(game->GetMasterServerQueryResult());
  524. else
  525. arraySize = 0;
  526. if (arraySize > 0)
  527. {
  528. printf("Enter room name: ");
  529. char rn[128];
  530. Gets(rn, 128);
  531. if (rn[0]==0)
  532. strcpy(rn, "Unnamed");
  533. game->gameName = rn;
  534. }
  535. else
  536. {
  537. game->gameName = "Default room name";
  538. }
  539. // Upload the room to the server
  540. PostRoomToMaster();
  541. // Room owner creates two teams and registers them for replication
  542. Team *team1 = new Team;
  543. team1->SetNetworkIDManager(networkIDManager);
  544. team1->teamName = "Team1";
  545. teamManager->GetWorldAtIndex(0)->ReferenceTeam(&team1->tmTeam, team1->GetNetworkID(), false);
  546. Team *team2 = new Team;
  547. team2->SetNetworkIDManager(networkIDManager);
  548. team2->teamName = "Team2";
  549. teamManager->GetWorldAtIndex(0)->ReferenceTeam(&team2->tmTeam, team2->GetNetworkID(), false);
  550. game->EnterPhase(Game::IN_LOBBY_WAITING_FOR_HOST);
  551. // So that time spent in single player does not count towards which system has been running the longest in multiplayer
  552. fullyConnectedMesh2->ResetHostCalculation();
  553. printf("(E)xit session\n");
  554. }
  555. #if USE_UPNP!=0
  556. struct UPNPOpenWorkerArgs
  557. {
  558. char buff[256];
  559. unsigned short portToOpen;
  560. unsigned int timeout;
  561. void *userData;
  562. void (*resultCallback)(bool success, unsigned short portToOpen, void *userData);
  563. void (*progressCallback)(const char *progressMsg, void *userData);
  564. };
  565. RAK_THREAD_DECLARATION(UPNPOpenWorker)
  566. {
  567. UPNPOpenWorkerArgs *args = ( UPNPOpenWorkerArgs * ) arguments;
  568. bool success=false;
  569. // Behind a NAT. Try to open with UPNP to avoid doing NAT punchthrough
  570. struct UPNPDev * devlist = 0;
  571. RakNet::Time t1 = GetTime();
  572. devlist = upnpDiscover(args->timeout, 0, 0, 0, 0, 0);
  573. RakNet::Time t2 = GetTime();
  574. if (devlist)
  575. {
  576. if (args->progressCallback)
  577. args->progressCallback("List of UPNP devices found on the network :\n", args->userData);
  578. struct UPNPDev * device;
  579. for(device = devlist; device; device = device->pNext)
  580. {
  581. sprintf(args->buff, " desc: %s\n st: %s\n\n", device->descURL, device->st);
  582. if (args->progressCallback)
  583. args->progressCallback(args->buff, args->userData);
  584. }
  585. char lanaddr[64]; /* my ip address on the LAN */
  586. struct UPNPUrls urls;
  587. struct IGDdatas data;
  588. if (UPNP_GetValidIGD(devlist, &urls, &data, lanaddr, sizeof(lanaddr))==1)
  589. {
  590. char iport[32];
  591. Itoa(args->portToOpen, iport,10);
  592. char eport[32];
  593. strcpy(eport, iport);
  594. // Version miniupnpc-1.6.20120410
  595. int r = UPNP_AddPortMapping(urls.controlURL, data.first.servicetype,
  596. eport, iport, lanaddr, 0, "UDP", 0, "0");
  597. if(r!=UPNPCOMMAND_SUCCESS)
  598. printf("AddPortMapping(%s, %s, %s) failed with code %d (%s)\n",
  599. eport, iport, lanaddr, r, strupnperror(r));
  600. char intPort[6];
  601. char intClient[16];
  602. // Version miniupnpc-1.6.20120410
  603. char desc[128];
  604. char enabled[128];
  605. char leaseDuration[128];
  606. r = UPNP_GetSpecificPortMappingEntry(urls.controlURL,
  607. data.first.servicetype,
  608. eport, "UDP",
  609. intClient, intPort,
  610. desc, enabled, leaseDuration);
  611. if(r!=UPNPCOMMAND_SUCCESS)
  612. {
  613. sprintf(args->buff, "GetSpecificPortMappingEntry() failed with code %d (%s)\n",
  614. r, strupnperror(r));
  615. if (args->progressCallback)
  616. args->progressCallback(args->buff, args->userData);
  617. }
  618. else
  619. {
  620. if (args->progressCallback)
  621. args->progressCallback("UPNP success.\n", args->userData);
  622. // game->myNatType=NAT_TYPE_SUPPORTS_UPNP;
  623. success=true;
  624. }
  625. }
  626. }
  627. if (args->resultCallback)
  628. args->resultCallback(success, args->portToOpen, args->userData);
  629. RakNet::OP_DELETE(args, _FILE_AND_LINE_);
  630. return 1;
  631. }
  632. void UPNPOpenAsynch(unsigned short portToOpen,
  633. unsigned int timeout,
  634. void (*progressCallback)(const char *progressMsg, void *userData),
  635. void (*resultCallback)(bool success, unsigned short portToOpen, void *userData),
  636. void *userData
  637. )
  638. {
  639. UPNPOpenWorkerArgs *args = RakNet::OP_NEW<UPNPOpenWorkerArgs>(_FILE_AND_LINE_);
  640. args->portToOpen = portToOpen;
  641. args->timeout = timeout;
  642. args->userData = userData;
  643. args->progressCallback = progressCallback;
  644. args->resultCallback = resultCallback;
  645. RakThread::Create(UPNPOpenWorker, args);
  646. }
  647. void UPNPProgressCallback(const char *progressMsg, void *userData)
  648. {
  649. printf(progressMsg);
  650. }
  651. void UPNPResultCallback(bool success, unsigned short portToOpen, void *userData)
  652. {
  653. if (success)
  654. game->myNatType=NAT_TYPE_SUPPORTS_UPNP;
  655. game->EnterPhase(Game::SEARCH_FOR_GAMES);
  656. }
  657. void OpenUPNP(void)
  658. {
  659. printf("Discovering UPNP...\n");
  660. DataStructures::List<RakNetSocket2* > sockets;
  661. rakPeer->GetSockets(sockets);
  662. UPNPOpenAsynch(sockets[0]->GetBoundAddress().GetPort(), 2000, UPNPProgressCallback, UPNPResultCallback, 0);
  663. }
  664. #endif
  665. // ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
  666. // main
  667. // ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
  668. int main(void)
  669. {
  670. printf("Demonstrates networking elements for a P2P game on the PC, self-released,\nwith player hosted game servers\n");
  671. printf("Difficulty: Advanced\n\n");
  672. /*
  673. json_t *jsonObject = json_object();
  674. json_object_set(jsonObject, "testKey", json_string("\"") );
  675. char *ds = json_dumps(jsonObject,0);
  676. */
  677. // ---------------------------------------------------------------------------------------------------------------------
  678. // Allocate plugins. See declaration in this file for description of each
  679. // ---------------------------------------------------------------------------------------------------------------------
  680. rakPeer=RakNet::RakPeerInterface::GetInstance();
  681. teamManager=TeamManager::GetInstance();
  682. fullyConnectedMesh2=FullyConnectedMesh2::GetInstance();
  683. networkIDManager = NetworkIDManager::GetInstance();
  684. tcp = TCPInterface::GetInstance();
  685. natPunchthroughClient = NatPunchthroughClient::GetInstance();
  686. #ifdef NAT_TYPE_DETECTION_SERVER
  687. natTypeDetectionClient = NatTypeDetectionClient::GetInstance();
  688. #endif
  689. rpc4 = RPC4::GetInstance();
  690. readyEvent = ReadyEvent::GetInstance();
  691. replicaManager3=new SampleRM3;
  692. httpConnection2 = HTTPConnection2::GetInstance();
  693. // ---------------------------------------------------------------------------------------------------------------------
  694. // Attach plugins
  695. // ---------------------------------------------------------------------------------------------------------------------
  696. rakPeer->AttachPlugin(fullyConnectedMesh2);
  697. rakPeer->AttachPlugin(teamManager);
  698. rakPeer->AttachPlugin(natPunchthroughClient);
  699. #ifdef NAT_TYPE_DETECTION_SERVER
  700. rakPeer->AttachPlugin(natTypeDetectionClient);
  701. #endif
  702. rakPeer->AttachPlugin(rpc4);
  703. rakPeer->AttachPlugin(readyEvent);
  704. rakPeer->AttachPlugin(replicaManager3);
  705. /// TCPInterface supports plugins too
  706. tcp->AttachPlugin(httpConnection2);
  707. // ---------------------------------------------------------------------------------------------------------------------
  708. // Setup plugins: Disable automatically adding new connections. Allocate initial objects and register for replication
  709. // ---------------------------------------------------------------------------------------------------------------------
  710. // Allocate a world instance to be used for team operations
  711. teamManager->AddWorld(0);
  712. // Do not automatically count new connections
  713. teamManager->SetAutoManageConnections(false);
  714. // New connections do not count until after login.
  715. fullyConnectedMesh2->SetAutoparticipateConnections(false);
  716. // Tell ReplicaManager3 which networkIDManager to use for object lookup, used for automatic serialization
  717. replicaManager3->SetNetworkIDManager(networkIDManager);
  718. // Do not automatically count new connections, but do drop lost connections automatically
  719. replicaManager3->SetAutoManageConnections(false,true);
  720. // Reference static game objects that always exist
  721. game = new Game;
  722. game->SetNetworkIDManager(networkIDManager);
  723. game->SetNetworkID(0);
  724. replicaManager3->Reference(game);
  725. // Setup my own user
  726. User *user = new User;
  727. user->SetNetworkIDManager(networkIDManager);
  728. user->userName = rakPeer->GetMyGUID().ToString();
  729. // Inform TeamManager of my user's team member info
  730. teamManager->GetWorldAtIndex(0)->ReferenceTeamMember(&user->tmTeamMember,user->GetNetworkID());
  731. // ---------------------------------------------------------------------------------------------------------------------
  732. // Startup RakNet on first available port
  733. // ---------------------------------------------------------------------------------------------------------------------
  734. RakNet::SocketDescriptor sd;
  735. sd.socketFamily=AF_INET; // Only IPV4 supports broadcast on 255.255.255.255
  736. sd.port=0;
  737. StartupResult sr = rakPeer->Startup(8,&sd,1);
  738. RakAssert(sr==RAKNET_STARTED);
  739. rakPeer->SetMaximumIncomingConnections(8);
  740. rakPeer->SetTimeoutTime(30000,RakNet::UNASSIGNED_SYSTEM_ADDRESS);
  741. printf("Our guid is %s\n", rakPeer->GetGuidFromSystemAddress(RakNet::UNASSIGNED_SYSTEM_ADDRESS).ToString());
  742. printf("Started on %s\n", rakPeer->GetMyBoundAddress().ToString(true));
  743. // Start TCPInterface and begin connecting to the NAT punchthrough server
  744. tcp->Start(0,0,1);
  745. // Connect to hosting server
  746. game->EnterPhase(Game::CONNECTING_TO_SERVER);
  747. // ---------------------------------------------------------------------------------------------------------------------
  748. // Read packets loop
  749. // ---------------------------------------------------------------------------------------------------------------------
  750. char ch;
  751. Packet *packet;
  752. while (game->phase!=Game::EXIT_SAMPLE)
  753. {
  754. for (packet = rakPeer->Receive(); packet; rakPeer->DeallocatePacket(packet), packet = rakPeer->Receive())
  755. {
  756. switch (packet->data[0])
  757. {
  758. case ID_NEW_INCOMING_CONNECTION:
  759. {
  760. printf("ID_NEW_INCOMING_CONNECTION from %s. guid=%s.\n", packet->systemAddress.ToString(true), packet->guid.ToString());
  761. }
  762. break;
  763. case ID_CONNECTION_REQUEST_ACCEPTED:
  764. {
  765. printf("ID_CONNECTION_REQUEST_ACCEPTED from %s,guid=%s\n", packet->systemAddress.ToString(true), packet->guid.ToString());
  766. if (game->phase==Game::CONNECTING_TO_SERVER)
  767. {
  768. game->natPunchServerAddress=packet->systemAddress;
  769. game->natPunchServerGuid=packet->guid;
  770. // ---------------------------------------------------------------------------------------------------------------------
  771. // PC self-hosted servers only: Use the NAT punch server to determine NAT type. Attempt to open router if needed.
  772. // ---------------------------------------------------------------------------------------------------------------------
  773. if (NAT_TYPE_DETECTION_SERVER)
  774. {
  775. game->EnterPhase(Game::DETERMINE_NAT_TYPE);
  776. }
  777. else
  778. {
  779. OpenUPNP();
  780. }
  781. }
  782. else if (game->phase==Game::CONNECTING_TO_GAME_HOST)
  783. {
  784. printf("Asking host to join session...\n");
  785. // So time in single player does not count towards which system has been running multiplayer the longest
  786. fullyConnectedMesh2->ResetHostCalculation();
  787. // Custom message to ask to join the game
  788. // We first connect to the game host, and the game host is responsible for calling StartVerifiedJoin() for us to join the session
  789. BitStream bsOut;
  790. bsOut.Write((MessageID)ID_USER_PACKET_ENUM);
  791. rakPeer->Send(&bsOut,HIGH_PRIORITY,RELIABLE_ORDERED,0,packet->guid,false);
  792. }
  793. }
  794. break;
  795. case ID_CONNECTION_LOST:
  796. case ID_DISCONNECTION_NOTIFICATION:
  797. if (game->phase==Game::DETERMINE_NAT_TYPE)
  798. {
  799. printf("Lost connection during NAT type detection. Reason %s. Retrying...\n", PacketLogger::BaseIDTOString(packet->data[0]));
  800. game->EnterPhase(Game::CONNECTING_TO_SERVER);
  801. }
  802. else if (game->phase==Game::NAT_PUNCH_TO_GAME_HOST)
  803. {
  804. printf("Lost connection during NAT punch to game host. Reason %s.\n", PacketLogger::BaseIDTOString(packet->data[0]));
  805. game->EnterPhase(Game::SEARCH_FOR_GAMES);
  806. }
  807. else
  808. {
  809. if (packet->guid==game->natPunchServerGuid)
  810. {
  811. printf("Server connection lost. Reason %s.\nGame session is no longer searchable.\n", PacketLogger::BaseIDTOString(packet->data[0]));
  812. }
  813. else
  814. {
  815. printf("Peer connection lost. Reason %s.\n", PacketLogger::BaseIDTOString(packet->data[0]));
  816. }
  817. }
  818. break;
  819. case ID_ALREADY_CONNECTED:
  820. printf("ID_ALREADY_CONNECTED with guid %" PRINTF_64_BIT_MODIFIER "u\n", packet->guid);
  821. break;
  822. case ID_INVALID_PASSWORD:
  823. case ID_NO_FREE_INCOMING_CONNECTIONS:
  824. case ID_CONNECTION_ATTEMPT_FAILED:
  825. case ID_CONNECTION_BANNED:
  826. case ID_IP_RECENTLY_CONNECTED:
  827. case ID_INCOMPATIBLE_PROTOCOL_VERSION:
  828. // Note: Failing to connect to another system does not automatically mean we cannot join a session, since that system may be disconnecting from the host simultaneously
  829. // FullyConnectedMesh2::StartVerifiedJoin() internally handles success or failure and notifies the client through ID_FCM2_VERIFIED_JOIN_FAILED if needed.
  830. printf("Failed to connect to %s. Reason %s\n", packet->systemAddress.ToString(true), PacketLogger::BaseIDTOString(packet->data[0]));
  831. if (game->phase==Game::CONNECTING_TO_SERVER)
  832. game->EnterPhase(Game::EXIT_SAMPLE);
  833. break;
  834. case ID_FCM2_NEW_HOST:
  835. {
  836. RakNet::BitStream bs(packet->data,packet->length,false);
  837. bs.IgnoreBytes(1);
  838. RakNetGUID oldHost;
  839. bs.Read(oldHost);
  840. if (packet->guid==rakPeer->GetMyGUID())
  841. {
  842. if (oldHost!=UNASSIGNED_RAKNET_GUID)
  843. {
  844. if (game->phase==Game::IN_LOBBY_WAITING_FOR_HOST)
  845. game->phase=Game::IN_LOBBY_WITH_HOST;
  846. PostRoomToMaster();
  847. printf("ID_FCM2_NEW_HOST: Taking over as host from the old host.\nNew options:\n");
  848. }
  849. else
  850. {
  851. // Room not hosted if we become host the first time since this was done in CreateRoom() already
  852. printf("ID_FCM2_NEW_HOST: We have become host for the first time. New options:\n");
  853. }
  854. printf("(L)ock and unlock game\n");
  855. }
  856. else
  857. {
  858. if (oldHost!=UNASSIGNED_RAKNET_GUID)
  859. printf("ID_FCM2_NEW_HOST: A new system %s has become host, GUID=%s\n", packet->systemAddress.ToString(true), packet->guid.ToString());
  860. else
  861. printf("ID_FCM2_NEW_HOST: System %s is host, GUID=%s\n", packet->systemAddress.ToString(true), packet->guid.ToString());
  862. }
  863. if (oldHost==UNASSIGNED_RAKNET_GUID)
  864. {
  865. // First time calculated host. Add existing connections to ReplicaManager3
  866. DataStructures::List<RakNetGUID> participantList;
  867. fullyConnectedMesh2->GetParticipantList(participantList);
  868. for (unsigned int i=0; i < participantList.Size(); i++)
  869. RegisterGameParticipant(participantList[i]);
  870. // Reference previously created replicated objects, which cannot be serialized until host is known the first time
  871. if (packet->guid==rakPeer->GetMyGUID())
  872. {
  873. // As host, reference the teams we created
  874. for (unsigned int i=0; i < game->teams.Size(); i++)
  875. replicaManager3->Reference(game->teams[i]);
  876. }
  877. // Reference the user we created (host or not)
  878. for (unsigned int i=0; i < game->users.Size(); i++)
  879. replicaManager3->Reference(game->users[i]);
  880. }
  881. }
  882. break;
  883. case ID_TEAM_BALANCER_TEAM_ASSIGNED:
  884. {
  885. printf("ID_TEAM_BALANCER_TEAM_ASSIGNED for ");
  886. TM_World *world;
  887. TM_TeamMember *teamMember;
  888. teamManager->DecodeTeamAssigned(packet, &world, &teamMember);
  889. printf("worldId=%i teamMember=%s", world->GetWorldId(), ((User*)teamMember->GetOwner())->userName.C_String());
  890. if (teamMember->GetCurrentTeam()==0)
  891. printf(" not on team\n");
  892. else
  893. printf(" on team %s\n", ((Team*)(teamMember->GetCurrentTeam()->GetOwner()))->teamName.C_String());
  894. }
  895. break;
  896. case ID_TEAM_BALANCER_REQUESTED_TEAM_FULL:
  897. {
  898. printf("ID_TEAM_BALANCER_REQUESTED_TEAM_FULL\n");
  899. }
  900. break;
  901. case ID_TEAM_BALANCER_REQUESTED_TEAM_LOCKED:
  902. {
  903. printf("ID_TEAM_BALANCER_REQUESTED_TEAM_LOCKED\n");
  904. }
  905. break;
  906. case ID_TEAM_BALANCER_TEAM_REQUESTED_CANCELLED:
  907. {
  908. printf("ID_TEAM_BALANCER_TEAM_REQUESTED_CANCELLED\n");
  909. }
  910. break;
  911. case ID_NAT_TARGET_NOT_CONNECTED:
  912. case ID_NAT_TARGET_UNRESPONSIVE:
  913. case ID_NAT_CONNECTION_TO_TARGET_LOST:
  914. case ID_NAT_PUNCHTHROUGH_FAILED:
  915. {
  916. // As with connection failed, this does not automatically mean we cannot join the session
  917. // We only fail on ID_FCM2_VERIFIED_JOIN_FAILED
  918. printf("NAT punch to %s failed. Reason %s\n", packet->guid.ToString(), PacketLogger::BaseIDTOString(packet->data[0]));
  919. if (game->phase==Game::NAT_PUNCH_TO_GAME_HOST)
  920. game->EnterPhase(Game::SEARCH_FOR_GAMES);
  921. }
  922. case ID_NAT_ALREADY_IN_PROGRESS:
  923. // Can ignore this
  924. break;
  925. case ID_NAT_PUNCHTHROUGH_SUCCEEDED:
  926. {
  927. if (game->phase==Game::NAT_PUNCH_TO_GAME_HOST || game->phase==Game::VERIFIED_JOIN)
  928. {
  929. // Connect to the session host
  930. ConnectionAttemptResult car = rakPeer->Connect(packet->systemAddress.ToString(false), packet->systemAddress.GetPort(), 0, 0);
  931. if (car!=RakNet::CONNECTION_ATTEMPT_STARTED)
  932. {
  933. printf("Failed connect call to %s. Code=%i\n", packet->systemAddress.ToString(false), car);
  934. game->EnterPhase(Game::SEARCH_FOR_GAMES);
  935. }
  936. else
  937. {
  938. if (game->phase==Game::NAT_PUNCH_TO_GAME_HOST)
  939. {
  940. printf("NAT punch completed. Connecting to %s (game host)...\n", packet->systemAddress.ToString(true));
  941. game->EnterPhase(Game::CONNECTING_TO_GAME_HOST);
  942. }
  943. else
  944. {
  945. printf("NAT punch completed. Connecting to %s (game client)...\n", packet->systemAddress.ToString(true));
  946. }
  947. }
  948. }
  949. }
  950. break;
  951. case ID_NAT_TYPE_DETECTION_RESULT:
  952. {
  953. game->myNatType = (RakNet::NATTypeDetectionResult) packet->data[1];
  954. printf("NAT Type is %s (%s)\n", NATTypeDetectionResultToString(game->myNatType), NATTypeDetectionResultToStringFriendly(game->myNatType));
  955. if (game->myNatType!=RakNet::NAT_TYPE_NONE)
  956. {
  957. OpenUPNP();
  958. }
  959. if (game->myNatType==RakNet::NAT_TYPE_PORT_RESTRICTED || game->myNatType==RakNet::NAT_TYPE_SYMMETRIC)
  960. {
  961. printf("Note: Your router must support UPNP or have the user manually forward ports.\n");
  962. printf("Otherwise NATPunchthrough may not always succeed.\n");
  963. }
  964. game->EnterPhase(Game::SEARCH_FOR_GAMES);
  965. }
  966. break;
  967. case ID_READY_EVENT_ALL_SET:
  968. printf("Got ID_READY_EVENT_ALL_SET from %s\n", packet->systemAddress.ToString(true));
  969. printf("All users ready.\n");
  970. if (fullyConnectedMesh2->IsConnectedHost())
  971. printf("New options:\n(B)egin gameplay\n");
  972. break;
  973. case ID_READY_EVENT_SET:
  974. printf("Got ID_READY_EVENT_SET from %s\n", packet->systemAddress.ToString(true));
  975. break;
  976. case ID_READY_EVENT_UNSET:
  977. printf("Got ID_READY_EVENT_UNSET from %s\n", packet->systemAddress.ToString(true));
  978. break;
  979. // ID_USER_PACKET_ENUM is used by this sample as a custom message to ask to join a game
  980. case ID_USER_PACKET_ENUM:
  981. if (game->phase > Game::SEARCH_FOR_GAMES)
  982. {
  983. printf("Got request from client to join session.\nExecuting StartVerifiedJoin()\n");
  984. fullyConnectedMesh2->StartVerifiedJoin(packet->guid);
  985. }
  986. else
  987. {
  988. BitStream bsOut;
  989. bsOut.Write((MessageID)(ID_USER_PACKET_ENUM+1));
  990. rakPeer->Send(&bsOut,HIGH_PRIORITY,RELIABLE_ORDERED,0,packet->guid,false);
  991. }
  992. break;
  993. // ID_USER_PACKET_ENUM+1 is used by this sample as a custom message to reject a join game request
  994. // Requests may also be later rejected through FullyConnectedMesh2::RespondOnVerifiedJoinCapable() to send ID_FCM2_VERIFIED_JOIN_REJECTED
  995. case (ID_USER_PACKET_ENUM+1):
  996. printf("Join request denied\n");
  997. game->EnterPhase(Game::SEARCH_FOR_GAMES);
  998. break;
  999. case ID_FCM2_VERIFIED_JOIN_START:
  1000. {
  1001. game->EnterPhase(Game::VERIFIED_JOIN);
  1002. // This message means the session host sent us a list of systems in the session
  1003. // Once we connect to, or fail to connect to, each of these systems we will get ID_FCM2_VERIFIED_JOIN_FAILED, ID_FCM2_VERIFIED_JOIN_ACCEPTED, or ID_FCM2_VERIFIED_JOIN_REJECTED
  1004. printf("Host sent us system list. Doing NAT punch to each system...\n");
  1005. DataStructures::List<SystemAddress> addresses;
  1006. DataStructures::List<RakNetGUID> guids;
  1007. fullyConnectedMesh2->GetVerifiedJoinRequiredProcessingList(packet->guid, addresses, guids);
  1008. for (unsigned int i=0; i < guids.Size(); i++)
  1009. natPunchthroughClient->OpenNAT(guids[i], game->natPunchServerAddress);
  1010. }
  1011. break;
  1012. case ID_FCM2_VERIFIED_JOIN_CAPABLE:
  1013. printf("Client is capable of joining FullyConnectedMesh2.\n");
  1014. if (game->lockGame)
  1015. {
  1016. RakNet::BitStream bsOut;
  1017. bsOut.Write("Game is locked");
  1018. fullyConnectedMesh2->RespondOnVerifiedJoinCapable(packet, false, &bsOut);
  1019. }
  1020. else
  1021. fullyConnectedMesh2->RespondOnVerifiedJoinCapable(packet, true, 0);
  1022. break;
  1023. case ID_FCM2_VERIFIED_JOIN_ACCEPTED:
  1024. {
  1025. DataStructures::List<RakNetGUID> systemsAccepted;
  1026. bool thisSystemAccepted;
  1027. fullyConnectedMesh2->GetVerifiedJoinAcceptedAdditionalData(packet, &thisSystemAccepted, systemsAccepted, 0);
  1028. if (thisSystemAccepted)
  1029. printf("Game join request accepted\n");
  1030. else
  1031. printf("System %s joined the mesh\n", systemsAccepted[0].ToString());
  1032. // Add the new participant to the game if we already know who the host is. Otherwise do this
  1033. // once ID_FCM2_NEW_HOST arrives
  1034. if (fullyConnectedMesh2->GetConnectedHost()!=UNASSIGNED_RAKNET_GUID)
  1035. {
  1036. // FullyConnectedMesh2 already called AddParticipant() for each accepted system
  1037. // Still need to add those systems to the other plugins though
  1038. for (unsigned int i=0; i < systemsAccepted.Size(); i++)
  1039. RegisterGameParticipant(systemsAccepted[i]);
  1040. if (thisSystemAccepted)
  1041. game->EnterPhase(Game::IN_LOBBY_WITH_HOST);
  1042. }
  1043. else
  1044. {
  1045. if (thisSystemAccepted)
  1046. game->EnterPhase(Game::IN_LOBBY_WAITING_FOR_HOST);
  1047. }
  1048. printf("(E)xit room\n");
  1049. }
  1050. break;
  1051. case ID_FCM2_VERIFIED_JOIN_REJECTED:
  1052. {
  1053. BitStream additionalData;
  1054. fullyConnectedMesh2->GetVerifiedJoinRejectedAdditionalData(packet, &additionalData);
  1055. RakString reason;
  1056. additionalData.Read(reason);
  1057. printf("Join rejected. Reason=%s\n", reason.C_String());
  1058. rakPeer->CloseConnection(packet->guid, true);
  1059. game->EnterPhase(Game::SEARCH_FOR_GAMES);
  1060. break;
  1061. }
  1062. case ID_REPLICA_MANAGER_DOWNLOAD_COMPLETE:
  1063. {
  1064. if (replicaManager3->GetAllConnectionDownloadsCompleted()==true)
  1065. {
  1066. printf("Completed all remote downloads\n");
  1067. if (game->gameInLobby)
  1068. game->EnterPhase(Game::IN_LOBBY_WITH_HOST);
  1069. else
  1070. game->EnterPhase(Game::IN_GAME);
  1071. }
  1072. break;
  1073. }
  1074. }
  1075. }
  1076. // The following code is TCP operations for talking to the master server, and parsing the reply
  1077. SystemAddress sa;
  1078. // This is kind of crappy, but for TCP plugins, always do HasCompletedConnectionAttempt, then Receive(), then HasFailedConnectionAttempt(),HasLostConnection()
  1079. sa = tcp->HasCompletedConnectionAttempt();
  1080. for (packet = tcp->Receive(); packet; tcp->DeallocatePacket(packet), packet = tcp->Receive())
  1081. ;
  1082. sa = tcp->HasFailedConnectionAttempt();
  1083. sa = tcp->HasLostConnection();
  1084. RakString stringTransmitted;
  1085. RakString hostTransmitted;
  1086. RakString responseReceived;
  1087. SystemAddress hostReceived;
  1088. int contentOffset;
  1089. if (httpConnection2->GetResponse(stringTransmitted, hostTransmitted, responseReceived, hostReceived, contentOffset))
  1090. {
  1091. if (responseReceived.IsEmpty()==false)
  1092. {
  1093. if (contentOffset==-1)
  1094. {
  1095. // No content
  1096. printf(responseReceived.C_String());
  1097. }
  1098. else
  1099. {
  1100. json_error_t error;
  1101. json_t *root = json_loads(responseReceived.C_String() + contentOffset, JSON_REJECT_DUPLICATES, &error);
  1102. if (!root)
  1103. {
  1104. printf("Error parsing JSON\n", __LINE__);
  1105. }
  1106. else
  1107. {
  1108. void *iter = json_object_iter(root);
  1109. while (iter)
  1110. {
  1111. const char *firstKey = json_object_iter_key(iter);
  1112. if (stricmp(firstKey, "GET")==0)
  1113. {
  1114. game->SetMasterServerQueryResult(root);
  1115. root=0;
  1116. json_t* jsonArray = json_object_iter_value(iter);
  1117. size_t arraySize = json_array_size(jsonArray);
  1118. for (unsigned int i=0; i < arraySize; i++)
  1119. {
  1120. json_t* object = json_array_get(jsonArray, i);
  1121. json_t* roomNameVal = json_object_get(object, "roomName");
  1122. RakAssert(roomNameVal->type==JSON_STRING);
  1123. json_t* natTypesVal = json_object_get(object, "natTypes");
  1124. RakAssert(natTypesVal->type==JSON_ARRAY);
  1125. size_t natTypesSize = json_array_size(natTypesVal);
  1126. printf("Room name: %s. Players: %i\n", json_string_value(roomNameVal), natTypesSize);
  1127. }
  1128. if (arraySize==0)
  1129. printf("No rooms.\n");
  1130. printf("(J)oin room\n");
  1131. printf("(C)reate room\n");
  1132. printf("(S)earch rooms\n");
  1133. break;
  1134. }
  1135. else if (stricmp(firstKey, "POST")==0)
  1136. {
  1137. RakAssert(stricmp(firstKey, "POST")==0);
  1138. json_t* jsonObject = json_object_iter_value(iter);
  1139. json_t* val1 = json_object_get(jsonObject, "__rowId");
  1140. RakAssert(val1->type==JSON_INTEGER);
  1141. game->masterServerRow = (int) json_integer_value(val1);
  1142. printf("Session posted to row %i\n", game->masterServerRow);
  1143. break;
  1144. }
  1145. else
  1146. {
  1147. iter = json_object_iter_next(root, iter);
  1148. RakAssert(iter != 0);
  1149. }
  1150. }
  1151. json_decref(root);
  1152. }
  1153. }
  1154. }
  1155. }
  1156. if (kbhit())
  1157. {
  1158. ch=getch();
  1159. if (game->phase==Game::SEARCH_FOR_GAMES)
  1160. {
  1161. if (ch=='c' || ch=='C')
  1162. {
  1163. CreateRoom();
  1164. }
  1165. if (ch=='s' || ch=='S')
  1166. {
  1167. game->SearchForGames();
  1168. }
  1169. else if (ch=='j' || ch=='J')
  1170. {
  1171. size_t arraySize = 0;
  1172. json_t *jsonArray = game->GetMasterServerQueryResult();
  1173. if (jsonArray)
  1174. {
  1175. arraySize = json_array_size(jsonArray);
  1176. }
  1177. // Join room
  1178. if (arraySize==0)
  1179. {
  1180. printf("No rooms to join.\n");
  1181. }
  1182. else
  1183. {
  1184. int index;
  1185. if (arraySize>1)
  1186. {
  1187. printf("Enter index of room to join.\n");
  1188. char indexstr[64];
  1189. Gets(indexstr,64);
  1190. index = atoi(indexstr);
  1191. }
  1192. else
  1193. {
  1194. index = 0;
  1195. }
  1196. if (index < 0 || (unsigned int) index >= arraySize)
  1197. {
  1198. printf("Index out of range.\n");
  1199. }
  1200. else
  1201. {
  1202. json_t* object = json_array_get(jsonArray, index);
  1203. json_t* guidVal = json_object_get(object, "guid");
  1204. RakAssert(guidVal->type==JSON_STRING);
  1205. RakNetGUID clientGUID;
  1206. clientGUID.FromString(json_string_value(guidVal));
  1207. if (clientGUID!=rakPeer->GetMyGUID())
  1208. {
  1209. natPunchthroughClient->OpenNAT(clientGUID, game->natPunchServerAddress);
  1210. game->EnterPhase(Game::NAT_PUNCH_TO_GAME_HOST);
  1211. }
  1212. else
  1213. {
  1214. printf("Cannot join your own room\n");
  1215. }
  1216. }
  1217. }
  1218. }
  1219. }
  1220. else
  1221. {
  1222. if (game->phase==Game::IN_GAME)
  1223. {
  1224. if (ch=='c' || ch=='C')
  1225. {
  1226. DataStructures::List<RakNetGUID> participantList;
  1227. fullyConnectedMesh2->GetParticipantList(participantList);
  1228. if (participantList.Size()>0)
  1229. {
  1230. printf("Enter in-game chat message: ");
  1231. char str[256];
  1232. Gets(str, 256);
  1233. RakString rs;
  1234. // Don't use RakString constructor to assign str, or will process % escape characters
  1235. rs=str;
  1236. BitStream bsOut;
  1237. bsOut.Write(rs);
  1238. for (unsigned int i=0; i < participantList.Size(); i++)
  1239. rpc4->Signal("InGameChat", &bsOut, HIGH_PRIORITY, RELIABLE_ORDERED, 0, participantList[i], false, false);
  1240. }
  1241. }
  1242. }
  1243. if (ch=='1')
  1244. {
  1245. user->tmTeamMember.RequestTeamSwitch(&game->teams[0]->tmTeam, 0);
  1246. }
  1247. else if (ch=='2')
  1248. {
  1249. user->tmTeamMember.RequestTeamSwitch(&game->teams[1]->tmTeam, 0);
  1250. }
  1251. else if (ch=='r' || ch=='R')
  1252. {
  1253. if (readyEvent->SetEvent(0, true))
  1254. printf("We are ready to start.\n");
  1255. }
  1256. else if (ch=='u' || ch=='U')
  1257. {
  1258. if (readyEvent->SetEvent(0, false))
  1259. printf("We are no longer ready to start.\n");
  1260. }
  1261. else if (ch=='l' || ch=='L')
  1262. {
  1263. if (fullyConnectedMesh2->IsConnectedHost())
  1264. {
  1265. if (game->lockGame)
  1266. {
  1267. printf("Game is no longer locked\n");
  1268. game->lockGame=false;
  1269. }
  1270. else
  1271. {
  1272. printf("Game is now locked\n");
  1273. game->lockGame=true;
  1274. }
  1275. }
  1276. }
  1277. else if (ch=='b' || ch=='B')
  1278. {
  1279. if (fullyConnectedMesh2->IsConnectedHost())
  1280. {
  1281. if (game->gameInLobby)
  1282. {
  1283. readyEvent->ForceCompletion(0);
  1284. game->gameInLobby=false;
  1285. game->EnterPhase(Game::IN_GAME);
  1286. }
  1287. else
  1288. {
  1289. readyEvent->DeleteEvent(0);
  1290. printf("Game ended, and now in lobby\n");
  1291. game->gameInLobby=true;
  1292. game->EnterPhase(Game::IN_LOBBY_WITH_HOST);
  1293. }
  1294. }
  1295. }
  1296. else if (ch=='e' || ch=='E')
  1297. {
  1298. // Disconnect from FullyConnectedMesh2 participants
  1299. DataStructures::List<RakNetGUID> participantList;
  1300. fullyConnectedMesh2->GetParticipantList(participantList);
  1301. for (unsigned int i=0; i < participantList.Size(); i++)
  1302. rakPeer->CloseConnection(participantList[i], true);
  1303. // User instances are deleted automatically from ReplicaManager3.
  1304. // However, teams are not deleted since the Team class can migrate between systems. So delete Team instances manually
  1305. while (game->teams.Size())
  1306. delete game->teams[game->teams.Size()-1];
  1307. // If we were the host, no longer list this session
  1308. // The new host will call PostRoomToCloud to reupload under a new IP address on ID_FCM2_NEW_HOST
  1309. ReleaseRoomFromCloud();
  1310. // Clear out state data from plugins
  1311. fullyConnectedMesh2->Clear();
  1312. readyEvent->DeleteEvent(0);
  1313. replicaManager3->Clear(false);
  1314. replicaManager3->Reference(game);
  1315. game->Reset();
  1316. game->EnterPhase(Game::SEARCH_FOR_GAMES);
  1317. }
  1318. else if (ch=='q' || ch=='Q')
  1319. {
  1320. printf("Quitting.\n");
  1321. RakString rspost = RakString::FormatForGET(
  1322. RakString(MASTER_SERVER_ADDRESS "/testServer?row=%i", game->masterServerRow));
  1323. httpConnection2->TransmitRequest(rspost, MASTER_SERVER_ADDRESS, MASTER_SERVER_PORT);
  1324. game->EnterPhase(Game::EXIT_SAMPLE);
  1325. }
  1326. }
  1327. }
  1328. // The game host updates the master server
  1329. RakNet::Time t = RakNet::GetTime();
  1330. if ((fullyConnectedMesh2->IsConnectedHost() || game->users.Size()==1) &&
  1331. t > game->whenToNextUpdateMasterServer &&
  1332. (game->phase == Game::IN_LOBBY_WITH_HOST ||
  1333. game->phase == Game::IN_GAME ||
  1334. game->phase == Game::IN_LOBBY_WAITING_FOR_HOST)
  1335. )
  1336. {
  1337. PostRoomToMaster();
  1338. }
  1339. RakSleep(30);
  1340. }
  1341. rakPeer->Shutdown(100);
  1342. while (game->teams.Size())
  1343. delete game->teams[game->teams.Size()-1];
  1344. while (game->users.Size())
  1345. delete game->users[game->users.Size()-1];
  1346. delete game;
  1347. RakPeerInterface::DestroyInstance(rakPeer);
  1348. TeamManager::DestroyInstance(teamManager);
  1349. FullyConnectedMesh2::DestroyInstance(fullyConnectedMesh2);
  1350. NatPunchthroughClient::DestroyInstance(natPunchthroughClient);
  1351. NatTypeDetectionClient::DestroyInstance(natTypeDetectionClient);
  1352. RPC4::DestroyInstance(rpc4);
  1353. ReadyEvent::DestroyInstance(readyEvent);
  1354. delete replicaManager3;
  1355. NetworkIDManager::DestroyInstance(networkIDManager);
  1356. HTTPConnection2::DestroyInstance(httpConnection2);
  1357. return 1;
  1358. }
粤ICP备19079148号