\n This project was the first project using our own engine, which we decided to call Eggine.\n I focused a lot on engine and rendering in this project, which I found very interesting\n and was something I was eager to continue with in following projects.\n
\n\n In this project I focused on implementing the ECS-system, the particle system, Deferred rendering and various\n other rendering features. I also dabbled in some optimizations using SIMD, although this was mostly code from\n stack overflow.\n
\n\n In this project we had a lot of freedom in the gameplay, as it was our third C++ project using the \n schools provided in house engine. I focused a lot on systems that required map editing in Tiled, \n the external map editor we used. I made systems for switches and buttons that could be connected to events,\n moving platforms. \n
\n\n I also looked into and added support for pitching and reverb on sounds, which required me to\n look through the documentation of BASS, the audio library provided in the engine. This proved to be a bigger challenge\n than expected, as the interface and implementation of BASS was very difficult to understand, along with the \n documentation being very minimal.\n
\n\n This project started as a networking project about lag compensation and input prediction, \n but ended up being more about library and interface design while learning about networking as a concept.\n
\n\n The end product is a library that does most of the setup for a UDP Client and server for games, \n with an easy interface for serialization and bitpacking.\n
\n\n The project is split into 3 parts, the Client, the server and the shared Library. This is to minimize copied code, \n as a lot of the functionality is very similar between the server and the client,\n Especially since it is using UDP.\n
\n\n There are a lot of classes that try to simplify the networking process. For example, the address class\n has a lot of different constructors to make it easy to use both ipv4 and ipv6, inputting both via strings and numbers.\n
\n\nclass Address //Incomplete, just the constructors\n{\npublic:\n Address();\n explicit Address(uint8_t aA, uint8_t aB, uint8_t aC, uint8_t aD, uint16_t aPort = 0);\n explicit Address(uint32_t aAddress, uint16_t aPort = 0);\n explicit Address(uint16_t aA, uint16_t aB, uint16_t aC, uint16_t aD,\n uint16_t aE, uint16_t aF, uint16_t aG, uint16_t aH, uint16_t aPort = 0);\n explicit Address(uint16_t aAddress[], uint16_t aPort = 0);\n explicit Address(const char* aAddress);\n explicit Address(const char* aAddress, uint16_t aPort);\n explicit Address(const sockaddr_storage& aAddress);\n //Continues...\n};\n
\n\n\n This is the bitpacking function, which abstracts to the point that the end user doesn't have to think about it. \n I learned a lot about low level code and bitwise operators, which will help me write more effective code in the future.\n
\nvoid Spooder::BitWriter::WriteBits(void* aData, int aBitsToWrite)\n{\n assert(aBitsToWrite > 0);\n myScratch |= (*(uint64_t*)aData) << myScratchBits; \n myScratchBits += aBitsToWrite; \n if (myScratchBits >= 32) \n {\n myBuffer[myWordIndex] = myScratch; \n myWordIndex++; \n myScratch >>= 32; \n myScratchBits -= 32; \n }\n myBitsWritten += aBitsToWrite;\n}\n
\n\n \n A lot of this code is taken and interpreted from Gaffer on games excellent articles, the main\n resource for this project\n
\n\n One of the main goals with this project was providing a simple interface for the end user, so \n that even if you don't know that much about networking, you can get something up and running.\n
\n\n The interface looks as follows:\n
\nstruct TestPacket : public Packet\n{\n int myA;\n float myB;\n\n TestPacket()\n : Packet(PACKET_TEST_PACKET){}\n\n template <typename Stream>\n bool Serialize(Stream& aStream)\n {\n SERIALIZE_INT(aStream, myA[i], 0, 255);\n SERIALIZE_FLOAT_COMPRESSED(aStream, myB, 0.f, 256.f, 0.01f);\n\n return true;\n }\n SPOODER_DECLARE_VIRTUAL_SERIALIZE_FUNCTIONS();\n};\n\n
\n \n The serialize macros return false if the serialization fails, so the failchecking\n is hidden, but is still there, making the interface simpler to use, at the cost of\n being a bit harder to understand in the background.\n
\n\n In the serialize macros there is also some extra parameters, for minimum and maximum values.\n This allows the user to specify a range of the variable, which translates into automatic bitpacking, \n to save space in the packets sent to and from the server.\n The float has two different serialize functions, one for normal floats and one for compressed values as shown here.\n This is also to save space in the packets for cases where you don't need the full 32 bits of accuracy.\n
\n\n Lastly, the SPOODER_DECLARE_VIRTUAL_SERIALIZE_FUNCTIONS
macro automatically declaring\n serializing functions for read and writestreams, so that we can use a virtual packet class, but as the definition of\n the function is the same across all packets, we just use a macro.\n
\n I wanted to add way more things to the library, but as time was limited I had to limit my scope.\n First I would add support for reliable messages, an essential feature in a networking library.\n I would also add support for packet fragmentation, as I think it would be very useful and interesting to implement.\n Next, my plan was to add support for different update rates for each networked struct, to allow for more\n effective communication over the network.\n
\n