1 /* 2 * DSFML - The Simple and Fast Multimedia Library for D 3 * 4 * Copyright (c) 2013 - 2017 Jeremy DeHaan (dehaan.jeremiah@gmail.com) 5 * 6 * This software is provided 'as-is', without any express or implied warranty. 7 * In no event will the authors be held liable for any damages arising from the 8 * use of this software. 9 * 10 * Permission is granted to anyone to use this software for any purpose, 11 * including commercial applications, and to alter it and redistribute it 12 * freely, subject to the following restrictions: 13 * 14 * 1. The origin of this software must not be misrepresented; you must not claim 15 * that you wrote the original software. If you use this software in a product, 16 * an acknowledgment in the product documentation would be appreciated but is 17 * not required. 18 * 19 * 2. Altered source versions must be plainly marked as such, and must not be 20 * misrepresented as being the original software. 21 * 22 * 3. This notice may not be removed or altered from any source distribution 23 */ 24 25 /** 26 * Packets provide a safe and easy way to serialize data, in order to send it 27 * over the network using sockets (sf::TcpSocket, sf::UdpSocket). 28 * 29 * Packets solve 2 fundamental problems that arise when transferring data over 30 * the network: 31 * $(UL 32 * $(LI data is interpreted correctly according to the endianness) 33 * $(LI the bounds of the packet are preserved (one send == one receive))) 34 * 35 * $(PARA The $(U Packet) class provides both input and output modes.) 36 * 37 * Example: 38 * --- 39 * int x = 24; 40 * string s = "hello"; 41 * double d = 5.89; 42 * 43 * // Group the variables to send into a packet 44 * auto packet = new Packet(); 45 * packet.write(x); 46 * packet.write(s); 47 * packet.write(d); 48 * 49 * // Send it over the network (socket is a valid TcpSocket) 50 * socket.send(packet); 51 * 52 * //////////////////////////////////////////////////////////////// 53 * 54 * // Receive the packet at the other end 55 * auto packet = new Packet(); 56 * socket.receive(packet); 57 * 58 * // Extract the variables contained in the packet 59 * int x; 60 * s; 61 * double d; 62 * if (packet.read(x) && packet.read(s) && packet.read(d)) 63 * { 64 * // Data extracted successfully... 65 * } 66 * --- 67 * 68 * $(PARA Packets also provide an extra feature that allows to apply custom 69 * transformations to the data before it is sent, and after it is received. This 70 * is typically used to handle automatic compression or encryption of the data. 71 * This is achieved by inheriting from sf::Packet, and overriding the onSend and 72 * onReceive functions.) 73 * 74 * Example: 75 * --- 76 * class ZipPacket : Packet 77 * { 78 * override const(void)[] onSend() 79 * { 80 * const(void)[] srcData = getData(); 81 * 82 * return MySuperZipFunction(srcData); 83 * } 84 * 85 * override void onReceive(const(void)[] data) 86 * { 87 * const(void)[] dstData = MySuperUnzipFunction(data); 88 * 89 * append(dstData); 90 * } 91 * } 92 * 93 * // Use like regular packets: 94 * auto packet = new ZipPacket(); 95 * packet.write(x); 96 * packet.write(s); 97 * packet.write(d); 98 * --- 99 * 100 * See_Also: 101 * $(TCPSOCKET_LINK), $(UDPSOCKET_LINK) 102 */ 103 module dsfml.network.packet; 104 105 import std.traits; 106 import std.range; 107 108 /** 109 * Utility class to build blocks of data to transfer over the network. 110 */ 111 class Packet 112 { 113 private 114 { 115 ubyte[] m_data; /// Data stored in the packet 116 size_t m_readPos; /// Current reading position in the packet 117 bool m_isValid; /// Reading state of the packet 118 } 119 120 /// Default constructor. 121 this() 122 { 123 m_readPos = 0; 124 m_isValid = true; 125 } 126 127 /// Destructor. 128 ~this() 129 { 130 import dsfml.system.config; 131 mixin(destructorOutput); 132 } 133 134 /** 135 * Get a slice of the data contained in the packet. 136 * 137 * Returns: Slice containing the data. 138 */ 139 const(void)[] getData() const 140 { 141 return m_data; 142 } 143 144 /** 145 * Append data to the end of the packet. 146 * 147 * Params: 148 * data = Pointer to the sequence of bytes to append 149 */ 150 void append(const(void)[] data) 151 { 152 if(data != null && data.length > 0) 153 { 154 m_data ~= cast(ubyte[])data; 155 } 156 } 157 158 /** 159 * Clear the packet. 160 * 161 * After calling Clear, the packet is empty. 162 */ 163 void clear() 164 { 165 m_data.length = 0; 166 m_readPos = 0; 167 m_isValid = true; 168 } 169 170 /** 171 * Tell if the reading position has reached the end of the packet. 172 * 173 * This function is useful to know if there is some data left to be read, 174 * without actually reading it. 175 * 176 * Returns: true if all data was read, false otherwise. 177 */ 178 bool endOfPacket() const 179 { 180 return m_readPos >= m_data.length; 181 } 182 183 /** 184 * Reads a primitive data type or string from the packet. 185 * 186 * The value in the packet at the current read position is set to value. 187 * 188 * Returns: true if last data extraction from packet was successful. 189 */ 190 bool read(T)(out T value) 191 if(isScalarType!T) 192 { 193 import std.bitmanip; 194 195 if (checkSize(T.sizeof)) 196 { 197 value = std.bitmanip.peek!T(m_data, m_readPos); 198 m_readPos += T.sizeof; 199 } 200 201 return m_isValid; 202 } 203 204 /// ditto 205 bool read(T)(out T value) 206 if(isSomeString!T) 207 { 208 import std.conv; 209 210 //Get the element type of the string 211 static if(isNarrowString!T) 212 alias ET = Unqual!(ElementEncodingType!T); 213 else 214 alias ET = Unqual!(ElementType!T); 215 216 //get string length 217 uint length; 218 read(length); 219 ET[] temp = new ET[](length); 220 221 //read each dchar of the string and put it in. 222 for(int i = 0; i < length && m_isValid; ++i) 223 { 224 read!ET(temp[i]); 225 } 226 227 value = temp.to!T(); 228 229 return m_isValid; 230 } 231 232 /// Writes a scalar data type or string to the packet. 233 void write(T)(T value) 234 if(isScalarType!T) 235 { 236 import std.bitmanip; 237 238 size_t index = m_data.length; 239 m_data.reserve(value.sizeof); 240 m_data.length += value.sizeof; 241 242 std.bitmanip.write!T(m_data, value, index); 243 } 244 245 /// ditto 246 void write(T)(T value) 247 if(isSomeString!T) 248 { 249 //write length of string. 250 write(cast(uint) value.length); 251 //write append the string data 252 for(int i = 0; i < value.length; ++i) 253 { 254 write(value[i]); 255 } 256 } 257 258 /** 259 * Called before the packet is sent over the network. 260 * 261 * This function can be defined by derived classes to transform the data 262 * before it is sent; this can be used for compression, encryption, etc. 263 * 264 * The function must return an array of the modified data, with a length of 265 * the number of bytes to send. The default implementation provides the 266 * packet's data without transforming it. 267 * 268 * Returns: Array of bytes to send. 269 */ 270 const(void)[] onSend() 271 { 272 return getData(); 273 } 274 275 /** 276 * Called after the packet is received over the network. 277 * 278 * This function can be defined by derived classes to transform the data 279 * after it is received; this can be used for uncompression, decryption, 280 * etc. 281 * 282 * The function receives an array of the received data, and must fill the 283 * packet with the transformed bytes. The default implementation fills the 284 * packet directly without transforming the data. 285 * 286 * Params: 287 * data = Array of the received bytes 288 */ 289 void onRecieve(const(void)[] data) 290 { 291 append(data); 292 } 293 294 private bool checkSize(size_t size) 295 { 296 m_isValid = m_isValid && (m_readPos + size <= m_data.length); 297 298 return m_isValid; 299 } 300 } 301 302 /* 303 * Utility class used internally to interact with DSFML-C to transfer Packet's 304 * data. 305 */ 306 package class SfPacket 307 { 308 package sfPacket* sfPtr; 309 310 //Default constructor 311 this() 312 { 313 sfPtr = sfPacket_create(); 314 } 315 316 //Destructor 317 ~this() 318 { 319 import dsfml.system.config; 320 mixin(destructorOutput); 321 sfPacket_destroy(sfPtr); 322 } 323 324 325 //Get a slice of the data contained in the packet. 326 // 327 //Returns: Slice containing the data. 328 const(void)[] getData() const 329 { 330 return sfPacket_getData(sfPtr)[0 .. sfPacket_getDataSize(sfPtr)]; 331 } 332 333 //Append data to the end of the packet. 334 //Params: 335 // data = Pointer to the sequence of bytes to append. 336 void append(const(void)[] data) 337 { 338 sfPacket_append(sfPtr, data.ptr, void.sizeof * data.length); 339 } 340 } 341 342 343 unittest 344 { 345 //TODO: Expand to use more of the mehtods found in Packet 346 version(DSFML_Unittest_Network) 347 { 348 import std.stdio; 349 350 import dsfml.network.socket; 351 import dsfml.network.tcpsocket; 352 import dsfml.network.tcplistener; 353 import dsfml.network.ipaddress; 354 355 import core.time; 356 357 writeln("Unittest for Packet"); 358 359 //packet to send data 360 auto sendPacket = new Packet(); 361 //Packet to receive data 362 auto receivePacket = new Packet(); 363 364 //Let's greet the server! 365 sendPacket.write("Hello, I'm a client!"); 366 sendPacket.write(42); 367 sendPacket.write(cast(double) 3.141592); 368 sendPacket.write(false); 369 370 receivePacket.onRecieve(sendPacket.onSend()); 371 372 //What did we get from the client? 373 string message; 374 int sentInt; 375 double sentDouble; 376 bool sentBool; 377 assert(receivePacket.read!string(message)); 378 assert(message == "Hello, I'm a client!"); 379 380 assert(receivePacket.read(sentInt)); 381 assert(sentInt == 42); 382 383 assert(receivePacket.read(sentDouble)); 384 assert(sentDouble == 3.141592); 385 386 assert(receivePacket.read(sentBool)); 387 assert(!sentBool); 388 writeln("Gotten from client: ", message, ", ", sentInt, ", ", sentDouble, ", ", sentBool); 389 390 //clear the packets to send/get new information 391 sendPacket.clear(); 392 receivePacket.clear(); 393 394 //Respond back to the client 395 sendPacket.write("Hello, I'm your server."); 396 sendPacket.write(420UL); 397 sendPacket.write(cast(float) 2.7182818); 398 sendPacket.write(true); 399 400 receivePacket.onRecieve(sendPacket.onSend()); 401 402 ulong sentULong; 403 float sentFloat; 404 assert(receivePacket.read!string(message)); 405 assert(message == "Hello, I'm your server."); 406 407 assert(receivePacket.read(sentULong)); 408 assert(sentULong == 420UL); 409 410 assert(receivePacket.read(sentFloat)); 411 assert(sentFloat == 2.7182818f); 412 413 assert(receivePacket.read(sentBool)); 414 assert(sentBool); 415 writeln("Gotten from server: ", message, ", ", sentULong, ", ", sentFloat, ", ", sentBool); 416 417 writeln("Done!"); 418 writeln(); 419 } 420 } 421 422 package extern(C): 423 424 struct sfPacket; 425 426 ///Create a new packet 427 sfPacket* sfPacket_create(); 428 429 430 ///Create a new packet by copying an existing one 431 sfPacket* sfPacket_copy(const sfPacket* packet); 432 433 434 ///Destroy a packet 435 void sfPacket_destroy(sfPacket* packet); 436 437 438 ///Append data to the end of a packet 439 void sfPacket_append(sfPacket* packet, const void* data, size_t sizeInBytes); 440 441 442 ///Clear a packet 443 void sfPacket_clear(sfPacket* packet); 444 445 446 ///Get a pointer to the data contained in a packet 447 const(void)* sfPacket_getData(const sfPacket* packet); 448 449 450 ///Get the size of the data contained in a packet 451 size_t sfPacket_getDataSize(const sfPacket* packet); 452 453 454 ///Tell if the reading position has reached the end of a packet 455 bool sfPacket_endOfPacket(const sfPacket* packet); 456 457 458 ///Test the validity of a packet, for reading 459 bool sfPacket_canRead(const sfPacket* packet); 460 461 462 ///Functions to extract data from a packet 463 bool sfPacket_readBool(sfPacket* packet); 464 byte sfPacket_readInt8(sfPacket* packet); 465 ubyte sfPacket_readUint8(sfPacket* packet); 466 short sfPacket_readInt16(sfPacket* packet); 467 ushort sfPacket_readUint16(sfPacket* packet); 468 int sfPacket_readInt32(sfPacket* packet); 469 uint sfPacket_readUint32(sfPacket* packet); 470 float sfPacket_readFloat(sfPacket* packet); 471 double sfPacket_readDouble(sfPacket* packet); 472 473 //Remove in lieu of readUint16 and readUint32 for W and D chars in D? 474 //void sfPacket_readString(sfPacket* packet, char* string); 475 //void sfPacket_readWideString(sfPacket* packet, wchar_t* string); 476 477 ///Functions to insert data into a packet 478 void sfPacket_writeBool(sfPacket* packet, bool); 479 void sfPacket_writeInt8(sfPacket* packet, byte); 480 void sfPacket_writeUint8(sfPacket* packet, ubyte); 481 void sfPacket_writeInt16(sfPacket* packet, short); 482 void sfPacket_writeUint16(sfPacket* packet, ushort); 483 void sfPacket_writeInt32(sfPacket* packet, int); 484 void sfPacket_writeUint32(sfPacket* packet, uint); 485 void sfPacket_writeFloat(sfPacket* packet, float); 486 void sfPacket_writeDouble(sfPacket* packet, double);