summaryrefslogtreecommitdiffhomepage
path: root/src/extras
diff options
context:
space:
mode:
authorRay <[email protected]>2021-05-31 11:43:56 +0200
committerRay <[email protected]>2021-05-31 11:43:56 +0200
commit9bd3d78374782b45d0d7f1a41f2b545d631dab33 (patch)
tree0f68089dd487d1b81038dd10647d7be4ca4c32d4 /src/extras
parent1c5de9721a8c9b9543044b461d4a9480c1d25b43 (diff)
downloadraylib-9bd3d78374782b45d0d7f1a41f2b545d631dab33.tar.gz
raylib-9bd3d78374782b45d0d7f1a41f2b545d631dab33.zip
WARNING: Moved some **extra** raylib libraries to /extras/ directory
Diffstat (limited to 'src/extras')
-rw-r--r--src/extras/easings.h263
-rw-r--r--src/extras/physac.h1988
-rw-r--r--src/extras/rmem.h739
-rw-r--r--src/extras/rnet.h2256
4 files changed, 5246 insertions, 0 deletions
diff --git a/src/extras/easings.h b/src/extras/easings.h
new file mode 100644
index 00000000..3441305f
--- /dev/null
+++ b/src/extras/easings.h
@@ -0,0 +1,263 @@
+/*******************************************************************************************
+*
+* raylib easings (header only file)
+*
+* Useful easing functions for values animation
+*
+* This header uses:
+* #define EASINGS_STATIC_INLINE // Inlines all functions code, so it runs faster.
+* // This requires lots of memory on system.
+* How to use:
+* The four inputs t,b,c,d are defined as follows:
+* t = current time (in any unit measure, but same unit as duration)
+* b = starting value to interpolate
+* c = the total change in value of b that needs to occur
+* d = total time it should take to complete (duration)
+*
+* Example:
+*
+* int currentTime = 0;
+* int duration = 100;
+* float startPositionX = 0.0f;
+* float finalPositionX = 30.0f;
+* float currentPositionX = startPositionX;
+*
+* while (currentPositionX < finalPositionX)
+* {
+* currentPositionX = EaseSineIn(currentTime, startPositionX, finalPositionX - startPositionX, duration);
+* currentTime++;
+* }
+*
+* A port of Robert Penner's easing equations to C (http://robertpenner.com/easing/)
+*
+* Robert Penner License
+* ---------------------------------------------------------------------------------
+* Open source under the BSD License.
+*
+* Copyright (c) 2001 Robert Penner. All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without modification,
+* are permitted provided that the following conditions are met:
+*
+* - Redistributions of source code must retain the above copyright notice,
+* this list of conditions and the following disclaimer.
+* - Redistributions in binary form must reproduce the above copyright notice,
+* this list of conditions and the following disclaimer in the documentation
+* and/or other materials provided with the distribution.
+* - Neither the name of the author nor the names of contributors may be used
+* to endorse or promote products derived from this software without specific
+* prior written permission.
+*
+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+* IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+* OF THE POSSIBILITY OF SUCH DAMAGE.
+* ---------------------------------------------------------------------------------
+*
+* Copyright (c) 2015 Ramon Santamaria
+*
+* This software is provided "as-is", without any express or implied warranty. In no event
+* will the authors be held liable for any damages arising from the use of this software.
+*
+* Permission is granted to anyone to use this software for any purpose, including commercial
+* applications, and to alter it and redistribute it freely, subject to the following restrictions:
+*
+* 1. The origin of this software must not be misrepresented; you must not claim that you
+* wrote the original software. If you use this software in a product, an acknowledgment
+* in the product documentation would be appreciated but is not required.
+*
+* 2. Altered source versions must be plainly marked as such, and must not be misrepresented
+* as being the original software.
+*
+* 3. This notice may not be removed or altered from any source distribution.
+*
+**********************************************************************************************/
+
+#ifndef EASINGS_H
+#define EASINGS_H
+
+#define EASINGS_STATIC_INLINE // NOTE: By default, compile functions as static inline
+
+#if defined(EASINGS_STATIC_INLINE)
+ #define EASEDEF static inline
+#else
+ #define EASEDEF extern
+#endif
+
+#include <math.h> // Required for: sinf(), cosf(), sqrtf(), powf()
+
+#ifndef PI
+ #define PI 3.14159265358979323846f //Required as PI is not always defined in math.h
+#endif
+
+#ifdef __cplusplus
+extern "C" { // Prevents name mangling of functions
+#endif
+
+// Linear Easing functions
+EASEDEF float EaseLinearNone(float t, float b, float c, float d) { return (c*t/d + b); }
+EASEDEF float EaseLinearIn(float t, float b, float c, float d) { return (c*t/d + b); }
+EASEDEF float EaseLinearOut(float t, float b, float c, float d) { return (c*t/d + b); }
+EASEDEF float EaseLinearInOut(float t,float b, float c, float d) { return (c*t/d + b); }
+
+// Sine Easing functions
+EASEDEF float EaseSineIn(float t, float b, float c, float d) { return (-c*cosf(t/d*(PI/2.0f)) + c + b); }
+EASEDEF float EaseSineOut(float t, float b, float c, float d) { return (c*sinf(t/d*(PI/2.0f)) + b); }
+EASEDEF float EaseSineInOut(float t, float b, float c, float d) { return (-c/2.0f*(cosf(PI*t/d) - 1.0f) + b); }
+
+// Circular Easing functions
+EASEDEF float EaseCircIn(float t, float b, float c, float d) { t /= d; return (-c*(sqrtf(1.0f - t*t) - 1.0f) + b); }
+EASEDEF float EaseCircOut(float t, float b, float c, float d) { t = t/d - 1.0f; return (c*sqrtf(1.0f - t*t) + b); }
+EASEDEF float EaseCircInOut(float t, float b, float c, float d)
+{
+ if ((t/=d/2.0f) < 1.0f) return (-c/2.0f*(sqrtf(1.0f - t*t) - 1.0f) + b);
+ t -= 2.0f; return (c/2.0f*(sqrtf(1.0f - t*t) + 1.0f) + b);
+}
+
+// Cubic Easing functions
+EASEDEF float EaseCubicIn(float t, float b, float c, float d) { t /= d; return (c*t*t*t + b); }
+EASEDEF float EaseCubicOut(float t, float b, float c, float d) { t = t/d - 1.0f; return (c*(t*t*t + 1.0f) + b); }
+EASEDEF float EaseCubicInOut(float t, float b, float c, float d)
+{
+ if ((t/=d/2.0f) < 1.0f) return (c/2.0f*t*t*t + b);
+ t -= 2.0f; return (c/2.0f*(t*t*t + 2.0f) + b);
+}
+
+// Quadratic Easing functions
+EASEDEF float EaseQuadIn(float t, float b, float c, float d) { t /= d; return (c*t*t + b); }
+EASEDEF float EaseQuadOut(float t, float b, float c, float d) { t /= d; return (-c*t*(t - 2.0f) + b); }
+EASEDEF float EaseQuadInOut(float t, float b, float c, float d)
+{
+ if ((t/=d/2) < 1) return (((c/2)*(t*t)) + b);
+ return (-c/2.0f*(((t - 1.0f)*(t - 3.0f)) - 1.0f) + b);
+}
+
+// Exponential Easing functions
+EASEDEF float EaseExpoIn(float t, float b, float c, float d) { return (t == 0.0f) ? b : (c*powf(2.0f, 10.0f*(t/d - 1.0f)) + b); }
+EASEDEF float EaseExpoOut(float t, float b, float c, float d) { return (t == d) ? (b + c) : (c*(-powf(2.0f, -10.0f*t/d) + 1.0f) + b); }
+EASEDEF float EaseExpoInOut(float t, float b, float c, float d)
+{
+ if (t == 0.0f) return b;
+ if (t == d) return (b + c);
+ if ((t/=d/2.0f) < 1.0f) return (c/2.0f*powf(2.0f, 10.0f*(t - 1.0f)) + b);
+
+ return (c/2.0f*(-powf(2.0f, -10.0f*(t - 1.0f)) + 2.0f) + b);
+}
+
+// Back Easing functions
+EASEDEF float EaseBackIn(float t, float b, float c, float d)
+{
+ float s = 1.70158f;
+ float postFix = t/=d;
+ return (c*(postFix)*t*((s + 1.0f)*t - s) + b);
+}
+
+EASEDEF float EaseBackOut(float t, float b, float c, float d)
+{
+ float s = 1.70158f;
+ t = t/d - 1.0f;
+ return (c*(t*t*((s + 1.0f)*t + s) + 1.0f) + b);
+}
+
+EASEDEF float EaseBackInOut(float t, float b, float c, float d)
+{
+ float s = 1.70158f;
+ if ((t/=d/2.0f) < 1.0f)
+ {
+ s *= 1.525f;
+ return (c/2.0f*(t*t*((s + 1.0f)*t - s)) + b);
+ }
+
+ float postFix = t-=2.0f;
+ s *= 1.525f;
+ return (c/2.0f*((postFix)*t*((s + 1.0f)*t + s) + 2.0f) + b);
+}
+
+// Bounce Easing functions
+EASEDEF float EaseBounceOut(float t, float b, float c, float d)
+{
+ if ((t/=d) < (1.0f/2.75f))
+ {
+ return (c*(7.5625f*t*t) + b);
+ }
+ else if (t < (2.0f/2.75f))
+ {
+ float postFix = t-=(1.5f/2.75f);
+ return (c*(7.5625f*(postFix)*t + 0.75f) + b);
+ }
+ else if (t < (2.5/2.75))
+ {
+ float postFix = t-=(2.25f/2.75f);
+ return (c*(7.5625f*(postFix)*t + 0.9375f) + b);
+ }
+ else
+ {
+ float postFix = t-=(2.625f/2.75f);
+ return (c*(7.5625f*(postFix)*t + 0.984375f) + b);
+ }
+}
+
+EASEDEF float EaseBounceIn(float t, float b, float c, float d) { return (c - EaseBounceOut(d - t, 0.0f, c, d) + b); }
+EASEDEF float EaseBounceInOut(float t, float b, float c, float d)
+{
+ if (t < d/2.0f) return (EaseBounceIn(t*2.0f, 0.0f, c, d)*0.5f + b);
+ else return (EaseBounceOut(t*2.0f - d, 0.0f, c, d)*0.5f + c*0.5f + b);
+}
+
+// Elastic Easing functions
+EASEDEF float EaseElasticIn(float t, float b, float c, float d)
+{
+ if (t == 0.0f) return b;
+ if ((t/=d) == 1.0f) return (b + c);
+
+ float p = d*0.3f;
+ float a = c;
+ float s = p/4.0f;
+ float postFix = a*powf(2.0f, 10.0f*(t-=1.0f));
+
+ return (-(postFix*sinf((t*d-s)*(2.0f*PI)/p )) + b);
+}
+
+EASEDEF float EaseElasticOut(float t, float b, float c, float d)
+{
+ if (t == 0.0f) return b;
+ if ((t/=d) == 1.0f) return (b + c);
+
+ float p = d*0.3f;
+ float a = c;
+ float s = p/4.0f;
+
+ return (a*powf(2.0f,-10.0f*t)*sinf((t*d-s)*(2.0f*PI)/p) + c + b);
+}
+
+EASEDEF float EaseElasticInOut(float t, float b, float c, float d)
+{
+ if (t == 0.0f) return b;
+ if ((t/=d/2.0f) == 2.0f) return (b + c);
+
+ float p = d*(0.3f*1.5f);
+ float a = c;
+ float s = p/4.0f;
+
+ if (t < 1.0f)
+ {
+ float postFix = a*powf(2.0f, 10.0f*(t-=1.0f));
+ return -0.5f*(postFix*sinf((t*d-s)*(2.0f*PI)/p)) + b;
+ }
+
+ float postFix = a*powf(2.0f, -10.0f*(t-=1.0f));
+
+ return (postFix*sinf((t*d-s)*(2.0f*PI)/p)*0.5f + c + b);
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // EASINGS_H
diff --git a/src/extras/physac.h b/src/extras/physac.h
new file mode 100644
index 00000000..676a9695
--- /dev/null
+++ b/src/extras/physac.h
@@ -0,0 +1,1988 @@
+/**********************************************************************************************
+*
+* Physac v1.1 - 2D Physics library for videogames
+*
+* DESCRIPTION:
+*
+* Physac is a small 2D physics engine written in pure C. The engine uses a fixed time-step thread loop
+* to simluate physics. A physics step contains the following phases: get collision information,
+* apply dynamics, collision solving and position correction. It uses a very simple struct for physic
+* bodies with a position vector to be used in any 3D rendering API.
+*
+* CONFIGURATION:
+*
+* #define PHYSAC_IMPLEMENTATION
+* Generates the implementation of the library into the included file.
+* If not defined, the library is in header only mode and can be included in other headers
+* or source files without problems. But only ONE file should hold the implementation.
+*
+* #define PHYSAC_STATIC (defined by default)
+* The generated implementation will stay private inside implementation file and all
+* internal symbols and functions will only be visible inside that file.
+*
+* #define PHYSAC_DEBUG
+* Show debug traces log messages about physic bodies creation/destruction, physic system errors,
+* some calculations results and NULL reference exceptions
+*
+* #define PHYSAC_DEFINE_VECTOR2_TYPE
+* Forces library to define struct Vector2 data type (float x; float y)
+*
+* #define PHYSAC_AVOID_TIMMING_SYSTEM
+* Disables internal timming system, used by UpdatePhysics() to launch timmed physic steps,
+* it allows just running UpdatePhysics() automatically on a separate thread at a desired time step.
+* In case physics steps update needs to be controlled by user with a custom timming mechanism,
+* just define this flag and the internal timming mechanism will be avoided, in that case,
+* timming libraries are neither required by the module.
+*
+* #define PHYSAC_MALLOC()
+* #define PHYSAC_CALLOC()
+* #define PHYSAC_FREE()
+* You can define your own malloc/free implementation replacing stdlib.h malloc()/free() functions.
+* Otherwise it will include stdlib.h and use the C standard library malloc()/free() function.
+*
+* COMPILATION:
+*
+* Use the following code to compile with GCC:
+* gcc -o $(NAME_PART).exe $(FILE_NAME) -s -static -lraylib -lopengl32 -lgdi32 -lwinmm -std=c99
+*
+* VERSIONS HISTORY:
+* 1.1 (20-Jan-2021) @raysan5: Library general revision
+* Removed threading system (up to the user)
+* Support MSVC C++ compilation using CLITERAL()
+* Review DEBUG mechanism for TRACELOG() and all TRACELOG() messages
+* Review internal variables/functions naming for consistency
+* Allow option to avoid internal timming system, to allow app manage the steps
+* 1.0 (12-Jun-2017) First release of the library
+*
+*
+* LICENSE: zlib/libpng
+*
+* Copyright (c) 2016-2021 Victor Fisac (@victorfisac) and Ramon Santamaria (@raysan5)
+*
+* This software is provided "as-is", without any express or implied warranty. In no event
+* will the authors be held liable for any damages arising from the use of this software.
+*
+* Permission is granted to anyone to use this software for any purpose, including commercial
+* applications, and to alter it and redistribute it freely, subject to the following restrictions:
+*
+* 1. The origin of this software must not be misrepresented; you must not claim that you
+* wrote the original software. If you use this software in a product, an acknowledgment
+* in the product documentation would be appreciated but is not required.
+*
+* 2. Altered source versions must be plainly marked as such, and must not be misrepresented
+* as being the original software.
+*
+* 3. This notice may not be removed or altered from any source distribution.
+*
+**********************************************************************************************/
+
+#if !defined(PHYSAC_H)
+#define PHYSAC_H
+
+#if defined(PHYSAC_STATIC)
+ #define PHYSACDEF static // Functions just visible to module including this file
+#else
+ #if defined(__cplusplus)
+ #define PHYSACDEF extern "C" // Functions visible from other files (no name mangling of functions in C++)
+ #else
+ #define PHYSACDEF extern // Functions visible from other files
+ #endif
+#endif
+
+// Allow custom memory allocators
+#ifndef PHYSAC_MALLOC
+ #define PHYSAC_MALLOC(size) malloc(size)
+#endif
+#ifndef PHYSAC_CALLOC
+ #define PHYSAC_CALLOC(size, n) calloc(size, n)
+#endif
+#ifndef PHYSAC_FREE
+ #define PHYSAC_FREE(ptr) free(ptr)
+#endif
+
+//----------------------------------------------------------------------------------
+// Defines and Macros
+//----------------------------------------------------------------------------------
+#define PHYSAC_MAX_BODIES 64 // Maximum number of physic bodies supported
+#define PHYSAC_MAX_MANIFOLDS 4096 // Maximum number of physic bodies interactions (64x64)
+#define PHYSAC_MAX_VERTICES 24 // Maximum number of vertex for polygons shapes
+#define PHYSAC_DEFAULT_CIRCLE_VERTICES 24 // Default number of vertices for circle shapes
+
+#define PHYSAC_COLLISION_ITERATIONS 100
+#define PHYSAC_PENETRATION_ALLOWANCE 0.05f
+#define PHYSAC_PENETRATION_CORRECTION 0.4f
+
+#define PHYSAC_PI 3.14159265358979323846f
+#define PHYSAC_DEG2RAD (PHYSAC_PI/180.0f)
+
+//----------------------------------------------------------------------------------
+// Data Types Structure Definition
+//----------------------------------------------------------------------------------
+#if defined(__STDC__) && __STDC_VERSION__ >= 199901L
+ #include <stdbool.h>
+#endif
+
+typedef enum PhysicsShapeType { PHYSICS_CIRCLE = 0, PHYSICS_POLYGON } PhysicsShapeType;
+
+// Previously defined to be used in PhysicsShape struct as circular dependencies
+typedef struct PhysicsBodyData *PhysicsBody;
+
+#if defined(PHYSAC_DEFINE_VECTOR2_TYPE)
+// Vector2 type
+typedef struct Vector2 {
+ float x;
+ float y;
+} Vector2;
+#endif
+
+// Matrix2x2 type (used for polygon shape rotation matrix)
+typedef struct Matrix2x2 {
+ float m00;
+ float m01;
+ float m10;
+ float m11;
+} Matrix2x2;
+
+typedef struct PhysicsVertexData {
+ unsigned int vertexCount; // Vertex count (positions and normals)
+ Vector2 positions[PHYSAC_MAX_VERTICES]; // Vertex positions vectors
+ Vector2 normals[PHYSAC_MAX_VERTICES]; // Vertex normals vectors
+} PhysicsVertexData;
+
+typedef struct PhysicsShape {
+ PhysicsShapeType type; // Shape type (circle or polygon)
+ PhysicsBody body; // Shape physics body data pointer
+ PhysicsVertexData vertexData; // Shape vertices data (used for polygon shapes)
+ float radius; // Shape radius (used for circle shapes)
+ Matrix2x2 transform; // Vertices transform matrix 2x2
+} PhysicsShape;
+
+typedef struct PhysicsBodyData {
+ unsigned int id; // Unique identifier
+ bool enabled; // Enabled dynamics state (collisions are calculated anyway)
+ Vector2 position; // Physics body shape pivot
+ Vector2 velocity; // Current linear velocity applied to position
+ Vector2 force; // Current linear force (reset to 0 every step)
+ float angularVelocity; // Current angular velocity applied to orient
+ float torque; // Current angular force (reset to 0 every step)
+ float orient; // Rotation in radians
+ float inertia; // Moment of inertia
+ float inverseInertia; // Inverse value of inertia
+ float mass; // Physics body mass
+ float inverseMass; // Inverse value of mass
+ float staticFriction; // Friction when the body has not movement (0 to 1)
+ float dynamicFriction; // Friction when the body has movement (0 to 1)
+ float restitution; // Restitution coefficient of the body (0 to 1)
+ bool useGravity; // Apply gravity force to dynamics
+ bool isGrounded; // Physics grounded on other body state
+ bool freezeOrient; // Physics rotation constraint
+ PhysicsShape shape; // Physics body shape information (type, radius, vertices, transform)
+} PhysicsBodyData;
+
+typedef struct PhysicsManifoldData {
+ unsigned int id; // Unique identifier
+ PhysicsBody bodyA; // Manifold first physics body reference
+ PhysicsBody bodyB; // Manifold second physics body reference
+ float penetration; // Depth of penetration from collision
+ Vector2 normal; // Normal direction vector from 'a' to 'b'
+ Vector2 contacts[2]; // Points of contact during collision
+ unsigned int contactsCount; // Current collision number of contacts
+ float restitution; // Mixed restitution during collision
+ float dynamicFriction; // Mixed dynamic friction during collision
+ float staticFriction; // Mixed static friction during collision
+} PhysicsManifoldData, *PhysicsManifold;
+
+#if defined(__cplusplus)
+extern "C" { // Prevents name mangling of functions
+#endif
+
+//----------------------------------------------------------------------------------
+// Module Functions Declaration
+//----------------------------------------------------------------------------------
+// Physics system management
+PHYSACDEF void InitPhysics(void); // Initializes physics system
+PHYSACDEF void UpdatePhysics(void); // Update physics system
+PHYSACDEF void ResetPhysics(void); // Reset physics system (global variables)
+PHYSACDEF void ClosePhysics(void); // Close physics system and unload used memory
+PHYSACDEF void SetPhysicsTimeStep(double delta); // Sets physics fixed time step in milliseconds. 1.666666 by default
+PHYSACDEF void SetPhysicsGravity(float x, float y); // Sets physics global gravity force
+
+// Physic body creation/destroy
+PHYSACDEF PhysicsBody CreatePhysicsBodyCircle(Vector2 pos, float radius, float density); // Creates a new circle physics body with generic parameters
+PHYSACDEF PhysicsBody CreatePhysicsBodyRectangle(Vector2 pos, float width, float height, float density); // Creates a new rectangle physics body with generic parameters
+PHYSACDEF PhysicsBody CreatePhysicsBodyPolygon(Vector2 pos, float radius, int sides, float density); // Creates a new polygon physics body with generic parameters
+PHYSACDEF void DestroyPhysicsBody(PhysicsBody body); // Destroy a physics body
+
+// Physic body forces
+PHYSACDEF void PhysicsAddForce(PhysicsBody body, Vector2 force); // Adds a force to a physics body
+PHYSACDEF void PhysicsAddTorque(PhysicsBody body, float amount); // Adds an angular force to a physics body
+PHYSACDEF void PhysicsShatter(PhysicsBody body, Vector2 position, float force); // Shatters a polygon shape physics body to little physics bodies with explosion force
+PHYSACDEF void SetPhysicsBodyRotation(PhysicsBody body, float radians); // Sets physics body shape transform based on radians parameter
+
+// Query physics info
+PHYSACDEF PhysicsBody GetPhysicsBody(int index); // Returns a physics body of the bodies pool at a specific index
+PHYSACDEF int GetPhysicsBodiesCount(void); // Returns the current amount of created physics bodies
+PHYSACDEF int GetPhysicsShapeType(int index); // Returns the physics body shape type (PHYSICS_CIRCLE or PHYSICS_POLYGON)
+PHYSACDEF int GetPhysicsShapeVerticesCount(int index); // Returns the amount of vertices of a physics body shape
+PHYSACDEF Vector2 GetPhysicsShapeVertex(PhysicsBody body, int vertex); // Returns transformed position of a body shape (body position + vertex transformed position)
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif // PHYSAC_H
+
+/***********************************************************************************
+*
+* PHYSAC IMPLEMENTATION
+*
+************************************************************************************/
+
+#if defined(PHYSAC_IMPLEMENTATION)
+
+// Support TRACELOG macros
+#if defined(PHYSAC_DEBUG)
+ #include <stdio.h> // Required for: printf()
+ #define TRACELOG(...) printf(__VA_ARGS__)
+#else
+ #define TRACELOG(...) (void)0;
+#endif
+
+#include <stdlib.h> // Required for: malloc(), calloc(), free()
+#include <math.h> // Required for: cosf(), sinf(), fabs(), sqrtf()
+
+#if !defined(PHYSAC_AVOID_TIMMING_SYSTEM)
+ // Time management functionality
+ #include <time.h> // Required for: time(), clock_gettime()
+ #if defined(_WIN32)
+ // Functions required to query time on Windows
+ int __stdcall QueryPerformanceCounter(unsigned long long int *lpPerformanceCount);
+ int __stdcall QueryPerformanceFrequency(unsigned long long int *lpFrequency);
+ #endif
+ #if defined(__linux__) || defined(__FreeBSD__)
+ #if _POSIX_C_SOURCE < 199309L
+ #undef _POSIX_C_SOURCE
+ #define _POSIX_C_SOURCE 199309L // Required for CLOCK_MONOTONIC if compiled with c99 without gnu ext.
+ #endif
+ #include <sys/time.h> // Required for: timespec
+ #endif
+ #if defined(__APPLE__) // macOS also defines __MACH__
+ #include <mach/mach_time.h> // Required for: mach_absolute_time()
+ #endif
+#endif
+
+// NOTE: MSVC C++ compiler does not support compound literals (C99 feature)
+// Plain structures in C++ (without constructors) can be initialized from { } initializers.
+#if defined(__cplusplus)
+ #define CLITERAL(type) type
+#else
+ #define CLITERAL(type) (type)
+#endif
+
+//----------------------------------------------------------------------------------
+// Defines and Macros
+//----------------------------------------------------------------------------------
+#define PHYSAC_MIN(a,b) (((a)<(b))?(a):(b))
+#define PHYSAC_MAX(a,b) (((a)>(b))?(a):(b))
+#define PHYSAC_FLT_MAX 3.402823466e+38f
+#define PHYSAC_EPSILON 0.000001f
+#define PHYSAC_K 1.0f/3.0f
+#define PHYSAC_VECTOR_ZERO CLITERAL(Vector2){ 0.0f, 0.0f }
+
+//----------------------------------------------------------------------------------
+// Global Variables Definition
+//----------------------------------------------------------------------------------
+static double deltaTime = 1.0/60.0/10.0 * 1000; // Delta time in milliseconds used for physics steps
+
+#if !defined(PHYSAC_AVOID_TIMMING_SYSTEM)
+// Time measure variables
+static double baseClockTicks = 0.0; // Offset clock ticks for MONOTONIC clock
+static unsigned long long int frequency = 0; // Hi-res clock frequency
+static double startTime = 0.0; // Start time in milliseconds
+static double currentTime = 0.0; // Current time in milliseconds
+#endif
+
+// Physics system configuration
+static PhysicsBody bodies[PHYSAC_MAX_BODIES]; // Physics bodies pointers array
+static unsigned int physicsBodiesCount = 0; // Physics world current bodies counter
+static PhysicsManifold contacts[PHYSAC_MAX_MANIFOLDS]; // Physics bodies pointers array
+static unsigned int physicsManifoldsCount = 0; // Physics world current manifolds counter
+
+static Vector2 gravityForce = { 0.0f, 9.81f }; // Physics world gravity force
+
+// Utilities variables
+static unsigned int usedMemory = 0; // Total allocated dynamic memory
+
+//----------------------------------------------------------------------------------
+// Module Internal Functions Declaration
+//----------------------------------------------------------------------------------
+#if !defined(PHYSAC_AVOID_TIMMING_SYSTEM)
+// Timming measure functions
+static void InitTimer(void); // Initializes hi-resolution MONOTONIC timer
+static unsigned long long int GetClockTicks(void); // Get hi-res MONOTONIC time measure in mseconds
+static double GetCurrentTime(void); // Get current time measure in milliseconds
+#endif
+
+static void UpdatePhysicsStep(void); // Update physics step (dynamics, collisions and position corrections)
+
+static int FindAvailableBodyIndex(); // Finds a valid index for a new physics body initialization
+static int FindAvailableManifoldIndex(); // Finds a valid index for a new manifold initialization
+static PhysicsVertexData CreateDefaultPolygon(float radius, int sides); // Creates a random polygon shape with max vertex distance from polygon pivot
+static PhysicsVertexData CreateRectanglePolygon(Vector2 pos, Vector2 size); // Creates a rectangle polygon shape based on a min and max positions
+
+static void InitializePhysicsManifolds(PhysicsManifold manifold); // Initializes physics manifolds to solve collisions
+static PhysicsManifold CreatePhysicsManifold(PhysicsBody a, PhysicsBody b); // Creates a new physics manifold to solve collision
+static void DestroyPhysicsManifold(PhysicsManifold manifold); // Unitializes and destroys a physics manifold
+
+static void SolvePhysicsManifold(PhysicsManifold manifold); // Solves a created physics manifold between two physics bodies
+static void SolveCircleToCircle(PhysicsManifold manifold); // Solves collision between two circle shape physics bodies
+static void SolveCircleToPolygon(PhysicsManifold manifold); // Solves collision between a circle to a polygon shape physics bodies
+static void SolvePolygonToCircle(PhysicsManifold manifold); // Solves collision between a polygon to a circle shape physics bodies
+static void SolvePolygonToPolygon(PhysicsManifold manifold); // Solves collision between two polygons shape physics bodies
+static void IntegratePhysicsForces(PhysicsBody body); // Integrates physics forces into velocity
+static void IntegratePhysicsVelocity(PhysicsBody body); // Integrates physics velocity into position and forces
+static void IntegratePhysicsImpulses(PhysicsManifold manifold); // Integrates physics collisions impulses to solve collisions
+static void CorrectPhysicsPositions(PhysicsManifold manifold); // Corrects physics bodies positions based on manifolds collision information
+static void FindIncidentFace(Vector2 *v0, Vector2 *v1, PhysicsShape ref, PhysicsShape inc, int index); // Finds two polygon shapes incident face
+static float FindAxisLeastPenetration(int *faceIndex, PhysicsShape shapeA, PhysicsShape shapeB); // Finds polygon shapes axis least penetration
+
+// Math required functions
+static Vector2 MathVector2Product(Vector2 vector, float value); // Returns the product of a vector and a value
+static float MathVector2CrossProduct(Vector2 v1, Vector2 v2); // Returns the cross product of two vectors
+static float MathVector2SqrLen(Vector2 vector); // Returns the len square root of a vector
+static float MathVector2DotProduct(Vector2 v1, Vector2 v2); // Returns the dot product of two vectors
+static inline float MathVector2SqrDistance(Vector2 v1, Vector2 v2); // Returns the square root of distance between two vectors
+static void MathVector2Normalize(Vector2 *vector); // Returns the normalized values of a vector
+static Vector2 MathVector2Add(Vector2 v1, Vector2 v2); // Returns the sum of two given vectors
+static Vector2 MathVector2Subtract(Vector2 v1, Vector2 v2); // Returns the subtract of two given vectors
+static Matrix2x2 MathMatFromRadians(float radians); // Returns a matrix 2x2 from a given radians value
+static inline Matrix2x2 MathMatTranspose(Matrix2x2 matrix); // Returns the transpose of a given matrix 2x2
+static inline Vector2 MathMatVector2Product(Matrix2x2 matrix, Vector2 vector); // Returns product between matrix 2x2 and vector
+static int MathVector2Clip(Vector2 normal, Vector2 *faceA, Vector2 *faceB, float clip); // Returns clipping value based on a normal and two faces
+static Vector2 MathTriangleBarycenter(Vector2 v1, Vector2 v2, Vector2 v3); // Returns the barycenter of a triangle given by 3 points
+
+//----------------------------------------------------------------------------------
+// Module Functions Definition
+//----------------------------------------------------------------------------------
+
+// Initializes physics values, pointers and creates physics loop thread
+PHYSACDEF void InitPhysics(void)
+{
+#if !defined(PHYSAC_AVOID_TIMMING_SYSTEM)
+ // Initialize high resolution timer
+ InitTimer();
+#endif
+
+ TRACELOG("[PHYSAC] Physics module initialized successfully\n");
+}
+
+// Sets physics global gravity force
+PHYSACDEF void SetPhysicsGravity(float x, float y)
+{
+ gravityForce.x = x;
+ gravityForce.y = y;
+}
+
+// Creates a new circle physics body with generic parameters
+PHYSACDEF PhysicsBody CreatePhysicsBodyCircle(Vector2 pos, float radius, float density)
+{
+ PhysicsBody body = CreatePhysicsBodyPolygon(pos, radius, PHYSAC_DEFAULT_CIRCLE_VERTICES, density);
+ return body;
+}
+
+// Creates a new rectangle physics body with generic parameters
+PHYSACDEF PhysicsBody CreatePhysicsBodyRectangle(Vector2 pos, float width, float height, float density)
+{
+ // NOTE: Make sure body data is initialized to 0
+ PhysicsBody body = (PhysicsBody)PHYSAC_CALLOC(sizeof(PhysicsBodyData), 1);
+ usedMemory += sizeof(PhysicsBodyData);
+
+ int id = FindAvailableBodyIndex();
+ if (id != -1)
+ {
+ // Initialize new body with generic values
+ body->id = id;
+ body->enabled = true;
+ body->position = pos;
+ body->shape.type = PHYSICS_POLYGON;
+ body->shape.body = body;
+ body->shape.transform = MathMatFromRadians(0.0f);
+ body->shape.vertexData = CreateRectanglePolygon(pos, CLITERAL(Vector2){ width, height });
+
+ // Calculate centroid and moment of inertia
+ Vector2 center = { 0.0f, 0.0f };
+ float area = 0.0f;
+ float inertia = 0.0f;
+
+ for (unsigned int i = 0; i < body->shape.vertexData.vertexCount; i++)
+ {
+ // Triangle vertices, third vertex implied as (0, 0)
+ Vector2 p1 = body->shape.vertexData.positions[i];
+ unsigned int nextIndex = (((i + 1) < body->shape.vertexData.vertexCount) ? (i + 1) : 0);
+ Vector2 p2 = body->shape.vertexData.positions[nextIndex];
+
+ float D = MathVector2CrossProduct(p1, p2);
+ float triangleArea = D/2;
+
+ area += triangleArea;
+
+ // Use area to weight the centroid average, not just vertex position
+ center.x += triangleArea*PHYSAC_K*(p1.x + p2.x);
+ center.y += triangleArea*PHYSAC_K*(p1.y + p2.y);
+
+ float intx2 = p1.x*p1.x + p2.x*p1.x + p2.x*p2.x;
+ float inty2 = p1.y*p1.y + p2.y*p1.y + p2.y*p2.y;
+ inertia += (0.25f*PHYSAC_K*D)*(intx2 + inty2);
+ }
+
+ center.x *= 1.0f/area;
+ center.y *= 1.0f/area;
+
+ // Translate vertices to centroid (make the centroid (0, 0) for the polygon in model space)
+ // Note: this is not really necessary
+ for (unsigned int i = 0; i < body->shape.vertexData.vertexCount; i++)
+ {
+ body->shape.vertexData.positions[i].x -= center.x;
+ body->shape.vertexData.positions[i].y -= center.y;
+ }
+
+ body->mass = density*area;
+ body->inverseMass = ((body->mass != 0.0f) ? 1.0f/body->mass : 0.0f);
+ body->inertia = density*inertia;
+ body->inverseInertia = ((body->inertia != 0.0f) ? 1.0f/body->inertia : 0.0f);
+ body->staticFriction = 0.4f;
+ body->dynamicFriction = 0.2f;
+ body->restitution = 0.0f;
+ body->useGravity = true;
+ body->isGrounded = false;
+ body->freezeOrient = false;
+
+ // Add new body to bodies pointers array and update bodies count
+ bodies[physicsBodiesCount] = body;
+ physicsBodiesCount++;
+
+ TRACELOG("[PHYSAC] Physic body created successfully (id: %i)\n", body->id);
+ }
+ else TRACELOG("[PHYSAC] Physic body could not be created, PHYSAC_MAX_BODIES reached\n");
+
+ return body;
+}
+
+// Creates a new polygon physics body with generic parameters
+PHYSACDEF PhysicsBody CreatePhysicsBodyPolygon(Vector2 pos, float radius, int sides, float density)
+{
+ PhysicsBody body = (PhysicsBody)PHYSAC_MALLOC(sizeof(PhysicsBodyData));
+ usedMemory += sizeof(PhysicsBodyData);
+
+ int id = FindAvailableBodyIndex();
+ if (id != -1)
+ {
+ // Initialize new body with generic values
+ body->id = id;
+ body->enabled = true;
+ body->position = pos;
+ body->velocity = PHYSAC_VECTOR_ZERO;
+ body->force = PHYSAC_VECTOR_ZERO;
+ body->angularVelocity = 0.0f;
+ body->torque = 0.0f;
+ body->orient = 0.0f;
+ body->shape.type = PHYSICS_POLYGON;
+ body->shape.body = body;
+ body->shape.transform = MathMatFromRadians(0.0f);
+ body->shape.vertexData = CreateDefaultPolygon(radius, sides);
+
+ // Calculate centroid and moment of inertia
+ Vector2 center = { 0.0f, 0.0f };
+ float area = 0.0f;
+ float inertia = 0.0f;
+
+ for (unsigned int i = 0; i < body->shape.vertexData.vertexCount; i++)
+ {
+ // Triangle vertices, third vertex implied as (0, 0)
+ Vector2 position1 = body->shape.vertexData.positions[i];
+ unsigned int nextIndex = (((i + 1) < body->shape.vertexData.vertexCount) ? (i + 1) : 0);
+ Vector2 position2 = body->shape.vertexData.positions[nextIndex];
+
+ float cross = MathVector2CrossProduct(position1, position2);
+ float triangleArea = cross/2;
+
+ area += triangleArea;
+
+ // Use area to weight the centroid average, not just vertex position
+ center.x += triangleArea*PHYSAC_K*(position1.x + position2.x);
+ center.y += triangleArea*PHYSAC_K*(position1.y + position2.y);
+
+ float intx2 = position1.x*position1.x + position2.x*position1.x + position2.x*position2.x;
+ float inty2 = position1.y*position1.y + position2.y*position1.y + position2.y*position2.y;
+ inertia += (0.25f*PHYSAC_K*cross)*(intx2 + inty2);
+ }
+
+ center.x *= 1.0f/area;
+ center.y *= 1.0f/area;
+
+ // Translate vertices to centroid (make the centroid (0, 0) for the polygon in model space)
+ // Note: this is not really necessary
+ for (unsigned int i = 0; i < body->shape.vertexData.vertexCount; i++)
+ {
+ body->shape.vertexData.positions[i].x -= center.x;
+ body->shape.vertexData.positions[i].y -= center.y;
+ }
+
+ body->mass = density*area;
+ body->inverseMass = ((body->mass != 0.0f) ? 1.0f/body->mass : 0.0f);
+ body->inertia = density*inertia;
+ body->inverseInertia = ((body->inertia != 0.0f) ? 1.0f/body->inertia : 0.0f);
+ body->staticFriction = 0.4f;
+ body->dynamicFriction = 0.2f;
+ body->restitution = 0.0f;
+ body->useGravity = true;
+ body->isGrounded = false;
+ body->freezeOrient = false;
+
+ // Add new body to bodies pointers array and update bodies count
+ bodies[physicsBodiesCount] = body;
+ physicsBodiesCount++;
+
+ TRACELOG("[PHYSAC] Physic body created successfully (id: %i)\n", body->id);
+ }
+ else TRACELOG("[PHYSAC] Physics body could not be created, PHYSAC_MAX_BODIES reached\n");
+
+ return body;
+}
+
+// Adds a force to a physics body
+PHYSACDEF void PhysicsAddForce(PhysicsBody body, Vector2 force)
+{
+ if (body != NULL) body->force = MathVector2Add(body->force, force);
+}
+
+// Adds an angular force to a physics body
+PHYSACDEF void PhysicsAddTorque(PhysicsBody body, float amount)
+{
+ if (body != NULL) body->torque += amount;
+}
+
+// Shatters a polygon shape physics body to little physics bodies with explosion force
+PHYSACDEF void PhysicsShatter(PhysicsBody body, Vector2 position, float force)
+{
+ if (body != NULL)
+ {
+ if (body->shape.type == PHYSICS_POLYGON)
+ {
+ PhysicsVertexData vertexData = body->shape.vertexData;
+ bool collision = false;
+
+ for (unsigned int i = 0; i < vertexData.vertexCount; i++)
+ {
+ Vector2 positionA = body->position;
+ Vector2 positionB = MathMatVector2Product(body->shape.transform, MathVector2Add(body->position, vertexData.positions[i]));
+ unsigned int nextIndex = (((i + 1) < vertexData.vertexCount) ? (i + 1) : 0);
+ Vector2 positionC = MathMatVector2Product(body->shape.transform, MathVector2Add(body->position, vertexData.positions[nextIndex]));
+
+ // Check collision between each triangle
+ float alpha = ((positionB.y - positionC.y)*(position.x - positionC.x) + (positionC.x - positionB.x)*(position.y - positionC.y))/
+ ((positionB.y - positionC.y)*(positionA.x - positionC.x) + (positionC.x - positionB.x)*(positionA.y - positionC.y));
+
+ float beta = ((positionC.y - positionA.y)*(position.x - positionC.x) + (positionA.x - positionC.x)*(position.y - positionC.y))/
+ ((positionB.y - positionC.y)*(positionA.x - positionC.x) + (positionC.x - positionB.x)*(positionA.y - positionC.y));
+
+ float gamma = 1.0f - alpha - beta;
+
+ if ((alpha > 0.0f) && (beta > 0.0f) & (gamma > 0.0f))
+ {
+ collision = true;
+ break;
+ }
+ }
+
+ if (collision)
+ {
+ int count = vertexData.vertexCount;
+ Vector2 bodyPos = body->position;
+ Vector2 *vertices = (Vector2 *)PHYSAC_MALLOC(sizeof(Vector2)*count);
+ Matrix2x2 trans = body->shape.transform;
+ for (int i = 0; i < count; i++) vertices[i] = vertexData.positions[i];
+
+ // Destroy shattered physics body
+ DestroyPhysicsBody(body);
+
+ for (int i = 0; i < count; i++)
+ {
+ int nextIndex = (((i + 1) < count) ? (i + 1) : 0);
+ Vector2 center = MathTriangleBarycenter(vertices[i], vertices[nextIndex], PHYSAC_VECTOR_ZERO);
+ center = MathVector2Add(bodyPos, center);
+ Vector2 offset = MathVector2Subtract(center, bodyPos);
+
+ PhysicsBody body = CreatePhysicsBodyPolygon(center, 10, 3, 10); // Create polygon physics body with relevant values
+
+ PhysicsVertexData vertexData = { 0 };
+ vertexData.vertexCount = 3;
+
+ vertexData.positions[0] = MathVector2Subtract(vertices[i], offset);
+ vertexData.positions[1] = MathVector2Subtract(vertices[nextIndex], offset);
+ vertexData.positions[2] = MathVector2Subtract(position, center);
+
+ // Separate vertices to avoid unnecessary physics collisions
+ vertexData.positions[0].x *= 0.95f;
+ vertexData.positions[0].y *= 0.95f;
+ vertexData.positions[1].x *= 0.95f;
+ vertexData.positions[1].y *= 0.95f;
+ vertexData.positions[2].x *= 0.95f;
+ vertexData.positions[2].y *= 0.95f;
+
+ // Calculate polygon faces normals
+ for (unsigned int j = 0; j < vertexData.vertexCount; j++)
+ {
+ unsigned int nextVertex = (((j + 1) < vertexData.vertexCount) ? (j + 1) : 0);
+ Vector2 face = MathVector2Subtract(vertexData.positions[nextVertex], vertexData.positions[j]);
+
+ vertexData.normals[j] = CLITERAL(Vector2){ face.y, -face.x };
+ MathVector2Normalize(&vertexData.normals[j]);
+ }
+
+ // Apply computed vertex data to new physics body shape
+ body->shape.vertexData = vertexData;
+ body->shape.transform = trans;
+
+ // Calculate centroid and moment of inertia
+ center = PHYSAC_VECTOR_ZERO;
+ float area = 0.0f;
+ float inertia = 0.0f;
+
+ for (unsigned int j = 0; j < body->shape.vertexData.vertexCount; j++)
+ {
+ // Triangle vertices, third vertex implied as (0, 0)
+ Vector2 p1 = body->shape.vertexData.positions[j];
+ unsigned int nextVertex = (((j + 1) < body->shape.vertexData.vertexCount) ? (j + 1) : 0);
+ Vector2 p2 = body->shape.vertexData.positions[nextVertex];
+
+ float D = MathVector2CrossProduct(p1, p2);
+ float triangleArea = D/2;
+
+ area += triangleArea;
+
+ // Use area to weight the centroid average, not just vertex position
+ center.x += triangleArea*PHYSAC_K*(p1.x + p2.x);
+ center.y += triangleArea*PHYSAC_K*(p1.y + p2.y);
+
+ float intx2 = p1.x*p1.x + p2.x*p1.x + p2.x*p2.x;
+ float inty2 = p1.y*p1.y + p2.y*p1.y + p2.y*p2.y;
+ inertia += (0.25f*PHYSAC_K*D)*(intx2 + inty2);
+ }
+
+ center.x *= 1.0f/area;
+ center.y *= 1.0f/area;
+
+ body->mass = area;
+ body->inverseMass = ((body->mass != 0.0f) ? 1.0f/body->mass : 0.0f);
+ body->inertia = inertia;
+ body->inverseInertia = ((body->inertia != 0.0f) ? 1.0f/body->inertia : 0.0f);
+
+ // Calculate explosion force direction
+ Vector2 pointA = body->position;
+ Vector2 pointB = MathVector2Subtract(vertexData.positions[1], vertexData.positions[0]);
+ pointB.x /= 2.0f;
+ pointB.y /= 2.0f;
+ Vector2 forceDirection = MathVector2Subtract(MathVector2Add(pointA, MathVector2Add(vertexData.positions[0], pointB)), body->position);
+ MathVector2Normalize(&forceDirection);
+ forceDirection.x *= force;
+ forceDirection.y *= force;
+
+ // Apply force to new physics body
+ PhysicsAddForce(body, forceDirection);
+ }
+
+ PHYSAC_FREE(vertices);
+ }
+ }
+ }
+ else TRACELOG("[PHYSAC] WARNING: PhysicsShatter: NULL physic body\n");
+}
+
+// Returns the current amount of created physics bodies
+PHYSACDEF int GetPhysicsBodiesCount(void)
+{
+ return physicsBodiesCount;
+}
+
+// Returns a physics body of the bodies pool at a specific index
+PHYSACDEF PhysicsBody GetPhysicsBody(int index)
+{
+ PhysicsBody body = NULL;
+
+ if (index < (int)physicsBodiesCount)
+ {
+ body = bodies[index];
+
+ if (body == NULL) TRACELOG("[PHYSAC] WARNING: GetPhysicsBody: NULL physic body\n");
+ }
+ else TRACELOG("[PHYSAC] WARNING: Physic body index is out of bounds\n");
+
+ return body;
+}
+
+// Returns the physics body shape type (PHYSICS_CIRCLE or PHYSICS_POLYGON)
+PHYSACDEF int GetPhysicsShapeType(int index)
+{
+ int result = -1;
+
+ if (index < (int)physicsBodiesCount)
+ {
+ PhysicsBody body = bodies[index];
+
+ if (body != NULL) result = body->shape.type;
+ else TRACELOG("[PHYSAC] WARNING: GetPhysicsShapeType: NULL physic body\n");
+ }
+ else TRACELOG("[PHYSAC] WARNING: Physic body index is out of bounds\n");
+
+ return result;
+}
+
+// Returns the amount of vertices of a physics body shape
+PHYSACDEF int GetPhysicsShapeVerticesCount(int index)
+{
+ int result = 0;
+
+ if (index < (int)physicsBodiesCount)
+ {
+ PhysicsBody body = bodies[index];
+
+ if (body != NULL)
+ {
+ switch (body->shape.type)
+ {
+ case PHYSICS_CIRCLE: result = PHYSAC_DEFAULT_CIRCLE_VERTICES; break;
+ case PHYSICS_POLYGON: result = body->shape.vertexData.vertexCount; break;
+ default: break;
+ }
+ }
+ else TRACELOG("[PHYSAC] WARNING: GetPhysicsShapeVerticesCount: NULL physic body\n");
+ }
+ else TRACELOG("[PHYSAC] WARNING: Physic body index is out of bounds\n");
+
+ return result;
+}
+
+// Returns transformed position of a body shape (body position + vertex transformed position)
+PHYSACDEF Vector2 GetPhysicsShapeVertex(PhysicsBody body, int vertex)
+{
+ Vector2 position = { 0.0f, 0.0f };
+
+ if (body != NULL)
+ {
+ switch (body->shape.type)
+ {
+ case PHYSICS_CIRCLE:
+ {
+ position.x = body->position.x + cosf(360.0f/PHYSAC_DEFAULT_CIRCLE_VERTICES*vertex*PHYSAC_DEG2RAD)*body->shape.radius;
+ position.y = body->position.y + sinf(360.0f/PHYSAC_DEFAULT_CIRCLE_VERTICES*vertex*PHYSAC_DEG2RAD)*body->shape.radius;
+ } break;
+ case PHYSICS_POLYGON:
+ {
+ PhysicsVertexData vertexData = body->shape.vertexData;
+ position = MathVector2Add(body->position, MathMatVector2Product(body->shape.transform, vertexData.positions[vertex]));
+ } break;
+ default: break;
+ }
+ }
+ else TRACELOG("[PHYSAC] WARNING: GetPhysicsShapeVertex: NULL physic body\n");
+
+ return position;
+}
+
+// Sets physics body shape transform based on radians parameter
+PHYSACDEF void SetPhysicsBodyRotation(PhysicsBody body, float radians)
+{
+ if (body != NULL)
+ {
+ body->orient = radians;
+
+ if (body->shape.type == PHYSICS_POLYGON) body->shape.transform = MathMatFromRadians(radians);
+ }
+}
+
+// Unitializes and destroys a physics body
+PHYSACDEF void DestroyPhysicsBody(PhysicsBody body)
+{
+ if (body != NULL)
+ {
+ int id = body->id;
+ int index = -1;
+
+ for (unsigned int i = 0; i < physicsBodiesCount; i++)
+ {
+ if (bodies[i]->id == id)
+ {
+ index = i;
+ break;
+ }
+ }
+
+ if (index == -1)
+ {
+ TRACELOG("[PHYSAC] WARNING: Requested body (id: %i) can not be found\n", id);
+ return; // Prevent access to index -1
+ }
+
+ // Free body allocated memory
+ PHYSAC_FREE(body);
+ usedMemory -= sizeof(PhysicsBodyData);
+ bodies[index] = NULL;
+
+ // Reorder physics bodies pointers array and its catched index
+ for (unsigned int i = index; i < physicsBodiesCount; i++)
+ {
+ if ((i + 1) < physicsBodiesCount) bodies[i] = bodies[i + 1];
+ }
+
+ // Update physics bodies count
+ physicsBodiesCount--;
+
+ TRACELOG("[PHYSAC] Physic body destroyed successfully (id: %i)\n", id);
+ }
+ else TRACELOG("[PHYSAC] WARNING: DestroyPhysicsBody: NULL physic body\n");
+}
+
+// Destroys created physics bodies and manifolds and resets global values
+PHYSACDEF void ResetPhysics(void)
+{
+ if (physicsBodiesCount > 0)
+ {
+ // Unitialize physics bodies dynamic memory allocations
+ for (int i = physicsBodiesCount - 1; i >= 0; i--)
+ {
+ PhysicsBody body = bodies[i];
+
+ if (body != NULL)
+ {
+ PHYSAC_FREE(body);
+ bodies[i] = NULL;
+ usedMemory -= sizeof(PhysicsBodyData);
+ }
+ }
+
+ physicsBodiesCount = 0;
+ }
+
+ if (physicsManifoldsCount > 0)
+ {
+ // Unitialize physics manifolds dynamic memory allocations
+ for (int i = physicsManifoldsCount - 1; i >= 0; i--)
+ {
+ PhysicsManifold manifold = contacts[i];
+
+ if (manifold != NULL)
+ {
+ PHYSAC_FREE(manifold);
+ contacts[i] = NULL;
+ usedMemory -= sizeof(PhysicsManifoldData);
+ }
+ }
+
+ physicsManifoldsCount = 0;
+ }
+
+ TRACELOG("[PHYSAC] Physics module reseted successfully\n");
+}
+
+// Unitializes physics pointers and exits physics loop thread
+PHYSACDEF void ClosePhysics(void)
+{
+ // Unitialize physics manifolds dynamic memory allocations
+ if (physicsManifoldsCount > 0)
+ {
+ for (unsigned int i = physicsManifoldsCount - 1; i >= 0; i--)
+ DestroyPhysicsManifold(contacts[i]);
+ }
+
+ // Unitialize physics bodies dynamic memory allocations
+ if (physicsBodiesCount > 0)
+ {
+ for (unsigned int i = physicsBodiesCount - 1; i >= 0; i--)
+ DestroyPhysicsBody(bodies[i]);
+ }
+
+ // Trace log info
+ if ((physicsBodiesCount > 0) || (usedMemory != 0))
+ {
+ TRACELOG("[PHYSAC] WARNING: Physics module closed with unallocated bodies (BODIES: %i, MEMORY: %i bytes)\n", physicsBodiesCount, usedMemory);
+ }
+ else if ((physicsManifoldsCount > 0) || (usedMemory != 0))
+ {
+ TRACELOG("[PHYSAC] WARNING: Pysics module closed with unallocated manifolds (MANIFOLDS: %i, MEMORY: %i bytes)\n", physicsManifoldsCount, usedMemory);
+ }
+ else TRACELOG("[PHYSAC] Physics module closed successfully\n");
+}
+
+//----------------------------------------------------------------------------------
+// Module Internal Functions Definition
+//----------------------------------------------------------------------------------
+// Finds a valid index for a new physics body initialization
+static int FindAvailableBodyIndex()
+{
+ int index = -1;
+ for (int i = 0; i < PHYSAC_MAX_BODIES; i++)
+ {
+ int currentId = i;
+
+ // Check if current id already exist in other physics body
+ for (unsigned int k = 0; k < physicsBodiesCount; k++)
+ {
+ if (bodies[k]->id == currentId)
+ {
+ currentId++;
+ break;
+ }
+ }
+
+ // If it is not used, use it as new physics body id
+ if (currentId == (int)i)
+ {
+ index = (int)i;
+ break;
+ }
+ }
+
+ return index;
+}
+
+// Creates a default polygon shape with max vertex distance from polygon pivot
+static PhysicsVertexData CreateDefaultPolygon(float radius, int sides)
+{
+ PhysicsVertexData data = { 0 };
+ data.vertexCount = sides;
+
+ // Calculate polygon vertices positions
+ for (unsigned int i = 0; i < data.vertexCount; i++)
+ {
+ data.positions[i].x = (float)cosf(360.0f/sides*i*PHYSAC_DEG2RAD)*radius;
+ data.positions[i].y = (float)sinf(360.0f/sides*i*PHYSAC_DEG2RAD)*radius;
+ }
+
+ // Calculate polygon faces normals
+ for (int i = 0; i < (int)data.vertexCount; i++)
+ {
+ int nextIndex = (((i + 1) < sides) ? (i + 1) : 0);
+ Vector2 face = MathVector2Subtract(data.positions[nextIndex], data.positions[i]);
+
+ data.normals[i] = CLITERAL(Vector2){ face.y, -face.x };
+ MathVector2Normalize(&data.normals[i]);
+ }
+
+ return data;
+}
+
+// Creates a rectangle polygon shape based on a min and max positions
+static PhysicsVertexData CreateRectanglePolygon(Vector2 pos, Vector2 size)
+{
+ PhysicsVertexData data = { 0 };
+ data.vertexCount = 4;
+
+ // Calculate polygon vertices positions
+ data.positions[0] = CLITERAL(Vector2){ pos.x + size.x/2, pos.y - size.y/2 };
+ data.positions[1] = CLITERAL(Vector2){ pos.x + size.x/2, pos.y + size.y/2 };
+ data.positions[2] = CLITERAL(Vector2){ pos.x - size.x/2, pos.y + size.y/2 };
+ data.positions[3] = CLITERAL(Vector2){ pos.x - size.x/2, pos.y - size.y/2 };
+
+ // Calculate polygon faces normals
+ for (unsigned int i = 0; i < data.vertexCount; i++)
+ {
+ int nextIndex = (((i + 1) < data.vertexCount) ? (i + 1) : 0);
+ Vector2 face = MathVector2Subtract(data.positions[nextIndex], data.positions[i]);
+
+ data.normals[i] = CLITERAL(Vector2){ face.y, -face.x };
+ MathVector2Normalize(&data.normals[i]);
+ }
+
+ return data;
+}
+
+// Update physics step (dynamics, collisions and position corrections)
+void UpdatePhysicsStep(void)
+{
+ // Clear previous generated collisions information
+ for (int i = (int)physicsManifoldsCount - 1; i >= 0; i--)
+ {
+ PhysicsManifold manifold = contacts[i];
+ if (manifold != NULL) DestroyPhysicsManifold(manifold);
+ }
+
+ // Reset physics bodies grounded state
+ for (unsigned int i = 0; i < physicsBodiesCount; i++)
+ {
+ PhysicsBody body = bodies[i];
+ body->isGrounded = false;
+ }
+
+ // Generate new collision information
+ for (unsigned int i = 0; i < physicsBodiesCount; i++)
+ {
+ PhysicsBody bodyA = bodies[i];
+
+ if (bodyA != NULL)
+ {
+ for (unsigned int j = i + 1; j < physicsBodiesCount; j++)
+ {
+ PhysicsBody bodyB = bodies[j];
+
+ if (bodyB != NULL)
+ {
+ if ((bodyA->inverseMass == 0) && (bodyB->inverseMass == 0)) continue;
+
+ PhysicsManifold manifold = CreatePhysicsManifold(bodyA, bodyB);
+ SolvePhysicsManifold(manifold);
+
+ if (manifold->contactsCount > 0)
+ {
+ // Create a new manifold with same information as previously solved manifold and add it to the manifolds pool last slot
+ PhysicsManifold manifold = CreatePhysicsManifold(bodyA, bodyB);
+ manifold->penetration = manifold->penetration;
+ manifold->normal = manifold->normal;
+ manifold->contacts[0] = manifold->contacts[0];
+ manifold->contacts[1] = manifold->contacts[1];
+ manifold->contactsCount = manifold->contactsCount;
+ manifold->restitution = manifold->restitution;
+ manifold->dynamicFriction = manifold->dynamicFriction;
+ manifold->staticFriction = manifold->staticFriction;
+ }
+ }
+ }
+ }
+ }
+
+ // Integrate forces to physics bodies
+ for (unsigned int i = 0; i < physicsBodiesCount; i++)
+ {
+ PhysicsBody body = bodies[i];
+ if (body != NULL) IntegratePhysicsForces(body);
+ }
+
+ // Initialize physics manifolds to solve collisions
+ for (unsigned int i = 0; i < physicsManifoldsCount; i++)
+ {
+ PhysicsManifold manifold = contacts[i];
+ if (manifold != NULL) InitializePhysicsManifolds(manifold);
+ }
+
+ // Integrate physics collisions impulses to solve collisions
+ for (unsigned int i = 0; i < PHYSAC_COLLISION_ITERATIONS; i++)
+ {
+ for (unsigned int j = 0; j < physicsManifoldsCount; j++)
+ {
+ PhysicsManifold manifold = contacts[i];
+ if (manifold != NULL) IntegratePhysicsImpulses(manifold);
+ }
+ }
+
+ // Integrate velocity to physics bodies
+ for (unsigned int i = 0; i < physicsBodiesCount; i++)
+ {
+ PhysicsBody body = bodies[i];
+ if (body != NULL) IntegratePhysicsVelocity(body);
+ }
+
+ // Correct physics bodies positions based on manifolds collision information
+ for (unsigned int i = 0; i < physicsManifoldsCount; i++)
+ {
+ PhysicsManifold manifold = contacts[i];
+ if (manifold != NULL) CorrectPhysicsPositions(manifold);
+ }
+
+ // Clear physics bodies forces
+ for (unsigned int i = 0; i < physicsBodiesCount; i++)
+ {
+ PhysicsBody body = bodies[i];
+ if (body != NULL)
+ {
+ body->force = PHYSAC_VECTOR_ZERO;
+ body->torque = 0.0f;
+ }
+ }
+}
+
+// Update physics system
+// Physics steps are launched at a fixed time step if enabled
+PHYSACDEF void UpdatePhysics(void)
+{
+#if !defined(PHYSAC_AVOID_TIMMING_SYSTEM)
+ static double deltaTimeAccumulator = 0.0;
+
+ // Calculate current time (ms)
+ currentTime = GetCurrentTime();
+
+ // Calculate current delta time (ms)
+ const double delta = currentTime - startTime;
+
+ // Store the time elapsed since the last frame began
+ deltaTimeAccumulator += delta;
+
+ // Fixed time stepping loop
+ while (deltaTimeAccumulator >= deltaTime)
+ {
+ UpdatePhysicsStep();
+ deltaTimeAccumulator -= deltaTime;
+ }
+
+ // Record the starting of this frame
+ startTime = currentTime;
+#else
+ UpdatePhysicsStep();
+#endif
+}
+
+PHYSACDEF void SetPhysicsTimeStep(double delta)
+{
+ deltaTime = delta;
+}
+
+// Finds a valid index for a new manifold initialization
+static int FindAvailableManifoldIndex()
+{
+ int index = -1;
+ for (int i = 0; i < PHYSAC_MAX_MANIFOLDS; i++)
+ {
+ int currentId = i;
+
+ // Check if current id already exist in other physics body
+ for (unsigned int k = 0; k < physicsManifoldsCount; k++)
+ {
+ if (contacts[k]->id == currentId)
+ {
+ currentId++;
+ break;
+ }
+ }
+
+ // If it is not used, use it as new physics body id
+ if (currentId == i)
+ {
+ index = i;
+ break;
+ }
+ }
+
+ return index;
+}
+
+// Creates a new physics manifold to solve collision
+static PhysicsManifold CreatePhysicsManifold(PhysicsBody a, PhysicsBody b)
+{
+ PhysicsManifold manifold = (PhysicsManifold)PHYSAC_MALLOC(sizeof(PhysicsManifoldData));
+ usedMemory += sizeof(PhysicsManifoldData);
+
+ int id = FindAvailableManifoldIndex();
+ if (id != -1)
+ {
+ // Initialize new manifold with generic values
+ manifold->id = id;
+ manifold->bodyA = a;
+ manifold->bodyB = b;
+ manifold->penetration = 0;
+ manifold->normal = PHYSAC_VECTOR_ZERO;
+ manifold->contacts[0] = PHYSAC_VECTOR_ZERO;
+ manifold->contacts[1] = PHYSAC_VECTOR_ZERO;
+ manifold->contactsCount = 0;
+ manifold->restitution = 0.0f;
+ manifold->dynamicFriction = 0.0f;
+ manifold->staticFriction = 0.0f;
+
+ // Add new body to bodies pointers array and update bodies count
+ contacts[physicsManifoldsCount] = manifold;
+ physicsManifoldsCount++;
+ }
+ else TRACELOG("[PHYSAC] Physic manifold could not be created, PHYSAC_MAX_MANIFOLDS reached\n");
+
+ return manifold;
+}
+
+// Unitializes and destroys a physics manifold
+static void DestroyPhysicsManifold(PhysicsManifold manifold)
+{
+ if (manifold != NULL)
+ {
+ int id = manifold->id;
+ int index = -1;
+
+ for (unsigned int i = 0; i < physicsManifoldsCount; i++)
+ {
+ if (contacts[i]->id == id)
+ {
+ index = i;
+ break;
+ }
+ }
+
+ if (index == -1) return; // Prevent access to index -1
+
+ // Free manifold allocated memory
+ PHYSAC_FREE(manifold);
+ usedMemory -= sizeof(PhysicsManifoldData);
+ contacts[index] = NULL;
+
+ // Reorder physics manifolds pointers array and its catched index
+ for (unsigned int i = index; i < physicsManifoldsCount; i++)
+ {
+ if ((i + 1) < physicsManifoldsCount) contacts[i] = contacts[i + 1];
+ }
+
+ // Update physics manifolds count
+ physicsManifoldsCount--;
+ }
+ else TRACELOG("[PHYSAC] WARNING: DestroyPhysicsManifold: NULL physic manifold\n");
+}
+
+// Solves a created physics manifold between two physics bodies
+static void SolvePhysicsManifold(PhysicsManifold manifold)
+{
+ switch (manifold->bodyA->shape.type)
+ {
+ case PHYSICS_CIRCLE:
+ {
+ switch (manifold->bodyB->shape.type)
+ {
+ case PHYSICS_CIRCLE: SolveCircleToCircle(manifold); break;
+ case PHYSICS_POLYGON: SolveCircleToPolygon(manifold); break;
+ default: break;
+ }
+ } break;
+ case PHYSICS_POLYGON:
+ {
+ switch (manifold->bodyB->shape.type)
+ {
+ case PHYSICS_CIRCLE: SolvePolygonToCircle(manifold); break;
+ case PHYSICS_POLYGON: SolvePolygonToPolygon(manifold); break;
+ default: break;
+ }
+ } break;
+ default: break;
+ }
+
+ // Update physics body grounded state if normal direction is down and grounded state is not set yet in previous manifolds
+ if (!manifold->bodyB->isGrounded) manifold->bodyB->isGrounded = (manifold->normal.y < 0);
+}
+
+// Solves collision between two circle shape physics bodies
+static void SolveCircleToCircle(PhysicsManifold manifold)
+{
+ PhysicsBody bodyA = manifold->bodyA;
+ PhysicsBody bodyB = manifold->bodyB;
+
+ if ((bodyA == NULL) || (bodyB == NULL)) return;
+
+ // Calculate translational vector, which is normal
+ Vector2 normal = MathVector2Subtract(bodyB->position, bodyA->position);
+
+ float distSqr = MathVector2SqrLen(normal);
+ float radius = bodyA->shape.radius + bodyB->shape.radius;
+
+ // Check if circles are not in contact
+ if (distSqr >= radius*radius)
+ {
+ manifold->contactsCount = 0;
+ return;
+ }
+
+ float distance = sqrtf(distSqr);
+ manifold->contactsCount = 1;
+
+ if (distance == 0.0f)
+ {
+ manifold->penetration = bodyA->shape.radius;
+ manifold->normal = CLITERAL(Vector2){ 1.0f, 0.0f };
+ manifold->contacts[0] = bodyA->position;
+ }
+ else
+ {
+ manifold->penetration = radius - distance;
+ manifold->normal = CLITERAL(Vector2){ normal.x/distance, normal.y/distance }; // Faster than using MathVector2Normalize() due to sqrt is already performed
+ manifold->contacts[0] = CLITERAL(Vector2){ manifold->normal.x*bodyA->shape.radius + bodyA->position.x, manifold->normal.y*bodyA->shape.radius + bodyA->position.y };
+ }
+
+ // Update physics body grounded state if normal direction is down
+ if (!bodyA->isGrounded) bodyA->isGrounded = (manifold->normal.y < 0);
+}
+
+// Solves collision between a circle to a polygon shape physics bodies
+static void SolveCircleToPolygon(PhysicsManifold manifold)
+{
+ PhysicsBody bodyA = manifold->bodyA;
+ PhysicsBody bodyB = manifold->bodyB;
+
+ if ((bodyA == NULL) || (bodyB == NULL)) return;
+
+ manifold->contactsCount = 0;
+
+ // Transform circle center to polygon transform space
+ Vector2 center = bodyA->position;
+ center = MathMatVector2Product(MathMatTranspose(bodyB->shape.transform), MathVector2Subtract(center, bodyB->position));
+
+ // Find edge with minimum penetration
+ // It is the same concept as using support points in SolvePolygonToPolygon
+ float separation = -PHYSAC_FLT_MAX;
+ int faceNormal = 0;
+ PhysicsVertexData vertexData = bodyB->shape.vertexData;
+
+ for (unsigned int i = 0; i < vertexData.vertexCount; i++)
+ {
+ float currentSeparation = MathVector2DotProduct(vertexData.normals[i], MathVector2Subtract(center, vertexData.positions[i]));
+
+ if (currentSeparation > bodyA->shape.radius) return;
+
+ if (currentSeparation > separation)
+ {
+ separation = currentSeparation;
+ faceNormal = i;
+ }
+ }
+
+ // Grab face's vertices
+ Vector2 v1 = vertexData.positions[faceNormal];
+ int nextIndex = (((faceNormal + 1) < (int)vertexData.vertexCount) ? (faceNormal + 1) : 0);
+ Vector2 v2 = vertexData.positions[nextIndex];
+
+ // Check to see if center is within polygon
+ if (separation < PHYSAC_EPSILON)
+ {
+ manifold->contactsCount = 1;
+ Vector2 normal = MathMatVector2Product(bodyB->shape.transform, vertexData.normals[faceNormal]);
+ manifold->normal = CLITERAL(Vector2){ -normal.x, -normal.y };
+ manifold->contacts[0] = CLITERAL(Vector2){ manifold->normal.x*bodyA->shape.radius + bodyA->position.x, manifold->normal.y*bodyA->shape.radius + bodyA->position.y };
+ manifold->penetration = bodyA->shape.radius;
+ return;
+ }
+
+ // Determine which voronoi region of the edge center of circle lies within
+ float dot1 = MathVector2DotProduct(MathVector2Subtract(center, v1), MathVector2Subtract(v2, v1));
+ float dot2 = MathVector2DotProduct(MathVector2Subtract(center, v2), MathVector2Subtract(v1, v2));
+ manifold->penetration = bodyA->shape.radius - separation;
+
+ if (dot1 <= 0.0f) // Closest to v1
+ {
+ if (MathVector2SqrDistance(center, v1) > bodyA->shape.radius*bodyA->shape.radius) return;
+
+ manifold->contactsCount = 1;
+ Vector2 normal = MathVector2Subtract(v1, center);
+ normal = MathMatVector2Product(bodyB->shape.transform, normal);
+ MathVector2Normalize(&normal);
+ manifold->normal = normal;
+ v1 = MathMatVector2Product(bodyB->shape.transform, v1);
+ v1 = MathVector2Add(v1, bodyB->position);
+ manifold->contacts[0] = v1;
+ }
+ else if (dot2 <= 0.0f) // Closest to v2
+ {
+ if (MathVector2SqrDistance(center, v2) > bodyA->shape.radius*bodyA->shape.radius) return;
+
+ manifold->contactsCount = 1;
+ Vector2 normal = MathVector2Subtract(v2, center);
+ v2 = MathMatVector2Product(bodyB->shape.transform, v2);
+ v2 = MathVector2Add(v2, bodyB->position);
+ manifold->contacts[0] = v2;
+ normal = MathMatVector2Product(bodyB->shape.transform, normal);
+ MathVector2Normalize(&normal);
+ manifold->normal = normal;
+ }
+ else // Closest to face
+ {
+ Vector2 normal = vertexData.normals[faceNormal];
+
+ if (MathVector2DotProduct(MathVector2Subtract(center, v1), normal) > bodyA->shape.radius) return;
+
+ normal = MathMatVector2Product(bodyB->shape.transform, normal);
+ manifold->normal = CLITERAL(Vector2){ -normal.x, -normal.y };
+ manifold->contacts[0] = CLITERAL(Vector2){ manifold->normal.x*bodyA->shape.radius + bodyA->position.x, manifold->normal.y*bodyA->shape.radius + bodyA->position.y };
+ manifold->contactsCount = 1;
+ }
+}
+
+// Solves collision between a polygon to a circle shape physics bodies
+static void SolvePolygonToCircle(PhysicsManifold manifold)
+{
+ PhysicsBody bodyA = manifold->bodyA;
+ PhysicsBody bodyB = manifold->bodyB;
+
+ if ((bodyA == NULL) || (bodyB == NULL)) return;
+
+ manifold->bodyA = bodyB;
+ manifold->bodyB = bodyA;
+ SolveCircleToPolygon(manifold);
+
+ manifold->normal.x *= -1.0f;
+ manifold->normal.y *= -1.0f;
+}
+
+// Solves collision between two polygons shape physics bodies
+static void SolvePolygonToPolygon(PhysicsManifold manifold)
+{
+ if ((manifold->bodyA == NULL) || (manifold->bodyB == NULL)) return;
+
+ PhysicsShape bodyA = manifold->bodyA->shape;
+ PhysicsShape bodyB = manifold->bodyB->shape;
+ manifold->contactsCount = 0;
+
+ // Check for separating axis with A shape's face planes
+ int faceA = 0;
+ float penetrationA = FindAxisLeastPenetration(&faceA, bodyA, bodyB);
+ if (penetrationA >= 0.0f) return;
+
+ // Check for separating axis with B shape's face planes
+ int faceB = 0;
+ float penetrationB = FindAxisLeastPenetration(&faceB, bodyB, bodyA);
+ if (penetrationB >= 0.0f) return;
+
+ int referenceIndex = 0;
+ bool flip = false; // Always point from A shape to B shape
+
+ PhysicsShape refPoly; // Reference
+ PhysicsShape incPoly; // Incident
+
+ // Determine which shape contains reference face
+ // Checking bias range for penetration
+ if (penetrationA >= (penetrationB*0.95f + penetrationA*0.01f))
+ {
+ refPoly = bodyA;
+ incPoly = bodyB;
+ referenceIndex = faceA;
+ }
+ else
+ {
+ refPoly = bodyB;
+ incPoly = bodyA;
+ referenceIndex = faceB;
+ flip = true;
+ }
+
+ // World space incident face
+ Vector2 incidentFace[2];
+ FindIncidentFace(&incidentFace[0], &incidentFace[1], refPoly, incPoly, referenceIndex);
+
+ // Setup reference face vertices
+ PhysicsVertexData refData = refPoly.vertexData;
+ Vector2 v1 = refData.positions[referenceIndex];
+ referenceIndex = (((referenceIndex + 1) < (int)refData.vertexCount) ? (referenceIndex + 1) : 0);
+ Vector2 v2 = refData.positions[referenceIndex];
+
+ // Transform vertices to world space
+ v1 = MathMatVector2Product(refPoly.transform, v1);
+ v1 = MathVector2Add(v1, refPoly.body->position);
+ v2 = MathMatVector2Product(refPoly.transform, v2);
+ v2 = MathVector2Add(v2, refPoly.body->position);
+
+ // Calculate reference face side normal in world space
+ Vector2 sidePlaneNormal = MathVector2Subtract(v2, v1);
+ MathVector2Normalize(&sidePlaneNormal);
+
+ // Orthogonalize
+ Vector2 refFaceNormal = { sidePlaneNormal.y, -sidePlaneNormal.x };
+ float refC = MathVector2DotProduct(refFaceNormal, v1);
+ float negSide = MathVector2DotProduct(sidePlaneNormal, v1)*-1;
+ float posSide = MathVector2DotProduct(sidePlaneNormal, v2);
+
+ // MathVector2Clip incident face to reference face side planes (due to floating point error, possible to not have required points
+ if (MathVector2Clip(CLITERAL(Vector2){ -sidePlaneNormal.x, -sidePlaneNormal.y }, &incidentFace[0], &incidentFace[1], negSide) < 2) return;
+ if (MathVector2Clip(sidePlaneNormal, &incidentFace[0], &incidentFace[1], posSide) < 2) return;
+
+ // Flip normal if required
+ manifold->normal = (flip ? CLITERAL(Vector2){ -refFaceNormal.x, -refFaceNormal.y } : refFaceNormal);
+
+ // Keep points behind reference face
+ int currentPoint = 0; // MathVector2Clipped points behind reference face
+ float separation = MathVector2DotProduct(refFaceNormal, incidentFace[0]) - refC;
+ if (separation <= 0.0f)
+ {
+ manifold->contacts[currentPoint] = incidentFace[0];
+ manifold->penetration = -separation;
+ currentPoint++;
+ }
+ else manifold->penetration = 0.0f;
+
+ separation = MathVector2DotProduct(refFaceNormal, incidentFace[1]) - refC;
+
+ if (separation <= 0.0f)
+ {
+ manifold->contacts[currentPoint] = incidentFace[1];
+ manifold->penetration += -separation;
+ currentPoint++;
+
+ // Calculate total penetration average
+ manifold->penetration /= currentPoint;
+ }
+
+ manifold->contactsCount = currentPoint;
+}
+
+// Integrates physics forces into velocity
+static void IntegratePhysicsForces(PhysicsBody body)
+{
+ if ((body == NULL) || (body->inverseMass == 0.0f) || !body->enabled) return;
+
+ body->velocity.x += (float)((body->force.x*body->inverseMass)*(deltaTime/2.0));
+ body->velocity.y += (float)((body->force.y*body->inverseMass)*(deltaTime/2.0));
+
+ if (body->useGravity)
+ {
+ body->velocity.x += (float)(gravityForce.x*(deltaTime/1000/2.0));
+ body->velocity.y += (float)(gravityForce.y*(deltaTime/1000/2.0));
+ }
+
+ if (!body->freezeOrient) body->angularVelocity += (float)(body->torque*body->inverseInertia*(deltaTime/2.0));
+}
+
+// Initializes physics manifolds to solve collisions
+static void InitializePhysicsManifolds(PhysicsManifold manifold)
+{
+ PhysicsBody bodyA = manifold->bodyA;
+ PhysicsBody bodyB = manifold->bodyB;
+
+ if ((bodyA == NULL) || (bodyB == NULL)) return;
+
+ // Calculate average restitution, static and dynamic friction
+ manifold->restitution = sqrtf(bodyA->restitution*bodyB->restitution);
+ manifold->staticFriction = sqrtf(bodyA->staticFriction*bodyB->staticFriction);
+ manifold->dynamicFriction = sqrtf(bodyA->dynamicFriction*bodyB->dynamicFriction);
+
+ for (unsigned int i = 0; i < manifold->contactsCount; i++)
+ {
+ // Caculate radius from center of mass to contact
+ Vector2 radiusA = MathVector2Subtract(manifold->contacts[i], bodyA->position);
+ Vector2 radiusB = MathVector2Subtract(manifold->contacts[i], bodyB->position);
+
+ Vector2 crossA = MathVector2Product(radiusA, bodyA->angularVelocity);
+ Vector2 crossB = MathVector2Product(radiusB, bodyB->angularVelocity);
+
+ Vector2 radiusV = { 0.0f, 0.0f };
+ radiusV.x = bodyB->velocity.x + crossB.x - bodyA->velocity.x - crossA.x;
+ radiusV.y = bodyB->velocity.y + crossB.y - bodyA->velocity.y - crossA.y;
+
+ // Determine if we should perform a resting collision or not;
+ // The idea is if the only thing moving this object is gravity, then the collision should be performed without any restitution
+ if (MathVector2SqrLen(radiusV) < (MathVector2SqrLen(CLITERAL(Vector2){ (float)(gravityForce.x*deltaTime/1000), (float)(gravityForce.y*deltaTime/1000) }) + PHYSAC_EPSILON)) manifold->restitution = 0;
+ }
+}
+
+// Integrates physics collisions impulses to solve collisions
+static void IntegratePhysicsImpulses(PhysicsManifold manifold)
+{
+ PhysicsBody bodyA = manifold->bodyA;
+ PhysicsBody bodyB = manifold->bodyB;
+
+ if ((bodyA == NULL) || (bodyB == NULL)) return;
+
+ // Early out and positional correct if both objects have infinite mass
+ if (fabs(bodyA->inverseMass + bodyB->inverseMass) <= PHYSAC_EPSILON)
+ {
+ bodyA->velocity = PHYSAC_VECTOR_ZERO;
+ bodyB->velocity = PHYSAC_VECTOR_ZERO;
+ return;
+ }
+
+ for (unsigned int i = 0; i < manifold->contactsCount; i++)
+ {
+ // Calculate radius from center of mass to contact
+ Vector2 radiusA = MathVector2Subtract(manifold->contacts[i], bodyA->position);
+ Vector2 radiusB = MathVector2Subtract(manifold->contacts[i], bodyB->position);
+
+ // Calculate relative velocity
+ Vector2 radiusV = { 0.0f, 0.0f };
+ radiusV.x = bodyB->velocity.x + MathVector2Product(radiusB, bodyB->angularVelocity).x - bodyA->velocity.x - MathVector2Product(radiusA, bodyA->angularVelocity).x;
+ radiusV.y = bodyB->velocity.y + MathVector2Product(radiusB, bodyB->angularVelocity).y - bodyA->velocity.y - MathVector2Product(radiusA, bodyA->angularVelocity).y;
+
+ // Relative velocity along the normal
+ float contactVelocity = MathVector2DotProduct(radiusV, manifold->normal);
+
+ // Do not resolve if velocities are separating
+ if (contactVelocity > 0.0f) return;
+
+ float raCrossN = MathVector2CrossProduct(radiusA, manifold->normal);
+ float rbCrossN = MathVector2CrossProduct(radiusB, manifold->normal);
+
+ float inverseMassSum = bodyA->inverseMass + bodyB->inverseMass + (raCrossN*raCrossN)*bodyA->inverseInertia + (rbCrossN*rbCrossN)*bodyB->inverseInertia;
+
+ // Calculate impulse scalar value
+ float impulse = -(1.0f + manifold->restitution)*contactVelocity;
+ impulse /= inverseMassSum;
+ impulse /= (float)manifold->contactsCount;
+
+ // Apply impulse to each physics body
+ Vector2 impulseV = { manifold->normal.x*impulse, manifold->normal.y*impulse };
+
+ if (bodyA->enabled)
+ {
+ bodyA->velocity.x += bodyA->inverseMass*(-impulseV.x);
+ bodyA->velocity.y += bodyA->inverseMass*(-impulseV.y);
+ if (!bodyA->freezeOrient) bodyA->angularVelocity += bodyA->inverseInertia*MathVector2CrossProduct(radiusA, CLITERAL(Vector2){ -impulseV.x, -impulseV.y });
+ }
+
+ if (bodyB->enabled)
+ {
+ bodyB->velocity.x += bodyB->inverseMass*(impulseV.x);
+ bodyB->velocity.y += bodyB->inverseMass*(impulseV.y);
+ if (!bodyB->freezeOrient) bodyB->angularVelocity += bodyB->inverseInertia*MathVector2CrossProduct(radiusB, impulseV);
+ }
+
+ // Apply friction impulse to each physics body
+ radiusV.x = bodyB->velocity.x + MathVector2Product(radiusB, bodyB->angularVelocity).x - bodyA->velocity.x - MathVector2Product(radiusA, bodyA->angularVelocity).x;
+ radiusV.y = bodyB->velocity.y + MathVector2Product(radiusB, bodyB->angularVelocity).y - bodyA->velocity.y - MathVector2Product(radiusA, bodyA->angularVelocity).y;
+
+ Vector2 tangent = { radiusV.x - (manifold->normal.x*MathVector2DotProduct(radiusV, manifold->normal)), radiusV.y - (manifold->normal.y*MathVector2DotProduct(radiusV, manifold->normal)) };
+ MathVector2Normalize(&tangent);
+
+ // Calculate impulse tangent magnitude
+ float impulseTangent = -MathVector2DotProduct(radiusV, tangent);
+ impulseTangent /= inverseMassSum;
+ impulseTangent /= (float)manifold->contactsCount;
+
+ float absImpulseTangent = (float)fabs(impulseTangent);
+
+ // Don't apply tiny friction impulses
+ if (absImpulseTangent <= PHYSAC_EPSILON) return;
+
+ // Apply coulumb's law
+ Vector2 tangentImpulse = { 0.0f, 0.0f };
+ if (absImpulseTangent < impulse*manifold->staticFriction) tangentImpulse = CLITERAL(Vector2){ tangent.x*impulseTangent, tangent.y*impulseTangent };
+ else tangentImpulse = CLITERAL(Vector2){ tangent.x*-impulse*manifold->dynamicFriction, tangent.y*-impulse*manifold->dynamicFriction };
+
+ // Apply friction impulse
+ if (bodyA->enabled)
+ {
+ bodyA->velocity.x += bodyA->inverseMass*(-tangentImpulse.x);
+ bodyA->velocity.y += bodyA->inverseMass*(-tangentImpulse.y);
+
+ if (!bodyA->freezeOrient) bodyA->angularVelocity += bodyA->inverseInertia*MathVector2CrossProduct(radiusA, CLITERAL(Vector2){ -tangentImpulse.x, -tangentImpulse.y });
+ }
+
+ if (bodyB->enabled)
+ {
+ bodyB->velocity.x += bodyB->inverseMass*(tangentImpulse.x);
+ bodyB->velocity.y += bodyB->inverseMass*(tangentImpulse.y);
+
+ if (!bodyB->freezeOrient) bodyB->angularVelocity += bodyB->inverseInertia*MathVector2CrossProduct(radiusB, tangentImpulse);
+ }
+ }
+}
+
+// Integrates physics velocity into position and forces
+static void IntegratePhysicsVelocity(PhysicsBody body)
+{
+ if ((body == NULL) ||!body->enabled) return;
+
+ body->position.x += (float)(body->velocity.x*deltaTime);
+ body->position.y += (float)(body->velocity.y*deltaTime);
+
+ if (!body->freezeOrient) body->orient += (float)(body->angularVelocity*deltaTime);
+ body->shape.transform = MathMatFromRadians(body->orient);
+
+ IntegratePhysicsForces(body);
+}
+
+// Corrects physics bodies positions based on manifolds collision information
+static void CorrectPhysicsPositions(PhysicsManifold manifold)
+{
+ PhysicsBody bodyA = manifold->bodyA;
+ PhysicsBody bodyB = manifold->bodyB;
+
+ if ((bodyA == NULL) || (bodyB == NULL)) return;
+
+ Vector2 correction = { 0.0f, 0.0f };
+ correction.x = (PHYSAC_MAX(manifold->penetration - PHYSAC_PENETRATION_ALLOWANCE, 0.0f)/(bodyA->inverseMass + bodyB->inverseMass))*manifold->normal.x*PHYSAC_PENETRATION_CORRECTION;
+ correction.y = (PHYSAC_MAX(manifold->penetration - PHYSAC_PENETRATION_ALLOWANCE, 0.0f)/(bodyA->inverseMass + bodyB->inverseMass))*manifold->normal.y*PHYSAC_PENETRATION_CORRECTION;
+
+ if (bodyA->enabled)
+ {
+ bodyA->position.x -= correction.x*bodyA->inverseMass;
+ bodyA->position.y -= correction.y*bodyA->inverseMass;
+ }
+
+ if (bodyB->enabled)
+ {
+ bodyB->position.x += correction.x*bodyB->inverseMass;
+ bodyB->position.y += correction.y*bodyB->inverseMass;
+ }
+}
+
+// Returns the extreme point along a direction within a polygon
+static Vector2 GetSupport(PhysicsShape shape, Vector2 dir)
+{
+ float bestProjection = -PHYSAC_FLT_MAX;
+ Vector2 bestVertex = { 0.0f, 0.0f };
+ PhysicsVertexData data = shape.vertexData;
+
+ for (unsigned int i = 0; i < data.vertexCount; i++)
+ {
+ Vector2 vertex = data.positions[i];
+ float projection = MathVector2DotProduct(vertex, dir);
+
+ if (projection > bestProjection)
+ {
+ bestVertex = vertex;
+ bestProjection = projection;
+ }
+ }
+
+ return bestVertex;
+}
+
+// Finds polygon shapes axis least penetration
+static float FindAxisLeastPenetration(int *faceIndex, PhysicsShape shapeA, PhysicsShape shapeB)
+{
+ float bestDistance = -PHYSAC_FLT_MAX;
+ int bestIndex = 0;
+
+ PhysicsVertexData dataA = shapeA.vertexData;
+ //PhysicsVertexData dataB = shapeB.vertexData;
+
+ for (unsigned int i = 0; i < dataA.vertexCount; i++)
+ {
+ // Retrieve a face normal from A shape
+ Vector2 normal = dataA.normals[i];
+ Vector2 transNormal = MathMatVector2Product(shapeA.transform, normal);
+
+ // Transform face normal into B shape's model space
+ Matrix2x2 buT = MathMatTranspose(shapeB.transform);
+ normal = MathMatVector2Product(buT, transNormal);
+
+ // Retrieve support point from B shape along -n
+ Vector2 support = GetSupport(shapeB, CLITERAL(Vector2){ -normal.x, -normal.y });
+
+ // Retrieve vertex on face from A shape, transform into B shape's model space
+ Vector2 vertex = dataA.positions[i];
+ vertex = MathMatVector2Product(shapeA.transform, vertex);
+ vertex = MathVector2Add(vertex, shapeA.body->position);
+ vertex = MathVector2Subtract(vertex, shapeB.body->position);
+ vertex = MathMatVector2Product(buT, vertex);
+
+ // Compute penetration distance in B shape's model space
+ float distance = MathVector2DotProduct(normal, MathVector2Subtract(support, vertex));
+
+ // Store greatest distance
+ if (distance > bestDistance)
+ {
+ bestDistance = distance;
+ bestIndex = i;
+ }
+ }
+
+ *faceIndex = bestIndex;
+ return bestDistance;
+}
+
+// Finds two polygon shapes incident face
+static void FindIncidentFace(Vector2 *v0, Vector2 *v1, PhysicsShape ref, PhysicsShape inc, int index)
+{
+ PhysicsVertexData refData = ref.vertexData;
+ PhysicsVertexData incData = inc.vertexData;
+
+ Vector2 referenceNormal = refData.normals[index];
+
+ // Calculate normal in incident's frame of reference
+ referenceNormal = MathMatVector2Product(ref.transform, referenceNormal); // To world space
+ referenceNormal = MathMatVector2Product(MathMatTranspose(inc.transform), referenceNormal); // To incident's model space
+
+ // Find most anti-normal face on polygon
+ int incidentFace = 0;
+ float minDot = PHYSAC_FLT_MAX;
+
+ for (unsigned int i = 0; i < incData.vertexCount; i++)
+ {
+ float dot = MathVector2DotProduct(referenceNormal, incData.normals[i]);
+
+ if (dot < minDot)
+ {
+ minDot = dot;
+ incidentFace = i;
+ }
+ }
+
+ // Assign face vertices for incident face
+ *v0 = MathMatVector2Product(inc.transform, incData.positions[incidentFace]);
+ *v0 = MathVector2Add(*v0, inc.body->position);
+ incidentFace = (((incidentFace + 1) < (int)incData.vertexCount) ? (incidentFace + 1) : 0);
+ *v1 = MathMatVector2Product(inc.transform, incData.positions[incidentFace]);
+ *v1 = MathVector2Add(*v1, inc.body->position);
+}
+
+// Returns clipping value based on a normal and two faces
+static int MathVector2Clip(Vector2 normal, Vector2 *faceA, Vector2 *faceB, float clip)
+{
+ int sp = 0;
+ Vector2 out[2] = { *faceA, *faceB };
+
+ // Retrieve distances from each endpoint to the line
+ float distanceA = MathVector2DotProduct(normal, *faceA) - clip;
+ float distanceB = MathVector2DotProduct(normal, *faceB) - clip;
+
+ // If negative (behind plane)
+ if (distanceA <= 0.0f) out[sp++] = *faceA;
+ if (distanceB <= 0.0f) out[sp++] = *faceB;
+
+ // If the points are on different sides of the plane
+ if ((distanceA*distanceB) < 0.0f)
+ {
+ // Push intersection point
+ float alpha = distanceA/(distanceA - distanceB);
+ out[sp] = *faceA;
+ Vector2 delta = MathVector2Subtract(*faceB, *faceA);
+ delta.x *= alpha;
+ delta.y *= alpha;
+ out[sp] = MathVector2Add(out[sp], delta);
+ sp++;
+ }
+
+ // Assign the new converted values
+ *faceA = out[0];
+ *faceB = out[1];
+
+ return sp;
+}
+
+// Returns the barycenter of a triangle given by 3 points
+static Vector2 MathTriangleBarycenter(Vector2 v1, Vector2 v2, Vector2 v3)
+{
+ Vector2 result = { 0.0f, 0.0f };
+
+ result.x = (v1.x + v2.x + v3.x)/3;
+ result.y = (v1.y + v2.y + v3.y)/3;
+
+ return result;
+}
+
+#if !defined(PHYSAC_AVOID_TIMMING_SYSTEM)
+// Initializes hi-resolution MONOTONIC timer
+static void InitTimer(void)
+{
+#if defined(_WIN32)
+ QueryPerformanceFrequency((unsigned long long int *) &frequency);
+#endif
+
+#if defined(__EMSCRIPTEN__) || defined(__linux__)
+ struct timespec now;
+ if (clock_gettime(CLOCK_MONOTONIC, &now) == 0) frequency = 1000000000;
+#endif
+
+#if defined(__APPLE__)
+ mach_timebase_info_data_t timebase;
+ mach_timebase_info(&timebase);
+ frequency = (timebase.denom*1e9)/timebase.numer;
+#endif
+
+ baseClockTicks = (double)GetClockTicks(); // Get MONOTONIC clock time offset
+ startTime = GetCurrentTime(); // Get current time in milliseconds
+}
+
+// Get hi-res MONOTONIC time measure in clock ticks
+static unsigned long long int GetClockTicks(void)
+{
+ unsigned long long int value = 0;
+
+#if defined(_WIN32)
+ QueryPerformanceCounter((unsigned long long int *) &value);
+#endif
+
+#if defined(__linux__)
+ struct timespec now;
+ clock_gettime(CLOCK_MONOTONIC, &now);
+ value = (unsigned long long int)now.tv_sec*(unsigned long long int)1000000000 + (unsigned long long int)now.tv_nsec;
+#endif
+
+#if defined(__APPLE__)
+ value = mach_absolute_time();
+#endif
+
+ return value;
+}
+
+// Get current time in milliseconds
+static double GetCurrentTime(void)
+{
+ return (double)(GetClockTicks() - baseClockTicks)/frequency*1000;
+}
+#endif // !PHYSAC_AVOID_TIMMING_SYSTEM
+
+
+// Returns the cross product of a vector and a value
+static inline Vector2 MathVector2Product(Vector2 vector, float value)
+{
+ Vector2 result = { -value*vector.y, value*vector.x };
+ return result;
+}
+
+// Returns the cross product of two vectors
+static inline float MathVector2CrossProduct(Vector2 v1, Vector2 v2)
+{
+ return (v1.x*v2.y - v1.y*v2.x);
+}
+
+// Returns the len square root of a vector
+static inline float MathVector2SqrLen(Vector2 vector)
+{
+ return (vector.x*vector.x + vector.y*vector.y);
+}
+
+// Returns the dot product of two vectors
+static inline float MathVector2DotProduct(Vector2 v1, Vector2 v2)
+{
+ return (v1.x*v2.x + v1.y*v2.y);
+}
+
+// Returns the square root of distance between two vectors
+static inline float MathVector2SqrDistance(Vector2 v1, Vector2 v2)
+{
+ Vector2 dir = MathVector2Subtract(v1, v2);
+ return MathVector2DotProduct(dir, dir);
+}
+
+// Returns the normalized values of a vector
+static void MathVector2Normalize(Vector2 *vector)
+{
+ float length, ilength;
+
+ Vector2 aux = *vector;
+ length = sqrtf(aux.x*aux.x + aux.y*aux.y);
+
+ if (length == 0) length = 1.0f;
+
+ ilength = 1.0f/length;
+
+ vector->x *= ilength;
+ vector->y *= ilength;
+}
+
+// Returns the sum of two given vectors
+static inline Vector2 MathVector2Add(Vector2 v1, Vector2 v2)
+{
+ Vector2 result = { v1.x + v2.x, v1.y + v2.y };
+ return result;
+}
+
+// Returns the subtract of two given vectors
+static inline Vector2 MathVector2Subtract(Vector2 v1, Vector2 v2)
+{
+ Vector2 result = { v1.x - v2.x, v1.y - v2.y };
+ return result;
+}
+
+// Creates a matrix 2x2 from a given radians value
+static Matrix2x2 MathMatFromRadians(float radians)
+{
+ float cos = cosf(radians);
+ float sin = sinf(radians);
+
+ Matrix2x2 result = { cos, -sin, sin, cos };
+ return result;
+}
+
+// Returns the transpose of a given matrix 2x2
+static inline Matrix2x2 MathMatTranspose(Matrix2x2 matrix)
+{
+ Matrix2x2 result = { matrix.m00, matrix.m10, matrix.m01, matrix.m11 };
+ return result;
+}
+
+// Multiplies a vector by a matrix 2x2
+static inline Vector2 MathMatVector2Product(Matrix2x2 matrix, Vector2 vector)
+{
+ Vector2 result = { matrix.m00*vector.x + matrix.m01*vector.y, matrix.m10*vector.x + matrix.m11*vector.y };
+ return result;
+}
+
+#endif // PHYSAC_IMPLEMENTATION
diff --git a/src/extras/rmem.h b/src/extras/rmem.h
new file mode 100644
index 00000000..dbf417fe
--- /dev/null
+++ b/src/extras/rmem.h
@@ -0,0 +1,739 @@
+/**********************************************************************************************
+*
+* rmem - raylib memory pool and objects pool
+*
+* A quick, efficient, and minimal free list and arena-based allocator
+*
+* PURPOSE:
+* - A quicker, efficient memory allocator alternative to 'malloc' and friends.
+* - Reduce the possibilities of memory leaks for beginner developers using Raylib.
+* - Being able to flexibly range check memory if necessary.
+*
+* CONFIGURATION:
+*
+* #define RMEM_IMPLEMENTATION
+* Generates the implementation of the library into the included file.
+* If not defined, the library is in header only mode and can be included in other headers
+* or source files without problems. But only ONE file should hold the implementation.
+*
+*
+* LICENSE: zlib/libpng
+*
+* Copyright (c) 2019 Kevin 'Assyrianic' Yonan (@assyrianic) and reviewed by Ramon Santamaria (@raysan5)
+*
+* This software is provided "as-is", without any express or implied warranty. In no event
+* will the authors be held liable for any damages arising from the use of this software.
+*
+* Permission is granted to anyone to use this software for any purpose, including commercial
+* applications, and to alter it and redistribute it freely, subject to the following restrictions:
+*
+* 1. The origin of this software must not be misrepresented; you must not claim that you
+* wrote the original software. If you use this software in a product, an acknowledgment
+* in the product documentation would be appreciated but is not required.
+*
+* 2. Altered source versions must be plainly marked as such, and must not be misrepresented
+* as being the original software.
+*
+* 3. This notice may not be removed or altered from any source distribution.
+*
+**********************************************************************************************/
+
+#ifndef RMEM_H
+#define RMEM_H
+
+#include <inttypes.h>
+#include <stdbool.h>
+
+//----------------------------------------------------------------------------------
+// Defines and Macros
+//----------------------------------------------------------------------------------
+#if defined(_WIN32) && defined(BUILD_LIBTYPE_SHARED)
+ #define RMEMAPI __declspec(dllexport) // We are building library as a Win32 shared library (.dll)
+#elif defined(_WIN32) && defined(USE_LIBTYPE_SHARED)
+ #define RMEMAPI __declspec(dllimport) // We are using library as a Win32 shared library (.dll)
+#else
+ #define RMEMAPI // We are building or using library as a static library (or Linux shared library)
+#endif
+
+#define RMEM_VERSION "v1.3" // changelog at bottom of header.
+
+//----------------------------------------------------------------------------------
+// Types and Structures Definition
+//----------------------------------------------------------------------------------
+
+// Memory Pool
+typedef struct MemNode MemNode;
+struct MemNode {
+ size_t size;
+ MemNode *next, *prev;
+};
+
+// Freelist implementation
+typedef struct AllocList {
+ MemNode *head, *tail;
+ size_t len;
+} AllocList;
+
+// Arena allocator.
+typedef struct Arena {
+ uintptr_t mem, offs;
+ size_t size;
+} Arena;
+
+
+enum {
+ MEMPOOL_BUCKET_SIZE = 8,
+ MEMPOOL_BUCKET_BITS = (sizeof(uintptr_t) >> 1) + 1,
+ MEM_SPLIT_THRESHOLD = sizeof(uintptr_t) * 4
+};
+
+typedef struct MemPool {
+ AllocList large, buckets[MEMPOOL_BUCKET_SIZE];
+ Arena arena;
+} MemPool;
+
+
+// Object Pool
+typedef struct ObjPool {
+ uintptr_t mem, offs;
+ size_t objSize, freeBlocks, memSize;
+} ObjPool;
+
+
+// Double-Ended Stack aka Deque
+typedef struct BiStack {
+ uintptr_t mem, front, back;
+ size_t size;
+} BiStack;
+
+
+#if defined(__cplusplus)
+extern "C" { // Prevents name mangling of functions
+#endif
+
+//------------------------------------------------------------------------------------
+// Functions Declaration - Memory Pool
+//------------------------------------------------------------------------------------
+RMEMAPI MemPool CreateMemPool(size_t bytes);
+RMEMAPI MemPool CreateMemPoolFromBuffer(void *buf, size_t bytes);
+RMEMAPI void DestroyMemPool(MemPool *mempool);
+
+RMEMAPI void *MemPoolAlloc(MemPool *mempool, size_t bytes);
+RMEMAPI void *MemPoolRealloc(MemPool *mempool, void *ptr, size_t bytes);
+RMEMAPI void MemPoolFree(MemPool *mempool, void *ptr);
+RMEMAPI void MemPoolCleanUp(MemPool *mempool, void **ptrref);
+RMEMAPI void MemPoolReset(MemPool *mempool);
+RMEMAPI size_t GetMemPoolFreeMemory(const MemPool mempool);
+
+//------------------------------------------------------------------------------------
+// Functions Declaration - Object Pool
+//------------------------------------------------------------------------------------
+RMEMAPI ObjPool CreateObjPool(size_t objsize, size_t len);
+RMEMAPI ObjPool CreateObjPoolFromBuffer(void *buf, size_t objsize, size_t len);
+RMEMAPI void DestroyObjPool(ObjPool *objpool);
+
+RMEMAPI void *ObjPoolAlloc(ObjPool *objpool);
+RMEMAPI void ObjPoolFree(ObjPool *objpool, void *ptr);
+RMEMAPI void ObjPoolCleanUp(ObjPool *objpool, void **ptrref);
+
+//------------------------------------------------------------------------------------
+// Functions Declaration - Double-Ended Stack
+//------------------------------------------------------------------------------------
+RMEMAPI BiStack CreateBiStack(size_t len);
+RMEMAPI BiStack CreateBiStackFromBuffer(void *buf, size_t len);
+RMEMAPI void DestroyBiStack(BiStack *destack);
+
+RMEMAPI void *BiStackAllocFront(BiStack *destack, size_t len);
+RMEMAPI void *BiStackAllocBack(BiStack *destack, size_t len);
+
+RMEMAPI void BiStackResetFront(BiStack *destack);
+RMEMAPI void BiStackResetBack(BiStack *destack);
+RMEMAPI void BiStackResetAll(BiStack *destack);
+
+RMEMAPI intptr_t BiStackMargins(BiStack destack);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // RMEM_H
+
+/***********************************************************************************
+*
+* RMEM IMPLEMENTATION
+*
+************************************************************************************/
+
+#if defined(RMEM_IMPLEMENTATION)
+
+#include <stdio.h> // Required for:
+#include <stdlib.h> // Required for:
+#include <string.h> // Required for:
+
+//----------------------------------------------------------------------------------
+// Defines and Macros
+//----------------------------------------------------------------------------------
+
+// Make sure restrict type qualifier for pointers is defined
+// NOTE: Not supported by C++, it is a C only keyword
+#if defined(_WIN32) || defined(_WIN64) || defined(__CYGWIN__) || defined(_MSC_VER)
+ #ifndef restrict
+ #define restrict __restrict
+ #endif
+#endif
+
+//----------------------------------------------------------------------------------
+// Global Variables Definition
+//----------------------------------------------------------------------------------
+// ...
+
+//----------------------------------------------------------------------------------
+// Module specific Functions Declaration
+//----------------------------------------------------------------------------------
+static inline size_t __AlignSize(const size_t size, const size_t align)
+{
+ return (size + (align - 1)) & -align;
+}
+
+static MemNode *__SplitMemNode(MemNode *const node, const size_t bytes)
+{
+ uintptr_t n = ( uintptr_t )node;
+ MemNode *const r = ( MemNode* )(n + (node->size - bytes));
+ node->size -= bytes;
+ r->size = bytes;
+ return r;
+}
+
+static void __InsertMemNodeBefore(AllocList *const list, MemNode *const insert, MemNode *const curr)
+{
+ insert->next = curr;
+ if (curr->prev==NULL) list->head = insert;
+ else
+ {
+ insert->prev = curr->prev;
+ curr->prev->next = insert;
+ }
+ curr->prev = insert;
+}
+
+static void __ReplaceMemNode(MemNode *const old, MemNode *const replace)
+{
+ replace->prev = old->prev;
+ replace->next = old->next;
+ if( old->prev != NULL )
+ old->prev->next = replace;
+ if( old->next != NULL )
+ old->next->prev = replace;
+}
+
+
+static MemNode *__RemoveMemNode(AllocList *const list, MemNode *const node)
+{
+ if (node->prev != NULL) node->prev->next = node->next;
+ else
+ {
+ list->head = node->next;
+ if (list->head != NULL) list->head->prev = NULL;
+ else list->tail = NULL;
+ }
+
+ if (node->next != NULL) node->next->prev = node->prev;
+ else
+ {
+ list->tail = node->prev;
+ if (list->tail != NULL) list->tail->next = NULL;
+ else list->head = NULL;
+ }
+ list->len--;
+ return node;
+}
+
+static MemNode *__FindMemNode(AllocList *const list, const size_t bytes)
+{
+ for (MemNode *node = list->head; node != NULL; node = node->next)
+ {
+ if (node->size < bytes) continue;
+ // close in size - reduce fragmentation by not splitting.
+ else if (node->size <= bytes + MEM_SPLIT_THRESHOLD) return __RemoveMemNode(list, node);
+ else return __SplitMemNode(node, bytes);
+ }
+ return NULL;
+}
+
+static void __InsertMemNode(MemPool *const mempool, AllocList *const list, MemNode *const node, const bool is_bucket)
+{
+ if (list->head == NULL)
+ {
+ list->head = node;
+ list->len++;
+ }
+ else
+ {
+ for (MemNode *iter = list->head; iter != NULL; iter = iter->next)
+ {
+ if (( uintptr_t )iter == mempool->arena.offs)
+ {
+ mempool->arena.offs += iter->size;
+ __RemoveMemNode(list, iter);
+ iter = list->head;
+ }
+ const uintptr_t inode = ( uintptr_t )node;
+ const uintptr_t iiter = ( uintptr_t )iter;
+ const uintptr_t iter_end = iiter + iter->size;
+ const uintptr_t node_end = inode + node->size;
+ if (iter==node) return;
+ else if (iter < node)
+ {
+ // node was coalesced prior.
+ if (iter_end > inode) return;
+ else if (iter_end==inode && !is_bucket)
+ {
+ // if we can coalesce, do so.
+ iter->size += node->size;
+ return;
+ }
+ }
+ else if (iter > node)
+ {
+ // Address sort, lowest to highest aka ascending order.
+ if (iiter < node_end) return;
+ else if (iter==list->head && !is_bucket)
+ {
+ if (iter_end==inode) iter->size += node->size;
+ else if (node_end==iiter)
+ {
+ node->size += list->head->size;
+ node->next = list->head->next;
+ node->prev = NULL;
+ list->head = node;
+ }
+ else
+ {
+ node->next = iter;
+ node->prev = NULL;
+ iter->prev = node;
+ list->head = node;
+ list->len++;
+ }
+ return;
+ }
+ else if (iter_end==inode && !is_bucket)
+ {
+ // if we can coalesce, do so.
+ iter->size += node->size;
+ return;
+ }
+ else
+ {
+ __InsertMemNodeBefore(list, iter, node);
+ list->len++;
+ return;
+ }
+ }
+ }
+ }
+}
+
+//----------------------------------------------------------------------------------
+// Module Functions Definition - Memory Pool
+//----------------------------------------------------------------------------------
+
+MemPool CreateMemPool(const size_t size)
+{
+ MemPool mempool = { 0 };
+
+ if (size == 0) return mempool;
+ else
+ {
+ // Align the mempool size to at least the size of an alloc node.
+ uint8_t *const restrict buf = malloc(size*sizeof *buf);
+ if (buf==NULL) return mempool;
+ else
+ {
+ mempool.arena.size = size;
+ mempool.arena.mem = ( uintptr_t )buf;
+ mempool.arena.offs = mempool.arena.mem + mempool.arena.size;
+ return mempool;
+ }
+ }
+}
+
+MemPool CreateMemPoolFromBuffer(void *const restrict buf, const size_t size)
+{
+ MemPool mempool = { 0 };
+ if ((size == 0) || (buf == NULL) || (size <= sizeof(MemNode))) return mempool;
+ else
+ {
+ mempool.arena.size = size;
+ mempool.arena.mem = ( uintptr_t )buf;
+ mempool.arena.offs = mempool.arena.mem + mempool.arena.size;
+ return mempool;
+ }
+}
+
+void DestroyMemPool(MemPool *const restrict mempool)
+{
+ if (mempool->arena.mem == 0) return;
+ else
+ {
+ void *const restrict ptr = ( void* )mempool->arena.mem;
+ free(ptr);
+ *mempool = (MemPool){ 0 };
+ }
+}
+
+void *MemPoolAlloc(MemPool *const mempool, const size_t size)
+{
+ if ((size == 0) || (size > mempool->arena.size)) return NULL;
+ else
+ {
+ MemNode *new_mem = NULL;
+ const size_t ALLOC_SIZE = __AlignSize(size + sizeof *new_mem, sizeof(intptr_t));
+ const size_t BUCKET_SLOT = (ALLOC_SIZE >> MEMPOOL_BUCKET_BITS) - 1;
+
+ // If the size is small enough, let's check if our buckets has a fitting memory block.
+ if (BUCKET_SLOT < MEMPOOL_BUCKET_SIZE)
+ {
+ new_mem = __FindMemNode(&mempool->buckets[BUCKET_SLOT], ALLOC_SIZE);
+ }
+ else if (mempool->large.head != NULL)
+ {
+ new_mem = __FindMemNode(&mempool->large, ALLOC_SIZE);
+ }
+
+ if (new_mem == NULL)
+ {
+ // not enough memory to support the size!
+ if ((mempool->arena.offs - ALLOC_SIZE) < mempool->arena.mem) return NULL;
+ else
+ {
+ // Couldn't allocate from a freelist, allocate from available mempool.
+ // Subtract allocation size from the mempool.
+ mempool->arena.offs -= ALLOC_SIZE;
+
+ // Use the available mempool space as the new node.
+ new_mem = ( MemNode* )mempool->arena.offs;
+ new_mem->size = ALLOC_SIZE;
+ }
+ }
+
+ // Visual of the allocation block.
+ // --------------
+ // | mem size | lowest addr of block
+ // | next node | 12 byte (32-bit) header
+ // | prev node | 24 byte (64-bit) header
+ // |------------|
+ // | alloc'd |
+ // | memory |
+ // | space | highest addr of block
+ // --------------
+ new_mem->next = new_mem->prev = NULL;
+ uint8_t *const restrict final_mem = ( uint8_t* )new_mem + sizeof *new_mem;
+ return memset(final_mem, 0, new_mem->size - sizeof *new_mem);
+ }
+}
+
+void *MemPoolRealloc(MemPool *const restrict mempool, void *const ptr, const size_t size)
+{
+ if (size > mempool->arena.size) return NULL;
+ // NULL ptr should make this work like regular Allocation.
+ else if (ptr == NULL) return MemPoolAlloc(mempool, size);
+ else if ((uintptr_t)ptr - sizeof(MemNode) < mempool->arena.mem) return NULL;
+ else
+ {
+ MemNode *const node = ( MemNode* )(( uint8_t* )ptr - sizeof *node);
+ const size_t NODE_SIZE = sizeof *node;
+ uint8_t *const resized_block = MemPoolAlloc(mempool, size);
+ if (resized_block == NULL) return NULL;
+ else
+ {
+ MemNode *const resized = ( MemNode* )(resized_block - sizeof *resized);
+ memmove(resized_block, ptr, (node->size > resized->size)? (resized->size - NODE_SIZE) : (node->size - NODE_SIZE));
+ MemPoolFree(mempool, ptr);
+ return resized_block;
+ }
+ }
+}
+
+void MemPoolFree(MemPool *const restrict mempool, void *const ptr)
+{
+ const uintptr_t p = ( uintptr_t )ptr;
+ if ((ptr == NULL) || (p - sizeof(MemNode) < mempool->arena.mem)) return;
+ else
+ {
+ // Behind the actual pointer data is the allocation info.
+ const uintptr_t block = p - sizeof(MemNode);
+ MemNode *const mem_node = ( MemNode* )block;
+ const size_t BUCKET_SLOT = (mem_node->size >> MEMPOOL_BUCKET_BITS) - 1;
+
+ // Make sure the pointer data is valid.
+ if ((block < mempool->arena.offs) ||
+ ((block - mempool->arena.mem) > mempool->arena.size) ||
+ (mem_node->size == 0) ||
+ (mem_node->size > mempool->arena.size)) return;
+ // If the mem_node is right at the arena offs, then merge it back to the arena.
+ else if (block == mempool->arena.offs)
+ {
+ mempool->arena.offs += mem_node->size;
+ }
+ else
+ {
+ // try to place it into bucket or large freelist.
+ struct AllocList *const l = (BUCKET_SLOT < MEMPOOL_BUCKET_SIZE) ? &mempool->buckets[BUCKET_SLOT] : &mempool->large;
+ __InsertMemNode(mempool, l, mem_node, (BUCKET_SLOT < MEMPOOL_BUCKET_SIZE));
+ }
+ }
+}
+
+void MemPoolCleanUp(MemPool *const restrict mempool, void **const ptrref)
+{
+ if ((ptrref == NULL) || (*ptrref == NULL)) return;
+ else
+ {
+ MemPoolFree(mempool, *ptrref);
+ *ptrref = NULL;
+ }
+}
+
+size_t GetMemPoolFreeMemory(const MemPool mempool)
+{
+ size_t total_remaining = mempool.arena.offs - mempool.arena.mem;
+
+ for (MemNode *n=mempool.large.head; n != NULL; n = n->next) total_remaining += n->size;
+
+ for (size_t i=0; i<MEMPOOL_BUCKET_SIZE; i++) for (MemNode *n = mempool.buckets[i].head; n != NULL; n = n->next) total_remaining += n->size;
+
+ return total_remaining;
+}
+
+void MemPoolReset(MemPool *const mempool)
+{
+ mempool->large.head = mempool->large.tail = NULL;
+ mempool->large.len = 0;
+ for (size_t i = 0; i < MEMPOOL_BUCKET_SIZE; i++)
+ {
+ mempool->buckets[i].head = mempool->buckets[i].tail = NULL;
+ mempool->buckets[i].len = 0;
+ }
+ mempool->arena.offs = mempool->arena.mem + mempool->arena.size;
+}
+
+//----------------------------------------------------------------------------------
+// Module Functions Definition - Object Pool
+//----------------------------------------------------------------------------------
+
+ObjPool CreateObjPool(const size_t objsize, const size_t len)
+{
+ ObjPool objpool = { 0 };
+ if ((len == 0) || (objsize == 0)) return objpool;
+ else
+ {
+ const size_t aligned_size = __AlignSize(objsize, sizeof(size_t));
+ uint8_t *const restrict buf = calloc(len, aligned_size);
+ if (buf == NULL) return objpool;
+ objpool.objSize = aligned_size;
+ objpool.memSize = objpool.freeBlocks = len;
+ objpool.mem = ( uintptr_t )buf;
+
+ for (size_t i=0; i<objpool.freeBlocks; i++)
+ {
+ size_t *const restrict index = ( size_t* )(objpool.mem + (i*aligned_size));
+ *index = i + 1;
+ }
+
+ objpool.offs = objpool.mem;
+ return objpool;
+ }
+}
+
+ObjPool CreateObjPoolFromBuffer(void *const restrict buf, const size_t objsize, const size_t len)
+{
+ ObjPool objpool = { 0 };
+
+ // If the object size isn't large enough to align to a size_t, then we can't use it.
+ const size_t aligned_size = __AlignSize(objsize, sizeof(size_t));
+ if ((buf == NULL) || (len == 0) || (objsize < sizeof(size_t)) || (objsize*len != aligned_size*len)) return objpool;
+ else
+ {
+ objpool.objSize = aligned_size;
+ objpool.memSize = objpool.freeBlocks = len;
+ objpool.mem = (uintptr_t)buf;
+
+ for (size_t i=0; i<objpool.freeBlocks; i++)
+ {
+ size_t *const restrict index = ( size_t* )(objpool.mem + (i*aligned_size));
+ *index = i + 1;
+ }
+
+ objpool.offs = objpool.mem;
+ return objpool;
+ }
+}
+
+void DestroyObjPool(ObjPool *const restrict objpool)
+{
+ if (objpool->mem == 0) return;
+ else
+ {
+ void *const restrict ptr = ( void* )objpool->mem;
+ free(ptr);
+ *objpool = (ObjPool){0};
+ }
+}
+
+void *ObjPoolAlloc(ObjPool *const objpool)
+{
+ if (objpool->freeBlocks > 0)
+ {
+ // For first allocation, head points to the very first index.
+ // Head = &pool[0];
+ // ret = Head == ret = &pool[0];
+ size_t *const restrict block = ( size_t* )objpool->offs;
+ objpool->freeBlocks--;
+
+ // after allocating, we set head to the address of the index that *Head holds.
+ // Head = &pool[*Head * pool.objsize];
+ objpool->offs = (objpool->freeBlocks != 0)? objpool->mem + (*block*objpool->objSize) : 0;
+ return memset(block, 0, objpool->objSize);
+ }
+ else return NULL;
+}
+
+void ObjPoolFree(ObjPool *const restrict objpool, void *const ptr)
+{
+ uintptr_t block = (uintptr_t)ptr;
+ if ((ptr == NULL) || (block < objpool->mem) || (block > objpool->mem + objpool->memSize*objpool->objSize)) return;
+ else
+ {
+ // When we free our pointer, we recycle the pointer space to store the previous index and then we push it as our new head.
+ // *p = index of Head in relation to the buffer;
+ // Head = p;
+ size_t *const restrict index = ( size_t* )block;
+ *index = (objpool->offs != 0)? (objpool->offs - objpool->mem)/objpool->objSize : objpool->memSize;
+ objpool->offs = block;
+ objpool->freeBlocks++;
+ }
+}
+
+void ObjPoolCleanUp(ObjPool *const restrict objpool, void **const restrict ptrref)
+{
+ if (ptrref == NULL) return;
+ else
+ {
+ ObjPoolFree(objpool, *ptrref);
+ *ptrref = NULL;
+ }
+}
+
+
+//----------------------------------------------------------------------------------
+// Module Functions Definition - Double-Ended Stack
+//----------------------------------------------------------------------------------
+BiStack CreateBiStack(const size_t len)
+{
+ BiStack destack = { 0 };
+ if (len == 0) return destack;
+
+ uint8_t *const buf = malloc(len*sizeof *buf);
+ if (buf==NULL) return destack;
+ destack.size = len;
+ destack.mem = ( uintptr_t )buf;
+ destack.front = destack.mem;
+ destack.back = destack.mem + len;
+ return destack;
+}
+
+BiStack CreateBiStackFromBuffer(void *const buf, const size_t len)
+{
+ BiStack destack = { 0 };
+ if (len == 0 || buf == NULL) return destack;
+ else
+ {
+ destack.size = len;
+ destack.mem = destack.front = ( uintptr_t )buf;
+ destack.back = destack.mem + len;
+ return destack;
+ }
+}
+
+void DestroyBiStack(BiStack *const restrict destack)
+{
+ if (destack->mem == 0) return;
+ else
+ {
+ uint8_t *const restrict buf = ( uint8_t* )destack->mem;
+ free(buf);
+ *destack = (BiStack){0};
+ }
+}
+
+void *BiStackAllocFront(BiStack *const restrict destack, const size_t len)
+{
+ if (destack->mem == 0) return NULL;
+ else
+ {
+ const size_t ALIGNED_LEN = __AlignSize(len, sizeof(uintptr_t));
+ // front end arena is too high!
+ if (destack->front + ALIGNED_LEN >= destack->back) return NULL;
+ else
+ {
+ uint8_t *const restrict ptr = ( uint8_t* )destack->front;
+ destack->front += ALIGNED_LEN;
+ return ptr;
+ }
+ }
+}
+
+void *BiStackAllocBack(BiStack *const restrict destack, const size_t len)
+{
+ if (destack->mem == 0) return NULL;
+ else
+ {
+ const size_t ALIGNED_LEN = __AlignSize(len, sizeof(uintptr_t));
+ // back end arena is too low
+ if (destack->back - ALIGNED_LEN <= destack->front) return NULL;
+ else
+ {
+ destack->back -= ALIGNED_LEN;
+ uint8_t *const restrict ptr = ( uint8_t* )destack->back;
+ return ptr;
+ }
+ }
+}
+
+void BiStackResetFront(BiStack *const destack)
+{
+ if (destack->mem == 0) return;
+ else destack->front = destack->mem;
+}
+
+void BiStackResetBack(BiStack *const destack)
+{
+ if (destack->mem == 0) return;
+ else destack->back = destack->mem + destack->size;
+}
+
+void BiStackResetAll(BiStack *const destack)
+{
+ BiStackResetBack(destack);
+ BiStackResetFront(destack);
+}
+
+inline intptr_t BiStackMargins(const BiStack destack)
+{
+ return destack.back - destack.front;
+}
+
+#endif // RMEM_IMPLEMENTATION
+
+/*******
+ * Changelog
+ * v1.0: First Creation.
+ * v1.1: bug patches for the mempool and addition of object pool.
+ * v1.2: addition of bidirectional arena.
+ * v1.3:
+ * optimizations of allocators.
+ * renamed 'Stack' to 'Arena'.
+ * replaced certain define constants with an anonymous enum.
+ * refactored MemPool to no longer require active or deferred defragging.
+ ********/
diff --git a/src/extras/rnet.h b/src/extras/rnet.h
new file mode 100644
index 00000000..439b105a
--- /dev/null
+++ b/src/extras/rnet.h
@@ -0,0 +1,2256 @@
+/**********************************************************************************************
+*
+* rnet - A simple and easy-to-use network module for raylib
+*
+* FEATURES:
+* - Provides a simple and (hopefully) easy to use wrapper around the Berkeley socket API
+*
+* INSPIRED BY:
+* SFML Sockets - https://www.sfml-dev.org/documentation/2.5.1/classsf_1_1Socket.php
+* SDL_net - https://www.libsdl.org/projects/SDL_net/
+* BSD Sockets - https://www.gnu.org/software/libc/manual/html_node/Sockets.html
+* BEEJ - https://beej.us/guide/bgnet/html/single/bgnet.html
+* Winsock2 - https://docs.microsoft.com/en-us/windows/desktop/api/winsock2
+*
+* CONTRIBUTORS:
+* Jak Barnes (github: @syphonx) (Feb. 2019) - Initial version
+*
+*
+* LICENSE: zlib/libpng
+*
+* Copyright (c) 2019-2020 Jak Barnes (@syphonx) and Ramon Santamaria (@raysan5)
+*
+* This software is provided "as-is", without any express or implied warranty. In no event
+* will the authors be held liable for any damages arising from the use of this software.
+*
+* Permission is granted to anyone to use this software for any purpose, including commercial
+* applications, and to alter it and redistribute it freely, subject to the following restrictions:
+*
+* 1. The origin of this software must not be misrepresented; you must not claim that you
+* wrote the original software. If you use this software in a product, an acknowledgment
+* in the product documentation would be appreciated but is not required.
+*
+* 2. Altered source versions must be plainly marked as such, and must not be misrepresented
+* as being the original software.
+*
+* 3. This notice may not be removed or altered from any source distribution.
+*
+**********************************************************************************************/
+
+#ifndef RNET_H
+#define RNET_H
+
+#include <limits.h> // Required for limits
+#include <inttypes.h> // Required for platform type sizes
+
+//----------------------------------------------------------------------------------
+// Defines and Macros
+//----------------------------------------------------------------------------------
+
+// Undefine any conflicting windows.h symbols
+// If defined, the following flags inhibit definition of the indicated items.
+#define NOGDICAPMASKS // CC_*, LC_*, PC_*, CP_*, TC_*, RC_
+#define NOVIRTUALKEYCODES // VK_*
+#define NOWINMESSAGES // WM_*, EM_*, LB_*, CB_*
+#define NOWINSTYLES // WS_*, CS_*, ES_*, LBS_*, SBS_*, CBS_*
+#define NOSYSMETRICS // SM_*
+#define NOMENUS // MF_*
+#define NOICONS // IDI_*
+#define NOKEYSTATES // MK_*
+#define NOSYSCOMMANDS // SC_*
+#define NORASTEROPS // Binary and Tertiary raster ops
+#define NOSHOWWINDOW // SW_*
+#define OEMRESOURCE // OEM Resource values
+#define NOATOM // Atom Manager routines
+#define NOCLIPBOARD // Clipboard routines
+#define NOCOLOR // Screen colors
+#define NOCTLMGR // Control and Dialog routines
+#define NODRAWTEXT // DrawText() and DT_*
+#define NOGDI // All GDI defines and routines
+#define NOKERNEL // All KERNEL defines and routines
+#define NOUSER // All USER defines and routines
+#define NONLS // All NLS defines and routines
+#define NOMB // MB_* and MessageBox()
+#define NOMEMMGR // GMEM_*, LMEM_*, GHND, LHND, associated routines
+#define NOMETAFILE // typedef METAFILEPICT
+#define NOMINMAX // Macros min(a,b) and max(a,b)
+#define NOMSG // typedef MSG and associated routines
+#define NOOPENFILE // OpenFile(), OemToAnsi, AnsiToOem, and OF_*
+#define NOSCROLL // SB_* and scrolling routines
+#define NOSERVICE // All Service Controller routines, SERVICE_ equates, etc.
+#define NOSOUND // Sound driver routines
+#define NOTEXTMETRIC // typedef TEXTMETRIC and associated routines
+#define NOWH // SetWindowsHook and WH_*
+#define NOWINOFFSETS // GWL_*, GCL_*, associated routines
+#define NOCOMM // COMM driver routines
+#define NOKANJI // Kanji support stuff.
+#define NOHELP // Help engine interface.
+#define NOPROFILER // Profiler interface.
+#define NODEFERWINDOWPOS // DeferWindowPos routines
+#define NOMCX // Modem Configuration Extensions
+#define MMNOSOUND
+
+// Allow custom memory allocators
+#ifndef RNET_MALLOC
+ #define RNET_MALLOC(sz) malloc(sz)
+#endif
+#ifndef RNET_CALLOC
+ #define RNET_CALLOC(n,sz) calloc(n,sz)
+#endif
+#ifndef RNET_FREE
+ #define RNET_FREE(p) free(p)
+#endif
+
+//----------------------------------------------------------------------------------
+// Platform type definitions
+// From: https://github.com/DFHack/clsocket/blob/master/src/Host.h
+//----------------------------------------------------------------------------------
+
+#ifdef WIN32
+typedef int socklen_t;
+#endif
+
+#ifndef RESULT_SUCCESS
+# define RESULT_SUCCESS 0
+#endif // RESULT_SUCCESS
+
+#ifndef RESULT_FAILURE
+# define RESULT_FAILURE 1
+#endif // RESULT_FAILURE
+
+#ifndef htonll
+# ifdef _BIG_ENDIAN
+# define htonll(x) (x)
+# define ntohll(x) (x)
+# else
+# define htonll(x) ((((uint64) htonl(x)) << 32) + htonl(x >> 32))
+# define ntohll(x) ((((uint64) ntohl(x)) << 32) + ntohl(x >> 32))
+# endif // _BIG_ENDIAN
+#endif // htonll
+
+//----------------------------------------------------------------------------------
+// Platform specific network includes
+// From: https://github.com/SDL-mirror/SDL_net/blob/master/SDLnetsys.h
+//----------------------------------------------------------------------------------
+
+// Include system network headers
+#if defined(_WIN32) // Windows
+ #define __USE_W32_SOCKETS
+ #define WIN32_LEAN_AND_MEAN
+ #include <winsock2.h>
+ #include <ws2tcpip.h>
+ #include <io.h>
+ #define IPTOS_LOWDELAY 0x10
+#else // Unix
+ #include <sys/types.h>
+ #include <fcntl.h>
+ #include <netinet/in.h>
+ #include <sys/ioctl.h>
+ #include <sys/time.h>
+ #include <unistd.h>
+ #include <net/if.h>
+ #include <netdb.h>
+ #include <netinet/tcp.h>
+ #include <sys/socket.h>
+ #include <arpa/inet.h>
+#endif
+
+#ifndef INVALID_SOCKET
+ #define INVALID_SOCKET ~(0)
+#endif
+
+#ifndef __USE_W32_SOCKETS
+ #define closesocket close
+ #define SOCKET int
+ #define INVALID_SOCKET -1
+ #define SOCKET_ERROR -1
+#endif
+
+#ifdef __USE_W32_SOCKETS
+ #ifndef EINTR
+ #define EINTR WSAEINTR
+ #endif
+#endif
+
+//----------------------------------------------------------------------------------
+// Module defines
+//----------------------------------------------------------------------------------
+
+// Network connection related defines
+#define SOCKET_MAX_SET_SIZE 32 // Maximum sockets in a set
+#define SOCKET_MAX_QUEUE_SIZE 16 // Maximum socket queue size
+#define SOCKET_MAX_SOCK_OPTS 4 // Maximum socket options
+#define SOCKET_MAX_UDPCHANNELS 32 // Maximum UDP channels
+#define SOCKET_MAX_UDPADDRESSES 4 // Maximum bound UDP addresses
+
+// Network address related defines
+#define ADDRESS_IPV4_ADDRSTRLEN 22 // IPv4 string length
+#define ADDRESS_IPV6_ADDRSTRLEN 65 // IPv6 string length
+#define ADDRESS_TYPE_ANY 0 // AF_UNSPEC
+#define ADDRESS_TYPE_IPV4 2 // AF_INET
+#define ADDRESS_TYPE_IPV6 23 // AF_INET6
+#define ADDRESS_MAXHOST 1025 // Max size of a fully-qualified domain name
+#define ADDRESS_MAXSERV 32 // Max size of a service name
+
+// Network address related defines
+#define ADDRESS_ANY (unsigned long)0x00000000
+#define ADDRESS_LOOPBACK 0x7f000001
+#define ADDRESS_BROADCAST (unsigned long)0xffffffff
+#define ADDRESS_NONE 0xffffffff
+
+// Network resolution related defines
+#define NAME_INFO_DEFAULT 0x00 // No flags set
+#define NAME_INFO_NOFQDN 0x01 // Only return nodename portion for local hosts
+#define NAME_INFO_NUMERICHOST 0x02 // Return numeric form of the host's address
+#define NAME_INFO_NAMEREQD 0x04 // Error if the host's name not in DNS
+#define NAME_INFO_NUMERICSERV 0x08 // Return numeric form of the service (port #)
+#define NAME_INFO_DGRAM 0x10 // Service is a datagram service
+
+// Address resolution related defines
+#if defined(_WIN32)
+ #define ADDRESS_INFO_PASSIVE (0x00000001) // Socket address will be used in bind() call
+ #define ADDRESS_INFO_CANONNAME (0x00000002) // Return canonical name in first ai_canonname
+ #define ADDRESS_INFO_NUMERICHOST (0x00000004) // Nodename must be a numeric address string
+ #define ADDRESS_INFO_NUMERICSERV (0x00000008) // Servicename must be a numeric port number
+ #define ADDRESS_INFO_DNS_ONLY (0x00000010) // Restrict queries to unicast DNS only (no LLMNR, netbios, etc.)
+ #define ADDRESS_INFO_ALL (0x00000100) // Query both IP6 and IP4 with AI_V4MAPPED
+ #define ADDRESS_INFO_ADDRCONFIG (0x00000400) // Resolution only if global address configured
+ #define ADDRESS_INFO_V4MAPPED (0x00000800) // On v6 failure, query v4 and convert to V4MAPPED format
+ #define ADDRESS_INFO_NON_AUTHORITATIVE (0x00004000) // LUP_NON_AUTHORITATIVE
+ #define ADDRESS_INFO_SECURE (0x00008000) // LUP_SECURE
+ #define ADDRESS_INFO_RETURN_PREFERRED_NAMES (0x00010000) // LUP_RETURN_PREFERRED_NAMES
+ #define ADDRESS_INFO_FQDN (0x00020000) // Return the FQDN in ai_canonname
+ #define ADDRESS_INFO_FILESERVER (0x00040000) // Resolving fileserver name resolution
+ #define ADDRESS_INFO_DISABLE_IDN_ENCODING (0x00080000) // Disable Internationalized Domain Names handling
+ #define ADDRESS_INFO_EXTENDED (0x80000000) // Indicates this is extended ADDRINFOEX(2/..) struct
+ #define ADDRESS_INFO_RESOLUTION_HANDLE (0x40000000) // Request resolution handle
+#endif
+
+//----------------------------------------------------------------------------------
+// Types and Structures Definition
+//----------------------------------------------------------------------------------
+
+// Boolean type
+#ifdef _WIN32
+ #include <stdbool.h>
+#else
+#if defined(__STDC__) && __STDC_VERSION__ >= 199901L
+ #include <stdbool.h>
+#elif !defined(__cplusplus) && !defined(bool)
+ typedef enum { false, true } bool;
+#endif
+#endif
+
+typedef enum {
+ SOCKET_TCP = 0, // SOCK_STREAM
+ SOCKET_UDP = 1 // SOCK_DGRAM
+} SocketType;
+
+// Network typedefs
+typedef uint32_t SocketChannel;
+typedef struct _AddressInformation *AddressInformation;
+typedef struct _SocketAddress *SocketAddress;
+typedef struct _SocketAddressIPv4 *SocketAddressIPv4;
+typedef struct _SocketAddressIPv6 *SocketAddressIPv6;
+typedef struct _SocketAddressStorage *SocketAddressStorage;
+
+// IPAddress definition (in network byte order)
+typedef struct IPAddress {
+ unsigned long host; // 32-bit IPv4 host address
+ unsigned short port; // 16-bit protocol port
+} IPAddress;
+
+typedef struct UDPChannel {
+ int numbound; // The total number of addresses this channel is bound to
+ IPAddress address[SOCKET_MAX_UDPADDRESSES]; // The list of remote addresses this channel is bound to
+} UDPChannel;
+
+// An option ID, value, sizeof(value) tuple for setsockopt(2).
+typedef struct SocketOpt {
+ int id; // Socked option id
+ int valueLen; // Socked option value len
+ void *value; // Socked option value data
+} SocketOpt;
+
+typedef struct Socket {
+ int ready; // Is the socket ready? i.e. has information
+ int status; // The last status code to have occured using this socket
+ bool isServer; // Is this socket a server socket (i.e. TCP/UDP Listen Server)
+ SocketChannel channel; // The socket handle id
+ SocketType type; // Is this socket a TCP or UDP socket?
+
+ bool isIPv6; // Is this socket address an ipv6 address?
+ SocketAddressIPv4 addripv4; // The host/target IPv4 for this socket (in network byte order)
+ SocketAddressIPv6 addripv6; // The host/target IPv6 for this socket (in network byte order)
+
+ struct UDPChannel binding[SOCKET_MAX_UDPCHANNELS]; // The amount of channels (if UDP) this socket is bound to
+} Socket;
+
+// Configuration for a socket
+typedef struct SocketConfig {
+ SocketType type; // The type of socket, TCP/UDP
+ char *host; // The host address in xxx.xxx.xxx.xxx form
+ char *port; // The target port/service in the form "http" or "25565"
+ bool server; // Listen for incoming clients?
+ bool nonblocking; // non-blocking operation?
+ int backlog_size; // set a custom backlog size
+ SocketOpt sockopts[SOCKET_MAX_SOCK_OPTS];
+} SocketConfig;
+
+typedef struct SocketDataPacket {
+ IPAddress address; // The source/dest address of an incoming/outgoing packet
+ int channel; // The src/dst channel of the packet
+ int maxlen; // The size of the data buffer
+ int status; // Packet status after sending
+ unsigned int len; // The length of the packet data
+ unsigned char *data; // The packet data
+} SocketDataPacket;
+
+// Result from calling open with a given config
+typedef struct SocketResult {
+ int status; // Socket result state
+ Socket *socket; // Socket ref
+} SocketResult;
+
+typedef struct SocketSet {
+ int numsockets; // Socket set count
+ int maxsockets; // Socket set max
+ struct Socket **sockets; // Sockets array
+} SocketSet;
+
+// Packet type
+typedef struct Packet {
+ uint32_t size; // The total size of bytes in data
+ uint32_t offs; // The offset to data access
+ uint32_t maxs; // The max size of data
+ uint8_t *data; // Data stored in network byte order
+} Packet;
+
+
+#ifdef __cplusplus
+extern "C" { // Prevents name mangling of functions
+#endif
+
+//----------------------------------------------------------------------------------
+// Global Variables Definition
+//----------------------------------------------------------------------------------
+//...
+
+//----------------------------------------------------------------------------------
+// Module Functions Declaration
+//----------------------------------------------------------------------------------
+
+// Initialisation and cleanup
+bool InitNetworkDevice(void);
+void CloseNetworkDevice(void);
+
+// Address API
+void ResolveIP(const char *ip, const char *service, int flags, char *outhost, char *outserv);
+int ResolveHost(const char *address, const char *service, int addressType, int flags, AddressInformation *outAddr);
+int GetAddressFamily(AddressInformation address);
+int GetAddressSocketType(AddressInformation address);
+int GetAddressProtocol(AddressInformation address);
+char *GetAddressCanonName(AddressInformation address);
+char *GetAddressHostAndPort(AddressInformation address, char *outhost, unsigned short *outport);
+
+// Address Memory API
+AddressInformation LoadAddress(void);
+void UnloadAddress(AddressInformation *addressInfo);
+AddressInformation *LoadAddressList(int size);
+
+// Socket API
+bool SocketCreate(SocketConfig *config, SocketResult *result);
+bool SocketBind(SocketConfig *config, SocketResult *result);
+bool SocketListen(SocketConfig *config, SocketResult *result);
+bool SocketConnect(SocketConfig *config, SocketResult *result);
+Socket *SocketAccept(Socket *server, SocketConfig *config);
+
+// General Socket API
+int SocketSend(Socket *sock, const void *datap, int len);
+int SocketReceive(Socket *sock, void *data, int maxlen);
+SocketAddressStorage SocketGetPeerAddress(Socket *sock);
+const char *GetSocketAddressHost(SocketAddressStorage storage);
+short GetSocketAddressPort(SocketAddressStorage storage);
+void SocketClose(Socket *sock);
+
+// UDP Socket API
+int SocketSetChannel(Socket *socket, int channel, const IPAddress *address);
+void SocketUnsetChannel(Socket *socket, int channel);
+
+// UDP DataPacket API
+SocketDataPacket *AllocPacket(int size);
+int ResizePacket(SocketDataPacket *packet, int newsize);
+void FreePacket(SocketDataPacket *packet);
+SocketDataPacket **AllocPacketList(int count, int size);
+void FreePacketList(SocketDataPacket **packets);
+
+// Socket Memory API
+Socket *LoadSocket(void);
+void UnloadSocket(Socket **sock);
+SocketResult *LoadSocketResult(void);
+void UnloadSocketResult(SocketResult **result);
+SocketSet *LoadSocketSet(int max);
+void UnloadSocketSet(SocketSet *sockset);
+
+// Socket I/O API
+bool IsSocketReady(Socket *sock);
+bool IsSocketConnected(Socket *sock);
+int AddSocket(SocketSet *set, Socket *sock);
+int RemoveSocket(SocketSet *set, Socket *sock);
+int CheckSockets(SocketSet *set, unsigned int timeout);
+
+// Packet API
+void PacketSend(Packet *packet);
+void PacketReceive(Packet *packet);
+void PacketWrite8(Packet *packet, uint16_t value);
+void PacketWrite16(Packet *packet, uint16_t value);
+void PacketWrite32(Packet *packet, uint32_t value);
+void PacketWrite64(Packet *packet, uint64_t value);
+uint16_t PacketRead8(Packet *packet);
+uint16_t PacketRead16(Packet *packet);
+uint32_t PacketRead32(Packet *packet);
+uint64_t PacketRead64(Packet *packet);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // RNET_H
+
+/***********************************************************************************
+*
+* RNET IMPLEMENTATION
+*
+************************************************************************************/
+
+#if defined(RNET_IMPLEMENTATION)
+
+#include <assert.h> // Required for: assert()
+#include <stdio.h> // Required for: FILE, fopen(), fclose(), fread()
+#include <stdlib.h> // Required for: malloc(), free()
+#include <string.h> // Required for: strcmp(), strncmp()
+
+#define NET_DEBUG_ENABLED 1
+
+#if defined(SUPPORT_TRACELOG)
+ #define TRACELOG(level, ...) TraceLog(level, __VA_ARGS__)
+
+ #if defined(SUPPORT_TRACELOG_DEBUG)
+ #define TRACELOGD(...) TraceLog(LOG_DEBUG, __VA_ARGS__)
+ #else
+ #define TRACELOGD(...) (void)0
+ #endif
+#else
+ #define TRACELOG(level, ...) (void)0
+ #define TRACELOGD(...) (void)0
+#endif
+
+//----------------------------------------------------------------------------------
+// Types and Structures Definition
+//----------------------------------------------------------------------------------
+
+typedef struct _SocketAddress
+{
+ struct sockaddr address;
+} _SocketAddress;
+
+typedef struct _SocketAddressIPv4
+{
+ struct sockaddr_in address;
+} _SocketAddressIPv4;
+
+typedef struct _SocketAddressIPv6
+{
+ struct sockaddr_in6 address;
+} _SocketAddressIPv6;
+
+typedef struct _SocketAddressStorage
+{
+ struct sockaddr_storage address;
+} _SocketAddressStorage;
+
+typedef struct _AddressInformation
+{
+ struct addrinfo addr;
+} _AddressInformation;
+
+//----------------------------------------------------------------------------------
+// Local module Functions Declarations
+//----------------------------------------------------------------------------------
+static void PrintSocket(struct sockaddr_storage *addr, const int family, const int socktype, const int protocol);
+static const char *SocketAddressToString(struct sockaddr_storage *sockaddr);
+static bool IsIPv4Address(const char *ip);
+static bool IsIPv6Address(const char *ip);
+static void *GetSocketPortPtr(struct sockaddr_storage *sa);
+static void *GetSocketAddressPtr(struct sockaddr_storage *sa);
+static bool IsSocketValid(Socket *sock);
+static void SocketSetLastError(int err);
+static int SocketGetLastError();
+static char *SocketGetLastErrorString();
+static char *SocketErrorCodeToString(int err);
+static bool SocketSetDefaults(SocketConfig *config);
+static bool InitSocket(Socket *sock, struct addrinfo *addr);
+static bool CreateSocket(SocketConfig *config, SocketResult *outresult);
+static bool SocketSetBlocking(Socket *sock);
+static bool SocketSetNonBlocking(Socket *sock);
+static bool SocketSetOptions(SocketConfig *config, Socket *sock);
+static void SocketSetHints(SocketConfig *config, struct addrinfo *hints);
+
+//----------------------------------------------------------------------------------
+// Local module Functions Definition
+//----------------------------------------------------------------------------------
+// Print socket information
+static void PrintSocket(struct sockaddr_storage *addr, const int family, const int socktype, const int protocol)
+{
+ switch (family)
+ {
+ case AF_UNSPEC: TRACELOG(LOG_DEBUG, "\tFamily: Unspecified"); break;
+ case AF_INET:
+ {
+ TRACELOG(LOG_DEBUG, "\tFamily: AF_INET (IPv4)");
+ TRACELOG(LOG_INFO, "\t- IPv4 address %s", SocketAddressToString(addr));
+ } break;
+ case AF_INET6:
+ {
+ TRACELOG(LOG_DEBUG, "\tFamily: AF_INET6 (IPv6)");
+ TRACELOG(LOG_INFO, "\t- IPv6 address %s", SocketAddressToString(addr));
+ } break;
+ case AF_NETBIOS:
+ {
+ TRACELOG(LOG_DEBUG, "\tFamily: AF_NETBIOS (NetBIOS)");
+ } break;
+ default: TRACELOG(LOG_DEBUG, "\tFamily: Other %ld", family); break;
+ }
+
+ TRACELOG(LOG_DEBUG, "\tSocket type:");
+ switch (socktype)
+ {
+ case 0: TRACELOG(LOG_DEBUG, "\t- Unspecified"); break;
+ case SOCK_STREAM: TRACELOG(LOG_DEBUG, "\t- SOCK_STREAM (stream)"); break;
+ case SOCK_DGRAM: TRACELOG(LOG_DEBUG, "\t- SOCK_DGRAM (datagram)"); break;
+ case SOCK_RAW: TRACELOG(LOG_DEBUG, "\t- SOCK_RAW (raw)"); break;
+ case SOCK_RDM: TRACELOG(LOG_DEBUG, "\t- SOCK_RDM (reliable message datagram)"); break;
+ case SOCK_SEQPACKET: TRACELOG(LOG_DEBUG, "\t- SOCK_SEQPACKET (pseudo-stream packet)"); break;
+ default: TRACELOG(LOG_DEBUG, "\t- Other %ld", socktype); break;
+ }
+
+ TRACELOG(LOG_DEBUG, "\tProtocol:");
+ switch (protocol)
+ {
+ case 0: TRACELOG(LOG_DEBUG, "\t- Unspecified"); break;
+ case IPPROTO_TCP: TRACELOG(LOG_DEBUG, "\t- IPPROTO_TCP (TCP)"); break;
+ case IPPROTO_UDP: TRACELOG(LOG_DEBUG, "\t- IPPROTO_UDP (UDP)"); break;
+ default: TRACELOG(LOG_DEBUG, "\t- Other %ld", protocol); break;
+ }
+}
+
+// Convert network ordered socket address to human readable string (127.0.0.1)
+static const char *SocketAddressToString(struct sockaddr_storage *sockaddr)
+{
+ //static const char* ipv6[INET6_ADDRSTRLEN];
+ assert(sockaddr != NULL);
+ assert(sockaddr->ss_family == AF_INET || sockaddr->ss_family == AF_INET6);
+
+ switch (sockaddr->ss_family)
+ {
+ case AF_INET:
+ {
+ //struct sockaddr_in *s = ((struct sockaddr_in *)sockaddr);
+ //return inet_ntop(AF_INET, &s->sin_addr, ipv6, INET_ADDRSTRLEN); // TODO.
+ }
+ break;
+ case AF_INET6:
+ {
+ //struct sockaddr_in6 *s = ((struct sockaddr_in6 *)sockaddr);
+ //return inet_ntop(AF_INET6, &s->sin6_addr, ipv6, INET6_ADDRSTRLEN); // TODO.
+ }
+ break;
+ }
+
+ return NULL;
+}
+
+// Check if the null terminated string ip is a valid IPv4 address
+static bool IsIPv4Address(const char *ip)
+{
+ /*
+ struct sockaddr_in sa;
+ int result = inet_pton(AF_INET, ip, &(sa.sin_addr)); // TODO.
+ return (result != 0);
+ */
+ return false;
+}
+
+// Check if the null terminated string ip is a valid IPv6 address
+static bool IsIPv6Address(const char *ip)
+{
+ /*
+ struct sockaddr_in6 sa;
+ int result = inet_pton(AF_INET6, ip, &(sa.sin6_addr)); // TODO.
+ return result != 0;
+ */
+ return false;
+}
+
+// Return a pointer to the port from the correct address family (IPv4, or IPv6)
+static void *GetSocketPortPtr(struct sockaddr_storage *sa)
+{
+ if (sa->ss_family == AF_INET)
+ {
+ return &(((struct sockaddr_in *)sa)->sin_port);
+ }
+
+ return &(((struct sockaddr_in6 *)sa)->sin6_port);
+}
+
+// Return a pointer to the address from the correct address family (IPv4, or IPv6)
+static void *GetSocketAddressPtr(struct sockaddr_storage *sa)
+{
+ if (sa->ss_family == AF_INET)
+ {
+ return &(((struct sockaddr_in *)sa)->sin_addr);
+ }
+
+ return &(((struct sockaddr_in6 *)sa)->sin6_addr);
+}
+
+// Is the socket in a valid state?
+static bool IsSocketValid(Socket *sock)
+{
+ if (sock != NULL)
+ {
+ return (sock->channel != INVALID_SOCKET);
+ }
+
+ return false;
+}
+
+// Sets the error code that can be retrieved through the WSAGetLastError function.
+static void SocketSetLastError(int err)
+{
+#if defined(_WIN32)
+ WSASetLastError(err);
+#else
+ errno = err;
+#endif
+}
+
+// Returns the error status for the last Sockets operation that failed
+static int SocketGetLastError(void)
+{
+#if defined(_WIN32)
+ return WSAGetLastError();
+#else
+ return errno;
+#endif
+}
+
+// Returns a human-readable string representing the last error message
+static char *SocketGetLastErrorString(void)
+{
+ return SocketErrorCodeToString(SocketGetLastError());
+}
+
+// Returns a human-readable string representing the error message (err)
+static char *SocketErrorCodeToString(int err)
+{
+#if defined(_WIN32)
+ static char gaiStrErrorBuffer[GAI_STRERROR_BUFFER_SIZE];
+ TRACELOG(LOG_INFO, gaiStrErrorBuffer, "%s", gai_strerror(err));
+ return gaiStrErrorBuffer;
+#else
+ return gai_strerror(err);
+#endif
+}
+
+// Set the defaults in the supplied SocketConfig if they're not already set
+static bool SocketSetDefaults(SocketConfig *config)
+{
+ if (config->backlog_size == 0) config->backlog_size = SOCKET_MAX_QUEUE_SIZE;
+
+ return true;
+}
+
+// Create the socket channel
+static bool InitSocket(Socket *sckt, struct addrinfo *address)
+{
+ switch (sckt->type)
+ {
+ case SOCKET_TCP:
+ {
+ if (address->ai_family == AF_INET) sckt->channel = socket(AF_INET, SOCK_STREAM, 0);
+ else sckt->channel = socket(AF_INET6, SOCK_STREAM, 0);
+ } break;
+ case SOCKET_UDP:
+ {
+ if (address->ai_family == AF_INET) sckt->channel = socket(AF_INET, SOCK_DGRAM, 0);
+ else sckt->channel = socket(AF_INET6, SOCK_DGRAM, 0);
+ } break;
+ default: TRACELOG(LOG_WARNING, "Invalid socket type specified."); break;
+ }
+
+ return IsSocketValid(sckt);
+}
+
+// CreateSocket() - Interally called by CreateSocket()
+//
+// This here is the bread and butter of the socket API, This function will
+// attempt to open a socket, bind and listen to it based on the config passed in
+//
+// SocketConfig* config - Configuration for which socket to open
+// SocketResult* result - The results of this function (if any, including errors)
+//
+// e.g.
+// SocketConfig server_config = { SocketConfig client_config = {
+// .host = "127.0.0.1", .host = "127.0.0.1",
+// .port = 8080, .port = 8080,
+// .server = true, };
+// .nonblocking = true,
+// };
+// SocketResult server_res; SocketResult client_res;
+static bool CreateSocket(SocketConfig *config, SocketResult *outresult)
+{
+ bool success = true;
+ int addrstatus;
+ struct addrinfo hints; // Address flags (IPV4, IPV6, UDP?)
+ struct addrinfo *res; // A pointer to the resulting address list
+
+ outresult->socket->channel = INVALID_SOCKET;
+ outresult->status = RESULT_FAILURE;
+
+ // Set the socket type
+ outresult->socket->type = config->type;
+
+ // Set the hints based on information in the config
+ //
+ // AI_CANONNAME Causes the ai_canonname of the result to the filled out with the host's canonical (real) name.
+ // AI_PASSIVE: Causes the result's IP address to be filled out with INADDR_ANY (IPv4)or in6addr_any (IPv6);
+ // Note: This causes a subsequent call to bind() to auto-fill the IP address
+ // of the struct sockaddr with the address of the current host.
+ //
+ SocketSetHints(config, &hints);
+
+ // Populate address information
+ addrstatus = getaddrinfo(config->host, // e.g. "www.example.com" or IP (Can be null if AI_PASSIVE flag is set
+ config->port, // e.g. "http" or port number
+ &hints, // e.g. SOCK_STREAM/SOCK_DGRAM
+ &res // The struct to populate
+ );
+
+ // Did we succeed?
+ if (addrstatus != 0)
+ {
+ outresult->socket->status = SocketGetLastError();
+ TRACELOG(LOG_WARNING, "Socket Error: %s", SocketErrorCodeToString(outresult->socket->status));
+ SocketSetLastError(0);
+ TRACELOG(LOG_WARNING, "Failed to get resolve host %s:%s: %s", config->host, config->port, SocketGetLastErrorString());
+
+ return (success = false);
+ }
+ else
+ {
+ char hoststr[NI_MAXHOST];
+ char portstr[NI_MAXSERV];
+ //socklen_t client_len = sizeof(struct sockaddr_storage);
+ //int rc = getnameinfo((struct sockaddr *)res->ai_addr, client_len, hoststr, sizeof(hoststr), portstr, sizeof(portstr), NI_NUMERICHOST | NI_NUMERICSERV);
+ TRACELOG(LOG_INFO, "Successfully resolved host %s:%s", hoststr, portstr);
+ }
+
+ // Walk the address information linked-list
+ struct addrinfo *it;
+ for (it = res; it != NULL; it = it->ai_next)
+ {
+ // Initialise the socket
+ if (!InitSocket(outresult->socket, it))
+ {
+ outresult->socket->status = SocketGetLastError();
+ TRACELOG(LOG_WARNING, "Socket Error: %s", SocketErrorCodeToString(outresult->socket->status));
+ SocketSetLastError(0);
+ continue;
+ }
+
+ // Set socket options
+ if (!SocketSetOptions(config, outresult->socket))
+ {
+ outresult->socket->status = SocketGetLastError();
+ TRACELOG(LOG_WARNING, "Socket Error: %s", SocketErrorCodeToString(outresult->socket->status));
+ SocketSetLastError(0);
+ freeaddrinfo(res);
+
+ return (success = false);
+ }
+ }
+
+ if (!IsSocketValid(outresult->socket))
+ {
+ outresult->socket->status = SocketGetLastError();
+ TRACELOG(LOG_WARNING, "Socket Error: %s", SocketErrorCodeToString(outresult->status));
+ SocketSetLastError(0);
+ freeaddrinfo(res);
+
+ return (success = false);
+ }
+
+ if (success)
+ {
+ outresult->status = RESULT_SUCCESS;
+ outresult->socket->ready = 0;
+ outresult->socket->status = 0;
+
+ if (!(config->type == SOCKET_UDP)) outresult->socket->isServer = config->server;
+
+ switch (res->ai_addr->sa_family)
+ {
+ case AF_INET:
+ {
+ outresult->socket->addripv4 = (struct _SocketAddressIPv4 *)RNET_MALLOC(sizeof(*outresult->socket->addripv4));
+
+ if (outresult->socket->addripv4 != NULL)
+ {
+ memset(outresult->socket->addripv4, 0, sizeof(*outresult->socket->addripv4));
+
+ if (outresult->socket->addripv4 != NULL)
+ {
+ memcpy(&outresult->socket->addripv4->address, (struct sockaddr_in *)res->ai_addr, sizeof(struct sockaddr_in));
+
+ outresult->socket->isIPv6 = false;
+ char hoststr[NI_MAXHOST];
+ char portstr[NI_MAXSERV];
+
+ socklen_t client_len = sizeof(struct sockaddr_storage);
+ getnameinfo((struct sockaddr *)&outresult->socket->addripv4->address, client_len, hoststr, sizeof(hoststr), portstr, sizeof(portstr), NI_NUMERICHOST | NI_NUMERICSERV);
+
+ TRACELOG(LOG_INFO, "Socket address set to %s:%s", hoststr, portstr);
+ }
+ }
+ } break;
+ case AF_INET6:
+ {
+ outresult->socket->addripv6 = (struct _SocketAddressIPv6 *)RNET_MALLOC(
+ sizeof(*outresult->socket->addripv6));
+ if (outresult->socket->addripv6 != NULL)
+ {
+ memset(outresult->socket->addripv6, 0,
+ sizeof(*outresult->socket->addripv6));
+ if (outresult->socket->addripv6 != NULL)
+ {
+ memcpy(&outresult->socket->addripv6->address,
+ (struct sockaddr_in6 *)res->ai_addr, sizeof(struct sockaddr_in6));
+ outresult->socket->isIPv6 = true;
+ char hoststr[NI_MAXHOST];
+ char portstr[NI_MAXSERV];
+ socklen_t client_len = sizeof(struct sockaddr_storage);
+ getnameinfo(
+ (struct sockaddr *)&outresult->socket->addripv6->address, client_len, hoststr, sizeof(hoststr), portstr, sizeof(portstr), NI_NUMERICHOST | NI_NUMERICSERV);
+ TRACELOG(LOG_INFO, "Socket address set to %s:%s", hoststr, portstr);
+ }
+ }
+ } break;
+ default: break;
+ }
+ }
+
+ freeaddrinfo(res);
+ return success;
+}
+
+// Set the state of the Socket sock to blocking
+static bool SocketSetBlocking(Socket *sock)
+{
+ bool ret = true;
+#if defined(_WIN32)
+ unsigned long mode = 0;
+ ret = ioctlsocket(sock->channel, FIONBIO, &mode);
+#else
+ const int flags = fcntl(sock->channel, F_GETFL, 0);
+ if (!(flags & O_NONBLOCK))
+ {
+ TRACELOG(LOG_DEBUG, "Socket was already in blocking mode");
+ return ret;
+ }
+
+ ret = (0 == fcntl(sock->channel, F_SETFL, (flags ^ O_NONBLOCK)));
+#endif
+ return ret;
+}
+
+// Set the state of the Socket sock to non-blocking
+static bool SocketSetNonBlocking(Socket *sock)
+{
+ bool ret = true;
+#if defined(_WIN32)
+ unsigned long mode = 1;
+ ret = ioctlsocket(sock->channel, FIONBIO, &mode);
+#else
+ const int flags = fcntl(sock->channel, F_GETFL, 0);
+
+ if ((flags & O_NONBLOCK))
+ {
+ TRACELOG(LOG_DEBUG, "Socket was already in non-blocking mode");
+ return ret;
+ }
+
+ ret = (0 == fcntl(sock->channel, F_SETFL, (flags | O_NONBLOCK)));
+#endif
+ return ret;
+}
+
+// Set options specified in SocketConfig to Socket sock
+static bool SocketSetOptions(SocketConfig *config, Socket *sock)
+{
+ for (int i = 0; i < SOCKET_MAX_SOCK_OPTS; i++)
+ {
+ SocketOpt *opt = &config->sockopts[i];
+
+ if (opt->id == 0) break;
+
+ if (setsockopt(sock->channel, SOL_SOCKET, opt->id, opt->value, opt->valueLen) < 0) return false;
+ }
+
+ return true;
+}
+
+// Set "hints" in an addrinfo struct, to be passed to getaddrinfo.
+static void SocketSetHints(SocketConfig *config, struct addrinfo *hints)
+{
+ if (config == NULL || hints == NULL) return;
+
+ memset(hints, 0, sizeof(*hints));
+
+ // Check if the ip supplied in the config is a valid ipv4 ip ipv6 address
+ if (IsIPv4Address(config->host))
+ {
+ hints->ai_family = AF_INET;
+ hints->ai_flags |= AI_NUMERICHOST;
+ }
+ else
+ {
+ if (IsIPv6Address(config->host))
+ {
+ hints->ai_family = AF_INET6;
+ hints->ai_flags |= AI_NUMERICHOST;
+ }
+ else hints->ai_family = AF_UNSPEC;
+ }
+
+ if (config->type == SOCKET_UDP) hints->ai_socktype = SOCK_DGRAM;
+ else hints->ai_socktype = SOCK_STREAM;
+
+
+ // Set passive unless UDP client
+ if (!(config->type == SOCKET_UDP) || config->server) hints->ai_flags = AI_PASSIVE;
+}
+
+//----------------------------------------------------------------------------------
+// Module implementation
+//----------------------------------------------------------------------------------
+
+// Initialise the network (requires for windows platforms only)
+bool InitNetworkDevice(void)
+{
+#if defined(_WIN32)
+ WORD wVersionRequested;
+ WSADATA wsaData;
+
+ wVersionRequested = MAKEWORD(2, 2);
+ int err = WSAStartup(wVersionRequested, &wsaData);
+
+ if (err != 0)
+ {
+ TRACELOG(LOG_WARNING, "WinSock failed to initialise.");
+ return false;
+ }
+ else TRACELOG(LOG_INFO, "WinSock initialised.");
+
+ if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2)
+ {
+ TRACELOG(LOG_WARNING, "WinSock failed to initialise.");
+ WSACleanup();
+ return false;
+ }
+
+ return true;
+#else
+ return true;
+#endif
+}
+
+// Cleanup, and close the network
+void CloseNetworkDevice(void)
+{
+#if defined(_WIN32)
+ WSACleanup();
+#endif
+}
+
+// Protocol-independent name resolution from an address to an ANSI host name
+// and from a port number to the ANSI service name.
+//
+// The flags parameter can be used to customize processing of the getnameinfo function
+//
+// The following flags are available:
+//
+// NAME_INFO_DEFAULT 0x00 // No flags set
+// NAME_INFO_NOFQDN 0x01 // Only return nodename portion for local hosts
+// NAME_INFO_NUMERICHOST 0x02 // Return numeric form of the host's address
+// NAME_INFO_NAMEREQD 0x04 // Error if the host's name not in DNS
+// NAME_INFO_NUMERICSERV 0x08 // Return numeric form of the service (port #)
+// NAME_INFO_DGRAM 0x10 // Service is a datagram service
+void ResolveIP(const char *ip, const char *port, int flags, char *host, char *serv)
+{
+ // Variables
+ int status; // Status value to return (0) is success
+ struct addrinfo hints; // Address flags (IPV4, IPV6, UDP?)
+ struct addrinfo *res; // A pointer to the resulting address list
+
+ // Set the hints
+ memset(&hints, 0, sizeof hints);
+ hints.ai_family = AF_UNSPEC; // Either IPv4 or IPv6 (AF_INET, AF_INET6)
+ hints.ai_protocol = 0; // Automatically select correct protocol (IPPROTO_TCP), (IPPROTO_UDP)
+
+ // Populate address information
+ status = getaddrinfo(ip, // e.g. "www.example.com" or IP
+ port, // e.g. "http" or port number
+ &hints, // e.g. SOCK_STREAM/SOCK_DGRAM
+ &res // The struct to populate
+ );
+
+ // Did we succeed?
+ if (status != 0) TRACELOG(LOG_WARNING, "Failed to get resolve host %s:%s: %s", ip, port, gai_strerror(errno));
+ else TRACELOG(LOG_DEBUG, "Resolving... %s::%s", ip, port);
+
+ // Attempt to resolve network byte order ip to hostname
+ switch (res->ai_family)
+ {
+ case AF_INET:
+ {
+ status = getnameinfo(&*((struct sockaddr *)res->ai_addr),
+ sizeof(*((struct sockaddr_in *)res->ai_addr)),
+ host, NI_MAXHOST, serv, NI_MAXSERV, flags);
+ } break;
+ case AF_INET6:
+ {
+ /*
+ status = getnameinfo(&*((struct sockaddr_in6 *)res->ai_addr), // TODO.
+ sizeof(*((struct sockaddr_in6 *)res->ai_addr)),
+ host, NI_MAXHOST, serv, NI_MAXSERV, flags);
+ */
+ } break;
+ default: break;
+ }
+
+ if (status != 0) TRACELOG(LOG_WARNING, "Failed to resolve ip %s: %s", ip, SocketGetLastErrorString());
+ else TRACELOG(LOG_DEBUG, "Successfully resolved %s::%s to %s", ip, port, host);
+
+ // Free the pointer to the data returned by addrinfo
+ freeaddrinfo(res);
+}
+
+// Protocol-independent translation from an ANSI host name to an address
+//
+// e.g.
+// const char* address = "127.0.0.1" (local address)
+// const char* port = "80"
+//
+// Parameters:
+// const char* address - A pointer to a NULL-terminated ANSI string that contains a host (node) name or a numeric host address string.
+// const char* service - A pointer to a NULL-terminated ANSI string that contains either a service name or port number represented as a string.
+//
+// Returns:
+// The total amount of addresses found, -1 on error
+//
+int ResolveHost(const char *address, const char *service, int addressType, int flags, AddressInformation *outAddr)
+{
+ // Variables
+ int status; // Status value to return (0) is success
+ struct addrinfo hints; // Address flags (IPV4, IPV6, UDP?)
+ struct addrinfo *res; // will point to the results
+ struct addrinfo *iterator;
+ assert(((address != NULL || address != 0) || (service != NULL || service != 0)));
+ assert(((addressType == AF_INET) || (addressType == AF_INET6) || (addressType == AF_UNSPEC)));
+
+ // Set the hints
+ memset(&hints, 0, sizeof hints);
+ hints.ai_family = addressType; // Either IPv4 or IPv6 (ADDRESS_TYPE_IPV4, ADDRESS_TYPE_IPV6)
+ hints.ai_protocol = 0; // Automatically select correct protocol (IPPROTO_TCP), (IPPROTO_UDP)
+ hints.ai_flags = flags;
+ assert((hints.ai_addrlen == 0) || (hints.ai_addrlen == 0));
+ assert((hints.ai_canonname == 0) || (hints.ai_canonname == 0));
+ assert((hints.ai_addr == 0) || (hints.ai_addr == 0));
+ assert((hints.ai_next == 0) || (hints.ai_next == 0));
+
+ // When the address is NULL, populate the IP for me
+ if (address == NULL)
+ {
+ if ((hints.ai_flags & AI_PASSIVE) == 0) hints.ai_flags |= AI_PASSIVE;
+ }
+
+ TRACELOG(LOG_INFO, "Resolving host...");
+
+ // Populate address information
+ status = getaddrinfo(address, // e.g. "www.example.com" or IP
+ service, // e.g. "http" or port number
+ &hints, // e.g. SOCK_STREAM/SOCK_DGRAM
+ &res // The struct to populate
+ );
+
+ // Did we succeed?
+ if (status != 0)
+ {
+ int error = SocketGetLastError();
+ SocketSetLastError(0);
+ TRACELOG(LOG_WARNING, "Failed to get resolve host: %s", SocketErrorCodeToString(error));
+ return -1;
+ }
+ else TRACELOG(LOG_INFO, "Successfully resolved host %s:%s", address, service);
+
+ // Calculate the size of the address information list
+ int size = 0;
+ for (iterator = res; iterator != NULL; iterator = iterator->ai_next) size++;
+
+ // Validate the size is > 0, otherwise return
+ if (size <= 0)
+ {
+ TRACELOG(LOG_WARNING, "Error, no addresses found.");
+ return -1;
+ }
+
+ // If not address list was allocated, allocate it dynamically with the known address size
+ if (outAddr == NULL) outAddr = (AddressInformation *)RNET_MALLOC(size * sizeof(AddressInformation));
+
+ // Dynamically allocate an array of address information structs
+ if (outAddr != NULL)
+ {
+ int i;
+ for (i = 0; i < size; ++i)
+ {
+ outAddr[i] = LoadAddress();
+ if (outAddr[i] == NULL)
+ {
+ break;
+ }
+ }
+
+ outAddr[i] = NULL;
+ if (i != size) outAddr = NULL;
+ }
+ else
+ {
+ TRACELOG(LOG_WARNING, "Error, failed to dynamically allocate memory for the address list");
+ return -1;
+ }
+
+ // Copy all the address information from res into outAddrList
+ int i = 0;
+ for (iterator = res; iterator != NULL; iterator = iterator->ai_next)
+ {
+ if (i < size)
+ {
+ outAddr[i]->addr.ai_flags = iterator->ai_flags;
+ outAddr[i]->addr.ai_family = iterator->ai_family;
+ outAddr[i]->addr.ai_socktype = iterator->ai_socktype;
+ outAddr[i]->addr.ai_protocol = iterator->ai_protocol;
+ outAddr[i]->addr.ai_addrlen = iterator->ai_addrlen;
+ *outAddr[i]->addr.ai_addr = *iterator->ai_addr;
+#if NET_DEBUG_ENABLED
+ TRACELOG(LOG_DEBUG, "GetAddressInformation");
+ TRACELOG(LOG_DEBUG, "\tFlags: 0x%x", iterator->ai_flags);
+ //PrintSocket(outAddr[i]->addr.ai_addr, outAddr[i]->addr.ai_family, outAddr[i]->addr.ai_socktype, outAddr[i]->addr.ai_protocol);
+ TRACELOG(LOG_DEBUG, "Length of this sockaddr: %d", outAddr[i]->addr.ai_addrlen);
+ TRACELOG(LOG_DEBUG, "Canonical name: %s", iterator->ai_canonname);
+#endif
+ i++;
+ }
+ }
+
+ // Free the pointer to the data returned by addrinfo
+ freeaddrinfo(res);
+
+ // Return the total count of addresses found
+ return size;
+}
+
+// This here is the bread and butter of the socket API, This function will
+// attempt to open a socket, bind and listen to it based on the config passed in
+//
+// SocketConfig* config - Configuration for which socket to open
+// SocketResult* result - The results of this function (if any, including errors)
+//
+// e.g.
+// SocketConfig server_config = { SocketConfig client_config = {
+// .host = "127.0.0.1", .host = "127.0.0.1",
+// .port = 8080, .port = 8080,
+// .server = true, };
+// .nonblocking = true,
+// };
+// SocketResult server_res; SocketResult client_res;
+bool SocketCreate(SocketConfig *config, SocketResult *result)
+{
+ // Socket creation result
+ bool success = true;
+
+ // Make sure we've not received a null config or result pointer
+ if (config == NULL || result == NULL) return (success = false);
+
+ // Set the defaults based on the config
+ if (!SocketSetDefaults(config))
+ {
+ TRACELOG(LOG_WARNING, "Configuration Error.");
+ success = false;
+ }
+ else
+ {
+ // Create the socket
+ if (CreateSocket(config, result))
+ {
+ if (config->nonblocking) SocketSetNonBlocking(result->socket);
+ else SocketSetBlocking(result->socket);
+ }
+ else success = false;
+ }
+
+ return success;
+}
+
+// Bind a socket to a local address
+// Note: The bind function is required on an unconnected socket before subsequent calls to the listen function.
+bool SocketBind(SocketConfig *config, SocketResult *result)
+{
+ bool success = false;
+ result->status = RESULT_FAILURE;
+ struct sockaddr_storage *sock_addr = NULL;
+
+ // Don't bind to a socket that isn't configured as a server
+ if (!IsSocketValid(result->socket) || !config->server)
+ {
+ TRACELOG(LOG_WARNING, Cannot bind to socket marked as \"Client\" in SocketConfig.");
+ success = false;
+ }
+ else
+ {
+ if (result->socket->isIPv6) sock_addr = (struct sockaddr_storage *)&result->socket->addripv6->address;
+ else sock_addr = (struct sockaddr_storage *)&result->socket->addripv4->address;
+
+ if (sock_addr != NULL)
+ {
+ if (bind(result->socket->channel, (struct sockaddr *)sock_addr, sizeof(*sock_addr)) != SOCKET_ERROR)
+ {
+ TRACELOG(LOG_INFO, "Successfully bound socket.");
+ success = true;
+ }
+ else
+ {
+ result->socket->status = SocketGetLastError();
+ TRACELOG(LOG_WARNING, "Socket Error: %s", SocketErrorCodeToString(result->socket->status));
+ SocketSetLastError(0);
+ success = false;
+ }
+ }
+ }
+
+ // Was the bind a success?
+ if (success)
+ {
+ result->status = RESULT_SUCCESS;
+ result->socket->ready = 0;
+ result->socket->status = 0;
+ socklen_t sock_len = sizeof(*sock_addr);
+
+ if (getsockname(result->socket->channel, (struct sockaddr *)sock_addr, &sock_len) < 0)
+ {
+ TRACELOG(LOG_WARNING, "Couldn't get socket address");
+ }
+ else
+ {
+ struct sockaddr_in *s = (struct sockaddr_in *)sock_addr;
+ // result->socket->address.host = s->sin_addr.s_addr;
+ // result->socket->address.port = s->sin_port;
+
+ result->socket->addripv4 = (struct _SocketAddressIPv4 *)RNET_MALLOC(sizeof(*result->socket->addripv4));
+
+ if (result->socket->addripv4 != NULL) memset(result->socket->addripv4, 0, sizeof(*result->socket->addripv4));
+
+ memcpy(&result->socket->addripv4->address, (struct sockaddr_in *)&s->sin_addr, sizeof(struct sockaddr_in));
+ }
+ }
+ return success;
+}
+
+// Listens (and queues) incoming connections requests for a bound port.
+bool SocketListen(SocketConfig *config, SocketResult *result)
+{
+ bool success = false;
+ result->status = RESULT_FAILURE;
+
+ // Don't bind to a socket that isn't configured as a server
+ if (!IsSocketValid(result->socket) || !config->server)
+ {
+ TRACELOG(LOG_WARNING, "Cannot listen on socket marked as \"Client\" in SocketConfig.");
+ success = false;
+ }
+ else
+ {
+ // Don't listen on UDP sockets
+ if (!(config->type == SOCKET_UDP))
+ {
+ if (listen(result->socket->channel, config->backlog_size) != SOCKET_ERROR)
+ {
+ TRACELOG(LOG_INFO, "Started listening on socket...");
+ success = true;
+ }
+ else
+ {
+ success = false;
+ result->socket->status = SocketGetLastError();
+ TRACELOG(LOG_WARNING, "Socket Error: %s", SocketErrorCodeToString(result->socket->status));
+ SocketSetLastError(0);
+ }
+ }
+ else
+ {
+ TRACELOG(LOG_WARNING, "Cannot listen on socket marked as \"UDP\" (datagram) in SocketConfig.");
+ success = false;
+ }
+ }
+
+ // Was the listen a success?
+ if (success)
+ {
+ result->status = RESULT_SUCCESS;
+ result->socket->ready = 0;
+ result->socket->status = 0;
+ }
+
+ return success;
+}
+
+// Connect the socket to the destination specified by "host" and "port" in SocketConfig
+bool SocketConnect(SocketConfig *config, SocketResult *result)
+{
+ bool success = true;
+ result->status = RESULT_FAILURE;
+
+ // Only bind to sockets marked as server
+ if (config->server)
+ {
+ TRACELOG(LOG_WARNING, "Cannot connect to socket marked as \"Server\" in SocketConfig.");
+ success = false;
+ }
+ else
+ {
+ if (IsIPv4Address(config->host))
+ {
+ struct sockaddr_in ip4addr;
+ ip4addr.sin_family = AF_INET;
+ unsigned long hport;
+ hport = strtoul(config->port, NULL, 0);
+ ip4addr.sin_port = (unsigned short)(hport);
+
+ // TODO: Changed the code to avoid the usage of inet_pton and inet_ntop replacing them with getnameinfo (that should have a better support on windows).
+
+ //inet_pton(AF_INET, config->host, &ip4addr.sin_addr);
+ int connect_result = connect(result->socket->channel, (struct sockaddr *)&ip4addr, sizeof(ip4addr));
+
+ if (connect_result == SOCKET_ERROR)
+ {
+ result->socket->status = SocketGetLastError();
+ SocketSetLastError(0);
+
+ switch (result->socket->status)
+ {
+ case WSAEWOULDBLOCK: success = true; break;
+ default:
+ {
+ TRACELOG(LOG_WARNING, "Socket Error: %s", SocketErrorCodeToString(result->socket->status));
+ success = false;
+ } break;
+ }
+ }
+ else
+ {
+ TRACELOG(LOG_INFO, "Successfully connected to socket.");
+ success = true;
+ }
+ }
+ else
+ {
+ if (IsIPv6Address(config->host))
+ {
+ struct sockaddr_in6 ip6addr;
+ ip6addr.sin6_family = AF_INET6;
+ unsigned long hport;
+ hport = strtoul(config->port, NULL, 0);
+ ip6addr.sin6_port = htons((unsigned short)hport);
+ //inet_pton(AF_INET6, config->host, &ip6addr.sin6_addr); // TODO.
+ int connect_result = connect(result->socket->channel, (struct sockaddr *)&ip6addr, sizeof(ip6addr));
+
+ if (connect_result == SOCKET_ERROR)
+ {
+ result->socket->status = SocketGetLastError();
+ SocketSetLastError(0);
+
+ switch (result->socket->status)
+ {
+ case WSAEWOULDBLOCK: success = true; break;
+ default:
+ {
+ TRACELOG(LOG_WARNING, "Socket Error: %s", SocketErrorCodeToString(result->socket->status));
+ success = false;
+ } break;
+ }
+ }
+ else
+ {
+ TRACELOG(LOG_INFO, "Successfully connected to socket.");
+ success = true;
+ }
+ }
+ }
+ }
+
+ if (success)
+ {
+ result->status = RESULT_SUCCESS;
+ result->socket->ready = 0;
+ result->socket->status = 0;
+ }
+
+ return success;
+}
+
+// Closes an existing socket
+//
+// SocketChannel socket - The id of the socket to close
+void SocketClose(Socket *sock)
+{
+ if (sock != NULL)
+ {
+ if (sock->channel != INVALID_SOCKET) closesocket(sock->channel);
+ }
+}
+
+// Returns the sockaddress for a specific socket in a generic storage struct
+SocketAddressStorage SocketGetPeerAddress(Socket *sock)
+{
+ // TODO.
+ /*
+ if (sock->isServer) return NULL;
+ if (sock->isIPv6) return sock->addripv6;
+ else return sock->addripv4;
+ */
+
+ return NULL;
+}
+
+// Return the address-type appropriate host portion of a socket address
+const char *GetSocketAddressHost(SocketAddressStorage storage)
+{
+ assert(storage->address.ss_family == AF_INET || storage->address.ss_family == AF_INET6);
+ return SocketAddressToString((struct sockaddr_storage *)storage);
+}
+
+// Return the address-type appropriate port(service) portion of a socket address
+short GetSocketAddressPort(SocketAddressStorage storage)
+{
+ //return ntohs(GetSocketPortPtr(storage)); // TODO.
+
+ return 0;
+}
+
+// The accept function permits an incoming connection attempt on a socket.
+//
+// SocketChannel listener - The socket to listen for incoming connections on (i.e. server)
+// SocketResult* out - The result of this function (if any, including errors)
+//
+// e.g.
+//
+// SocketResult connection;
+// bool connected = false;
+// if (!connected)
+// {
+// if (SocketAccept(server_res.socket.channel, &connection))
+// {
+// connected = true;
+// }
+// }
+Socket *SocketAccept(Socket *server, SocketConfig *config)
+{
+ if (!server->isServer || server->type == SOCKET_UDP) return NULL;
+
+ struct sockaddr_storage sock_addr;
+ socklen_t sock_alen;
+ Socket *sock = LoadSocket();
+ server->ready = 0;
+ sock_alen = sizeof(sock_addr);
+ sock->channel = accept(server->channel, (struct sockaddr *)&sock_addr, &sock_alen);
+
+ if (sock->channel == INVALID_SOCKET)
+ {
+ sock->status = SocketGetLastError();
+ TRACELOG(LOG_WARNING, "Socket Error: %s", SocketErrorCodeToString(sock->status));
+ SocketSetLastError(0);
+ SocketClose(sock);
+
+ return NULL;
+ }
+
+ (config->nonblocking) ? SocketSetNonBlocking(sock) : SocketSetBlocking(sock);
+ sock->isServer = false;
+ sock->ready = 0;
+ sock->type = server->type;
+
+ switch (sock_addr.ss_family)
+ {
+ case AF_INET:
+ {
+ struct sockaddr_in *s = ((struct sockaddr_in *)&sock_addr);
+ sock->addripv4 = (struct _SocketAddressIPv4 *)RNET_MALLOC(sizeof(*sock->addripv4));
+
+ if (sock->addripv4 != NULL)
+ {
+ memset(sock->addripv4, 0, sizeof(*sock->addripv4));
+ memcpy(&sock->addripv4->address, (struct sockaddr_in *)&s->sin_addr, sizeof(struct sockaddr_in));
+ TRACELOG(LOG_INFO, "Server: Got connection from %s::%hu", SocketAddressToString((struct sockaddr_storage *)s), ntohs(sock->addripv4->address.sin_port));
+ }
+ } break;
+ case AF_INET6:
+ {
+ struct sockaddr_in6 *s = ((struct sockaddr_in6 *)&sock_addr);
+ sock->addripv6 = (struct _SocketAddressIPv6 *)RNET_MALLOC(sizeof(*sock->addripv6));
+
+ if (sock->addripv6 != NULL)
+ {
+ memset(sock->addripv6, 0, sizeof(*sock->addripv6));
+ memcpy(&sock->addripv6->address, (struct sockaddr_in6 *)&s->sin6_addr, sizeof(struct sockaddr_in6));
+ TRACELOG(LOG_INFO, "Server: Got connection from %s::%hu", SocketAddressToString((struct sockaddr_storage *)s), ntohs(sock->addripv6->address.sin6_port));
+ }
+ } break;
+ }
+
+ return sock;
+}
+
+// Verify that the channel is in the valid range
+static int ValidChannel(int channel)
+{
+ if ((channel < 0) || (channel >= SOCKET_MAX_UDPCHANNELS))
+ {
+ TRACELOG(LOG_WARNING, "Invalid channel");
+ return 0;
+ }
+
+ return 1;
+}
+
+// Set the socket channel
+int SocketSetChannel(Socket *socket, int channel, const IPAddress *address)
+{
+ struct UDPChannel *binding;
+
+ if (socket == NULL)
+ {
+ TRACELOG(LOG_WARNING, "Passed a NULL socket");
+ return (-1);
+ }
+
+ if (channel == -1)
+ {
+ for (channel = 0; channel < SOCKET_MAX_UDPCHANNELS; ++channel)
+ {
+ binding = &socket->binding[channel];
+ if (binding->numbound < SOCKET_MAX_UDPADDRESSES) break;
+ }
+ }
+ else
+ {
+ if (!ValidChannel(channel)) return (-1);
+
+ binding = &socket->binding[channel];
+ }
+
+ if (binding->numbound == SOCKET_MAX_UDPADDRESSES)
+ {
+ TRACELOG(LOG_WARNING, "No room for new addresses");
+ return (-1);
+ }
+
+ binding->address[binding->numbound++] = *address;
+
+ return (channel);
+}
+
+// Remove the socket channel
+void SocketUnsetChannel(Socket *socket, int channel)
+{
+ if ((channel >= 0) && (channel < SOCKET_MAX_UDPCHANNELS)) socket->binding[channel].numbound = 0;
+}
+
+/* Allocate/free a single UDP packet 'size' bytes long.
+ The new packet is returned, or NULL if the function ran out of memory.
+ */
+SocketDataPacket *AllocPacket(int size)
+{
+ SocketDataPacket *packet = (SocketDataPacket *)RNET_MALLOC(sizeof(*packet));
+ int error = 1;
+
+ if (packet != NULL)
+ {
+ packet->maxlen = size;
+ packet->data = (uint8_t *)RNET_MALLOC(size);
+ if (packet->data != NULL)
+ {
+ error = 0;
+ }
+ }
+
+ if (error)
+ {
+ FreePacket(packet);
+ packet = NULL;
+ }
+
+ return (packet);
+}
+
+int ResizePacket(SocketDataPacket *packet, int newsize)
+{
+ uint8_t *newdata = (uint8_t *)RNET_MALLOC(newsize);
+
+ if (newdata != NULL)
+ {
+ RNET_FREE(packet->data);
+ packet->data = newdata;
+ packet->maxlen = newsize;
+ }
+
+ return (packet->maxlen);
+}
+
+void FreePacket(SocketDataPacket *packet)
+{
+ if (packet)
+ {
+ RNET_FREE(packet->data);
+ RNET_FREE(packet);
+ }
+}
+
+// Allocate/Free a UDP packet vector (array of packets) of 'howmany' packets, each 'size' bytes long.
+// A pointer to the packet array is returned, or NULL if the function ran out of memory.
+SocketDataPacket **AllocPacketList(int howmany, int size)
+{
+ SocketDataPacket **packetV = (SocketDataPacket **)RNET_MALLOC((howmany + 1) * sizeof(*packetV));
+
+ if (packetV != NULL)
+ {
+ int i;
+ for (i = 0; i < howmany; ++i)
+ {
+ packetV[i] = AllocPacket(size);
+ if (packetV[i] == NULL)
+ {
+ break;
+ }
+ }
+ packetV[i] = NULL;
+
+ if (i != howmany)
+ {
+ FreePacketList(packetV);
+ packetV = NULL;
+ }
+ }
+
+ return (packetV);
+}
+
+void FreePacketList(SocketDataPacket **packetV)
+{
+ if (packetV)
+ {
+ for (int i = 0; packetV[i]; ++i) FreePacket(packetV[i]);
+ RNET_FREE(packetV);
+ }
+}
+
+// Send 'len' bytes of 'data' over the non-server socket 'sock'
+int SocketSend(Socket *sock, const void *datap, int length)
+{
+ int sent = 0;
+ int left = length;
+ int status = -1;
+ int numsent = 0;
+ const unsigned char *data = (const unsigned char *)datap;
+
+ // Server sockets are for accepting connections only
+ if (sock->isServer)
+ {
+ TRACELOG(LOG_WARNING, "Cannot send information on a server socket");
+ return -1;
+ }
+
+ // Which socket are we trying to send data on
+ switch (sock->type)
+ {
+ case SOCKET_TCP:
+ {
+ SocketSetLastError(0);
+ do
+ {
+ length = send(sock->channel, (const char *)data, left, 0);
+ if (length > 0)
+ {
+ sent += length;
+ left -= length;
+ data += length;
+ }
+ } while ((left > 0) && // While we still have bytes left to send
+ ((length > 0) || // The amount of bytes we actually sent is > 0
+ (SocketGetLastError() == WSAEINTR)) // The socket was interupted
+ );
+
+ if (length == SOCKET_ERROR)
+ {
+ sock->status = SocketGetLastError();
+ TRACELOG(LOG_DEBUG, "Socket Error: %s", SocketErrorCodeToString(sock->status));
+ SocketSetLastError(0);
+ }
+ else TRACELOG(LOG_DEBUG, "Successfully sent \"%s\" (%d bytes)", datap, sent);
+
+ return sent;
+ } break;
+ case SOCKET_UDP:
+ {
+ SocketSetLastError(0);
+
+ if (sock->isIPv6) status = sendto(sock->channel, (const char *)data, left, 0, (struct sockaddr *)&sock->addripv6->address, sizeof(sock->addripv6->address));
+ else status = sendto(sock->channel, (const char *)data, left, 0, (struct sockaddr *)&sock->addripv4->address, sizeof(sock->addripv4->address));
+
+ if (sent >= 0)
+ {
+ sock->status = 0;
+ ++numsent;
+ TRACELOG(LOG_DEBUG, "Successfully sent \"%s\" (%d bytes)", datap, status);
+ }
+ else
+ {
+ sock->status = SocketGetLastError();
+ TRACELOG(LOG_DEBUG, "Socket Error: %s", SocketGetLastErrorString(sock->status));
+ SocketSetLastError(0);
+ return 0;
+ }
+
+ return numsent;
+ } break;
+ default: break;
+ }
+
+ return -1;
+}
+
+// Receive up to 'maxlen' bytes of data over the non-server socket 'sock',
+// and store them in the buffer pointed to by 'data'.
+// This function returns the actual amount of data received. If the return
+// value is less than or equal to zero, then either the remote connection was
+// closed, or an unknown socket error occurred.
+int SocketReceive(Socket *sock, void *data, int maxlen)
+{
+ int len = 0;
+ int numrecv = 0;
+ int status = 0;
+ socklen_t sock_len;
+ struct sockaddr_storage sock_addr;
+ //char ip[INET6_ADDRSTRLEN];
+
+ // Server sockets are for accepting connections only
+ if (sock->isServer && (sock->type == SOCKET_TCP))
+ {
+ sock->status = SocketGetLastError();
+ TRACELOG(LOG_DEBUG, "Socket Error: %s", "Server sockets cannot be used to receive data");
+ SocketSetLastError(0);
+ return 0;
+ }
+
+ // Which socket are we trying to send data on
+ switch (sock->type)
+ {
+ case SOCKET_TCP:
+ {
+ SocketSetLastError(0);
+ do
+ {
+ len = recv(sock->channel, (char *)data, maxlen, 0);
+ } while (SocketGetLastError() == WSAEINTR);
+
+ if (len > 0)
+ {
+ // Who sent the packet?
+ if (sock->type == SOCKET_UDP)
+ {
+ //TRACELOG(LOG_DEBUG, "Received data from: %s", inet_ntop(sock_addr.ss_family, GetSocketAddressPtr((struct sockaddr *)&sock_addr), ip, sizeof(ip)));
+ }
+
+ ((unsigned char *)data)[len] = '\0'; // Add null terminating character to the end of the stream
+ TRACELOG(LOG_DEBUG, "Received \"%s\" (%d bytes)", data, len);
+ }
+
+ sock->ready = 0;
+ return len;
+ } break;
+ case SOCKET_UDP:
+ {
+ SocketSetLastError(0);
+ sock_len = sizeof(sock_addr);
+ status = recvfrom(sock->channel, // The receving channel
+ data, // A pointer to the data buffer to fill
+ maxlen, // The max length of the data to fill
+ 0, // Flags
+ (struct sockaddr *)&sock_addr, // The address of the recevied data
+ &sock_len // The length of the received data address
+ );
+
+ if (status >= 0) ++numrecv;
+ else
+ {
+ sock->status = SocketGetLastError();
+
+ switch (sock->status)
+ {
+ case WSAEWOULDBLOCK: break;
+ default: TRACELOG(LOG_WARNING, "Socket Error: %s", SocketErrorCodeToString(sock->status)); break;
+ }
+
+ SocketSetLastError(0);
+ return 0;
+ }
+
+ sock->ready = 0;
+ return numrecv;
+ } break;
+ default: break;
+ }
+
+ return -1;
+}
+
+// Does the socket have it's 'ready' flag set?
+bool IsSocketReady(Socket *sock)
+{
+ return (sock != NULL) && (sock->ready);
+}
+
+// Check if the socket is considered connected
+bool IsSocketConnected(Socket *sock)
+{
+#if defined(_WIN32)
+ FD_SET writefds;
+ FD_ZERO(&writefds);
+ FD_SET(sock->channel, &writefds);
+ struct timeval timeout;
+ timeout.tv_sec = 1;
+ timeout.tv_usec = 1000000000UL;
+ int total = select(0, NULL, &writefds, NULL, &timeout);
+
+ if (total == -1)
+ { // Error
+ sock->status = SocketGetLastError();
+ TRACELOG(LOG_WARNING, "Socket Error: %s", SocketErrorCodeToString(sock->status));
+ SocketSetLastError(0);
+ }
+ else if (total == 0) return false; // Timeout
+ else if (FD_ISSET(sock->channel, &writefds)) return true;
+
+ return false;
+#else
+ return true;
+#endif
+}
+
+// Allocate and return a SocketResult struct
+SocketResult *LoadSocketResult(void)
+{
+ struct SocketResult *res = (struct SocketResult *)RNET_MALLOC(sizeof(*res));
+
+ if (res != NULL)
+ {
+ memset(res, 0, sizeof(*res));
+ if ((res->socket = LoadSocket()) == NULL)
+ {
+ RNET_FREE(res);
+ res = NULL;
+ }
+ }
+
+ return res;
+}
+
+// Free an allocated SocketResult
+void UnloadSocketResult(SocketResult **result)
+{
+ if (*result != NULL)
+ {
+ if ((*result)->socket != NULL) UnloadSocket(&((*result)->socket));
+
+ RNET_FREE(*result);
+ *result = NULL;
+ }
+}
+
+// Allocate a Socket
+Socket *LoadSocket(void)
+{
+ struct Socket *sock;
+ sock = (Socket *)RNET_MALLOC(sizeof(*sock));
+
+ if (sock != NULL) memset(sock, 0, sizeof(*sock));
+ else
+ {
+ TRACELOG(LOG_WARNING, "Ran out of memory attempting to allocate a socket");
+ SocketClose(sock);
+ RNET_FREE(sock);
+ sock = NULL;
+ }
+
+ return sock;
+}
+
+// Free an allocated Socket
+void UnloadSocket(Socket **sock)
+{
+ if (*sock != NULL)
+ {
+ RNET_FREE(*sock);
+ *sock = NULL;
+ }
+}
+
+// Allocate a SocketSet
+SocketSet *LoadSocketSet(int max)
+{
+ struct SocketSet *set = (struct SocketSet *)RNET_MALLOC(sizeof(*set));
+
+ if (set != NULL)
+ {
+ set->numsockets = 0;
+ set->maxsockets = max;
+ set->sockets = (struct Socket **)RNET_MALLOC(max * sizeof(*set->sockets));
+ if (set->sockets != NULL)
+ {
+ for (int i = 0; i < max; ++i) set->sockets[i] = NULL;
+ }
+ else
+ {
+ RNET_FREE(set);
+ set = NULL;
+ }
+ }
+
+ return (set);
+}
+
+// Free an allocated SocketSet
+void UnloadSocketSet(SocketSet *set)
+{
+ if (set)
+ {
+ RNET_FREE(set->sockets);
+ RNET_FREE(set);
+ }
+}
+
+// Add a Socket "sock" to the SocketSet "set"
+int AddSocket(SocketSet *set, Socket *sock)
+{
+ if (sock != NULL)
+ {
+ if (set->numsockets == set->maxsockets)
+ {
+ TRACELOG(LOG_DEBUG, "Socket Error: %s", "SocketSet is full");
+ SocketSetLastError(0);
+ return (-1);
+ }
+ set->sockets[set->numsockets++] = (struct Socket *)sock;
+ }
+ else
+ {
+ TRACELOG(LOG_DEBUG, "Socket Error: %s", "Socket was null");
+ SocketSetLastError(0);
+ return (-1);
+ }
+
+ return (set->numsockets);
+}
+
+// Remove a Socket "sock" to the SocketSet "set"
+int RemoveSocket(SocketSet *set, Socket *sock)
+{
+ if (sock != NULL)
+ {
+ int i = 0;
+ for (i = 0; i < set->numsockets; ++i)
+ {
+ if (set->sockets[i] == (struct Socket *)sock) break;
+ }
+
+ if (i == set->numsockets)
+ {
+ TRACELOG(LOG_DEBUG, "Socket Error: %s", "Socket not found");
+ SocketSetLastError(0);
+ return (-1);
+ }
+
+ --set->numsockets;
+ for (; i < set->numsockets; ++i) set->sockets[i] = set->sockets[i + 1];
+ }
+
+ return (set->numsockets);
+}
+
+// Check the sockets in the socket set for pending information
+int CheckSockets(SocketSet *set, unsigned int timeout)
+{
+ int i;
+ SOCKET maxfd;
+ int retval;
+ struct timeval tv;
+ fd_set mask;
+
+ /* Find the largest file descriptor */
+ maxfd = 0;
+ for (i = set->numsockets - 1; i >= 0; --i)
+ {
+ if (set->sockets[i]->channel > maxfd)
+ {
+ maxfd = set->sockets[i]->channel;
+ }
+ }
+
+ // Check the file descriptors for available data
+ do
+ {
+ SocketSetLastError(0);
+
+ // Set up the mask of file descriptors
+ FD_ZERO(&mask);
+ for (i = set->numsockets - 1; i >= 0; --i)
+ {
+ FD_SET(set->sockets[i]->channel, &mask);
+ } // Set up the timeout
+
+ tv.tv_sec = timeout / 1000;
+ tv.tv_usec = (timeout % 1000) * 1000;
+
+ /* Look! */
+ retval = select(maxfd + 1, &mask, NULL, NULL, &tv);
+ } while (SocketGetLastError() == WSAEINTR);
+
+ // Mark all file descriptors ready that have data available
+ if (retval > 0)
+ {
+ for (i = set->numsockets - 1; i >= 0; --i)
+ {
+ if (FD_ISSET(set->sockets[i]->channel, &mask)) set->sockets[i]->ready = 1;
+ }
+ }
+
+ return retval;
+}
+
+// Allocate an AddressInformation
+AddressInformation LoadAddress(void)
+{
+ AddressInformation addressInfo = NULL;
+ addressInfo = (AddressInformation)RNET_CALLOC(1, sizeof(*addressInfo));
+
+ if (addressInfo != NULL)
+ {
+ addressInfo->addr.ai_addr = (struct sockaddr *)RNET_CALLOC(1, sizeof(struct sockaddr));
+ if (addressInfo->addr.ai_addr == NULL) TRACELOG(LOG_WARNING, "Failed to allocate memory for \"struct sockaddr\"");
+ }
+ else TRACELOG(LOG_WARNING, "Failed to allocate memory for \"struct AddressInformation\"");
+
+ return addressInfo;
+}
+
+// Free an AddressInformation struct
+void UnloadAddress(AddressInformation *addressInfo)
+{
+ if (*addressInfo != NULL)
+ {
+ if ((*addressInfo)->addr.ai_addr != NULL)
+ {
+ RNET_FREE((*addressInfo)->addr.ai_addr);
+ (*addressInfo)->addr.ai_addr = NULL;
+ }
+
+ RNET_FREE(*addressInfo);
+ *addressInfo = NULL;
+ }
+}
+
+// Allocate a list of AddressInformation
+AddressInformation *LoadAddressList(int size)
+{
+ AddressInformation *addr;
+ addr = (AddressInformation *)RNET_MALLOC(size * sizeof(AddressInformation));
+ return addr;
+}
+
+// Opaque datatype accessor addrinfo->ai_family
+int GetAddressFamily(AddressInformation address)
+{
+ return address->addr.ai_family;
+}
+
+// Opaque datatype accessor addrinfo->ai_socktype
+int GetAddressSocketType(AddressInformation address)
+{
+ return address->addr.ai_socktype;
+}
+
+// Opaque datatype accessor addrinfo->ai_protocol
+int GetAddressProtocol(AddressInformation address)
+{
+ return address->addr.ai_protocol;
+}
+
+// Opaque datatype accessor addrinfo->ai_canonname
+char *GetAddressCanonName(AddressInformation address)
+{
+ return address->addr.ai_canonname;
+}
+
+// Opaque datatype accessor addrinfo->ai_addr
+char *GetAddressHostAndPort(AddressInformation address, char *outhost, unsigned short *outport)
+{
+ //char *ip[INET6_ADDRSTRLEN];
+ char *result = NULL;
+ struct sockaddr_storage *storage = (struct sockaddr_storage *)address->addr.ai_addr;
+
+ switch (storage->ss_family)
+ {
+ case AF_INET:
+ {
+ struct sockaddr_in *s = ((struct sockaddr_in *)address->addr.ai_addr);
+ //result = inet_ntop(AF_INET, &s->sin_addr, ip, INET_ADDRSTRLEN); // TODO.
+ *outport = ntohs(s->sin_port);
+ } break;
+ case AF_INET6:
+ {
+ struct sockaddr_in6 *s = ((struct sockaddr_in6 *)address->addr.ai_addr);
+ //result = inet_ntop(AF_INET6, &s->sin6_addr, ip, INET6_ADDRSTRLEN); // TODO.
+ *outport = ntohs(s->sin6_port);
+ } break;
+ default: break;
+ }
+
+ if (result == NULL)
+ {
+ TRACELOG(LOG_WARNING, "Socket Error: %s", SocketErrorCodeToString(SocketGetLastError()));
+ SocketSetLastError(0);
+ }
+ else
+ {
+ strcpy(outhost, result);
+ }
+ return result;
+}
+
+//
+void PacketSend(Packet *packet)
+{
+ TRACELOG(LOG_INFO, "Sending packet (%s) with size %d\n", packet->data, packet->size);
+}
+
+//
+void PacketReceive(Packet *packet)
+{
+ TRACELOG(LOG_INFO, "Receiving packet (%s) with size %d\n", packet->data, packet->size);
+}
+
+//
+void PacketWrite16(Packet *packet, uint16_t value)
+{
+ TRACELOG(LOG_INFO, "Original: 0x%04" PRIX16 " - %" PRIu16 "\n", value, value);
+ uint8_t *data = packet->data + packet->offs;
+ *data++ = (uint8_t)(value >> 8);
+ *data++ = (uint8_t)(value);
+ packet->size += sizeof(uint16_t);
+ packet->offs += sizeof(uint16_t);
+ TRACELOG(LOG_INFO, "Network: 0x%04" PRIX16 " - %" PRIu16 "\n", (uint16_t) *data, (uint16_t) *data);
+}
+
+//
+void PacketWrite32(Packet *packet, uint32_t value)
+{
+ TRACELOG(LOG_INFO, "Original: 0x%08" PRIX32 " - %" PRIu32 "\n", value, value);
+ uint8_t *data = packet->data + packet->offs;
+ *data++ = (uint8_t)(value >> 24);
+ *data++ = (uint8_t)(value >> 16);
+ *data++ = (uint8_t)(value >> 8);
+ *data++ = (uint8_t)(value);
+ packet->size += sizeof(uint32_t);
+ packet->offs += sizeof(uint32_t);
+
+ TRACELOG(LOG_INFO, "Network: 0x%08" PRIX32 " - %" PRIu32 "\n",
+ (uint32_t)(((intptr_t) packet->data) - packet->offs),
+ (uint32_t)(((intptr_t) packet->data) - packet->offs));
+}
+
+//
+void PacketWrite64(Packet *packet, uint64_t value)
+{
+ TRACELOG(LOG_INFO, "Original: 0x%016" PRIX64 " - %" PRIu64 "\n", value, value);
+
+ uint8_t *data = packet->data + packet->offs;
+ *data++ = (uint8_t)(value >> 56);
+ *data++ = (uint8_t)(value >> 48);
+ *data++ = (uint8_t)(value >> 40);
+ *data++ = (uint8_t)(value >> 32);
+ *data++ = (uint8_t)(value >> 24);
+ *data++ = (uint8_t)(value >> 16);
+ *data++ = (uint8_t)(value >> 8);
+ *data++ = (uint8_t)(value);
+ packet->size += sizeof(uint64_t);
+ packet->offs += sizeof(uint64_t);
+
+ TRACELOG(LOG_INFO, "Network: 0x%016" PRIX64 " - %" PRIu64 "\n", (uint64_t)(packet->data - packet->offs), (uint64_t)(packet->data - packet->offs));
+}
+
+//
+uint16_t PacketRead16(Packet *packet)
+{
+ uint8_t *data = packet->data + packet->offs;
+ packet->size += sizeof(uint16_t);
+ packet->offs += sizeof(uint16_t);
+ uint16_t value = ((uint16_t) data[0] << 8) | data[1];
+ TRACELOG(LOG_INFO, "Original: 0x%04" PRIX16 " - %" PRIu16 "\n", value, value);
+
+ return value;
+}
+
+//
+uint32_t PacketRead32(Packet *packet)
+{
+ uint8_t *data = packet->data + packet->offs;
+ packet->size += sizeof(uint32_t);
+ packet->offs += sizeof(uint32_t);
+ uint32_t value = ((uint32_t) data[0] << 24) | ((uint32_t) data[1] << 16) | ((uint32_t) data[2] << 8) | data[3];
+ TRACELOG(LOG_INFO, "Original: 0x%08" PRIX32 " - %" PRIu32 "\n", value, value);
+
+ return value;
+}
+
+//
+uint64_t PacketRead64(Packet *packet)
+{
+ uint8_t *data = packet->data + packet->offs;
+ packet->size += sizeof(uint64_t);
+ packet->offs += sizeof(uint64_t);
+ uint64_t value = ((uint64_t) data[0] << 56) | ((uint64_t) data[1] << 48) | ((uint64_t) data[2] << 40) | ((uint64_t) data[3] << 32) | ((uint64_t) data[4] << 24) | ((uint64_t) data[5] << 16) | ((uint64_t) data[6] << 8) | data[7];
+ TRACELOG(LOG_INFO, "Original: 0x%016" PRIX64 " - %" PRIu64 "\n", value, value);
+
+ return value;
+}
+
+#endif // RNET_IMPLEMENTATION \ No newline at end of file