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