1 // ----------------------------------------------------------------------
2 // RakNet version 1.411
3 // BitStreamSample.cpp
4 // Created by Rakkar Software (rakkar@jenkinssoftware.com) December 8, 2003
5 // Shows simple use of the bitstream class
6 // ----------------------------------------------------------------------
7
8 #include "RakPeerInterface.h"
9 #include "RakNetworkFactory.h"
10 #include "BitStream.h"
11 #include "RakSleep.h"
12 #include <stdlib.h> // For atoi
13 #include <cstring> // For strlen
14 #include <stdio.h>
15 #include <string.h>
16 #include "Kbhit.h"
17
18 #ifdef _COMPATIBILITY_1
19 #include "Compatibility1Includes.h" // Developers of a certain platform will know what to do here.
20 #elif defined(_PS3) || defined(__PS3__)
21 #include "Console2SampleIncludes.h"
22 #elif defined (_XBOX)
23 #elif defined(_WIN32)
24 #include "WindowsIncludes.h" // Sleep
25 #else
26 #include <unistd.h> // usleep
27 #include <cstdio>
28 #endif
29
30 bool quit;
31
32 using namespace RakNet;
33
34 // We want to send information about a person and their job.
35 // We are interested in their name, age, salary, and years employed.
36 // For this sample we encode the data as follows:
37 // 1. Number of characters of the person's name
38 // 2. Person's name (not null terminated)
39 // 3. Person's age (compressed unsigned char)
40 // 4. The EmploymentStruct (below)
41 struct EmploymentStruct
42 {
43 int salary;
44 unsigned char yearsEmployed;
45 };
46
47 // You can parse the input data in two ways.
48 // Either cast input to a struct (such as if you sent a struct)
49 // Or create a BitStream instance with input as data such as
50 // BitStream myBitStream(input, (numberOfBitsOfData-1)/8+1);
51 // where (numberOfBitsOfData-1)/8+1 is the number of bytes of data
52 // If you didn't pass any data then of course you don't do either
53 // Here we pass a bitstream
clientRPC(RPCParameters * rpcParameters)54 void clientRPC(RPCParameters *rpcParameters)
55 {
56 // Since we were passed a bitstream, to make things easier to parse convert the input into into a BitStream.
57 // We can either do this in the constructor which takes the data and the number of BYTES
58 // BitStream b(input, (numberOfBitsOfData-1)/8+1);
59 // or we can do it by writing out the bits
60 // BitStream b;
61 // b.WriteBits(input, numberOfBitsOfData);
62
63 // Here I will do the constructor version because it is easier. numberOfBitsOfData is always > 0
64 // The third parameter of false means don't internally copy input but just maintain a pointer to it. This is fine because we never change it
65 BitStream b(rpcParameters->input, BITS_TO_BYTES(rpcParameters->numberOfBitsOfData), false);
66 char name[200];
67
68 // printf("GOT RPC:\n");
69 // b.PrintBits();
70
71 // The length of the person's name. It is critical to read the same types in the same order using the same level of compression as we wrote them
72 unsigned char nameLength;
73 b.Read(nameLength);
74 if (b.Read(name, nameLength)==false) // Name is not null terminated
75 // Note we always check if the read functions return false. This is important in a real example in case hackers or programmer error causes too-short bitstreams
76 // You would probably also log this event or throw an assert in a real game
77 {
78 printf("Name was not null-terminated!\n");
79 return;
80 }
81
82 name[nameLength]=0; // Name is now null terminated
83
84 printf("In clientRPC:\n");
85 printf("Name is %s\n", name);
86
87 unsigned int age;
88 // We used WriteCompressed for the age, so have to use ReadCompressed to get it
89 if (b.ReadCompressed(age)==false)
90 return;
91
92 printf("Age is %i\n", age);
93 fflush(stdout);
94
95 bool wroteEmploymentStruct;
96 if (b.Read(wroteEmploymentStruct)==false)
97 {
98 return;
99 }
100
101 if (wroteEmploymentStruct)
102 {
103 printf("We are employed.\n");
104
105 EmploymentStruct employmentStruct;
106 // Reading a struct consists of deserializing the bitstream:
107 if (b.Read(employmentStruct.salary)==false) return;
108 if (b.Read(employmentStruct.yearsEmployed)==false) return;
109
110 printf("Salary is %i. Years employed is %i\n", employmentStruct.salary, (int)employmentStruct.yearsEmployed);
111 }
112 else
113 printf("We are between jobs :)\n");
114
115 quit=true;
116 }
117
118 #if defined(_PS3) || defined(__PS3__)
119 _PS3_SetSystemProcessParams
120 #endif
121
main(void)122 int main(void)
123 {
124 RakPeerInterface *rakClient=RakNetworkFactory::GetRakPeerInterface();
125 RakPeerInterface *rakServer=RakNetworkFactory::GetRakPeerInterface();
126 #ifndef WIN32
127 #define getch getchar
128 #endif
129
130 #if defined(_PS3) || defined(__PS3__)
131 PS3LoadModules();
132 #endif
133 quit=false;
134 char text[255];
135
136 // Defined in RakNetTypes.h.
137 // You can register a function anytime
138 REGISTER_STATIC_RPC(rakClient, clientRPC);
139
140 //rakServer->InitializeSecurity(0,0,0,0);
141
142 SocketDescriptor socketDescriptor(10000,0);
143 if (rakServer->Startup(1,30,&socketDescriptor, 1)==false)
144 {
145 printf("Start call failed!\n");
146
147 fflush(stdout);
148 fgets(text, sizeof(text), stdin);
149 printf("\n");
150 return 0;
151 }
152 rakServer->SetMaximumIncomingConnections(1);
153 socketDescriptor.port=0;
154 rakClient->Startup(1, 30, &socketDescriptor, 1);
155 if (rakClient->Connect("127.0.0.1", 10000, 0, 0)==false)
156 {
157 printf("Connect call failed\n");
158 fflush(stdout);
159 fgets(text, sizeof(text), stdin);
160 printf("\n");
161 return 0;
162 }
163
164 BitStream outgoingBitstream;
165 unsigned int age;
166
167 printf("A sample on how to use RakNet's bitstream class\n");
168 printf("Difficulty: Beginner\n\n");
169
170 printf("Enter your name.\n");
171 fflush(stdout);
172 fgets(text, sizeof(text), stdin);
173 printf("\n");
174 if (text[0]==0)
175 strcpy(text, "Unnamed!");
176 // Write the number of characters of the name into the bitstream
177 // We put the unsigned char cast to use the overload that writes 1 byte.
178 outgoingBitstream.Write((unsigned char)strlen(text));
179
180 // Now write the name to the bitstream. This overload takes a char* and the number of bytes to write
181 outgoingBitstream.Write(text, (int) strlen(text));
182
183 printf("Enter your age (numbers only).\n");
184 fflush(stdout);
185 fgets(text, sizeof(text), stdin);
186 printf("\n");
187 if (text[0]==0)
188 age=0;
189 else
190 age=atoi(text);
191
192 // Write age to the bitstream. Since the range of age is
193 // probably pretty low compared to the range of the variable (uint),
194 // we use a compressed write. This can reduce the number
195 // of bits used.
196 outgoingBitstream.WriteCompressed(age);
197
198 // Now demonstrate dynamic packets by choosing one of two paths in which to write different amounts / types of data.
199 printf("Are you employed (y/n)?\n");
200 fflush(stdout);
201 fgets(text, sizeof(text), stdin);
202 printf("\n");
203 if (text[0]=='y')
204 {
205 outgoingBitstream.Write(true); // Writing a bool takes 1 bit
206
207 // Read some data into a struct
208 EmploymentStruct employmentStruct;
209 printf("What is your salary (enter a number only)?\n");
210 fflush(stdout);
211 fgets(text, sizeof(text), stdin);
212 printf("\n");
213 employmentStruct.salary = atoi(text);
214 printf("How many years have you been employed (enter a number only)?\n");
215 fflush(stdout);
216 fgets(text, sizeof(text), stdin);
217 printf("\n");
218 employmentStruct.yearsEmployed = atoi(text);
219
220 // We can write structs to a bitstream but this is not portable due to:
221 // 1. Different-endian CPUs
222 // 2. Different 'padding' of structs depending on compiler, etc
223 // The only safe way to send a struct is by using the BitStream
224 // to write out every single member which you want to send.
225 outgoingBitstream.Write(employmentStruct.salary);
226 outgoingBitstream.Write(employmentStruct.yearsEmployed);
227 // We're done writing to the struct
228 }
229 else
230 {
231 //printf("Number of bits before [false]: %d\n",
232 //outgoingBitstream.GetNumberOfBitsUsed() );
233 outgoingBitstream.Write(false); // Writing a bool takes 1 bit
234 // We're done writing to the struct. Compare this to the example above - we wrote quite a bit less.
235 }
236
237 printf("Waiting for connection...\n");
238 while (rakClient->GetSystemAddressFromIndex(0)==UNASSIGNED_SYSTEM_ADDRESS)
239 RakSleep(30);
240 printf("Connected.\n");
241
242 // printf("SEND RPC:\n");
243 // outgoingBitstream.PrintBits();
244
245 // RPC functions as well as send can take bitstreams directly
246 bool success = rakServer->RPC("clientRPC",&outgoingBitstream, HIGH_PRIORITY, RELIABLE, 0, UNASSIGNED_SYSTEM_ADDRESS, true, 0, UNASSIGNED_NETWORK_ID, 0); // broadcast to everyone, which happens to be our one client
247 if (!success)
248 printf("RPC call failed\n");
249
250 while (!quit)
251 {
252 rakClient->DeallocatePacket(rakClient->Receive());
253 rakServer->DeallocatePacket(rakServer->Receive());
254
255 RakSleep(30);
256 }
257 printf("Press enter to quit\n");
258 fflush(stdout);
259 fgets(text, sizeof(text), stdin);
260 printf("\n");
261
262 rakClient->Shutdown(100,0);
263 rakServer->Shutdown(100,0);
264
265 // This is not necessary since on shutdown everything is unregistered. This is just here to show usage
266 UNREGISTER_STATIC_RPC(rakClient, clientRPC);
267
268 RakNetworkFactory::DestroyRakPeerInterface(rakClient);
269 RakNetworkFactory::DestroyRakPeerInterface(rakServer);
270
271 return 0;
272 }
273