diff --git a/.gitignore b/.gitignore
index bea91a67..37c9fe57 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,3 +6,4 @@
*.pdb
Debug/
+packages/
diff --git a/Assignment3README.txt b/Assignment3README.txt
new file mode 100644
index 00000000..f6583087
--- /dev/null
+++ b/Assignment3README.txt
@@ -0,0 +1,13 @@
+run the server FIRST via command line: SocketDemo.exe server
+run the client via command line: SocketDemo.exe
+
+press the escape key to end the Allegro Display
+
+server is a green circle
+client is a red square
+
+both can shoot with space bar but the projectiles do nothing and never die.
+
+movement on both apps is just Up Down Left Right
+
+the rain is simulated on the server and sent to the client.
diff --git a/RoboCat/Chapter3.vcxproj b/RoboCat/Chapter3.vcxproj
index 10d69fef..6fd098a7 100644
--- a/RoboCat/Chapter3.vcxproj
+++ b/RoboCat/Chapter3.vcxproj
@@ -92,6 +92,10 @@
true
true
Bin\$(Configuration)\
+ true
+ true
+ true
+ true
true
@@ -127,12 +131,12 @@
WIN32;_DEBUG;DEBUG;PROFILE;_WINDOWS;_WIN32_WINNT=0x0600;%(PreprocessorDefinitions)
ProgramDatabase
EnableFastChecks
- ..\SDL\include;Inc;..\
- Create
+ ..\SDL\include;Inc;..\;$(SolutionDir)packages\Allegro.5.2.7.1\build\native\include\allegro5
+ NotUsing
RoboCatPCH.h
- d3d11.lib;dxguid.lib;winmm.lib;comctl32.lib;%(AdditionalDependencies);Ws2_32.lib
+ allegro_color.lib;allegro_primitives.lib;allegro.lib;d3d11.lib;dxguid.lib;winmm.lib;comctl32.lib;%(AdditionalDependencies);Ws2_32.lib
true
true
true
@@ -141,6 +145,7 @@
AsInvoker
%(DelayLoadDLLs)
Console
+ $(SolutionDir)packages/Allegro.5.2.7.1\build\native\v143\win32\lib
true
@@ -370,7 +375,17 @@
+
+
+
+
+
+
+
+
+
+
@@ -378,8 +393,14 @@
+
+
+
+
+
+
@@ -392,8 +413,22 @@
Create
RoboCatPCH.h
+
+
+
+
-
+
+
+
+
+
+
+ This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.
+
+
+
+
\ No newline at end of file
diff --git a/RoboCat/Inc/AckRange.h b/RoboCat/Inc/AckRange.h
new file mode 100644
index 00000000..004d14c9
--- /dev/null
+++ b/RoboCat/Inc/AckRange.h
@@ -0,0 +1,35 @@
+//typedef PacketSequenceNumber uint16_t;
+
+class AckRange
+{
+public:
+ AckRange() : mStart( 0 ), mCount( 0 ) {}
+
+ AckRange( PacketSequenceNumber inStart ) : mStart( inStart ), mCount( 1 ) {}
+
+ //if this is the next in sequence, just extend the range
+ inline bool ExtendIfShould( PacketSequenceNumber inSequenceNumber );
+
+ PacketSequenceNumber GetStart() const { return mStart; }
+ uint32_t GetCount() const { return mCount; }
+
+ void Write( OutputMemoryBitStream& inOutputStream ) const;
+ void Read( InputMemoryBitStream& inInputStream );
+
+private:
+ PacketSequenceNumber mStart;
+ uint32_t mCount;
+};
+
+inline bool AckRange::ExtendIfShould( PacketSequenceNumber inSequenceNumber )
+{
+ if( inSequenceNumber == mStart + mCount )
+ {
+ ++mCount;
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
\ No newline at end of file
diff --git a/RoboCat/Inc/ByteSwap.h b/RoboCat/Inc/ByteSwap.h
new file mode 100644
index 00000000..5407540c
--- /dev/null
+++ b/RoboCat/Inc/ByteSwap.h
@@ -0,0 +1,117 @@
+
+#ifndef RoboCat_ByteSwap_h
+#define RoboCat_ByteSwap_h
+
+inline uint16_t ByteSwap2( uint16_t inData )
+{
+ return ( inData >> 8 ) | ( inData << 8 );
+}
+
+inline uint32_t ByteSwap4( uint32_t inData )
+{
+ return ( ( inData >> 24 ) & 0x000000ff ) |
+ ( ( inData >> 8 ) & 0x0000ff00 ) |
+ ( ( inData << 8 ) & 0x00ff0000 ) |
+ ( ( inData << 24 ) & 0xff000000 );
+}
+
+inline uint64_t ByteSwap8( uint64_t inData )
+{
+ return ( ( inData >> 56 ) & 0x00000000000000ff ) |
+ ( ( inData >> 40 ) & 0x000000000000ff00 ) |
+ ( ( inData >> 24 ) & 0x0000000000ff0000 ) |
+ ( ( inData >> 8 ) & 0x00000000ff000000 ) |
+ ( ( inData << 8 ) & 0x000000ff00000000 ) |
+ ( ( inData << 24 ) & 0x0000ff0000000000 ) |
+ ( ( inData << 40 ) & 0x00ff000000000000 ) |
+ ( ( inData << 56 ) & 0xff00000000000000 );
+}
+
+
+template < typename tFrom, typename tTo >
+class TypeAliaser
+{
+public:
+ TypeAliaser( tFrom inFromValue ) :
+ mAsFromType( inFromValue ) {}
+ tTo& Get() { return mAsToType; }
+
+ union
+ {
+ tFrom mAsFromType;
+ tTo mAsToType;
+ };
+};
+
+
+template class ByteSwapper;
+
+//specialize for 1...
+template
+class ByteSwapper< T, 1 >
+{
+public:
+ T Swap( T inData ) const
+ {
+ return inData;
+ }
+};
+
+
+//specialize for 2...
+template
+class ByteSwapper< T, 2 >
+{
+public:
+ T Swap( T inData ) const
+ {
+ uint16_t result =
+ ByteSwap2( TypeAliaser< T, uint16_t >( inData ).Get() );
+ return TypeAliaser< uint16_t, T >( result ).Get();
+ }
+};
+
+//specialize for 4...
+template
+class ByteSwapper< T, 4 >
+{
+public:
+ T Swap( T inData ) const
+ {
+ uint32_t result =
+ ByteSwap4( TypeAliaser< T, uint32_t >( inData ).Get() );
+ return TypeAliaser< uint32_t, T >( result ).Get();
+ }
+};
+
+
+//specialize for 8...
+template
+class ByteSwapper< T, 8 >
+{
+public:
+ T Swap( T inData ) const
+ {
+ uint64_t result =
+ ByteSwap8( TypeAliaser< T, uint64_t >( inData ).Get() );
+ return TypeAliaser< uint64_t, T >( result ).Get();
+ }
+};
+
+template < typename T >
+T ByteSwap( T inData )
+{
+ return ByteSwapper< T, sizeof( T ) >().Swap( inData );
+}
+
+inline void TestByteSwap()
+{
+ int32_t test = 0x12345678;
+ float floatTest = 1.f;
+
+ printf( "swapped 0x%x is 0x%x\n", test, ByteSwap( test ) );
+ printf( "swapped %f is %f\n", floatTest, ByteSwap( floatTest ) );
+ printf( "swapped 0x%x is 0x%x\n", TypeAliaser< float, uint32_t >( floatTest ).Get(), TypeAliaser< float, uint32_t >( ByteSwap( floatTest ) ).Get() );
+}
+
+#endif
diff --git a/RoboCat/Inc/CircleClass.h b/RoboCat/Inc/CircleClass.h
new file mode 100644
index 00000000..e4674b16
--- /dev/null
+++ b/RoboCat/Inc/CircleClass.h
@@ -0,0 +1,76 @@
+#pragma once
+
+#include "allegro.h"
+#include "allegro_primitives.h"
+#include
+#include
+
+class CircleClass
+{
+public:
+ CircleClass(string name, int windowWidth, int windowHeight, int xStart, int yStart, float newRadius);
+ CircleClass();
+ void Read(InputMemoryBitStream& iStream);
+ void Write(OutputMemoryBitStream& oStream) const;
+ void Draw();
+ void UpdatePos(int xChange, int yChange);
+
+ string mName;
+ int mWindowWidth;
+ int mWindowHeight;
+ std::vector position;
+ float radius;
+};
+
+void CircleClass::Read(InputMemoryBitStream& iStream)
+{
+ iStream.Read(mName);
+ iStream.Read(position[0]);
+ iStream.Read(position[1]);
+ iStream.Read(radius);
+}
+
+void CircleClass::Write(OutputMemoryBitStream& oStream) const
+{
+ oStream.Write(mName);
+ oStream.Write(position[0]);
+ oStream.Write(position[1]);
+ oStream.Write(radius);
+}
+
+void CircleClass::Draw()
+{
+ al_draw_filled_circle(position[0], position[1], radius, al_map_rgb(0, 255, 0));
+}
+
+CircleClass::CircleClass(string name, int windowWidth, int windowHeight, int xStart, int yStart, float newRadius)
+{
+ mName = name;
+ mWindowWidth = windowWidth;
+ mWindowHeight = windowHeight;
+ position.push_back(xStart);
+ position.push_back(yStart);
+ radius = newRadius;
+}
+
+CircleClass::CircleClass()
+{
+ mName = "";
+ mWindowWidth = 100;
+ mWindowHeight = 100;
+ int xTemp = 0;
+ int yTemp = 0;
+ position.push_back(xTemp);
+ position.push_back(yTemp);
+ radius = 10;
+}
+
+void CircleClass::UpdatePos(int xChange, int yChange)
+{
+ position[0] += xChange;
+ position[1] += yChange;
+ if (position[0] > mWindowWidth) position[0] = 0;
+ if (position[1] > mWindowHeight) position[1] = 0;
+ if (position[0] < 0) position[0] = mWindowWidth;
+ if (position[1] < 0) position[1] = mWindowHeight;
+}
\ No newline at end of file
diff --git a/RoboCat/Inc/DeliveryNotificationManager.h b/RoboCat/Inc/DeliveryNotificationManager.h
new file mode 100644
index 00000000..b6a795a4
--- /dev/null
+++ b/RoboCat/Inc/DeliveryNotificationManager.h
@@ -0,0 +1,72 @@
+
+class DeliveryNotificationManager
+{
+public:
+
+
+ DeliveryNotificationManager( bool inShouldSendAcks, bool inShouldProcessAcks );
+ ~DeliveryNotificationManager();
+
+ inline InFlightPacket* WriteState( OutputMemoryBitStream& inOutputStream );
+ inline bool ReadAndProcessState( InputMemoryBitStream& inInputStream );
+
+ void ProcessTimedOutPackets();
+
+ uint32_t GetDroppedPacketCount() const { return mDroppedPacketCount; }
+ uint32_t GetDeliveredPacketCount() const { return mDeliveredPacketCount; }
+ uint32_t GetDispatchedPacketCount() const { return mDispatchedPacketCount; }
+
+ const deque< InFlightPacket >& GetInFlightPackets() const { return mInFlightPackets; }
+
+private:
+
+
+
+ InFlightPacket* WriteSequenceNumber( OutputMemoryBitStream& inOutputStream );
+ void WriteAckData( OutputMemoryBitStream& inOutputStream );
+
+ //returns wether to drop the packet- if sequence number is too low!
+ bool ProcessSequenceNumber( InputMemoryBitStream& inInputStream );
+ void ProcessAcks( InputMemoryBitStream& inInputStream );
+
+
+ void AddPendingAck( PacketSequenceNumber inSequenceNumber );
+ void HandlePacketDeliveryFailure( const InFlightPacket& inFlightPacket );
+ void HandlePacketDeliverySuccess( const InFlightPacket& inFlightPacket );
+
+ PacketSequenceNumber mNextOutgoingSequenceNumber;
+ PacketSequenceNumber mNextExpectedSequenceNumber;
+
+ deque< InFlightPacket > mInFlightPackets;
+ deque< AckRange > mPendingAcks;
+
+ bool mShouldSendAcks;
+ bool mShouldProcessAcks;
+
+ uint32_t mDeliveredPacketCount;
+ uint32_t mDroppedPacketCount;
+ uint32_t mDispatchedPacketCount;
+
+};
+
+
+
+inline InFlightPacket* DeliveryNotificationManager::WriteState( OutputMemoryBitStream& inOutputStream )
+{
+ InFlightPacket* toRet = WriteSequenceNumber( inOutputStream );
+ if( mShouldSendAcks )
+ {
+ WriteAckData( inOutputStream );
+ }
+ return toRet;
+}
+
+inline bool DeliveryNotificationManager::ReadAndProcessState( InputMemoryBitStream& inInputStream )
+{
+ bool toRet = ProcessSequenceNumber( inInputStream );
+ if( mShouldProcessAcks )
+ {
+ ProcessAcks( inInputStream );
+ }
+ return toRet;
+}
\ No newline at end of file
diff --git a/RoboCat/Inc/InFlightPacket.h b/RoboCat/Inc/InFlightPacket.h
new file mode 100644
index 00000000..494e097e
--- /dev/null
+++ b/RoboCat/Inc/InFlightPacket.h
@@ -0,0 +1,33 @@
+class DeliveryNotificationManager;
+
+//in case we decide to change the type of the sequence number to use fewer or more bits
+typedef uint16_t PacketSequenceNumber;
+
+class InFlightPacket
+{
+public:
+
+ InFlightPacket( PacketSequenceNumber inSequenceNumber );
+
+ PacketSequenceNumber GetSequenceNumber() const { return mSequenceNumber; }
+ float GetTimeDispatched() const { return mTimeDispatched; }
+
+ void SetTransmissionData( int inKey, TransmissionDataPtr inTransmissionData )
+ {
+ mTransmissionDataMap[ inKey ] = inTransmissionData;
+ }
+ const TransmissionDataPtr GetTransmissionData( int inKey ) const
+ {
+ auto it = mTransmissionDataMap.find( inKey );
+ return ( it != mTransmissionDataMap.end() ) ? it->second : nullptr;
+ }
+
+ void HandleDeliveryFailure( DeliveryNotificationManager* inDeliveryNotificationManager ) const;
+ void HandleDeliverySuccess( DeliveryNotificationManager* inDeliveryNotificationManager ) const;
+
+private:
+ PacketSequenceNumber mSequenceNumber;
+ float mTimeDispatched;
+
+ unordered_map< int, TransmissionDataPtr > mTransmissionDataMap;
+};
\ No newline at end of file
diff --git a/RoboCat/Inc/LinkingContext.h b/RoboCat/Inc/LinkingContext.h
new file mode 100644
index 00000000..893e73aa
--- /dev/null
+++ b/RoboCat/Inc/LinkingContext.h
@@ -0,0 +1,67 @@
+
+#ifndef RoboCat_LinkingContext_h
+#define RoboCat_LinkingContext_h
+
+class GameObject;
+
+class LinkingContext
+{
+public:
+
+ LinkingContext() :
+ mNextNetworkId( 1 )
+ {}
+
+ uint32_t GetNetworkId( GameObject* inGameObject, bool inShouldCreateIfNotFound )
+ {
+ auto it = mGameObjectToNetworkIdMap.find( inGameObject );
+ if( it != mGameObjectToNetworkIdMap.end() )
+ {
+ return it->second;
+ }
+ else if( inShouldCreateIfNotFound )
+ {
+ uint32_t newNetworkId = mNextNetworkId++;
+ AddGameObject( inGameObject, newNetworkId );
+ return newNetworkId;
+ }
+ else
+ {
+ return 0;
+ }
+ }
+
+ GameObject* GetGameObject( uint32_t inNetworkId ) const
+ {
+ auto it = mNetworkIdToGameObjectMap.find( inNetworkId );
+ if( it != mNetworkIdToGameObjectMap.end() )
+ {
+ return it->second;
+ }
+ else
+ {
+ return nullptr;
+ }
+ }
+
+ void AddGameObject( GameObject* inGameObject, uint32_t inNetworkId )
+ {
+ mNetworkIdToGameObjectMap[ inNetworkId ] = inGameObject;
+ mGameObjectToNetworkIdMap[ inGameObject ] = inNetworkId;
+ }
+
+ void RemoveGameObject( GameObject *inGameObject )
+ {
+ uint32_t networkId = mGameObjectToNetworkIdMap[ inGameObject ];
+ mGameObjectToNetworkIdMap.erase( inGameObject );
+ mNetworkIdToGameObjectMap.erase( networkId );
+ }
+
+private:
+ std::unordered_map< uint32_t, GameObject* > mNetworkIdToGameObjectMap;
+ std::unordered_map< const GameObject*, uint32_t > mGameObjectToNetworkIdMap;
+
+ uint32_t mNextNetworkId;
+};
+
+#endif
diff --git a/RoboCat/Inc/MemoryBitStream.h b/RoboCat/Inc/MemoryBitStream.h
new file mode 100644
index 00000000..0bfd1874
--- /dev/null
+++ b/RoboCat/Inc/MemoryBitStream.h
@@ -0,0 +1,161 @@
+
+#include
+#include
+#include
+#include
+
+class GameObject;
+
+inline uint32_t ConvertToFixed( float inNumber, float inMin, float inPrecision )
+{
+ return static_cast< int > ( ( inNumber - inMin ) / inPrecision );
+}
+
+inline float ConvertFromFixed( uint32_t inNumber, float inMin, float inPrecision )
+{
+ return static_cast< float >( inNumber ) * inPrecision + inMin;
+}
+
+
+class OutputMemoryBitStream
+{
+public:
+
+ OutputMemoryBitStream() :
+ mBitHead(0),
+ mBuffer(nullptr)
+ {
+ ReallocBuffer( 1500 * 8 );
+ }
+
+ ~OutputMemoryBitStream() { std::free(mBuffer); }
+
+ void WriteBits( uint8_t inData, uint32_t inBitCount );
+ void WriteBits( const void* inData, uint32_t inBitCount );
+
+ const char* GetBufferPtr() const { return mBuffer; }
+ uint32_t GetBitLength() const { return mBitHead; }
+ uint32_t GetByteLength() const { return ( mBitHead + 7 ) >> 3; }
+
+ void WriteBytes( const void* inData, uint32_t inByteCount ) { WriteBits( inData, inByteCount << 3 ); }
+
+ /*
+ void Write( uint32_t inData, uint32_t inBitCount = 32 ) { WriteBits( &inData, inBitCount ); }
+ void Write( int inData, uint32_t inBitCount = 32 ) { WriteBits( &inData, inBitCount ); }
+ void Write( float inData ) { WriteBits( &inData, 32 ); }
+
+ void Write( uint16_t inData, uint32_t inBitCount = 16 ) { WriteBits( &inData, inBitCount ); }
+ void Write( int16_t inData, uint32_t inBitCount = 16 ) { WriteBits( &inData, inBitCount ); }
+
+ void Write( uint8_t inData, uint32_t inBitCount = 8 ) { WriteBits( &inData, inBitCount ); }
+ */
+
+ template< typename T >
+ void Write( T inData, uint32_t inBitCount = sizeof( T ) * 8 )
+ {
+ static_assert( std::is_arithmetic< T >::value ||
+ std::is_enum< T >::value,
+ "Generic Write only supports primitive data types" );
+ WriteBits( &inData, inBitCount );
+ }
+
+ void Write( bool inData ) { WriteBits( &inData, 1 ); }
+
+ void Write( const Vector3& inVector );
+ void Write( const Quaternion& inQuat );
+
+ void Write( const std::string& inString )
+ {
+ uint32_t elementCount = static_cast< uint32_t >( inString.size() );
+ Write( elementCount );
+ for( const auto& element : inString )
+ {
+ Write( element );
+ }
+ }
+
+private:
+ void ReallocBuffer( uint32_t inNewBitCapacity );
+
+ char* mBuffer;
+ uint32_t mBitHead;
+ uint32_t mBitCapacity;
+};
+
+class InputMemoryBitStream
+{
+public:
+
+ InputMemoryBitStream( char* inBuffer, uint32_t inBitCount ) :
+ mBuffer( inBuffer ),
+ mBitCapacity( inBitCount ),
+ mBitHead( 0 ),
+ mIsBufferOwner( false ) {}
+
+ InputMemoryBitStream( const InputMemoryBitStream& inOther ) :
+ mBitCapacity( inOther.mBitCapacity ),
+ mBitHead( inOther.mBitHead ),
+ mIsBufferOwner( true )
+ {
+ //allocate buffer of right size
+ int byteCount = ( mBitCapacity + 7 ) / 8;
+ mBuffer = static_cast< char* >( malloc( byteCount ) );
+ //copy
+ memcpy( mBuffer, inOther.mBuffer, byteCount );
+ }
+
+ ~InputMemoryBitStream() { if (mIsBufferOwner) { /*std::cout << "Freeing " << mBuffer << std::endl;*/ free(mBuffer); }; }
+
+ const char* GetBufferPtr() const { return mBuffer; }
+ uint32_t GetRemainingBitCount() const { return mBitCapacity - mBitHead; }
+
+ void ReadBits( uint8_t& outData, uint32_t inBitCount );
+ void ReadBits( void* outData, uint32_t inBitCount );
+
+ void ReadBytes( void* outData, uint32_t inByteCount ) { ReadBits( outData, inByteCount << 3 ); }
+
+ template< typename T >
+ void Read( T& inData, uint32_t inBitCount = sizeof( T ) * 8 )
+ {
+ static_assert( std::is_arithmetic< T >::value ||
+ std::is_enum< T >::value,
+ "Generic Read only supports primitive data types" );
+ ReadBits( &inData, inBitCount );
+ }
+
+ void Read( uint32_t& outData, uint32_t inBitCount = 32 ) { ReadBits( &outData, inBitCount ); }
+ void Read( int& outData, uint32_t inBitCount = 32 ) { ReadBits( &outData, inBitCount ); }
+ void Read( float& outData ) { ReadBits( &outData, 32 ); }
+
+ void Read( uint16_t& outData, uint32_t inBitCount = 16 ) { ReadBits( &outData, inBitCount ); }
+ void Read( int16_t& outData, uint32_t inBitCount = 16 ) { ReadBits( &outData, inBitCount ); }
+
+ void Read( uint8_t& outData, uint32_t inBitCount = 8 ) { ReadBits( &outData, inBitCount ); }
+ void Read( bool& outData ) { ReadBits( &outData, 1 ); }
+
+ void Read( Quaternion& outQuat );
+
+ void ResetToCapacity( uint32_t inByteCapacity ) { mBitCapacity = inByteCapacity << 3; mBitHead = 0; }
+
+
+ void Read( std::string& inString )
+ {
+ uint32_t elementCount;
+ Read( elementCount );
+ inString.resize( elementCount );
+ for( auto& element : inString )
+ {
+ Read( element );
+ }
+ }
+
+ void Read( Vector3& inVector );
+
+private:
+ char* mBuffer;
+ uint32_t mBitHead;
+ uint32_t mBitCapacity;
+ bool mIsBufferOwner;
+
+};
+
diff --git a/RoboCat/Inc/RainParticle.h b/RoboCat/Inc/RainParticle.h
new file mode 100644
index 00000000..c018305f
--- /dev/null
+++ b/RoboCat/Inc/RainParticle.h
@@ -0,0 +1,97 @@
+#pragma once
+
+#include "allegro.h"
+#include "allegro_primitives.h"
+#include "vector"
+
+#include
+
+class RainParticle
+{
+public:
+ RainParticle(int windowWidth, int windowHeight, float xStart, float yStart, float newRadius, float newR, float newG, float newB, float newA);
+ RainParticle();
+ void Read(InputMemoryBitStream& iStream);
+ void Write(OutputMemoryBitStream& oStream) const;
+ void Draw();
+ void UpdatePos(float xChange, float yChange);
+
+ int mWindowWidth;
+ int mWindowHeight;
+ std::vector color;
+ std::vector position;
+ float radius;
+};
+
+RainParticle::RainParticle(int windowWidth, int windowHeight, float xStart, float yStart, float newRadius, float newR, float newG, float newB, float newA)
+{
+ mWindowWidth = windowWidth;
+ mWindowHeight = windowHeight;
+ color.push_back(newR);
+ color.push_back(newG);
+ color.push_back(newB);
+ color.push_back(newA);
+
+ position.push_back(xStart);
+ position.push_back(yStart);
+
+ radius = newRadius;
+}
+
+RainParticle::RainParticle()
+{
+ float tempR, tempG, tempB, tempA, tempX, tempY;
+
+ mWindowWidth = 100;
+ mWindowHeight = 100;
+ color.push_back(tempR);
+ color.push_back(tempG);
+ color.push_back(tempB);
+ color.push_back(tempA);
+
+ position.push_back(tempX);
+ position.push_back(tempY);
+
+ radius = float();
+}
+
+void RainParticle::Read(InputMemoryBitStream& iStream)
+{
+ iStream.Read(color[0]);
+ iStream.Read(color[1]);
+ iStream.Read(color[2]);
+ iStream.Read(color[3]);
+
+ iStream.Read(position[0]);
+ iStream.Read(position[1]);
+
+ iStream.Read(radius);
+}
+
+void RainParticle::Write(OutputMemoryBitStream& oStream) const
+{
+ oStream.Write(color[0]);
+ oStream.Write(color[1]);
+ oStream.Write(color[2]);
+ oStream.Write(color[3]);
+
+ oStream.Write(position[0]);
+ oStream.Write(position[1]);
+
+ oStream.Write(radius);
+}
+
+void RainParticle::Draw()
+{
+ al_draw_filled_circle(position[0], position[1], radius, al_map_rgba(color[0], color[1], color[2], color[3]));
+}
+
+void RainParticle::UpdatePos(float xChange, float yChange)
+{
+ position[0] += xChange;
+ position[1] += yChange;
+ if (position[0] > mWindowWidth) position[0] = 0;
+ if (position[1] > mWindowHeight) position[1] = 0;
+ if (position[0] < 0) position[0] = mWindowWidth;
+ if (position[1] < 0) position[1] = mWindowHeight;
+}
\ No newline at end of file
diff --git a/RoboCat/Inc/RectangleObject.h b/RoboCat/Inc/RectangleObject.h
new file mode 100644
index 00000000..428f13eb
--- /dev/null
+++ b/RoboCat/Inc/RectangleObject.h
@@ -0,0 +1,82 @@
+#pragma once
+
+#include "allegro.h"
+#include "allegro_primitives.h"
+#include
+
+class RectangleObject
+{
+public:
+ string mName;
+ int mWindowWidth;
+ int mWindowHeight;
+ int width;
+ int height;
+ int xPos;
+ int yPos;
+ void Read(InputMemoryBitStream& stream);
+ void Write(OutputMemoryBitStream& stream) const;
+ RectangleObject(string name, int windowWidth, int windowHeight, int newWidth, int newHeight, int startPosX, int startPosY);
+ RectangleObject();
+ void UpdatePos(int xChange, int yChange);
+ void Draw();
+ //bool operator==(const RectangleObject& other);
+
+};
+//bool RectangleObject::operator==(const RectangleObject& other)
+//{
+// return mName == other.mName;
+//}
+void RectangleObject::Read(InputMemoryBitStream& stream)
+{
+ stream.Read(mName);
+ stream.Read(width);
+ stream.Read(height);
+ stream.Read(xPos);
+ stream.Read(yPos);
+}
+
+void RectangleObject::Write(OutputMemoryBitStream& stream) const
+{
+ stream.Write(mName);
+ stream.Write(width);
+ stream.Write(height);
+ stream.Write(xPos);
+ stream.Write(yPos);
+}
+
+RectangleObject::RectangleObject(string name, int windowWidth, int windowHeight, int newWidth, int newHeight, int startPosX, int startPosY)
+{
+ mName = name;
+ mWindowWidth = windowWidth;
+ mWindowHeight = windowHeight;
+ width = newWidth;
+ height = newHeight;
+ xPos = startPosX;
+ yPos = startPosY;
+}
+RectangleObject::RectangleObject()
+{
+ mName = "";
+ mWindowWidth = 100;
+ mWindowHeight = 100;
+ width = 10;
+ height = 10;
+ xPos = rand() % 100 + 1;
+ yPos = rand() % 100 + 1;
+}
+
+void RectangleObject::UpdatePos(int xChange, int yChange)
+{
+ xPos += xChange;
+ yPos += yChange;
+ if (xPos > mWindowWidth) xPos = 0;
+ if (yPos > mWindowHeight) yPos = 0;
+ if (xPos < 0) xPos = mWindowWidth;
+ if (yPos < 0) yPos = mWindowHeight;
+}
+
+void RectangleObject::Draw()
+{
+ al_draw_filled_rectangle(xPos, yPos, xPos + width, yPos + height, al_map_rgb(255, 0, 0));
+}
\ No newline at end of file
diff --git a/RoboCat/Inc/RoboCatShared.h b/RoboCat/Inc/RoboCatShared.h
index 83a3871a..d7eac1bc 100644
--- a/RoboCat/Inc/RoboCatShared.h
+++ b/RoboCat/Inc/RoboCatShared.h
@@ -49,6 +49,17 @@ using std::unordered_set;
class RoboCat;
class GameObject;
+#include "RoboMath.h"
+
+#include "TransmissionData.h"
+#include "InFlightPacket.h"
+
+#include "LinkingContext.h"
+#include "ByteSwap.h"
+#include "MemoryBitStream.h"
+#include "AckRange.h"
+
+#include "Timing.h"
#include "StringUtils.h"
#include "SocketAddress.h"
#include "SocketAddressFactory.h"
@@ -56,3 +67,4 @@ class GameObject;
#include "TCPSocket.h"
#include "SocketUtil.h"
#include "OutputWindow.h"
+#include "DeliveryNotificationManager.h"
diff --git a/RoboCat/Inc/RoboMath.h b/RoboCat/Inc/RoboMath.h
new file mode 100644
index 00000000..718080a9
--- /dev/null
+++ b/RoboCat/Inc/RoboMath.h
@@ -0,0 +1,199 @@
+class Vector3
+{
+public:
+
+ float mX, mY, mZ;
+
+ Vector3( float x, float y, float z ) :
+ mX( x ),
+ mY( y ),
+ mZ( z )
+ {}
+
+ Vector3() :
+ mX( 0.0f ),
+ mY( 0.0f ),
+ mZ( 0.0f )
+ {}
+
+ void Set( float x, float y, float z )
+ {
+ mX = x;
+ mY = y;
+ mZ = z;
+ }
+
+ friend Vector3 operator+( const Vector3& inLeft, const Vector3& inRight )
+ {
+ return Vector3( inLeft.mX + inRight.mX, inLeft.mY + inRight.mY, inLeft.mZ + inRight.mZ );
+ }
+
+ friend Vector3 operator-( const Vector3& inLeft, const Vector3& inRight )
+ {
+ return Vector3( inLeft.mX - inRight.mX, inLeft.mY - inRight.mY, inLeft.mZ - inRight.mZ );
+ }
+
+ // Component-wise multiplication
+ friend Vector3 operator*( const Vector3& inLeft, const Vector3& inRight )
+ {
+ return Vector3( inLeft.mX * inRight.mX, inLeft.mY * inRight.mY, inLeft.mZ * inRight.mZ );
+ }
+
+ // Scalar multiply
+ friend Vector3 operator*( float inScalar, const Vector3& inVec )
+ {
+ return Vector3( inVec.mX * inScalar, inVec.mY * inScalar, inVec.mZ * inScalar );
+ }
+
+ friend Vector3 operator*( const Vector3& inVec, float inScalar )
+ {
+ return Vector3( inVec.mX * inScalar, inVec.mY * inScalar, inVec.mZ * inScalar );
+ }
+
+ Vector3& operator*=( float inScalar )
+ {
+ mX *= inScalar;
+ mY *= inScalar;
+ mZ *= inScalar;
+ return *this;
+ }
+
+ Vector3& operator+=( const Vector3& inRight )
+ {
+ mX += inRight.mX;
+ mY += inRight.mY;
+ mZ += inRight.mZ;
+ return *this;
+ }
+
+ Vector3& operator-=( const Vector3& inRight )
+ {
+ mX -= inRight.mX;
+ mY -= inRight.mY;
+ mZ -= inRight.mZ;
+ return *this;
+ }
+
+ float Length()
+ {
+ return sqrtf( mX * mX + mY * mY + mZ * mZ );
+ }
+
+ float LengthSq()
+ {
+ return mX * mX + mY * mY + mZ * mZ;
+ }
+
+ float Length2D()
+ {
+ return sqrtf( mX * mX + mY * mY );
+ }
+
+ float LengthSq2D()
+ {
+ return mX * mX + mY * mY;
+ }
+
+ void Normalize()
+ {
+ float length = Length();
+ mX /= length;
+ mY /= length;
+ mZ /= length;
+ }
+
+ void Normalize2D()
+ {
+ float length = Length2D();
+ mX /= length;
+ mY /= length;
+ }
+
+ friend float Dot( const Vector3& inLeft, const Vector3& inRight )
+ {
+ return ( inLeft.mX * inRight.mX + inLeft.mY * inRight.mY + inLeft.mZ * inRight.mZ );
+ }
+
+ friend float Dot2D( const Vector3& inLeft, const Vector3& inRight )
+ {
+ return ( inLeft.mX * inRight.mX + inLeft.mY * inRight.mY );
+ }
+
+ friend Vector3 Cross( const Vector3& inLeft, const Vector3& inRight )
+ {
+ Vector3 temp;
+ temp.mX = inLeft.mY * inRight.mZ - inLeft.mZ * inRight.mY;
+ temp.mY = inLeft.mZ * inRight.mX - inLeft.mX * inRight.mZ;
+ temp.mZ = inLeft.mX * inRight.mY - inLeft.mY * inRight.mX;
+ return temp;
+ }
+
+ friend Vector3 Lerp( const Vector3& inA, const Vector3& inB, float t )
+ {
+ return Vector3( inA + t * ( inB - inA ) );
+ }
+
+ static const Vector3 Zero;
+ static const Vector3 UnitX;
+ static const Vector3 UnitY;
+ static const Vector3 UnitZ;
+};
+
+
+class Quaternion
+{
+public:
+
+ float mX, mY, mZ, mW;
+
+};
+
+
+template< int tValue, int tBits >
+struct GetRequiredBitsHelper
+{
+ enum { Value = GetRequiredBitsHelper< ( tValue >> 1 ), tBits + 1 >::Value };
+};
+
+template< int tBits >
+struct GetRequiredBitsHelper< 0, tBits >
+{
+ enum { Value = tBits };
+};
+
+template< int tValue >
+struct GetRequiredBits
+{
+ enum { Value = GetRequiredBitsHelper< tValue, 0 >::Value };
+};
+
+namespace RoboMath
+{
+ const float PI = 3.1415926535f;
+ float GetRandomFloat();
+
+ Vector3 GetRandomVector( const Vector3& inMin, const Vector3& inMax );
+
+ inline bool Is2DVectorEqual( const Vector3& inA, const Vector3& inB )
+ {
+ return ( inA.mX == inB.mX && inA.mY == inB.mY );
+ }
+
+ inline float ToDegrees( float inRadians )
+ {
+ return inRadians * 180.0f / PI;
+ }
+}
+
+namespace Colors
+{
+ static const Vector3 Black( 0.0f, 0.0f, 0.0f );
+ static const Vector3 White( 1.0f, 1.0f, 1.0f );
+ static const Vector3 Red( 1.0f, 0.0f, 0.0f );
+ static const Vector3 Green( 0.0f, 1.0f, 0.0f );
+ static const Vector3 Blue( 0.0f, 0.0f, 1.0f );
+ static const Vector3 LightYellow( 1.0f, 1.0f, 0.88f );
+ static const Vector3 LightBlue( 0.68f, 0.85f, 0.9f );
+ static const Vector3 LightPink( 1.0f, 0.71f, 0.76f );
+ static const Vector3 LightGreen( 0.56f, 0.93f, 0.56f );
+}
diff --git a/RoboCat/Inc/Timing.h b/RoboCat/Inc/Timing.h
new file mode 100644
index 00000000..b5af28d8
--- /dev/null
+++ b/RoboCat/Inc/Timing.h
@@ -0,0 +1,31 @@
+class Timing
+{
+public:
+
+ Timing();
+
+ void Update();
+
+ float GetDeltaTime() const { return mDeltaTime; }
+
+ double GetTime() const;
+
+ float GetTimef() const
+ {
+ return static_cast< float >( GetTime() );
+ }
+
+ float GetFrameStartTime() const { return mFrameStartTimef; }
+
+
+ static Timing sInstance;
+
+private:
+ float mDeltaTime;
+ uint64_t mDeltaTick;
+
+ double mLastFrameStartTime;
+ float mFrameStartTimef;
+ double mPerfCountDuration;
+
+};
\ No newline at end of file
diff --git a/RoboCat/Inc/TransmissionData.h b/RoboCat/Inc/TransmissionData.h
new file mode 100644
index 00000000..cc599ec0
--- /dev/null
+++ b/RoboCat/Inc/TransmissionData.h
@@ -0,0 +1,9 @@
+class DeliveryNotificationManager;
+
+class TransmissionData
+{
+public:
+ virtual void HandleDeliveryFailure( DeliveryNotificationManager* inDeliveryNotificationManager ) const = 0;
+ virtual void HandleDeliverySuccess( DeliveryNotificationManager* inDeliveryNotificationManager ) const = 0;
+};
+typedef shared_ptr< TransmissionData > TransmissionDataPtr;
\ No newline at end of file
diff --git a/RoboCat/RectangleObject.h b/RoboCat/RectangleObject.h
new file mode 100644
index 00000000..e69de29b
diff --git a/RoboCat/Src/AckRange.cpp b/RoboCat/Src/AckRange.cpp
new file mode 100644
index 00000000..99bed803
--- /dev/null
+++ b/RoboCat/Src/AckRange.cpp
@@ -0,0 +1,33 @@
+#include "RoboCatPCH.h"
+
+void AckRange::Write( OutputMemoryBitStream& inOutputStream ) const
+{
+ inOutputStream.Write( mStart );
+ bool hasCount = mCount > 1;
+ inOutputStream.Write( hasCount );
+ if( hasCount )
+ {
+ //most you can ack is 255...
+ uint32_t countMinusOne = mCount - 1;
+ uint8_t countToAck = countMinusOne > 255 ? 255 : static_cast< uint8_t >( countMinusOne );
+ inOutputStream.Write( countToAck );
+ }
+}
+
+void AckRange::Read( InputMemoryBitStream& inInputStream )
+{
+ inInputStream.Read( mStart );
+ bool hasCount;
+ inInputStream.Read( hasCount );
+ if( hasCount )
+ {
+ uint8_t countMinusOne;
+ inInputStream.Read( countMinusOne );
+ mCount = countMinusOne + 1;
+ }
+ else
+ {
+ //default!
+ mCount = 1;
+ }
+}
\ No newline at end of file
diff --git a/RoboCat/Src/DeliveryNotificationManager.cpp b/RoboCat/Src/DeliveryNotificationManager.cpp
new file mode 100644
index 00000000..ea85baa9
--- /dev/null
+++ b/RoboCat/Src/DeliveryNotificationManager.cpp
@@ -0,0 +1,209 @@
+#include "RoboCatPCH.h"
+
+namespace
+{
+ const float kDelayBeforeAckTimeout = 0.5f;
+}
+
+DeliveryNotificationManager::DeliveryNotificationManager( bool inShouldSendAcks, bool inShouldProcessAcks ) :
+mNextOutgoingSequenceNumber( 0 ),
+mNextExpectedSequenceNumber( 0 ),
+//everybody starts at 0...
+mShouldSendAcks( inShouldSendAcks ),
+mShouldProcessAcks( inShouldProcessAcks ),
+mDeliveredPacketCount( 0 ),
+mDroppedPacketCount( 0 ),
+mDispatchedPacketCount( 0 )
+{
+}
+
+
+//we're going away- log how well we did...
+DeliveryNotificationManager::~DeliveryNotificationManager()
+{
+ LOG( "DNM destructor. Delivery rate %d%%, Drop rate %d%%",
+ ( 100 * mDeliveredPacketCount ) / mDispatchedPacketCount,
+ ( 100 * mDroppedPacketCount ) / mDispatchedPacketCount );
+}
+
+
+
+InFlightPacket* DeliveryNotificationManager::WriteSequenceNumber( OutputMemoryBitStream& inOutputStream )
+{
+ //write the sequence number, but also create an inflight packet for this...
+ PacketSequenceNumber sequenceNumber = mNextOutgoingSequenceNumber++;
+ inOutputStream.Write( sequenceNumber );
+
+ ++mDispatchedPacketCount;
+
+ if( mShouldProcessAcks )
+ {
+ mInFlightPackets.emplace_back( sequenceNumber );
+
+ return &mInFlightPackets.back();
+ }
+ else
+ {
+ return nullptr;
+ }
+}
+
+void DeliveryNotificationManager::WriteAckData( OutputMemoryBitStream& inOutputStream )
+{
+ //we usually will only have one packet to ack
+ //so we'll follow that with a 0 bit if that's the case
+ //however, if we have more than 1, we'll make that 1 bit a 1 and then write 8 bits of how many packets
+ //we could do some statistical analysis to determine if this is the best strategy but we'll use it for now
+
+ //do we have any pending acks?
+ //if so, write a 1 bit and write the first range
+ //otherwise, write 0 bit
+ bool hasAcks = ( mPendingAcks.size() > 0 );
+
+ inOutputStream.Write( hasAcks );
+ if( hasAcks )
+ {
+ //note, we could write all the acks
+ mPendingAcks.front().Write( inOutputStream );
+ mPendingAcks.pop_front();
+ }
+}
+
+
+
+//returns wether to drop the packet- if sequence number is too low!
+bool DeliveryNotificationManager::ProcessSequenceNumber( InputMemoryBitStream& inInputStream )
+{
+ PacketSequenceNumber sequenceNumber;
+
+ inInputStream.Read( sequenceNumber );
+ if( sequenceNumber == mNextExpectedSequenceNumber )
+ {
+ mNextExpectedSequenceNumber = sequenceNumber + 1;
+ //is this what we expect? great, let's add an ack to our pending list
+ if( mShouldSendAcks )
+ {
+ AddPendingAck( sequenceNumber );
+ }
+ //and let's continue processing this packet...
+ return true;
+ }
+ //is the sequence number less than our current expected sequence? silently drop it.
+ //if this is due to wrapping around, we might fail to ack some packets that we should ack, but they'll get resent, so it's not a big deal
+ //note that we don't have to re-ack it because our system doesn't reuse sequence numbers
+ else if( sequenceNumber < mNextExpectedSequenceNumber )
+ {
+ return false;
+ }
+ else if( sequenceNumber > mNextExpectedSequenceNumber )
+ {
+ //we missed a lot of packets!
+ //so our next expected packet comes after this one...
+ mNextExpectedSequenceNumber = sequenceNumber + 1;
+ //we should nack the missing packets..this will happen automatically inside AddPendingAck because
+ //we're adding an unconsequitive ack
+ //and then we can ack this and process it
+ if( mShouldSendAcks )
+ {
+ AddPendingAck( sequenceNumber );
+ }
+ return true;
+ }
+
+ //drop packet if we couldn't even read sequence number!
+ return false;
+}
+
+
+//in each packet we can ack a range
+//anything in flight before the range will be considered nackd by the other side immediately
+void DeliveryNotificationManager::ProcessAcks( InputMemoryBitStream& inInputStream )
+{
+
+ bool hasAcks;
+ inInputStream.Read( hasAcks );
+ if( hasAcks )
+ {
+ AckRange ackRange;
+ ackRange.Read( inInputStream );
+
+ //for each InfilghtPacket with a sequence number less than the start, handle delivery failure...
+ PacketSequenceNumber nextAckdSequenceNumber = ackRange.GetStart();
+ uint32_t onePastAckdSequenceNumber = nextAckdSequenceNumber + ackRange.GetCount();
+ while( nextAckdSequenceNumber < onePastAckdSequenceNumber && !mInFlightPackets.empty() )
+ {
+ const auto& nextInFlightPacket = mInFlightPackets.front();
+ //if the packet has a lower sequence number, we didn't get an ack for it, so it probably wasn't delivered
+ PacketSequenceNumber nextInFlightPacketSequenceNumber = nextInFlightPacket.GetSequenceNumber();
+ if( nextInFlightPacketSequenceNumber < nextAckdSequenceNumber )
+ {
+ //copy this so we can remove it before handling the failure- we don't want to find it when checking for state
+ auto copyOfInFlightPacket = nextInFlightPacket;
+ mInFlightPackets.pop_front();
+ HandlePacketDeliveryFailure( copyOfInFlightPacket );
+ }
+ else if( nextInFlightPacketSequenceNumber == nextAckdSequenceNumber )
+ {
+ HandlePacketDeliverySuccess( nextInFlightPacket );
+ //received!
+ mInFlightPackets.pop_front();
+ //decrement count, advance nextAckdSequenceNumber
+ ++nextAckdSequenceNumber;
+ }
+ else if( nextInFlightPacketSequenceNumber > nextAckdSequenceNumber )
+ {
+ //we've already ackd some packets in here.
+ //keep this packet in flight, but keep going through the ack...
+ ++nextAckdSequenceNumber;
+ }
+ }
+ }
+}
+
+void DeliveryNotificationManager::ProcessTimedOutPackets()
+{
+ float timeoutTime = Timing::sInstance.GetTimef() - kDelayBeforeAckTimeout;
+
+ while( !mInFlightPackets.empty() )
+ {
+ const auto& nextInFlightPacket = mInFlightPackets.front();
+
+ //was this packet dispatched before the current time minus the timeout duration?
+ if( nextInFlightPacket.GetTimeDispatched() < timeoutTime )
+ {
+ //it failed! let us know about that
+ HandlePacketDeliveryFailure( nextInFlightPacket );
+ mInFlightPackets.pop_front();
+ }
+ else
+ {
+ //it wasn't, and packets are all in order by time here, so we know we don't have to check farther
+ break;
+ }
+ }
+}
+
+void DeliveryNotificationManager::AddPendingAck( PacketSequenceNumber inSequenceNumber )
+{
+ //if you don't have a range yet, or you can't correctly extend the final range with the sequence number,
+ //start a new range
+ if( mPendingAcks.size() == 0 || !mPendingAcks.back().ExtendIfShould( inSequenceNumber ) )
+ {
+ mPendingAcks.emplace_back( inSequenceNumber );
+ }
+}
+
+
+void DeliveryNotificationManager::HandlePacketDeliveryFailure( const InFlightPacket& inFlightPacket )
+{
+ ++mDroppedPacketCount;
+ inFlightPacket.HandleDeliveryFailure( this );
+
+}
+
+
+void DeliveryNotificationManager::HandlePacketDeliverySuccess( const InFlightPacket& inFlightPacket )
+{
+ ++mDeliveredPacketCount;
+ inFlightPacket.HandleDeliverySuccess( this );
+}
diff --git a/RoboCat/Src/InFlightPacket.cpp b/RoboCat/Src/InFlightPacket.cpp
new file mode 100644
index 00000000..251abe36
--- /dev/null
+++ b/RoboCat/Src/InFlightPacket.cpp
@@ -0,0 +1,25 @@
+#include "RoboCatPCH.h"
+
+InFlightPacket::InFlightPacket( PacketSequenceNumber inSequenceNumber ) :
+mSequenceNumber( inSequenceNumber ),
+mTimeDispatched( Timing::sInstance.GetTimef() )
+{
+ //null out other transmision data params...
+}
+
+
+void InFlightPacket::HandleDeliveryFailure( DeliveryNotificationManager* inDeliveryNotificationManager ) const
+{
+ for( const auto& pair : mTransmissionDataMap )
+ {
+ pair.second->HandleDeliveryFailure( inDeliveryNotificationManager );
+ }
+}
+
+void InFlightPacket::HandleDeliverySuccess( DeliveryNotificationManager* inDeliveryNotificationManager ) const
+{
+ for( const auto& pair : mTransmissionDataMap )
+ {
+ pair.second->HandleDeliverySuccess( inDeliveryNotificationManager );
+ }
+}
\ No newline at end of file
diff --git a/RoboCat/Src/Main.cpp b/RoboCat/Src/Main.cpp
index f8c6c7d0..6b7185f4 100644
--- a/RoboCat/Src/Main.cpp
+++ b/RoboCat/Src/Main.cpp
@@ -1,14 +1,664 @@
#include "RoboCatPCH.h"
+#include "allegro5.h"
+#include "allegro_primitives.h"
+#include "allegro_color.h"
+#include "RectangleObject.h"
+#include "RainParticle.h"
+#include "CircleClass.h"
+#include "math.h"
#include
#include
#include
#include
+#include
+#include
+
+using namespace std;
+int windowWidth = 800;
+int windowHeight = 600;
+int FRAMERATE = 16;
#if _WIN32
+class Packet
+{
+public:
+ Packet();
+ Packet(double stamp, char* buffer, int byteCount);
+ ~Packet();
+ double timeStamp;
+ char* mBufferPtr;
+ int mByteCount;
+private:
+
+};
+Packet::Packet(double stamp, char* buffer, int byteCount) {
+ timeStamp = stamp;
+ mBufferPtr = buffer;
+ mByteCount = byteCount;
+};
+
+Packet::Packet()
+{
+ timeStamp = 0;
+ mBufferPtr = new char[4096];
+ mByteCount = 4096;
+}
+
+Packet::~Packet()
+{
+}
+
+
+void ThrowSocketError(std::string errorMsg)
+{
+ SocketUtil::ReportError(errorMsg.c_str());
+ ExitProcess(1); // kill
+}
+
+ALLEGRO_TIMER* GiveMeATimer(float incr)
+{
+ ALLEGRO_TIMER* timer = al_create_timer(incr);
+ if (timer == nullptr)
+ {
+ ERROR;
+ }
+ al_start_timer(timer);
+ return timer;
+}
+
+
+TCPSocketPtr StartServer()
+{
+ TCPSocketPtr listeningSocket = SocketUtil::CreateTCPSocket(SocketAddressFamily::INET);
+ if (listeningSocket == nullptr)
+ ThrowSocketError("Could not create socket: NullPtr");
+
+ SocketAddressPtr addressPtr = SocketAddressFactory::CreateIPv4FromString("0.0.0.0:8080"); // a hard-coded port to bind to
+ if (addressPtr == nullptr)
+ ThrowSocketError("Could not create server address");
+
+ bool bindError = listeningSocket->Bind(*addressPtr) != NO_ERROR; // bind the socket and check if there is an error
+ if (bindError)
+ ThrowSocketError("Could not bind to address");
+
+ bool listenError = listeningSocket->Listen() != NO_ERROR;
+ if (listenError)
+ ThrowSocketError("Listen Error");
+
+ LOG("%s", "Waiting to accept connections...");
+
+ SocketAddress incomingAddress;
+ TCPSocketPtr incomingSocket = listeningSocket->Accept(incomingAddress);
+ while (incomingSocket == nullptr) // if it didnt work the first time, try again
+ {
+ incomingSocket = listeningSocket->Accept(incomingAddress);
+ }
+ LOG("Request accepted from: %s", incomingAddress.ToString().c_str());
+
+ return incomingSocket;
+}
+
+TCPSocketPtr StartClientConnection()
+{
+ TCPSocketPtr clientSocket = SocketUtil::CreateTCPSocket(SocketAddressFamily::INET);
+ if (clientSocket == nullptr)
+ ThrowSocketError("Could not create client socket");
+
+ std::string address = StringUtils::Sprintf("127.0.0.1:8081");
+ SocketAddressPtr clientAddress = SocketAddressFactory::CreateIPv4FromString(address.c_str());
+ if (clientAddress == nullptr)
+ ThrowSocketError("Creating client address");
+
+ if (clientSocket->Bind(*clientAddress) != NO_ERROR)
+ ThrowSocketError("Binding Client Socket");
+
+ SocketAddressPtr serverAddress = SocketAddressFactory::CreateIPv4FromString("127.0.0.1:8080");
+ if (serverAddress == nullptr)
+ ThrowSocketError("Creating server address");
+
+ if (clientSocket->Connect(*serverAddress) != NO_ERROR)
+ ThrowSocketError("Connecting to server");
+
+ return clientSocket;
+}
+
+void BradsTotallyOriginalServer()
+{
+ TCPSocketPtr incomingSocket = StartServer();
+ incomingSocket->SetNonBlockingMode(true);
+
+ //Timer
+ ALLEGRO_TIMER* timer = GiveMeATimer(0.001);
+ double currentTime = al_get_timer_count(timer);
+ float lastTime = 0;
+ float deltaTime = 0;
+
+ // Game Loop Variables
+ bool exit = false;
+ const int numObjects = 1;
+
+ //Greate GameObjects
+ CircleClass greenCircle[numObjects];
+ for (int j = 0; j < numObjects; j++)
+ {
+ greenCircle[j] = CircleClass("serverPlayer", windowWidth, windowHeight, 100, 100, 20);
+ }
+
+ const int numDroplets = 10;
+ RainParticle rain[numDroplets];
+ for (int k = 0; k < numDroplets; k++)
+ {
+ rain[k] = RainParticle(windowWidth, windowHeight, rand() % windowWidth + 1, rand() % windowHeight + 1, rand() % 5 + 5, 0, 0, rand() % 254 + 1, rand() % 254 + 1);
+ }
+
+ RectangleObject inObjects[numObjects];
+ list < RectangleObject* > clientProjectiles;
+ list < CircleClass* > serverProjectiles;
+
+
+ ALLEGRO_DISPLAY* display;
+ display = al_create_display(windowWidth, windowHeight);
+
+ ALLEGRO_KEYBOARD_STATE keyState;
+ ALLEGRO_EVENT_QUEUE* eventQueue = al_create_event_queue();
+ al_register_event_source(eventQueue, al_get_keyboard_event_source());
+
+ int xAxis = 0;
+ int yAxis = 0;
+
+ bool xMove = true;
+ bool yMove = true;
+
+ int projShot = 0;
+
+ list packets = {};
+ DeliveryNotificationManager deliveryManager(true, true);
+ list deadRects = {};
+ list deadCircles = {};
+
+ while (!exit) //GameLoop
+ {
+ //Timer
+ currentTime = al_get_timer_count(timer);
+ deltaTime = currentTime - lastTime;
+ lastTime = currentTime;
+
+ //send Data
+ OutputMemoryBitStream oStream = OutputMemoryBitStream();
+
+ deliveryManager.WriteState(oStream);
+ for (int i = 0; i < numObjects; i++)
+ {
+ greenCircle[i].Write(oStream);
+ }
+
+ for (int j = 0; j < numDroplets; j++)
+ {
+ rain[j].Write(oStream);
+ }
+
+ int projectileCount = serverProjectiles.size();
+ oStream.Write(projectileCount);
+ for each (CircleClass * projectile in serverProjectiles)
+ {
+ projectile->Write(oStream);
+ }
+
+ incomingSocket->Send(oStream.GetBufferPtr(), oStream.GetByteLength());
+
+ //recieve Data
+ char* buffer = new char[4096];
+ int32_t bytesReceived = int32_t(); // get username
+
+ bytesReceived = incomingSocket->Receive(buffer, 4096);
+ if (bytesReceived != -10035)
+ {
+ double stampTime = al_get_timer_count(timer);
+ int randomSin = rand() % 2;
+ if (randomSin == 0)
+ {
+ stampTime += rand() % 200 + 1; // jitter
+ }
+ else
+ {
+ stampTime -= rand() % 200 + 1; // jitter
+ }
+ stampTime += 1000; //latency
+ Packet* pack = new Packet(stampTime, buffer, 4096);
+ if (!((rand() % 100 + 0) < 5)) //5% packet loss
+ packets.push_back(pack);
+ if (packets.size() > 1)
+ packets.sort([](const Packet* a, const Packet* b) { return a->timeStamp < b->timeStamp; });
+
+ }
+ if (!packets.empty())
+ {
+ bool processOrNot = false;
+
+ processOrNot = packets.front()->timeStamp < al_get_timer_count(timer);
+
+ while (processOrNot) // maybe while
+ {
+ InputMemoryBitStream packStream(packets.front()->mBufferPtr, 4096);
+
+ deliveryManager.ReadAndProcessState(packStream);
+ deliveryManager.ProcessTimedOutPackets();
+
+ for (int i = 0; i < numObjects; i++)
+ {
+ inObjects[i].Read(packStream);
+ }
+ int clientProjectilesCount;
+ packStream.Read(clientProjectilesCount);
+ clientProjectiles.clear();
+ for (int i = 0; i < clientProjectilesCount; i++)
+ {
+ RectangleObject* temp = new RectangleObject();
+ temp->Read(packStream);
+ clientProjectiles.push_back(temp);
+ }
+ if(!packets.empty())
+ packets.pop_front();
+
+ //process timed out acks
+ if (!packets.empty())
+ processOrNot = packets.front()->timeStamp < al_get_timer_count(timer);
+ else
+ processOrNot = false;
+
+ }
+ }
+
+
+ ALLEGRO_EVENT events;
+
+ al_get_next_event(eventQueue, &events);
+ if (events.type == ALLEGRO_EVENT_KEY_UP)
+ {
+ switch (events.keyboard.keycode)
+ {
+ case ALLEGRO_KEY_ESCAPE:
+ exit = true;
+ case ALLEGRO_KEY_UP:
+ yMove = false;
+ yAxis = 0;
+ case ALLEGRO_KEY_DOWN:
+ yMove = false;
+ yAxis = 0;
+ case ALLEGRO_KEY_RIGHT:
+ xAxis = 0;
+ xMove = false;
+ case ALLEGRO_KEY_LEFT:
+ xAxis = 0;
+ xMove = false;
+ }
+ }
+ if (events.type == ALLEGRO_EVENT_KEY_DOWN)
+ {
+ al_get_keyboard_state(&keyState);
+ if (al_key_down(&keyState, ALLEGRO_KEY_UP))
+ {
+ yMove = true;
+ yAxis = -1;
+ }
+ if (al_key_down(&keyState, ALLEGRO_KEY_DOWN))
+ {
+ yMove = true;
+ yAxis = 1;
+ }
+ if (al_key_down(&keyState, ALLEGRO_KEY_RIGHT))
+ {
+ xAxis = 1;
+ xMove = true;
+ }
+ if (al_key_down(&keyState, ALLEGRO_KEY_LEFT))
+ {
+ xAxis = -1;
+ xMove = true;
+ }
+ if (al_key_down(&keyState, ALLEGRO_KEY_SPACE))
+ {
+ ++projShot;
+ CircleClass* proj = new CircleClass(to_string(projShot), windowWidth, windowHeight, greenCircle[0].position[0], greenCircle[0].position[1], 10);
+ serverProjectiles.push_back(proj);
+ }
+ }
+
+ for (int i = 0; i < numObjects; i++)
+ {
+ if (xMove || yMove)
+ {
+ greenCircle[i].UpdatePos(xAxis, yAxis);
+ }
+ inObjects[i].Draw();
+ greenCircle[i].Draw();
+ }
+
+ /*if (!deadRects.empty())
+ {
+ for (RectangleObject* deadRect : deadRects)
+ {
+ if(!clientProjectiles.empty())
+ for (RectangleObject* liveRect : clientProjectiles)
+ {
+ if (deadRect == liveRect)
+ {
+ cout << "remove" << endl;
+ RectangleObject* temp = deadRect;
+ clientProjectiles.remove(temp);
+ deadRects.remove(temp);
+
+ if (!clientProjectiles.empty())
+ clientProjectiles.front();
+
+ if (!deadRects.empty())
+ deadRects.front();
+
+ cout << "remove worked at least 1 time" << endl;
+ delete[] temp;
+ break;
+ }
+ }
+ }
+ }*/
+
+ for each (RectangleObject * projectile in clientProjectiles)
+ {
+ projectile->Draw();
+ }
+ /*while (deadCircles.size() > 0)
+ {
+ for each (CircleClass * deadCircle in deadCircles)
+ {
+ for each (CircleClass * liveRect in serverProjectiles)
+ {
+ if (deadCircle == liveRect)
+ {
+ serverProjectiles.remove(deadCircle);
+ deadCircles.remove(deadCircle);
+ }
+ }
+ }
+ }*/
+ for (CircleClass * projectile: serverProjectiles)
+ {
+ projectile->UpdatePos(0, -1);
+ projectile->Draw();
+ /*for (RectangleObject* rects: clientProjectiles)
+ {
+ int xDist = rects->xPos - projectile->position[0];
+ int yDist = rects->yPos - projectile->position[1];
+ float dist = sqrt((xDist*xDist) + (yDist * yDist));
+ if (dist < 1000)
+ {
+ cout << "kill eachother" << endl;
+ deadRects.push_back(rects);
+ deadCircles.push_back(projectile);
+ cout << "kill eachother worked" << endl;
+ break;
+ }
+ }*/
+ }
+ for (int j = 0; j < numDroplets; j++)
+ {
+ rain[j].UpdatePos(1, 3);
+ rain[j].Draw();
+ }
+ al_flip_display();
+ al_clear_to_color(al_map_rgb(0,0,0));
+
+ //wait for the frame to end
+ int currentPeriod = al_get_timer_count(timer) - currentTime;
+ while (currentPeriod < FRAMERATE)
+ {
+ currentPeriod = al_get_timer_count(timer) - currentTime;
+ }
+ if (currentTime > 600000)
+ al_set_timer_count(timer, 0);
+ }
+ al_destroy_display(display);
+}
+
+void BradsLessOriginalClient()
+{
+ TCPSocketPtr clientSocket = StartClientConnection();
+ clientSocket->SetNonBlockingMode(true);
+
+ //timer
+ ALLEGRO_TIMER* timer = GiveMeATimer(0.001);
+ double cTime = al_get_timer_count(timer);
+ float lastTime = 0;
+ float deltaTime = 0;
+
+ bool exit = false;
+ const int numObjects = 1;
+
+ //create Game Objects
+ RectangleObject outRectangles[numObjects];
+
+ for (int j = 0; j < numObjects; j++)
+ {
+ outRectangles[j] = RectangleObject("clientPlayer", windowWidth, windowHeight, 20, 20, rand() % windowWidth + 1, rand() % windowHeight + 1);
+ }
+
+ // Read In Objects
+ CircleClass inObjects[numObjects];
+
+ list < RectangleObject* > clientProjectiles = {};
+ list < CircleClass* > serverProjectiles = {};
+
+ const int numDroplets = 10;
+ RainParticle rain[numDroplets];
+
+ // Main Game Loop
+ ALLEGRO_DISPLAY* display;
+ display = al_create_display(windowWidth, windowHeight);
+
+ ALLEGRO_KEYBOARD_STATE keyState;
+ ALLEGRO_EVENT_QUEUE* eventQueue = al_create_event_queue();
+ al_register_event_source(eventQueue, al_get_keyboard_event_source());
+
+ int xAxis = 0;
+ int yAxis = 0;
+
+ bool xMove = true;
+ bool yMove = true;
+
+ int projShot = 0;
+
+ list packets = {};
+ DeliveryNotificationManager deliveryManager(true, true);
+ while (!exit) //GameLoop
+ {
+ //Timer
+ cTime = al_get_timer_count(timer);
+ deltaTime = cTime - lastTime;
+ lastTime = cTime;
+
+ //send Data
+ OutputMemoryBitStream oStream = OutputMemoryBitStream();
+
+ deliveryManager.WriteState(oStream);
+
+ for (int i = 0; i < numObjects; i++)
+ {
+ outRectangles[i].Write(oStream);
+ }
+
+ int projectileCount = clientProjectiles.size();
+ oStream.Write(projectileCount);
+ for each (RectangleObject* projectile in clientProjectiles)
+ {
+ projectile->Write(oStream);
+ }
+ clientSocket->Send(oStream.GetBufferPtr(), oStream.GetByteLength());
+
+ // Recieve data
+ char* buffer = new char[4096];
+ int32_t bytesReceived = int32_t();
+ bytesReceived = clientSocket->Receive(buffer, 4096);
+
+ if (bytesReceived != -10035)
+ {
+ double stampTime = al_get_timer_count(timer);
+ int randomSin = rand() % 2;
+ if (randomSin == 0)
+ {
+ stampTime += rand() % 200 + 1; // jitter
+ }
+ else
+ {
+ stampTime -= rand() % 200 + 1; // jitter
+ }
+ stampTime += 1000; //latency
+ Packet* pack = new Packet(stampTime, buffer, 4096);
+ if(!(rand()%100+0 < 5)) //5% packet loss
+ packets.push_back(pack);
+ if (packets.size() > 1)
+ packets.sort([](const Packet* a, const Packet* b) { return a->timeStamp < b->timeStamp; });
+ }
+
+ if (!packets.empty())
+ {
+ bool processOrNot = false;
+
+ processOrNot = packets.front()->timeStamp < al_get_timer_count(timer);
+
+ while(processOrNot) // maybe while
+ {
+
+ InputMemoryBitStream packStream(packets.front()->mBufferPtr, 4096);
+
+ deliveryManager.ReadAndProcessState(packStream);
+ deliveryManager.ProcessTimedOutPackets();
+
+
+ for (int i = 0; i < numObjects; i++)
+ {
+ inObjects[i].Read(packStream);
+ }
+ for (int j = 0; j < numDroplets; j++)
+ {
+ rain[j].Read(packStream);
+ }
+ int serverProjectilesCount;
+ packStream.Read(serverProjectilesCount);
+ serverProjectiles.clear();
+ for (int i = 0; i < serverProjectilesCount; i++)
+ {
+ CircleClass* temp = new CircleClass();
+ temp->Read(packStream);
+ serverProjectiles.emplace_back(temp);
+ }
+ packets.pop_front();
+ //readack
+ if(!packets.empty())
+ processOrNot = packets.front()->timeStamp < al_get_timer_count(timer);
+ else
+ processOrNot = false;
+
+
+ }
+
+ }
+
+ ALLEGRO_EVENT events;
+
+ al_get_next_event(eventQueue, &events);
+
+ if (events.type == ALLEGRO_EVENT_KEY_UP)
+ {
+ switch (events.keyboard.keycode)
+ {
+ case ALLEGRO_KEY_ESCAPE:
+ exit = true;
+ case ALLEGRO_KEY_UP:
+ yMove = false;
+ yAxis = 0;
+ case ALLEGRO_KEY_DOWN:
+ yMove = false;
+ yAxis = 0;
+ case ALLEGRO_KEY_RIGHT:
+ xAxis = 0;
+ xMove = false;
+ case ALLEGRO_KEY_LEFT:
+ xAxis = 0;
+ xMove = false;
+ }
+ }
+ if (events.type == ALLEGRO_EVENT_KEY_DOWN)
+ {
+ al_get_keyboard_state(&keyState);
+ if (al_key_down(&keyState, ALLEGRO_KEY_UP))
+ {
+ yMove = true;
+ yAxis = -1;
+ }
+ if (al_key_down(&keyState, ALLEGRO_KEY_DOWN))
+ {
+ yMove = true;
+ yAxis = 1;
+ }
+ if (al_key_down(&keyState, ALLEGRO_KEY_RIGHT))
+ {
+ xAxis = 1;
+ xMove = true;
+ }
+ if (al_key_down(&keyState, ALLEGRO_KEY_LEFT))
+ {
+ xAxis = -1;
+ xMove = true;
+ }
+ if (al_key_down(&keyState, ALLEGRO_KEY_SPACE))
+ {
+ ++projShot;
+ RectangleObject* proj = new RectangleObject(to_string(projShot), windowWidth, windowHeight, 10, 10, outRectangles[0].xPos, outRectangles[0].yPos);
+ clientProjectiles.push_back(proj);
+ }
+ }
+
+ for (int i = 0; i < numObjects; i++)
+ {
+ if (xMove || yMove)
+ {
+ outRectangles[i].UpdatePos(xAxis, yAxis);
+ }
+ outRectangles[i].Draw();
+ inObjects[i].Draw();
+ }
+ for each (CircleClass * projectile in serverProjectiles)
+ {
+ projectile->Draw();
+ }
+ for each (RectangleObject* projectile in clientProjectiles)
+ {
+ projectile->UpdatePos(0, 1);
+ projectile->Draw();
+ }
+
+ for (int j = 0; j < numDroplets; j++)
+ {
+ rain[j].Draw();
+ }
+ al_flip_display();
+ al_clear_to_color(al_map_rgb(0, 0, 0));
+
+ //wait for the frame to end
+ int currentPeriod = al_get_timer_count(timer) - cTime;
+ while (currentPeriod < FRAMERATE)
+ {
+ currentPeriod = al_get_timer_count(timer) - cTime;
+ }
+ if (cTime > 600000)
+ al_set_timer_count(timer, 0);
+ }
+ al_destroy_display(display);
+}
+
int main(int argc, const char** argv)
{
UNREFERENCED_PARAMETER(argc);
@@ -22,32 +672,26 @@ int main(int argc, const char** argv)
__argv = argv;
#endif
- SocketUtil::StaticInit();
+ SocketUtil::StaticInit();
+ if (!al_init()) return -1;
+
+ al_init_primitives_addon();
- OutputWindow win;
+ al_install_keyboard();
- std::thread t([&win]()
- {
- int msgNo = 1;
- while (true)
- {
- std::this_thread::sleep_for(std::chrono::milliseconds(250));
- std::string msgIn("~~~auto message~~~");
- std::stringstream ss(msgIn);
- ss << msgNo;
- win.Write(ss.str());
- msgNo++;
- }
- });
+ srand(time(NULL));
- while (true)
+ bool isServer = StringUtils::GetCommandLineArg(1) == "server"; // check if the command on the executable is 'server'
+ if (isServer)
{
- std::string input;
- std::getline(std::cin, input);
- win.WriteFromStdin(input);
+ BradsTotallyOriginalServer();
+ }
+ else
+ {
+ BradsLessOriginalClient();
}
- SocketUtil::CleanUp();
-
+ SocketUtil::CleanUp(); // socket cleanup
return 0;
}
+
diff --git a/RoboCat/Src/MemoryBitStream.cpp b/RoboCat/Src/MemoryBitStream.cpp
new file mode 100644
index 00000000..39958ad7
--- /dev/null
+++ b/RoboCat/Src/MemoryBitStream.cpp
@@ -0,0 +1,171 @@
+#include "RoboCatPCH.h"
+
+void OutputMemoryBitStream::WriteBits( uint8_t inData,
+ uint32_t inBitCount )
+{
+ uint32_t nextBitHead = mBitHead + static_cast< uint32_t >( inBitCount );
+
+ if( nextBitHead > mBitCapacity )
+ {
+ ReallocBuffer( std::max( mBitCapacity * 2, nextBitHead ) );
+ }
+
+ //calculate the byteOffset into our buffer
+ //by dividing the head by 8
+ //and the bitOffset by taking the last 3 bits
+ uint32_t byteOffset = mBitHead >> 3;
+ uint32_t bitOffset = mBitHead & 0x7;
+
+ uint8_t currentMask = ~( 0xff << bitOffset );
+ mBuffer[ byteOffset ] = ( mBuffer[ byteOffset ] & currentMask ) | ( inData << bitOffset );
+
+ //calculate how many bits were not yet used in
+ //our target byte in the buffer
+ uint32_t bitsFreeThisByte = 8 - bitOffset;
+
+ //if we needed more than that, carry to the next byte
+ if( bitsFreeThisByte < inBitCount )
+ {
+ //we need another byte
+ mBuffer[ byteOffset + 1 ] = inData >> bitsFreeThisByte;
+ }
+
+ mBitHead = nextBitHead;
+}
+
+void OutputMemoryBitStream::WriteBits( const void* inData, uint32_t inBitCount )
+{
+ const char* srcByte = static_cast< const char* >( inData );
+ //write all the bytes
+ while( inBitCount > 8 )
+ {
+ WriteBits( *srcByte, 8 );
+ ++srcByte;
+ inBitCount -= 8;
+ }
+ //write anything left
+ if( inBitCount > 0 )
+ {
+ WriteBits( *srcByte, inBitCount );
+ }
+}
+
+void OutputMemoryBitStream::Write( const Vector3& inVector )
+{
+ Write( inVector.mX );
+ Write( inVector.mY );
+ Write( inVector.mZ );
+}
+
+void InputMemoryBitStream::Read( Vector3& outVector )
+{
+ Read( outVector.mX );
+ Read( outVector.mY );
+ Read( outVector.mZ );
+}
+
+void OutputMemoryBitStream::Write( const Quaternion& inQuat )
+{
+ float precision = ( 2.f / 65535.f );
+ Write( ConvertToFixed( inQuat.mX, -1.f, precision ), 16 );
+ Write( ConvertToFixed( inQuat.mY, -1.f, precision ), 16 );
+ Write( ConvertToFixed( inQuat.mZ, -1.f, precision ), 16 );
+ Write( inQuat.mW < 0 );
+}
+
+
+
+void OutputMemoryBitStream::ReallocBuffer( uint32_t inNewBitLength )
+{
+ if( mBuffer == nullptr )
+ {
+ //just need to memset on first allocation
+ mBuffer = static_cast( std::malloc( inNewBitLength >> 3 ) );
+ memset( mBuffer, 0, inNewBitLength >> 3 );
+ }
+ else
+ {
+ //need to memset, then copy the buffer
+ char* tempBuffer = static_cast( std::malloc( inNewBitLength >> 3 ) );
+ memset( tempBuffer, 0, inNewBitLength >> 3 );
+ memcpy( tempBuffer, mBuffer, mBitCapacity >> 3 );
+ std::free( mBuffer );
+ mBuffer = tempBuffer;
+ }
+
+ //handle realloc failure
+ //...
+ mBitCapacity = inNewBitLength;
+}
+
+
+void test1()
+{
+ OutputMemoryBitStream mbs;
+
+ mbs.WriteBits( 11, 5 );
+ mbs.WriteBits( 52, 6 );
+}
+
+void InputMemoryBitStream::ReadBits( uint8_t& outData, uint32_t inBitCount )
+{
+ uint32_t byteOffset = mBitHead >> 3;
+ uint32_t bitOffset = mBitHead & 0x7;
+
+ outData = static_cast< uint8_t >( mBuffer[ byteOffset ] ) >> bitOffset;
+
+ uint32_t bitsFreeThisByte = 8 - bitOffset;
+ if( bitsFreeThisByte < inBitCount )
+ {
+ //we need another byte
+ outData |= static_cast< uint8_t >( mBuffer[ byteOffset + 1 ] ) << bitsFreeThisByte;
+ }
+
+ //don't forget a mask so that we only read the bit we wanted...
+ outData &= ( ~( 0x00ff << inBitCount ) );
+
+ mBitHead += inBitCount;
+}
+
+void InputMemoryBitStream::ReadBits( void* outData, uint32_t inBitCount )
+{
+ uint8_t* destByte = reinterpret_cast< uint8_t* >( outData );
+ //write all the bytes
+ while( inBitCount > 8 )
+ {
+ ReadBits( *destByte, 8 );
+ ++destByte;
+ inBitCount -= 8;
+ }
+ //write anything left
+ if( inBitCount > 0 )
+ {
+ ReadBits( *destByte, inBitCount );
+ }
+}
+
+void InputMemoryBitStream::Read( Quaternion& outQuat )
+{
+ float precision = ( 2.f / 65535.f );
+
+ uint32_t f = 0;
+
+ Read( f, 16 );
+ outQuat.mX = ConvertFromFixed( f, -1.f, precision );
+ Read( f, 16 );
+ outQuat.mY = ConvertFromFixed( f, -1.f, precision );
+ Read( f, 16 );
+ outQuat.mZ = ConvertFromFixed( f, -1.f, precision );
+
+ outQuat.mW = sqrtf( 1.f -
+ (outQuat.mX * outQuat.mX +
+ outQuat.mY * outQuat.mY +
+ outQuat.mZ * outQuat.mZ));
+ bool isNegative;
+ Read( isNegative );
+
+ if( isNegative )
+ {
+ outQuat.mW *= -1;
+ }
+}
diff --git a/RoboCat/Src/TCPSocket.cpp b/RoboCat/Src/TCPSocket.cpp
index 494f776c..0cfe2b20 100644
--- a/RoboCat/Src/TCPSocket.cpp
+++ b/RoboCat/Src/TCPSocket.cpp
@@ -55,8 +55,12 @@ int32_t TCPSocket::Receive( void* inData, size_t inLen )
int bytesReceivedCount = recv( mSocket, static_cast< char* >( inData ), inLen, 0 );
if( bytesReceivedCount < 0 )
{
- SocketUtil::ReportError( "TCPSocket::Receive" );
- return -SocketUtil::GetLastError();
+ int error = -SocketUtil::GetLastError();
+ if (error != -10035)
+ {
+ SocketUtil::ReportError("TCPSocket::Receive");
+ }
+ return error;
}
return bytesReceivedCount;
}
diff --git a/RoboCat/Src/Timing.cpp b/RoboCat/Src/Timing.cpp
new file mode 100644
index 00000000..9a8be86c
--- /dev/null
+++ b/RoboCat/Src/Timing.cpp
@@ -0,0 +1,62 @@
+#include "RoboCatPCH.h"
+
+
+#if !_WIN32
+ #include
+ using namespace std::chrono;
+#endif
+
+Timing Timing::sInstance;
+
+namespace
+{
+#if _WIN32
+ LARGE_INTEGER sStartTime = { 0 };
+#else
+ high_resolution_clock::time_point sStartTime;
+#endif
+}
+
+Timing::Timing()
+{
+#if _WIN32
+ LARGE_INTEGER perfFreq;
+ QueryPerformanceFrequency( &perfFreq );
+ mPerfCountDuration = 1.0 / perfFreq.QuadPart;
+
+ QueryPerformanceCounter( &sStartTime );
+
+ mLastFrameStartTime = GetTime();
+#else
+ sStartTime = high_resolution_clock::now();
+#endif
+}
+
+void Timing::Update()
+{
+
+ double currentTime = GetTime();
+
+ mDeltaTime = ( float ) ( currentTime - mLastFrameStartTime );
+
+ mLastFrameStartTime = currentTime;
+ mFrameStartTimef = static_cast< float > ( mLastFrameStartTime );
+
+}
+
+double Timing::GetTime() const
+{
+#if _WIN32
+ LARGE_INTEGER curTime, timeSinceStart;
+ QueryPerformanceCounter( &curTime );
+
+ timeSinceStart.QuadPart = curTime.QuadPart - sStartTime.QuadPart;
+
+ return timeSinceStart.QuadPart * mPerfCountDuration;
+#else
+ auto now = high_resolution_clock::now();
+ auto ms = duration_cast< milliseconds >( now - sStartTime ).count();
+ //a little uncool to then convert into a double just to go back, but oh well.
+ return static_cast< double >( ms ) / 1000;
+#endif
+}
\ No newline at end of file
diff --git a/RoboCat/packages.config b/RoboCat/packages.config
new file mode 100644
index 00000000..9ff6486d
--- /dev/null
+++ b/RoboCat/packages.config
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file