diff --git a/raylib/easings.h b/raylib/easings.h deleted file mode 100644 index 3441305..0000000 --- a/raylib/easings.h +++ /dev/null @@ -1,263 +0,0 @@ -/******************************************************************************************* -* -* 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 // 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/raylib/physac.h b/raylib/physac.h deleted file mode 100644 index d9b534b..0000000 --- a/raylib/physac.h +++ /dev/null @@ -1,2063 +0,0 @@ -/********************************************************************************************** -* -* Physac v1.0 - 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_NO_THREADS -* The generated implementation won't include pthread library and user must create a secondary thread to call PhysicsThread(). -* It is so important that the thread where PhysicsThread() is called must not have v-sync or any other CPU limitation. -* -* #define PHYSAC_STANDALONE -* Avoid raylib.h header inclusion in this file. Data types defined on raylib are defined -* internally in the library and input management and drawing functions must be provided by -* the user (check library implementation for further details). -* -* #define PHYSAC_DEBUG -* Traces log messages when creating and destroying physics bodies and detects errors in physics -* calculations and reference exceptions; it is useful for debug purposes -* -* #define PHYSAC_MALLOC() -* #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. -* -* -* NOTE 1: Physac requires multi-threading, when InitPhysics() a second thread is created to manage physics calculations. -* NOTE 2: Physac requires static C library linkage to avoid dependency on MinGW DLL (-static -lpthread) -* -* Use the following code to compile: -* gcc -o $(NAME_PART).exe $(FILE_NAME) -s -static -lraylib -lpthread -lopengl32 -lgdi32 -lwinmm -std=c99 -* -* VERY THANKS TO: -* Ramon Santamaria (github: @raysan5) -* -* -* LICENSE: zlib/libpng -* -* Copyright (c) 2016-2018 Victor Fisac (github: @victorfisac) -* -* 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_FREE - #define PHYSAC_FREE(ptr) free(ptr) -#endif - -//---------------------------------------------------------------------------------- -// Defines and Macros -//---------------------------------------------------------------------------------- -#define PHYSAC_MAX_BODIES 64 -#define PHYSAC_MAX_MANIFOLDS 4096 -#define PHYSAC_MAX_VERTICES 24 -#define PHYSAC_CIRCLE_VERTICES 24 - -#define PHYSAC_COLLISION_ITERATIONS 100 -#define PHYSAC_PENETRATION_ALLOWANCE 0.05f -#define PHYSAC_PENETRATION_CORRECTION 0.4f - -#define PHYSAC_PI 3.14159265358979323846 -#define PHYSAC_DEG2RAD (PHYSAC_PI/180.0f) - -//---------------------------------------------------------------------------------- -// Types and Structures Definition -// NOTE: Below types are required for PHYSAC_STANDALONE usage -//---------------------------------------------------------------------------------- -#if defined(PHYSAC_STANDALONE) - // Boolean type - #if defined(__STDC__) && __STDC_VERSION__ >= 199901L - #include - #elif !defined(__cplusplus) && !defined(bool) - typedef enum { false, true } bool; - #endif - - // Vector2 type - typedef struct Vector2 { - float x; - float y; - } Vector2; -#endif - -typedef enum PhysicsShapeType { PHYSICS_CIRCLE, PHYSICS_POLYGON } PhysicsShapeType; - -// Previously defined to be used in PhysicsShape struct as circular dependencies -typedef struct PhysicsBodyData *PhysicsBody; - -#if defined(__cplusplus) -extern "C" { // Prevents name mangling of functions -#endif - -//---------------------------------------------------------------------------------- -// Module Functions Declaration -//---------------------------------------------------------------------------------- -PHYSACDEF void InitPhysics(void); // Initializes physics values, pointers and creates physics loop thread -PHYSACDEF void RunPhysicsStep(void); // Run physics step, to be used if PHYSICS_NO_THREADS is set in your main loop -PHYSACDEF void SetPhysicsTimeStep(double delta); // Sets physics fixed time step in milliseconds. 1.666666 by default -PHYSACDEF bool IsPhysicsEnabled(void); // Returns true if physics thread is currently enabled -PHYSACDEF void SetPhysicsGravity(float x, float y); // Sets physics global gravity force -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 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 int GetPhysicsBodiesCount(void); // Returns the current amount of created physics bodies -PHYSACDEF PhysicsBody GetPhysicsBody(int index); // Returns a physics body of the bodies pool at a specific index -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) -PHYSACDEF void SetPhysicsBodyRotation(PhysicsBody body, float radians); // Sets physics body shape transform based on radians parameter -PHYSACDEF void DestroyPhysicsBody(PhysicsBody body); // Unitializes and destroy a physics body -PHYSACDEF void ResetPhysics(void); // Destroys created physics bodies and manifolds and resets global values -PHYSACDEF void ClosePhysics(void); // Unitializes physics pointers and closes physics loop thread - -#if defined(__cplusplus) -} -#endif - -#endif // PHYSAC_H - -/*********************************************************************************** -* -* PHYSAC IMPLEMENTATION -* -************************************************************************************/ - -#if defined(PHYSAC_IMPLEMENTATION) - -#if !defined(PHYSAC_NO_THREADS) - #include // Required for: pthread_t, pthread_create() -#endif - -#if defined(PHYSAC_DEBUG) - -#endif - -// Support TRACELOG macros -#if defined(PHYSAC_DEBUG) - #include // Required for: printf() - #define TRACELOG(...) printf(__VA_ARGS__) -#else - #define TRACELOG(...) (void)0 -#endif - -#include // Required for: malloc(), free(), srand(), rand() -#include // Required for: cosf(), sinf(), fabs(), sqrtf() - -#if !defined(PHYSAC_STANDALONE) - #include "raymath.h" // Required for: Vector2Add(), Vector2Subtract() -#endif - -// Time management functionality -#include // 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); -#elif defined(__linux__) - #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 // Required for: timespec -#elif defined(__APPLE__) // macOS also defines __MACH__ - #include // Required for: mach_absolute_time() -#endif - -//---------------------------------------------------------------------------------- -// Defines and Macros -//---------------------------------------------------------------------------------- -#define min(a,b) (((a)<(b))?(a):(b)) -#define 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 (Vector2){ 0.0f, 0.0f } - -//---------------------------------------------------------------------------------- -// Data Types Structure Definition -//---------------------------------------------------------------------------------- - -// Matrix2x2 type (used for polygon shape rotation matrix) -typedef struct Matrix2x2 { - float m00; - float m01; - float m10; - float m11; -} Matrix2x2; - -typedef struct PolygonData { - unsigned int vertexCount; // Current used vertex and normals count - Vector2 positions[PHYSAC_MAX_VERTICES]; // Polygon vertex positions vectors - Vector2 normals[PHYSAC_MAX_VERTICES]; // Polygon vertex normals vectors -} PolygonData; - -typedef struct PhysicsShape { - PhysicsShapeType type; // Physics shape type (circle or polygon) - PhysicsBody body; // Shape physics body reference - float radius; // Circle shape radius (used for circle shapes) - Matrix2x2 transform; // Vertices transform matrix 2x2 - PolygonData vertexData; // Polygon shape vertices position and normals data (just used for polygon shapes) -} PhysicsShape; - -typedef struct PhysicsBodyData { - unsigned int id; // Reference 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, normals) -} PhysicsBodyData; - -typedef struct PhysicsManifoldData { - unsigned int id; // Reference 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; - -//---------------------------------------------------------------------------------- -// Global Variables Definition -//---------------------------------------------------------------------------------- -#if !defined(PHYSAC_NO_THREADS) -static pthread_t physicsThreadId; // Physics thread id -#endif -static unsigned int usedMemory = 0; // Total allocated dynamic memory -static bool physicsThreadEnabled = false; // Physics thread enabled state -static double baseTime = 0.0; // Offset time for MONOTONIC clock -static double startTime = 0.0; // Start time in milliseconds -static double deltaTime = 1.0/60.0/10.0 * 1000; // Delta time used for physics steps, in milliseconds -static double currentTime = 0.0; // Current time in milliseconds -static unsigned long long int frequency = 0; // Hi-res clock frequency - -static double accumulator = 0.0; // Physics time step delta time accumulator -static unsigned int stepsCount = 0; // Total physics steps processed -static Vector2 gravityForce = { 0.0f, 9.81f }; // Physics world gravity force -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 - -//---------------------------------------------------------------------------------- -// Module Internal Functions Declaration -//---------------------------------------------------------------------------------- -static int FindAvailableBodyIndex(); // Finds a valid index for a new physics body initialization -static PolygonData CreateRandomPolygon(float radius, int sides); // Creates a random polygon shape with max vertex distance from polygon pivot -static PolygonData CreateRectanglePolygon(Vector2 pos, Vector2 size); // Creates a rectangle polygon shape based on a min and max positions -static void *PhysicsLoop(void *arg); // Physics loop thread function -static void PhysicsStep(void); // Physics steps calculations (dynamics, collisions and position corrections) -static int FindAvailableManifoldIndex(); // Finds a valid index for a new manifold initialization -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 InitializePhysicsManifolds(PhysicsManifold manifold); // Initializes physics manifolds to solve collisions -static void IntegratePhysicsImpulses(PhysicsManifold manifold); // Integrates physics collisions impulses to solve collisions -static void IntegratePhysicsVelocity(PhysicsBody body); // Integrates physics velocity into position and forces -static void CorrectPhysicsPositions(PhysicsManifold manifold); // Corrects physics bodies positions based on manifolds collision information -static float FindAxisLeastPenetration(int *faceIndex, PhysicsShape shapeA, PhysicsShape shapeB); // Finds polygon shapes axis least penetration -static void FindIncidentFace(Vector2 *v0, Vector2 *v1, PhysicsShape ref, PhysicsShape inc, int index); // Finds two polygon shapes incident face -static int Clip(Vector2 normal, float clip, Vector2 *faceA, Vector2 *faceB); // Calculates clipping based on a normal and two faces -static bool BiasGreaterThan(float valueA, float valueB); // Check if values are between bias range -static Vector2 TriangleBarycenter(Vector2 v1, Vector2 v2, Vector2 v3); // Returns the barycenter of a triangle given by 3 points - -static void InitTimer(void); // Initializes hi-resolution MONOTONIC timer -static unsigned long long int GetTimeCount(void); // Get hi-res MONOTONIC time measure in mseconds -static double GetCurrentTime(void); // Get current time measure in milliseconds - -// Math functions -static Vector2 MathCross(float value, Vector2 vector); // Returns the cross product of a vector and a value -static float MathCrossVector2(Vector2 v1, Vector2 v2); // Returns the cross product of two vectors -static float MathLenSqr(Vector2 vector); // Returns the len square root of a vector -static float MathDot(Vector2 v1, Vector2 v2); // Returns the dot product of two vectors -static inline float DistSqr(Vector2 v1, Vector2 v2); // Returns the square root of distance between two vectors -static void MathNormalize(Vector2 *vector); // Returns the normalized values of a vector -#if defined(PHYSAC_STANDALONE) -static Vector2 Vector2Add(Vector2 v1, Vector2 v2); // Returns the sum of two given vectors -static Vector2 Vector2Subtract(Vector2 v1, Vector2 v2); // Returns the subtract of two given vectors -#endif - -static Matrix2x2 Mat2Radians(float radians); // Creates a matrix 2x2 from a given radians value -static void Mat2Set(Matrix2x2 *matrix, float radians); // Set values from radians to a created matrix 2x2 -static inline Matrix2x2 Mat2Transpose(Matrix2x2 matrix); // Returns the transpose of a given matrix 2x2 -static inline Vector2 Mat2MultiplyVector2(Matrix2x2 matrix, Vector2 vector); // Multiplies a vector by a matrix 2x2 - -//---------------------------------------------------------------------------------- -// Module Functions Definition -//---------------------------------------------------------------------------------- -// Initializes physics values, pointers and creates physics loop thread -PHYSACDEF void InitPhysics(void) -{ - #if !defined(PHYSAC_NO_THREADS) - // NOTE: if defined, user will need to create a thread for PhysicsThread function manually - // Create physics thread using POSIXS thread libraries - pthread_create(&physicsThreadId, NULL, &PhysicsLoop, NULL); - #endif - - // Initialize high resolution timer - InitTimer(); - - #if defined(PHYSAC_DEBUG) - TRACELOG("[PHYSAC] physics module initialized successfully\n"); - #endif - - accumulator = 0.0; -} - -// Returns true if physics thread is currently enabled -PHYSACDEF bool IsPhysicsEnabled(void) -{ - return physicsThreadEnabled; -} - -// 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 newBody = CreatePhysicsBodyPolygon(pos, radius, PHYSAC_CIRCLE_VERTICES, density); - return newBody; -} - -// Creates a new rectangle physics body with generic parameters -PHYSACDEF PhysicsBody CreatePhysicsBodyRectangle(Vector2 pos, float width, float height, float density) -{ - PhysicsBody newBody = (PhysicsBody)PHYSAC_MALLOC(sizeof(PhysicsBodyData)); - usedMemory += sizeof(PhysicsBodyData); - - int newId = FindAvailableBodyIndex(); - if (newId != -1) - { - // Initialize new body with generic values - newBody->id = newId; - newBody->enabled = true; - newBody->position = pos; - newBody->velocity = (Vector2){ 0.0f }; - newBody->force = (Vector2){ 0.0f }; - newBody->angularVelocity = 0.0f; - newBody->torque = 0.0f; - newBody->orient = 0.0f; - newBody->shape.type = PHYSICS_POLYGON; - newBody->shape.body = newBody; - newBody->shape.radius = 0.0f; - newBody->shape.transform = Mat2Radians(0.0f); - newBody->shape.vertexData = CreateRectanglePolygon(pos, (Vector2){ width, height }); - - // Calculate centroid and moment of inertia - Vector2 center = { 0.0f, 0.0f }; - float area = 0.0f; - float inertia = 0.0f; - - for (int i = 0; i < newBody->shape.vertexData.vertexCount; i++) - { - // Triangle vertices, third vertex implied as (0, 0) - Vector2 p1 = newBody->shape.vertexData.positions[i]; - int nextIndex = (((i + 1) < newBody->shape.vertexData.vertexCount) ? (i + 1) : 0); - Vector2 p2 = newBody->shape.vertexData.positions[nextIndex]; - - float D = MathCrossVector2(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 (int i = 0; i < newBody->shape.vertexData.vertexCount; i++) - { - newBody->shape.vertexData.positions[i].x -= center.x; - newBody->shape.vertexData.positions[i].y -= center.y; - } - - newBody->mass = density*area; - newBody->inverseMass = ((newBody->mass != 0.0f) ? 1.0f/newBody->mass : 0.0f); - newBody->inertia = density*inertia; - newBody->inverseInertia = ((newBody->inertia != 0.0f) ? 1.0f/newBody->inertia : 0.0f); - newBody->staticFriction = 0.4f; - newBody->dynamicFriction = 0.2f; - newBody->restitution = 0.0f; - newBody->useGravity = true; - newBody->isGrounded = false; - newBody->freezeOrient = false; - - // Add new body to bodies pointers array and update bodies count - bodies[physicsBodiesCount] = newBody; - physicsBodiesCount++; - - #if defined(PHYSAC_DEBUG) - TRACELOG("[PHYSAC] created polygon physics body id %i\n", newBody->id); - #endif - } - #if defined(PHYSAC_DEBUG) - else TRACELOG("[PHYSAC] new physics body creation failed because there is any available id to use\n"); - #endif - - return newBody; -} - -// Creates a new polygon physics body with generic parameters -PHYSACDEF PhysicsBody CreatePhysicsBodyPolygon(Vector2 pos, float radius, int sides, float density) -{ - PhysicsBody newBody = (PhysicsBody)PHYSAC_MALLOC(sizeof(PhysicsBodyData)); - usedMemory += sizeof(PhysicsBodyData); - - int newId = FindAvailableBodyIndex(); - if (newId != -1) - { - // Initialize new body with generic values - newBody->id = newId; - newBody->enabled = true; - newBody->position = pos; - newBody->velocity = PHYSAC_VECTOR_ZERO; - newBody->force = PHYSAC_VECTOR_ZERO; - newBody->angularVelocity = 0.0f; - newBody->torque = 0.0f; - newBody->orient = 0.0f; - newBody->shape.type = PHYSICS_POLYGON; - newBody->shape.body = newBody; - newBody->shape.transform = Mat2Radians(0.0f); - newBody->shape.vertexData = CreateRandomPolygon(radius, sides); - - // Calculate centroid and moment of inertia - Vector2 center = { 0.0f, 0.0f }; - float area = 0.0f; - float inertia = 0.0f; - - for (int i = 0; i < newBody->shape.vertexData.vertexCount; i++) - { - // Triangle vertices, third vertex implied as (0, 0) - Vector2 position1 = newBody->shape.vertexData.positions[i]; - int nextIndex = (((i + 1) < newBody->shape.vertexData.vertexCount) ? (i + 1) : 0); - Vector2 position2 = newBody->shape.vertexData.positions[nextIndex]; - - float cross = MathCrossVector2(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 (int i = 0; i < newBody->shape.vertexData.vertexCount; i++) - { - newBody->shape.vertexData.positions[i].x -= center.x; - newBody->shape.vertexData.positions[i].y -= center.y; - } - - newBody->mass = density*area; - newBody->inverseMass = ((newBody->mass != 0.0f) ? 1.0f/newBody->mass : 0.0f); - newBody->inertia = density*inertia; - newBody->inverseInertia = ((newBody->inertia != 0.0f) ? 1.0f/newBody->inertia : 0.0f); - newBody->staticFriction = 0.4f; - newBody->dynamicFriction = 0.2f; - newBody->restitution = 0.0f; - newBody->useGravity = true; - newBody->isGrounded = false; - newBody->freezeOrient = false; - - // Add new body to bodies pointers array and update bodies count - bodies[physicsBodiesCount] = newBody; - physicsBodiesCount++; - - #if defined(PHYSAC_DEBUG) - TRACELOG("[PHYSAC] created polygon physics body id %i\n", newBody->id); - #endif - } - #if defined(PHYSAC_DEBUG) - else TRACELOG("[PHYSAC] new physics body creation failed because there is any available id to use\n"); - #endif - - return newBody; -} - -// Adds a force to a physics body -PHYSACDEF void PhysicsAddForce(PhysicsBody body, Vector2 force) -{ - if (body != NULL) body->force = Vector2Add(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) - { - PolygonData vertexData = body->shape.vertexData; - bool collision = false; - - for (int i = 0; i < vertexData.vertexCount; i++) - { - Vector2 positionA = body->position; - Vector2 positionB = Mat2MultiplyVector2(body->shape.transform, Vector2Add(body->position, vertexData.positions[i])); - int nextIndex = (((i + 1) < vertexData.vertexCount) ? (i + 1) : 0); - Vector2 positionC = Mat2MultiplyVector2(body->shape.transform, Vector2Add(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 = TriangleBarycenter(vertices[i], vertices[nextIndex], PHYSAC_VECTOR_ZERO); - center = Vector2Add(bodyPos, center); - Vector2 offset = Vector2Subtract(center, bodyPos); - - PhysicsBody newBody = CreatePhysicsBodyPolygon(center, 10, 3, 10); // Create polygon physics body with relevant values - - PolygonData newData = { 0 }; - newData.vertexCount = 3; - - newData.positions[0] = Vector2Subtract(vertices[i], offset); - newData.positions[1] = Vector2Subtract(vertices[nextIndex], offset); - newData.positions[2] = Vector2Subtract(position, center); - - // Separate vertices to avoid unnecessary physics collisions - newData.positions[0].x *= 0.95f; - newData.positions[0].y *= 0.95f; - newData.positions[1].x *= 0.95f; - newData.positions[1].y *= 0.95f; - newData.positions[2].x *= 0.95f; - newData.positions[2].y *= 0.95f; - - // Calculate polygon faces normals - for (int j = 0; j < newData.vertexCount; j++) - { - int nextVertex = (((j + 1) < newData.vertexCount) ? (j + 1) : 0); - Vector2 face = Vector2Subtract(newData.positions[nextVertex], newData.positions[j]); - - newData.normals[j] = (Vector2){ face.y, -face.x }; - MathNormalize(&newData.normals[j]); - } - - // Apply computed vertex data to new physics body shape - newBody->shape.vertexData = newData; - newBody->shape.transform = trans; - - // Calculate centroid and moment of inertia - center = PHYSAC_VECTOR_ZERO; - float area = 0.0f; - float inertia = 0.0f; - - for (int j = 0; j < newBody->shape.vertexData.vertexCount; j++) - { - // Triangle vertices, third vertex implied as (0, 0) - Vector2 p1 = newBody->shape.vertexData.positions[j]; - int nextVertex = (((j + 1) < newBody->shape.vertexData.vertexCount) ? (j + 1) : 0); - Vector2 p2 = newBody->shape.vertexData.positions[nextVertex]; - - float D = MathCrossVector2(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; - - newBody->mass = area; - newBody->inverseMass = ((newBody->mass != 0.0f) ? 1.0f/newBody->mass : 0.0f); - newBody->inertia = inertia; - newBody->inverseInertia = ((newBody->inertia != 0.0f) ? 1.0f/newBody->inertia : 0.0f); - - // Calculate explosion force direction - Vector2 pointA = newBody->position; - Vector2 pointB = Vector2Subtract(newData.positions[1], newData.positions[0]); - pointB.x /= 2.0f; - pointB.y /= 2.0f; - Vector2 forceDirection = Vector2Subtract(Vector2Add(pointA, Vector2Add(newData.positions[0], pointB)), newBody->position); - MathNormalize(&forceDirection); - forceDirection.x *= force; - forceDirection.y *= force; - - // Apply force to new physics body - PhysicsAddForce(newBody, forceDirection); - } - - PHYSAC_FREE(vertices); - } - } - } - #if defined(PHYSAC_DEBUG) - else TRACELOG("[PHYSAC] error when trying to shatter a null reference physics body"); - #endif -} - -// 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 < physicsBodiesCount) - { - body = bodies[index]; - - if (body == NULL) - { - #if defined(PHYSAC_DEBUG) - TRACELOG("[PHYSAC] error when trying to get a null reference physics body"); - #endif - } - } - #if defined(PHYSAC_DEBUG) - else TRACELOG("[PHYSAC] physics body index is out of bounds"); - #endif - - return body; -} - -// Returns the physics body shape type (PHYSICS_CIRCLE or PHYSICS_POLYGON) -PHYSACDEF int GetPhysicsShapeType(int index) -{ - int result = -1; - - if (index < physicsBodiesCount) - { - PhysicsBody body = bodies[index]; - - if (body != NULL) result = body->shape.type; - #if defined(PHYSAC_DEBUG) - else TRACELOG("[PHYSAC] error when trying to get a null reference physics body"); - #endif - } - #if defined(PHYSAC_DEBUG) - else TRACELOG("[PHYSAC] physics body index is out of bounds"); - #endif - - return result; -} - -// Returns the amount of vertices of a physics body shape -PHYSACDEF int GetPhysicsShapeVerticesCount(int index) -{ - int result = 0; - - if (index < physicsBodiesCount) - { - PhysicsBody body = bodies[index]; - - if (body != NULL) - { - switch (body->shape.type) - { - case PHYSICS_CIRCLE: result = PHYSAC_CIRCLE_VERTICES; break; - case PHYSICS_POLYGON: result = body->shape.vertexData.vertexCount; break; - default: break; - } - } - #if defined(PHYSAC_DEBUG) - else TRACELOG("[PHYSAC] error when trying to get a null reference physics body"); - #endif - } - #if defined(PHYSAC_DEBUG) - else TRACELOG("[PHYSAC] physics body index is out of bounds"); - #endif - - 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_CIRCLE_VERTICES*vertex*PHYSAC_DEG2RAD)*body->shape.radius; - position.y = body->position.y + sinf(360.0f/PHYSAC_CIRCLE_VERTICES*vertex*PHYSAC_DEG2RAD)*body->shape.radius; - } break; - case PHYSICS_POLYGON: - { - PolygonData vertexData = body->shape.vertexData; - position = Vector2Add(body->position, Mat2MultiplyVector2(body->shape.transform, vertexData.positions[vertex])); - } break; - default: break; - } - } - #if defined(PHYSAC_DEBUG) - else TRACELOG("[PHYSAC] error when trying to get a null reference physics body"); - #endif - - 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 = Mat2Radians(radians); - } -} - -// Unitializes and destroys a physics body -PHYSACDEF void DestroyPhysicsBody(PhysicsBody body) -{ - if (body != NULL) - { - int id = body->id; - int index = -1; - - for (int i = 0; i < physicsBodiesCount; i++) - { - if (bodies[i]->id == id) - { - index = i; - break; - } - } - - if (index == -1) - { - #if defined(PHYSAC_DEBUG) - TRACELOG("[PHYSAC] Not possible to find body id %i in pointers array\n", id); - #endif - 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 (int i = index; i < physicsBodiesCount; i++) - { - if ((i + 1) < physicsBodiesCount) bodies[i] = bodies[i + 1]; - } - - // Update physics bodies count - physicsBodiesCount--; - - #if defined(PHYSAC_DEBUG) - TRACELOG("[PHYSAC] destroyed physics body id %i\n", id); - #endif - } - #if defined(PHYSAC_DEBUG) - else TRACELOG("[PHYSAC] error trying to destroy a null referenced body\n"); - #endif -} - -// Destroys created physics bodies and manifolds and resets global values -PHYSACDEF void ResetPhysics(void) -{ - // 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; - - // 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; - - #if defined(PHYSAC_DEBUG) - TRACELOG("[PHYSAC] physics module reset successfully\n"); - #endif -} - -// Unitializes physics pointers and exits physics loop thread -PHYSACDEF void ClosePhysics(void) -{ - // Exit physics loop thread - physicsThreadEnabled = false; - - #if !defined(PHYSAC_NO_THREADS) - pthread_join(physicsThreadId, NULL); - #endif - - // Unitialize physics manifolds dynamic memory allocations - for (int i = physicsManifoldsCount - 1; i >= 0; i--) DestroyPhysicsManifold(contacts[i]); - - // Unitialize physics bodies dynamic memory allocations - for (int i = physicsBodiesCount - 1; i >= 0; i--) DestroyPhysicsBody(bodies[i]); - - #if defined(PHYSAC_DEBUG) - if (physicsBodiesCount > 0 || usedMemory != 0) TRACELOG("[PHYSAC] physics module closed with %i still allocated bodies [MEMORY: %i bytes]\n", physicsBodiesCount, usedMemory); - else if (physicsManifoldsCount > 0 || usedMemory != 0) TRACELOG("[PHYSAC] physics module closed with %i still allocated manifolds [MEMORY: %i bytes]\n", physicsManifoldsCount, usedMemory); - else TRACELOG("[PHYSAC] physics module closed successfully\n"); - #endif -} - -//---------------------------------------------------------------------------------- -// 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 (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 == i) - { - index = i; - break; - } - } - - return index; -} - -// Creates a random polygon shape with max vertex distance from polygon pivot -static PolygonData CreateRandomPolygon(float radius, int sides) -{ - PolygonData data = { 0 }; - data.vertexCount = sides; - - // Calculate polygon vertices positions - for (int i = 0; i < data.vertexCount; i++) - { - data.positions[i].x = cosf(360.0f/sides*i*PHYSAC_DEG2RAD)*radius; - data.positions[i].y = sinf(360.0f/sides*i*PHYSAC_DEG2RAD)*radius; - } - - // Calculate polygon faces normals - for (int i = 0; i < data.vertexCount; i++) - { - int nextIndex = (((i + 1) < sides) ? (i + 1) : 0); - Vector2 face = Vector2Subtract(data.positions[nextIndex], data.positions[i]); - - data.normals[i] = (Vector2){ face.y, -face.x }; - MathNormalize(&data.normals[i]); - } - - return data; -} - -// Creates a rectangle polygon shape based on a min and max positions -static PolygonData CreateRectanglePolygon(Vector2 pos, Vector2 size) -{ - PolygonData data = { 0 }; - data.vertexCount = 4; - - // Calculate polygon vertices positions - data.positions[0] = (Vector2){ pos.x + size.x/2, pos.y - size.y/2 }; - data.positions[1] = (Vector2){ pos.x + size.x/2, pos.y + size.y/2 }; - data.positions[2] = (Vector2){ pos.x - size.x/2, pos.y + size.y/2 }; - data.positions[3] = (Vector2){ pos.x - size.x/2, pos.y - size.y/2 }; - - // Calculate polygon faces normals - for (int i = 0; i < data.vertexCount; i++) - { - int nextIndex = (((i + 1) < data.vertexCount) ? (i + 1) : 0); - Vector2 face = Vector2Subtract(data.positions[nextIndex], data.positions[i]); - - data.normals[i] = (Vector2){ face.y, -face.x }; - MathNormalize(&data.normals[i]); - } - - return data; -} - -// Physics loop thread function -static void *PhysicsLoop(void *arg) -{ -#if !defined(PHYSAC_NO_THREADS) - #if defined(PHYSAC_DEBUG) - TRACELOG("[PHYSAC] physics thread created successfully\n"); - #endif - - // Initialize physics loop thread values - physicsThreadEnabled = true; - - // Physics update loop - while (physicsThreadEnabled) - { - RunPhysicsStep(); - } -#endif - - return NULL; -} - -// Physics steps calculations (dynamics, collisions and position corrections) -static void PhysicsStep(void) -{ - // Update current steps count - stepsCount++; - - // Clear previous generated collisions information - for (int i = physicsManifoldsCount - 1; i >= 0; i--) - { - PhysicsManifold manifold = contacts[i]; - if (manifold != NULL) DestroyPhysicsManifold(manifold); - } - - // Reset physics bodies grounded state - for (int i = 0; i < physicsBodiesCount; i++) - { - PhysicsBody body = bodies[i]; - body->isGrounded = false; - } - - // Generate new collision information - for (int i = 0; i < physicsBodiesCount; i++) - { - PhysicsBody bodyA = bodies[i]; - - if (bodyA != NULL) - { - for (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 newManifold = CreatePhysicsManifold(bodyA, bodyB); - newManifold->penetration = manifold->penetration; - newManifold->normal = manifold->normal; - newManifold->contacts[0] = manifold->contacts[0]; - newManifold->contacts[1] = manifold->contacts[1]; - newManifold->contactsCount = manifold->contactsCount; - newManifold->restitution = manifold->restitution; - newManifold->dynamicFriction = manifold->dynamicFriction; - newManifold->staticFriction = manifold->staticFriction; - } - } - } - } - } - - // Integrate forces to physics bodies - for (int i = 0; i < physicsBodiesCount; i++) - { - PhysicsBody body = bodies[i]; - if (body != NULL) IntegratePhysicsForces(body); - } - - // Initialize physics manifolds to solve collisions - for (int i = 0; i < physicsManifoldsCount; i++) - { - PhysicsManifold manifold = contacts[i]; - if (manifold != NULL) InitializePhysicsManifolds(manifold); - } - - // Integrate physics collisions impulses to solve collisions - for (int i = 0; i < PHYSAC_COLLISION_ITERATIONS; i++) - { - for (int j = 0; j < physicsManifoldsCount; j++) - { - PhysicsManifold manifold = contacts[i]; - if (manifold != NULL) IntegratePhysicsImpulses(manifold); - } - } - - // Integrate velocity to physics bodies - for (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 (int i = 0; i < physicsManifoldsCount; i++) - { - PhysicsManifold manifold = contacts[i]; - if (manifold != NULL) CorrectPhysicsPositions(manifold); - } - - // Clear physics bodies forces - for (int i = 0; i < physicsBodiesCount; i++) - { - PhysicsBody body = bodies[i]; - if (body != NULL) - { - body->force = PHYSAC_VECTOR_ZERO; - body->torque = 0.0f; - } - } -} - -// Wrapper to ensure PhysicsStep is run with at a fixed time step -PHYSACDEF void RunPhysicsStep(void) -{ - // Calculate current time - currentTime = GetCurrentTime(); - - // Calculate current delta time - const double delta = currentTime - startTime; - - // Store the time elapsed since the last frame began - accumulator += delta; - - // Fixed time stepping loop - while (accumulator >= deltaTime) - { -#ifdef PHYSAC_DEBUG - //TRACELOG("currentTime %f, startTime %f, accumulator-pre %f, accumulator-post %f, delta %f, deltaTime %f\n", - // currentTime, startTime, accumulator, accumulator-deltaTime, delta, deltaTime); -#endif - PhysicsStep(); - accumulator -= deltaTime; - } - - // Record the starting of this frame - startTime = currentTime; -} - -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 (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 newManifold = (PhysicsManifold)PHYSAC_MALLOC(sizeof(PhysicsManifoldData)); - usedMemory += sizeof(PhysicsManifoldData); - - int newId = FindAvailableManifoldIndex(); - if (newId != -1) - { - // Initialize new manifold with generic values - newManifold->id = newId; - newManifold->bodyA = a; - newManifold->bodyB = b; - newManifold->penetration = 0; - newManifold->normal = PHYSAC_VECTOR_ZERO; - newManifold->contacts[0] = PHYSAC_VECTOR_ZERO; - newManifold->contacts[1] = PHYSAC_VECTOR_ZERO; - newManifold->contactsCount = 0; - newManifold->restitution = 0.0f; - newManifold->dynamicFriction = 0.0f; - newManifold->staticFriction = 0.0f; - - // Add new body to bodies pointers array and update bodies count - contacts[physicsManifoldsCount] = newManifold; - physicsManifoldsCount++; - } - #if defined(PHYSAC_DEBUG) - else TRACELOG("[PHYSAC] new physics manifold creation failed because there is any available id to use\n"); - #endif - - return newManifold; -} - -// Unitializes and destroys a physics manifold -static void DestroyPhysicsManifold(PhysicsManifold manifold) -{ - if (manifold != NULL) - { - int id = manifold->id; - int index = -1; - - for (int i = 0; i < physicsManifoldsCount; i++) - { - if (contacts[i]->id == id) - { - index = i; - break; - } - } - - if (index == -1) - { - #if defined(PHYSAC_DEBUG) - TRACELOG("[PHYSAC] Not possible to manifold id %i in pointers array\n", id); - #endif - 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 (int i = index; i < physicsManifoldsCount; i++) - { - if ((i + 1) < physicsManifoldsCount) contacts[i] = contacts[i + 1]; - } - - // Update physics manifolds count - physicsManifoldsCount--; - } - #if defined(PHYSAC_DEBUG) - else TRACELOG("[PHYSAC] error trying to destroy a null referenced manifold\n"); - #endif -} - -// 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 = Vector2Subtract(bodyB->position, bodyA->position); - - float distSqr = MathLenSqr(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 = (Vector2){ 1.0f, 0.0f }; - manifold->contacts[0] = bodyA->position; - } - else - { - manifold->penetration = radius - distance; - manifold->normal = (Vector2){ normal.x/distance, normal.y/distance }; // Faster than using MathNormalize() due to sqrt is already performed - manifold->contacts[0] = (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 = Mat2MultiplyVector2(Mat2Transpose(bodyB->shape.transform), Vector2Subtract(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; - PolygonData vertexData = bodyB->shape.vertexData; - - for (int i = 0; i < vertexData.vertexCount; i++) - { - float currentSeparation = MathDot(vertexData.normals[i], Vector2Subtract(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) < 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 = Mat2MultiplyVector2(bodyB->shape.transform, vertexData.normals[faceNormal]); - manifold->normal = (Vector2){ -normal.x, -normal.y }; - manifold->contacts[0] = (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 = MathDot(Vector2Subtract(center, v1), Vector2Subtract(v2, v1)); - float dot2 = MathDot(Vector2Subtract(center, v2), Vector2Subtract(v1, v2)); - manifold->penetration = bodyA->shape.radius - separation; - - if (dot1 <= 0.0f) // Closest to v1 - { - if (DistSqr(center, v1) > bodyA->shape.radius*bodyA->shape.radius) return; - - manifold->contactsCount = 1; - Vector2 normal = Vector2Subtract(v1, center); - normal = Mat2MultiplyVector2(bodyB->shape.transform, normal); - MathNormalize(&normal); - manifold->normal = normal; - v1 = Mat2MultiplyVector2(bodyB->shape.transform, v1); - v1 = Vector2Add(v1, bodyB->position); - manifold->contacts[0] = v1; - } - else if (dot2 <= 0.0f) // Closest to v2 - { - if (DistSqr(center, v2) > bodyA->shape.radius*bodyA->shape.radius) return; - - manifold->contactsCount = 1; - Vector2 normal = Vector2Subtract(v2, center); - v2 = Mat2MultiplyVector2(bodyB->shape.transform, v2); - v2 = Vector2Add(v2, bodyB->position); - manifold->contacts[0] = v2; - normal = Mat2MultiplyVector2(bodyB->shape.transform, normal); - MathNormalize(&normal); - manifold->normal = normal; - } - else // Closest to face - { - Vector2 normal = vertexData.normals[faceNormal]; - - if (MathDot(Vector2Subtract(center, v1), normal) > bodyA->shape.radius) return; - - normal = Mat2MultiplyVector2(bodyB->shape.transform, normal); - manifold->normal = (Vector2){ -normal.x, -normal.y }; - manifold->contacts[0] = (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 - if (BiasGreaterThan(penetrationA, penetrationB)) - { - 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 - PolygonData refData = refPoly.vertexData; - Vector2 v1 = refData.positions[referenceIndex]; - referenceIndex = (((referenceIndex + 1) < refData.vertexCount) ? (referenceIndex + 1) : 0); - Vector2 v2 = refData.positions[referenceIndex]; - - // Transform vertices to world space - v1 = Mat2MultiplyVector2(refPoly.transform, v1); - v1 = Vector2Add(v1, refPoly.body->position); - v2 = Mat2MultiplyVector2(refPoly.transform, v2); - v2 = Vector2Add(v2, refPoly.body->position); - - // Calculate reference face side normal in world space - Vector2 sidePlaneNormal = Vector2Subtract(v2, v1); - MathNormalize(&sidePlaneNormal); - - // Orthogonalize - Vector2 refFaceNormal = { sidePlaneNormal.y, -sidePlaneNormal.x }; - float refC = MathDot(refFaceNormal, v1); - float negSide = MathDot(sidePlaneNormal, v1)*-1; - float posSide = MathDot(sidePlaneNormal, v2); - - // Clip incident face to reference face side planes (due to floating point error, possible to not have required points - if (Clip((Vector2){ -sidePlaneNormal.x, -sidePlaneNormal.y }, negSide, &incidentFace[0], &incidentFace[1]) < 2) return; - if (Clip(sidePlaneNormal, posSide, &incidentFace[0], &incidentFace[1]) < 2) return; - - // Flip normal if required - manifold->normal = (flip ? (Vector2){ -refFaceNormal.x, -refFaceNormal.y } : refFaceNormal); - - // Keep points behind reference face - int currentPoint = 0; // Clipped points behind reference face - float separation = MathDot(refFaceNormal, incidentFace[0]) - refC; - if (separation <= 0.0f) - { - manifold->contacts[currentPoint] = incidentFace[0]; - manifold->penetration = -separation; - currentPoint++; - } - else manifold->penetration = 0.0f; - - separation = MathDot(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 += (body->force.x*body->inverseMass)*(deltaTime/2.0); - body->velocity.y += (body->force.y*body->inverseMass)*(deltaTime/2.0); - - if (body->useGravity) - { - body->velocity.x += gravityForce.x*(deltaTime/1000/2.0); - body->velocity.y += gravityForce.y*(deltaTime/1000/2.0); - } - - if (!body->freezeOrient) body->angularVelocity += 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 (int i = 0; i < manifold->contactsCount; i++) - { - // Caculate radius from center of mass to contact - Vector2 radiusA = Vector2Subtract(manifold->contacts[i], bodyA->position); - Vector2 radiusB = Vector2Subtract(manifold->contacts[i], bodyB->position); - - Vector2 crossA = MathCross(bodyA->angularVelocity, radiusA); - Vector2 crossB = MathCross(bodyB->angularVelocity, radiusB); - - 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 (MathLenSqr(radiusV) < (MathLenSqr((Vector2){ gravityForce.x*deltaTime/1000, 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 (int i = 0; i < manifold->contactsCount; i++) - { - // Calculate radius from center of mass to contact - Vector2 radiusA = Vector2Subtract(manifold->contacts[i], bodyA->position); - Vector2 radiusB = Vector2Subtract(manifold->contacts[i], bodyB->position); - - // Calculate relative velocity - Vector2 radiusV = { 0.0f, 0.0f }; - radiusV.x = bodyB->velocity.x + MathCross(bodyB->angularVelocity, radiusB).x - bodyA->velocity.x - MathCross(bodyA->angularVelocity, radiusA).x; - radiusV.y = bodyB->velocity.y + MathCross(bodyB->angularVelocity, radiusB).y - bodyA->velocity.y - MathCross(bodyA->angularVelocity, radiusA).y; - - // Relative velocity along the normal - float contactVelocity = MathDot(radiusV, manifold->normal); - - // Do not resolve if velocities are separating - if (contactVelocity > 0.0f) return; - - float raCrossN = MathCrossVector2(radiusA, manifold->normal); - float rbCrossN = MathCrossVector2(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*MathCrossVector2(radiusA, (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*MathCrossVector2(radiusB, impulseV); - } - - // Apply friction impulse to each physics body - radiusV.x = bodyB->velocity.x + MathCross(bodyB->angularVelocity, radiusB).x - bodyA->velocity.x - MathCross(bodyA->angularVelocity, radiusA).x; - radiusV.y = bodyB->velocity.y + MathCross(bodyB->angularVelocity, radiusB).y - bodyA->velocity.y - MathCross(bodyA->angularVelocity, radiusA).y; - - Vector2 tangent = { radiusV.x - (manifold->normal.x*MathDot(radiusV, manifold->normal)), radiusV.y - (manifold->normal.y*MathDot(radiusV, manifold->normal)) }; - MathNormalize(&tangent); - - // Calculate impulse tangent magnitude - float impulseTangent = -MathDot(radiusV, tangent); - impulseTangent /= inverseMassSum; - impulseTangent /= (float)manifold->contactsCount; - - float absImpulseTangent = 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 = (Vector2){ tangent.x*impulseTangent, tangent.y*impulseTangent }; - else tangentImpulse = (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*MathCrossVector2(radiusA, (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*MathCrossVector2(radiusB, tangentImpulse); - } - } -} - -// Integrates physics velocity into position and forces -static void IntegratePhysicsVelocity(PhysicsBody body) -{ - if ((body == NULL) ||!body->enabled) return; - - body->position.x += body->velocity.x*deltaTime; - body->position.y += body->velocity.y*deltaTime; - - if (!body->freezeOrient) body->orient += body->angularVelocity*deltaTime; - Mat2Set(&body->shape.transform, 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 = (max(manifold->penetration - PHYSAC_PENETRATION_ALLOWANCE, 0.0f)/(bodyA->inverseMass + bodyB->inverseMass))*manifold->normal.x*PHYSAC_PENETRATION_CORRECTION; - correction.y = (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 }; - PolygonData data = shape.vertexData; - - for (int i = 0; i < data.vertexCount; i++) - { - Vector2 vertex = data.positions[i]; - float projection = MathDot(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; - - PolygonData dataA = shapeA.vertexData; - //PolygonData dataB = shapeB.vertexData; - - for (int i = 0; i < dataA.vertexCount; i++) - { - // Retrieve a face normal from A shape - Vector2 normal = dataA.normals[i]; - Vector2 transNormal = Mat2MultiplyVector2(shapeA.transform, normal); - - // Transform face normal into B shape's model space - Matrix2x2 buT = Mat2Transpose(shapeB.transform); - normal = Mat2MultiplyVector2(buT, transNormal); - - // Retrieve support point from B shape along -n - Vector2 support = GetSupport(shapeB, (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 = Mat2MultiplyVector2(shapeA.transform, vertex); - vertex = Vector2Add(vertex, shapeA.body->position); - vertex = Vector2Subtract(vertex, shapeB.body->position); - vertex = Mat2MultiplyVector2(buT, vertex); - - // Compute penetration distance in B shape's model space - float distance = MathDot(normal, Vector2Subtract(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) -{ - PolygonData refData = ref.vertexData; - PolygonData incData = inc.vertexData; - - Vector2 referenceNormal = refData.normals[index]; - - // Calculate normal in incident's frame of reference - referenceNormal = Mat2MultiplyVector2(ref.transform, referenceNormal); // To world space - referenceNormal = Mat2MultiplyVector2(Mat2Transpose(inc.transform), referenceNormal); // To incident's model space - - // Find most anti-normal face on polygon - int incidentFace = 0; - float minDot = PHYSAC_FLT_MAX; - - for (int i = 0; i < incData.vertexCount; i++) - { - float dot = MathDot(referenceNormal, incData.normals[i]); - - if (dot < minDot) - { - minDot = dot; - incidentFace = i; - } - } - - // Assign face vertices for incident face - *v0 = Mat2MultiplyVector2(inc.transform, incData.positions[incidentFace]); - *v0 = Vector2Add(*v0, inc.body->position); - incidentFace = (((incidentFace + 1) < incData.vertexCount) ? (incidentFace + 1) : 0); - *v1 = Mat2MultiplyVector2(inc.transform, incData.positions[incidentFace]); - *v1 = Vector2Add(*v1, inc.body->position); -} - -// Calculates clipping based on a normal and two faces -static int Clip(Vector2 normal, float clip, Vector2 *faceA, Vector2 *faceB) -{ - int sp = 0; - Vector2 out[2] = { *faceA, *faceB }; - - // Retrieve distances from each endpoint to the line - float distanceA = MathDot(normal, *faceA) - clip; - float distanceB = MathDot(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 = Vector2Subtract(*faceB, *faceA); - delta.x *= alpha; - delta.y *= alpha; - out[sp] = Vector2Add(out[sp], delta); - sp++; - } - - // Assign the new converted values - *faceA = out[0]; - *faceB = out[1]; - - return sp; -} - -// Check if values are between bias range -static bool BiasGreaterThan(float valueA, float valueB) -{ - return (valueA >= (valueB*0.95f + valueA*0.01f)); -} - -// Returns the barycenter of a triangle given by 3 points -static Vector2 TriangleBarycenter(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; -} - -// Initializes hi-resolution MONOTONIC timer -static void InitTimer(void) -{ - srand(time(NULL)); // Initialize random seed - -#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 - - baseTime = GetTimeCount(); // Get MONOTONIC clock time offset - startTime = GetCurrentTime(); // Get current time -} - -// Get hi-res MONOTONIC time measure in seconds -static unsigned long long int GetTimeCount(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)(GetTimeCount() - baseTime)/frequency*1000; -} - -// Returns the cross product of a vector and a value -static inline Vector2 MathCross(float value, Vector2 vector) -{ - return (Vector2){ -value*vector.y, value*vector.x }; -} - -// Returns the cross product of two vectors -static inline float MathCrossVector2(Vector2 v1, Vector2 v2) -{ - return (v1.x*v2.y - v1.y*v2.x); -} - -// Returns the len square root of a vector -static inline float MathLenSqr(Vector2 vector) -{ - return (vector.x*vector.x + vector.y*vector.y); -} - -// Returns the dot product of two vectors -static inline float MathDot(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 DistSqr(Vector2 v1, Vector2 v2) -{ - Vector2 dir = Vector2Subtract(v1, v2); - return MathDot(dir, dir); -} - -// Returns the normalized values of a vector -static void MathNormalize(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; -} - -#if defined(PHYSAC_STANDALONE) -// Returns the sum of two given vectors -static inline Vector2 Vector2Add(Vector2 v1, Vector2 v2) -{ - return (Vector2){ v1.x + v2.x, v1.y + v2.y }; -} - -// Returns the subtract of two given vectors -static inline Vector2 Vector2Subtract(Vector2 v1, Vector2 v2) -{ - return (Vector2){ v1.x - v2.x, v1.y - v2.y }; -} -#endif - -// Creates a matrix 2x2 from a given radians value -static Matrix2x2 Mat2Radians(float radians) -{ - float c = cosf(radians); - float s = sinf(radians); - - return (Matrix2x2){ c, -s, s, c }; -} - -// Set values from radians to a created matrix 2x2 -static void Mat2Set(Matrix2x2 *matrix, float radians) -{ - float cos = cosf(radians); - float sin = sinf(radians); - - matrix->m00 = cos; - matrix->m01 = -sin; - matrix->m10 = sin; - matrix->m11 = cos; -} - -// Returns the transpose of a given matrix 2x2 -static inline Matrix2x2 Mat2Transpose(Matrix2x2 matrix) -{ - return (Matrix2x2){ matrix.m00, matrix.m10, matrix.m01, matrix.m11 }; -} - -// Multiplies a vector by a matrix 2x2 -static inline Vector2 Mat2MultiplyVector2(Matrix2x2 matrix, Vector2 vector) -{ - return (Vector2){ matrix.m00*vector.x + matrix.m01*vector.y, matrix.m10*vector.x + matrix.m11*vector.y }; -} - -#endif // PHYSAC_IMPLEMENTATION diff --git a/raylib/rmem.h b/raylib/rmem.h deleted file mode 100644 index 76c6645..0000000 --- a/raylib/rmem.h +++ /dev/null @@ -1,739 +0,0 @@ -/********************************************************************************************** -* -* 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 -#include - -//---------------------------------------------------------------------------------- -// 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 // Required for: -#include // Required for: -#include // 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; inext) 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; imem == 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/raylib/rnet.h b/raylib/rnet.h deleted file mode 100644 index 21c8361..0000000 --- a/raylib/rnet.h +++ /dev/null @@ -1,2252 +0,0 @@ -/********************************************************************************************** -* -* 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 // Required for limits -#include // 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 - #include - #include - #define IPTOS_LOWDELAY 0x10 -#else // Unix - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include -#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 -#if defined(__STDC__) && __STDC_VERSION__ >= 199901L - #include -#elif !defined(__cplusplus) && !defined(bool) - typedef enum { false, true } bool; -#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, int *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); -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 // Required for: assert() -#include // Required for: FILE, fopen(), fclose(), fread() -#include // Required for: malloc(), free() -#include // 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 = htons(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(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 -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, int *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 diff --git a/raylib/uwp_events.h b/raylib/uwp_events.h deleted file mode 100644 index 2c403fd..0000000 --- a/raylib/uwp_events.h +++ /dev/null @@ -1,119 +0,0 @@ -/********************************************************************************************** -* -* raylib.uwp_events - Functions for bootstrapping UWP functionality within raylib's core. -* -* -* LICENSE: zlib/libpng -* -* Copyright (c) 2020-2020 Reece Mackie (@Rover656) -* -* 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 UWP_EVENTS_H -#define UWP_EVENTS_H - -#if defined(__cplusplus) -extern "C" { -#endif - -#if defined(PLATFORM_UWP) - -// Determine if UWP functions are set and ready for raylib's use. -bool UWPIsConfigured(); - -// Call this to set the UWP data path you wish for saving and loading. -void UWPSetDataPath(const char* path); - -// Function for getting program time. -typedef double(*UWPQueryTimeFunc)(); -UWPQueryTimeFunc UWPGetQueryTimeFunc(void); -void UWPSetQueryTimeFunc(UWPQueryTimeFunc func); - -// Function for sleeping the current thread -typedef void (*UWPSleepFunc)(double sleepUntil); -UWPSleepFunc UWPGetSleepFunc(void); -void UWPSetSleepFunc(UWPSleepFunc func); - -// Function for querying the display size -typedef void(*UWPDisplaySizeFunc)(int* width, int* height); -UWPDisplaySizeFunc UWPGetDisplaySizeFunc(void); -void UWPSetDisplaySizeFunc(UWPDisplaySizeFunc func); - -// Functions for mouse cursor control -typedef void(*UWPMouseFunc)(void); -UWPMouseFunc UWPGetMouseLockFunc(); -void UWPSetMouseLockFunc(UWPMouseFunc func); -UWPMouseFunc UWPGetMouseUnlockFunc(); -void UWPSetMouseUnlockFunc(UWPMouseFunc func); -UWPMouseFunc UWPGetMouseShowFunc(); -void UWPSetMouseShowFunc(UWPMouseFunc func); -UWPMouseFunc UWPGetMouseHideFunc(); -void UWPSetMouseHideFunc(UWPMouseFunc func); - -// Function for setting mouse cursor position. -typedef void (*UWPMouseSetPosFunc)(int x, int y); -UWPMouseSetPosFunc UWPGetMouseSetPosFunc(); -void UWPSetMouseSetPosFunc(UWPMouseSetPosFunc func); - -// The below functions are implemented in core.c but are placed here so they can be called by user code. -// This choice is made as platform-specific code is preferred to be kept away from raylib.h - -// Call this when a Key is pressed or released. -void UWPKeyDownEvent(int key, bool down, bool controlKey); - -// Call this on the CoreWindow::CharacterRecieved event -void UWPKeyCharEvent(int key); - -// Call when a mouse button state changes -void UWPMouseButtonEvent(int button, bool down); - -// Call when the mouse cursor moves -void UWPMousePosEvent(double x, double y); - -// Call when the mouse wheel moves -void UWPMouseWheelEvent(int deltaY); - -// Call when the window resizes -void UWPResizeEvent(int width, int height); - -// Call when a gamepad is made active -void UWPActivateGamepadEvent(int gamepad, bool active); - -// Call when a gamepad button state changes -void UWPRegisterGamepadButton(int gamepad, int button, bool down); - -// Call when a gamepad axis state changes -void UWPRegisterGamepadAxis(int gamepad, int axis, float value); - -// Call when the touch point moves -void UWPGestureMove(int pointer, float x, float y); - -// Call when there is a touch down or up -void UWPGestureTouch(int pointer, float x, float y, bool touch); - -// Set the core window pointer so that we can pass it to EGL. -void* UWPGetCoreWindowPtr(); -void UWPSetCoreWindowPtr(void* ptr); - -#if defined(__cplusplus) -} -#endif - -#endif // PLATFORM_UWP - -#endif // UWP_EVENTS_H