From 9bd3d78374782b45d0d7f1a41f2b545d631dab33 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 31 May 2021 11:43:56 +0200 Subject: WARNING: Moved some **extra** raylib libraries to /extras/ directory --- src/easings.h | 263 ------ src/extras/easings.h | 263 ++++++ src/extras/physac.h | 1988 ++++++++++++++++++++++++++++++++++++++++++++ src/extras/rmem.h | 739 +++++++++++++++++ src/extras/rnet.h | 2256 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/physac.h | 1988 -------------------------------------------- src/rmem.h | 739 ----------------- src/rnet.h | 2256 -------------------------------------------------- 8 files changed, 5246 insertions(+), 5246 deletions(-) delete mode 100644 src/easings.h create mode 100644 src/extras/easings.h create mode 100644 src/extras/physac.h create mode 100644 src/extras/rmem.h create mode 100644 src/extras/rnet.h delete mode 100644 src/physac.h delete mode 100644 src/rmem.h delete mode 100644 src/rnet.h (limited to 'src') diff --git a/src/easings.h b/src/easings.h deleted file mode 100644 index 3441305f..00000000 --- a/src/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/src/extras/easings.h b/src/extras/easings.h new file mode 100644 index 00000000..3441305f --- /dev/null +++ b/src/extras/easings.h @@ -0,0 +1,263 @@ +/******************************************************************************************* +* +* raylib easings (header only file) +* +* Useful easing functions for values animation +* +* This header uses: +* #define EASINGS_STATIC_INLINE // Inlines all functions code, so it runs faster. +* // This requires lots of memory on system. +* How to use: +* The four inputs t,b,c,d are defined as follows: +* t = current time (in any unit measure, but same unit as duration) +* b = starting value to interpolate +* c = the total change in value of b that needs to occur +* d = total time it should take to complete (duration) +* +* Example: +* +* int currentTime = 0; +* int duration = 100; +* float startPositionX = 0.0f; +* float finalPositionX = 30.0f; +* float currentPositionX = startPositionX; +* +* while (currentPositionX < finalPositionX) +* { +* currentPositionX = EaseSineIn(currentTime, startPositionX, finalPositionX - startPositionX, duration); +* currentTime++; +* } +* +* A port of Robert Penner's easing equations to C (http://robertpenner.com/easing/) +* +* Robert Penner License +* --------------------------------------------------------------------------------- +* Open source under the BSD License. +* +* Copyright (c) 2001 Robert Penner. All rights reserved. +* +* Redistribution and use in source and binary forms, with or without modification, +* are permitted provided that the following conditions are met: +* +* - Redistributions of source code must retain the above copyright notice, +* this list of conditions and the following disclaimer. +* - Redistributions in binary form must reproduce the above copyright notice, +* this list of conditions and the following disclaimer in the documentation +* and/or other materials provided with the distribution. +* - Neither the name of the author nor the names of contributors may be used +* to endorse or promote products derived from this software without specific +* prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +* IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +* OF THE POSSIBILITY OF SUCH DAMAGE. +* --------------------------------------------------------------------------------- +* +* Copyright (c) 2015 Ramon Santamaria +* +* This software is provided "as-is", without any express or implied warranty. In no event +* will the authors be held liable for any damages arising from the use of this software. +* +* Permission is granted to anyone to use this software for any purpose, including commercial +* applications, and to alter it and redistribute it freely, subject to the following restrictions: +* +* 1. The origin of this software must not be misrepresented; you must not claim that you +* wrote the original software. If you use this software in a product, an acknowledgment +* in the product documentation would be appreciated but is not required. +* +* 2. Altered source versions must be plainly marked as such, and must not be misrepresented +* as being the original software. +* +* 3. This notice may not be removed or altered from any source distribution. +* +**********************************************************************************************/ + +#ifndef EASINGS_H +#define EASINGS_H + +#define EASINGS_STATIC_INLINE // NOTE: By default, compile functions as static inline + +#if defined(EASINGS_STATIC_INLINE) + #define EASEDEF static inline +#else + #define EASEDEF extern +#endif + +#include // Required for: sinf(), cosf(), sqrtf(), powf() + +#ifndef PI + #define PI 3.14159265358979323846f //Required as PI is not always defined in math.h +#endif + +#ifdef __cplusplus +extern "C" { // Prevents name mangling of functions +#endif + +// Linear Easing functions +EASEDEF float EaseLinearNone(float t, float b, float c, float d) { return (c*t/d + b); } +EASEDEF float EaseLinearIn(float t, float b, float c, float d) { return (c*t/d + b); } +EASEDEF float EaseLinearOut(float t, float b, float c, float d) { return (c*t/d + b); } +EASEDEF float EaseLinearInOut(float t,float b, float c, float d) { return (c*t/d + b); } + +// Sine Easing functions +EASEDEF float EaseSineIn(float t, float b, float c, float d) { return (-c*cosf(t/d*(PI/2.0f)) + c + b); } +EASEDEF float EaseSineOut(float t, float b, float c, float d) { return (c*sinf(t/d*(PI/2.0f)) + b); } +EASEDEF float EaseSineInOut(float t, float b, float c, float d) { return (-c/2.0f*(cosf(PI*t/d) - 1.0f) + b); } + +// Circular Easing functions +EASEDEF float EaseCircIn(float t, float b, float c, float d) { t /= d; return (-c*(sqrtf(1.0f - t*t) - 1.0f) + b); } +EASEDEF float EaseCircOut(float t, float b, float c, float d) { t = t/d - 1.0f; return (c*sqrtf(1.0f - t*t) + b); } +EASEDEF float EaseCircInOut(float t, float b, float c, float d) +{ + if ((t/=d/2.0f) < 1.0f) return (-c/2.0f*(sqrtf(1.0f - t*t) - 1.0f) + b); + t -= 2.0f; return (c/2.0f*(sqrtf(1.0f - t*t) + 1.0f) + b); +} + +// Cubic Easing functions +EASEDEF float EaseCubicIn(float t, float b, float c, float d) { t /= d; return (c*t*t*t + b); } +EASEDEF float EaseCubicOut(float t, float b, float c, float d) { t = t/d - 1.0f; return (c*(t*t*t + 1.0f) + b); } +EASEDEF float EaseCubicInOut(float t, float b, float c, float d) +{ + if ((t/=d/2.0f) < 1.0f) return (c/2.0f*t*t*t + b); + t -= 2.0f; return (c/2.0f*(t*t*t + 2.0f) + b); +} + +// Quadratic Easing functions +EASEDEF float EaseQuadIn(float t, float b, float c, float d) { t /= d; return (c*t*t + b); } +EASEDEF float EaseQuadOut(float t, float b, float c, float d) { t /= d; return (-c*t*(t - 2.0f) + b); } +EASEDEF float EaseQuadInOut(float t, float b, float c, float d) +{ + if ((t/=d/2) < 1) return (((c/2)*(t*t)) + b); + return (-c/2.0f*(((t - 1.0f)*(t - 3.0f)) - 1.0f) + b); +} + +// Exponential Easing functions +EASEDEF float EaseExpoIn(float t, float b, float c, float d) { return (t == 0.0f) ? b : (c*powf(2.0f, 10.0f*(t/d - 1.0f)) + b); } +EASEDEF float EaseExpoOut(float t, float b, float c, float d) { return (t == d) ? (b + c) : (c*(-powf(2.0f, -10.0f*t/d) + 1.0f) + b); } +EASEDEF float EaseExpoInOut(float t, float b, float c, float d) +{ + if (t == 0.0f) return b; + if (t == d) return (b + c); + if ((t/=d/2.0f) < 1.0f) return (c/2.0f*powf(2.0f, 10.0f*(t - 1.0f)) + b); + + return (c/2.0f*(-powf(2.0f, -10.0f*(t - 1.0f)) + 2.0f) + b); +} + +// Back Easing functions +EASEDEF float EaseBackIn(float t, float b, float c, float d) +{ + float s = 1.70158f; + float postFix = t/=d; + return (c*(postFix)*t*((s + 1.0f)*t - s) + b); +} + +EASEDEF float EaseBackOut(float t, float b, float c, float d) +{ + float s = 1.70158f; + t = t/d - 1.0f; + return (c*(t*t*((s + 1.0f)*t + s) + 1.0f) + b); +} + +EASEDEF float EaseBackInOut(float t, float b, float c, float d) +{ + float s = 1.70158f; + if ((t/=d/2.0f) < 1.0f) + { + s *= 1.525f; + return (c/2.0f*(t*t*((s + 1.0f)*t - s)) + b); + } + + float postFix = t-=2.0f; + s *= 1.525f; + return (c/2.0f*((postFix)*t*((s + 1.0f)*t + s) + 2.0f) + b); +} + +// Bounce Easing functions +EASEDEF float EaseBounceOut(float t, float b, float c, float d) +{ + if ((t/=d) < (1.0f/2.75f)) + { + return (c*(7.5625f*t*t) + b); + } + else if (t < (2.0f/2.75f)) + { + float postFix = t-=(1.5f/2.75f); + return (c*(7.5625f*(postFix)*t + 0.75f) + b); + } + else if (t < (2.5/2.75)) + { + float postFix = t-=(2.25f/2.75f); + return (c*(7.5625f*(postFix)*t + 0.9375f) + b); + } + else + { + float postFix = t-=(2.625f/2.75f); + return (c*(7.5625f*(postFix)*t + 0.984375f) + b); + } +} + +EASEDEF float EaseBounceIn(float t, float b, float c, float d) { return (c - EaseBounceOut(d - t, 0.0f, c, d) + b); } +EASEDEF float EaseBounceInOut(float t, float b, float c, float d) +{ + if (t < d/2.0f) return (EaseBounceIn(t*2.0f, 0.0f, c, d)*0.5f + b); + else return (EaseBounceOut(t*2.0f - d, 0.0f, c, d)*0.5f + c*0.5f + b); +} + +// Elastic Easing functions +EASEDEF float EaseElasticIn(float t, float b, float c, float d) +{ + if (t == 0.0f) return b; + if ((t/=d) == 1.0f) return (b + c); + + float p = d*0.3f; + float a = c; + float s = p/4.0f; + float postFix = a*powf(2.0f, 10.0f*(t-=1.0f)); + + return (-(postFix*sinf((t*d-s)*(2.0f*PI)/p )) + b); +} + +EASEDEF float EaseElasticOut(float t, float b, float c, float d) +{ + if (t == 0.0f) return b; + if ((t/=d) == 1.0f) return (b + c); + + float p = d*0.3f; + float a = c; + float s = p/4.0f; + + return (a*powf(2.0f,-10.0f*t)*sinf((t*d-s)*(2.0f*PI)/p) + c + b); +} + +EASEDEF float EaseElasticInOut(float t, float b, float c, float d) +{ + if (t == 0.0f) return b; + if ((t/=d/2.0f) == 2.0f) return (b + c); + + float p = d*(0.3f*1.5f); + float a = c; + float s = p/4.0f; + + if (t < 1.0f) + { + float postFix = a*powf(2.0f, 10.0f*(t-=1.0f)); + return -0.5f*(postFix*sinf((t*d-s)*(2.0f*PI)/p)) + b; + } + + float postFix = a*powf(2.0f, -10.0f*(t-=1.0f)); + + return (postFix*sinf((t*d-s)*(2.0f*PI)/p)*0.5f + c + b); +} + +#ifdef __cplusplus +} +#endif + +#endif // EASINGS_H diff --git a/src/extras/physac.h b/src/extras/physac.h new file mode 100644 index 00000000..676a9695 --- /dev/null +++ b/src/extras/physac.h @@ -0,0 +1,1988 @@ +/********************************************************************************************** +* +* Physac v1.1 - 2D Physics library for videogames +* +* DESCRIPTION: +* +* Physac is a small 2D physics engine written in pure C. The engine uses a fixed time-step thread loop +* to simluate physics. A physics step contains the following phases: get collision information, +* apply dynamics, collision solving and position correction. It uses a very simple struct for physic +* bodies with a position vector to be used in any 3D rendering API. +* +* CONFIGURATION: +* +* #define PHYSAC_IMPLEMENTATION +* Generates the implementation of the library into the included file. +* If not defined, the library is in header only mode and can be included in other headers +* or source files without problems. But only ONE file should hold the implementation. +* +* #define PHYSAC_STATIC (defined by default) +* The generated implementation will stay private inside implementation file and all +* internal symbols and functions will only be visible inside that file. +* +* #define PHYSAC_DEBUG +* Show debug traces log messages about physic bodies creation/destruction, physic system errors, +* some calculations results and NULL reference exceptions +* +* #define PHYSAC_DEFINE_VECTOR2_TYPE +* Forces library to define struct Vector2 data type (float x; float y) +* +* #define PHYSAC_AVOID_TIMMING_SYSTEM +* Disables internal timming system, used by UpdatePhysics() to launch timmed physic steps, +* it allows just running UpdatePhysics() automatically on a separate thread at a desired time step. +* In case physics steps update needs to be controlled by user with a custom timming mechanism, +* just define this flag and the internal timming mechanism will be avoided, in that case, +* timming libraries are neither required by the module. +* +* #define PHYSAC_MALLOC() +* #define PHYSAC_CALLOC() +* #define PHYSAC_FREE() +* You can define your own malloc/free implementation replacing stdlib.h malloc()/free() functions. +* Otherwise it will include stdlib.h and use the C standard library malloc()/free() function. +* +* COMPILATION: +* +* Use the following code to compile with GCC: +* gcc -o $(NAME_PART).exe $(FILE_NAME) -s -static -lraylib -lopengl32 -lgdi32 -lwinmm -std=c99 +* +* VERSIONS HISTORY: +* 1.1 (20-Jan-2021) @raysan5: Library general revision +* Removed threading system (up to the user) +* Support MSVC C++ compilation using CLITERAL() +* Review DEBUG mechanism for TRACELOG() and all TRACELOG() messages +* Review internal variables/functions naming for consistency +* Allow option to avoid internal timming system, to allow app manage the steps +* 1.0 (12-Jun-2017) First release of the library +* +* +* LICENSE: zlib/libpng +* +* Copyright (c) 2016-2021 Victor Fisac (@victorfisac) and Ramon Santamaria (@raysan5) +* +* This software is provided "as-is", without any express or implied warranty. In no event +* will the authors be held liable for any damages arising from the use of this software. +* +* Permission is granted to anyone to use this software for any purpose, including commercial +* applications, and to alter it and redistribute it freely, subject to the following restrictions: +* +* 1. The origin of this software must not be misrepresented; you must not claim that you +* wrote the original software. If you use this software in a product, an acknowledgment +* in the product documentation would be appreciated but is not required. +* +* 2. Altered source versions must be plainly marked as such, and must not be misrepresented +* as being the original software. +* +* 3. This notice may not be removed or altered from any source distribution. +* +**********************************************************************************************/ + +#if !defined(PHYSAC_H) +#define PHYSAC_H + +#if defined(PHYSAC_STATIC) + #define PHYSACDEF static // Functions just visible to module including this file +#else + #if defined(__cplusplus) + #define PHYSACDEF extern "C" // Functions visible from other files (no name mangling of functions in C++) + #else + #define PHYSACDEF extern // Functions visible from other files + #endif +#endif + +// Allow custom memory allocators +#ifndef PHYSAC_MALLOC + #define PHYSAC_MALLOC(size) malloc(size) +#endif +#ifndef PHYSAC_CALLOC + #define PHYSAC_CALLOC(size, n) calloc(size, n) +#endif +#ifndef PHYSAC_FREE + #define PHYSAC_FREE(ptr) free(ptr) +#endif + +//---------------------------------------------------------------------------------- +// Defines and Macros +//---------------------------------------------------------------------------------- +#define PHYSAC_MAX_BODIES 64 // Maximum number of physic bodies supported +#define PHYSAC_MAX_MANIFOLDS 4096 // Maximum number of physic bodies interactions (64x64) +#define PHYSAC_MAX_VERTICES 24 // Maximum number of vertex for polygons shapes +#define PHYSAC_DEFAULT_CIRCLE_VERTICES 24 // Default number of vertices for circle shapes + +#define PHYSAC_COLLISION_ITERATIONS 100 +#define PHYSAC_PENETRATION_ALLOWANCE 0.05f +#define PHYSAC_PENETRATION_CORRECTION 0.4f + +#define PHYSAC_PI 3.14159265358979323846f +#define PHYSAC_DEG2RAD (PHYSAC_PI/180.0f) + +//---------------------------------------------------------------------------------- +// Data Types Structure Definition +//---------------------------------------------------------------------------------- +#if defined(__STDC__) && __STDC_VERSION__ >= 199901L + #include +#endif + +typedef enum PhysicsShapeType { PHYSICS_CIRCLE = 0, PHYSICS_POLYGON } PhysicsShapeType; + +// Previously defined to be used in PhysicsShape struct as circular dependencies +typedef struct PhysicsBodyData *PhysicsBody; + +#if defined(PHYSAC_DEFINE_VECTOR2_TYPE) +// Vector2 type +typedef struct Vector2 { + float x; + float y; +} Vector2; +#endif + +// Matrix2x2 type (used for polygon shape rotation matrix) +typedef struct Matrix2x2 { + float m00; + float m01; + float m10; + float m11; +} Matrix2x2; + +typedef struct PhysicsVertexData { + unsigned int vertexCount; // Vertex count (positions and normals) + Vector2 positions[PHYSAC_MAX_VERTICES]; // Vertex positions vectors + Vector2 normals[PHYSAC_MAX_VERTICES]; // Vertex normals vectors +} PhysicsVertexData; + +typedef struct PhysicsShape { + PhysicsShapeType type; // Shape type (circle or polygon) + PhysicsBody body; // Shape physics body data pointer + PhysicsVertexData vertexData; // Shape vertices data (used for polygon shapes) + float radius; // Shape radius (used for circle shapes) + Matrix2x2 transform; // Vertices transform matrix 2x2 +} PhysicsShape; + +typedef struct PhysicsBodyData { + unsigned int id; // Unique identifier + bool enabled; // Enabled dynamics state (collisions are calculated anyway) + Vector2 position; // Physics body shape pivot + Vector2 velocity; // Current linear velocity applied to position + Vector2 force; // Current linear force (reset to 0 every step) + float angularVelocity; // Current angular velocity applied to orient + float torque; // Current angular force (reset to 0 every step) + float orient; // Rotation in radians + float inertia; // Moment of inertia + float inverseInertia; // Inverse value of inertia + float mass; // Physics body mass + float inverseMass; // Inverse value of mass + float staticFriction; // Friction when the body has not movement (0 to 1) + float dynamicFriction; // Friction when the body has movement (0 to 1) + float restitution; // Restitution coefficient of the body (0 to 1) + bool useGravity; // Apply gravity force to dynamics + bool isGrounded; // Physics grounded on other body state + bool freezeOrient; // Physics rotation constraint + PhysicsShape shape; // Physics body shape information (type, radius, vertices, transform) +} PhysicsBodyData; + +typedef struct PhysicsManifoldData { + unsigned int id; // Unique identifier + PhysicsBody bodyA; // Manifold first physics body reference + PhysicsBody bodyB; // Manifold second physics body reference + float penetration; // Depth of penetration from collision + Vector2 normal; // Normal direction vector from 'a' to 'b' + Vector2 contacts[2]; // Points of contact during collision + unsigned int contactsCount; // Current collision number of contacts + float restitution; // Mixed restitution during collision + float dynamicFriction; // Mixed dynamic friction during collision + float staticFriction; // Mixed static friction during collision +} PhysicsManifoldData, *PhysicsManifold; + +#if defined(__cplusplus) +extern "C" { // Prevents name mangling of functions +#endif + +//---------------------------------------------------------------------------------- +// Module Functions Declaration +//---------------------------------------------------------------------------------- +// Physics system management +PHYSACDEF void InitPhysics(void); // Initializes physics system +PHYSACDEF void UpdatePhysics(void); // Update physics system +PHYSACDEF void ResetPhysics(void); // Reset physics system (global variables) +PHYSACDEF void ClosePhysics(void); // Close physics system and unload used memory +PHYSACDEF void SetPhysicsTimeStep(double delta); // Sets physics fixed time step in milliseconds. 1.666666 by default +PHYSACDEF void SetPhysicsGravity(float x, float y); // Sets physics global gravity force + +// Physic body creation/destroy +PHYSACDEF PhysicsBody CreatePhysicsBodyCircle(Vector2 pos, float radius, float density); // Creates a new circle physics body with generic parameters +PHYSACDEF PhysicsBody CreatePhysicsBodyRectangle(Vector2 pos, float width, float height, float density); // Creates a new rectangle physics body with generic parameters +PHYSACDEF PhysicsBody CreatePhysicsBodyPolygon(Vector2 pos, float radius, int sides, float density); // Creates a new polygon physics body with generic parameters +PHYSACDEF void DestroyPhysicsBody(PhysicsBody body); // Destroy a physics body + +// Physic body forces +PHYSACDEF void PhysicsAddForce(PhysicsBody body, Vector2 force); // Adds a force to a physics body +PHYSACDEF void PhysicsAddTorque(PhysicsBody body, float amount); // Adds an angular force to a physics body +PHYSACDEF void PhysicsShatter(PhysicsBody body, Vector2 position, float force); // Shatters a polygon shape physics body to little physics bodies with explosion force +PHYSACDEF void SetPhysicsBodyRotation(PhysicsBody body, float radians); // Sets physics body shape transform based on radians parameter + +// Query physics info +PHYSACDEF PhysicsBody GetPhysicsBody(int index); // Returns a physics body of the bodies pool at a specific index +PHYSACDEF int GetPhysicsBodiesCount(void); // Returns the current amount of created physics bodies +PHYSACDEF int GetPhysicsShapeType(int index); // Returns the physics body shape type (PHYSICS_CIRCLE or PHYSICS_POLYGON) +PHYSACDEF int GetPhysicsShapeVerticesCount(int index); // Returns the amount of vertices of a physics body shape +PHYSACDEF Vector2 GetPhysicsShapeVertex(PhysicsBody body, int vertex); // Returns transformed position of a body shape (body position + vertex transformed position) + +#if defined(__cplusplus) +} +#endif + +#endif // PHYSAC_H + +/*********************************************************************************** +* +* PHYSAC IMPLEMENTATION +* +************************************************************************************/ + +#if defined(PHYSAC_IMPLEMENTATION) + +// Support TRACELOG macros +#if defined(PHYSAC_DEBUG) + #include // Required for: printf() + #define TRACELOG(...) printf(__VA_ARGS__) +#else + #define TRACELOG(...) (void)0; +#endif + +#include // Required for: malloc(), calloc(), free() +#include // Required for: cosf(), sinf(), fabs(), sqrtf() + +#if !defined(PHYSAC_AVOID_TIMMING_SYSTEM) + // 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); + #endif + #if defined(__linux__) || defined(__FreeBSD__) + #if _POSIX_C_SOURCE < 199309L + #undef _POSIX_C_SOURCE + #define _POSIX_C_SOURCE 199309L // Required for CLOCK_MONOTONIC if compiled with c99 without gnu ext. + #endif + #include // Required for: timespec + #endif + #if defined(__APPLE__) // macOS also defines __MACH__ + #include // Required for: mach_absolute_time() + #endif +#endif + +// NOTE: MSVC C++ compiler does not support compound literals (C99 feature) +// Plain structures in C++ (without constructors) can be initialized from { } initializers. +#if defined(__cplusplus) + #define CLITERAL(type) type +#else + #define CLITERAL(type) (type) +#endif + +//---------------------------------------------------------------------------------- +// Defines and Macros +//---------------------------------------------------------------------------------- +#define PHYSAC_MIN(a,b) (((a)<(b))?(a):(b)) +#define PHYSAC_MAX(a,b) (((a)>(b))?(a):(b)) +#define PHYSAC_FLT_MAX 3.402823466e+38f +#define PHYSAC_EPSILON 0.000001f +#define PHYSAC_K 1.0f/3.0f +#define PHYSAC_VECTOR_ZERO CLITERAL(Vector2){ 0.0f, 0.0f } + +//---------------------------------------------------------------------------------- +// Global Variables Definition +//---------------------------------------------------------------------------------- +static double deltaTime = 1.0/60.0/10.0 * 1000; // Delta time in milliseconds used for physics steps + +#if !defined(PHYSAC_AVOID_TIMMING_SYSTEM) +// Time measure variables +static double baseClockTicks = 0.0; // Offset clock ticks for MONOTONIC clock +static unsigned long long int frequency = 0; // Hi-res clock frequency +static double startTime = 0.0; // Start time in milliseconds +static double currentTime = 0.0; // Current time in milliseconds +#endif + +// Physics system configuration +static PhysicsBody bodies[PHYSAC_MAX_BODIES]; // Physics bodies pointers array +static unsigned int physicsBodiesCount = 0; // Physics world current bodies counter +static PhysicsManifold contacts[PHYSAC_MAX_MANIFOLDS]; // Physics bodies pointers array +static unsigned int physicsManifoldsCount = 0; // Physics world current manifolds counter + +static Vector2 gravityForce = { 0.0f, 9.81f }; // Physics world gravity force + +// Utilities variables +static unsigned int usedMemory = 0; // Total allocated dynamic memory + +//---------------------------------------------------------------------------------- +// Module Internal Functions Declaration +//---------------------------------------------------------------------------------- +#if !defined(PHYSAC_AVOID_TIMMING_SYSTEM) +// Timming measure functions +static void InitTimer(void); // Initializes hi-resolution MONOTONIC timer +static unsigned long long int GetClockTicks(void); // Get hi-res MONOTONIC time measure in mseconds +static double GetCurrentTime(void); // Get current time measure in milliseconds +#endif + +static void UpdatePhysicsStep(void); // Update physics step (dynamics, collisions and position corrections) + +static int FindAvailableBodyIndex(); // Finds a valid index for a new physics body initialization +static int FindAvailableManifoldIndex(); // Finds a valid index for a new manifold initialization +static PhysicsVertexData CreateDefaultPolygon(float radius, int sides); // Creates a random polygon shape with max vertex distance from polygon pivot +static PhysicsVertexData CreateRectanglePolygon(Vector2 pos, Vector2 size); // Creates a rectangle polygon shape based on a min and max positions + +static void InitializePhysicsManifolds(PhysicsManifold manifold); // Initializes physics manifolds to solve collisions +static PhysicsManifold CreatePhysicsManifold(PhysicsBody a, PhysicsBody b); // Creates a new physics manifold to solve collision +static void DestroyPhysicsManifold(PhysicsManifold manifold); // Unitializes and destroys a physics manifold + +static void SolvePhysicsManifold(PhysicsManifold manifold); // Solves a created physics manifold between two physics bodies +static void SolveCircleToCircle(PhysicsManifold manifold); // Solves collision between two circle shape physics bodies +static void SolveCircleToPolygon(PhysicsManifold manifold); // Solves collision between a circle to a polygon shape physics bodies +static void SolvePolygonToCircle(PhysicsManifold manifold); // Solves collision between a polygon to a circle shape physics bodies +static void SolvePolygonToPolygon(PhysicsManifold manifold); // Solves collision between two polygons shape physics bodies +static void IntegratePhysicsForces(PhysicsBody body); // Integrates physics forces into velocity +static void IntegratePhysicsVelocity(PhysicsBody body); // Integrates physics velocity into position and forces +static void IntegratePhysicsImpulses(PhysicsManifold manifold); // Integrates physics collisions impulses to solve collisions +static void CorrectPhysicsPositions(PhysicsManifold manifold); // Corrects physics bodies positions based on manifolds collision information +static void FindIncidentFace(Vector2 *v0, Vector2 *v1, PhysicsShape ref, PhysicsShape inc, int index); // Finds two polygon shapes incident face +static float FindAxisLeastPenetration(int *faceIndex, PhysicsShape shapeA, PhysicsShape shapeB); // Finds polygon shapes axis least penetration + +// Math required functions +static Vector2 MathVector2Product(Vector2 vector, float value); // Returns the product of a vector and a value +static float MathVector2CrossProduct(Vector2 v1, Vector2 v2); // Returns the cross product of two vectors +static float MathVector2SqrLen(Vector2 vector); // Returns the len square root of a vector +static float MathVector2DotProduct(Vector2 v1, Vector2 v2); // Returns the dot product of two vectors +static inline float MathVector2SqrDistance(Vector2 v1, Vector2 v2); // Returns the square root of distance between two vectors +static void MathVector2Normalize(Vector2 *vector); // Returns the normalized values of a vector +static Vector2 MathVector2Add(Vector2 v1, Vector2 v2); // Returns the sum of two given vectors +static Vector2 MathVector2Subtract(Vector2 v1, Vector2 v2); // Returns the subtract of two given vectors +static Matrix2x2 MathMatFromRadians(float radians); // Returns a matrix 2x2 from a given radians value +static inline Matrix2x2 MathMatTranspose(Matrix2x2 matrix); // Returns the transpose of a given matrix 2x2 +static inline Vector2 MathMatVector2Product(Matrix2x2 matrix, Vector2 vector); // Returns product between matrix 2x2 and vector +static int MathVector2Clip(Vector2 normal, Vector2 *faceA, Vector2 *faceB, float clip); // Returns clipping value based on a normal and two faces +static Vector2 MathTriangleBarycenter(Vector2 v1, Vector2 v2, Vector2 v3); // Returns the barycenter of a triangle given by 3 points + +//---------------------------------------------------------------------------------- +// Module Functions Definition +//---------------------------------------------------------------------------------- + +// Initializes physics values, pointers and creates physics loop thread +PHYSACDEF void InitPhysics(void) +{ +#if !defined(PHYSAC_AVOID_TIMMING_SYSTEM) + // Initialize high resolution timer + InitTimer(); +#endif + + TRACELOG("[PHYSAC] Physics module initialized successfully\n"); +} + +// Sets physics global gravity force +PHYSACDEF void SetPhysicsGravity(float x, float y) +{ + gravityForce.x = x; + gravityForce.y = y; +} + +// Creates a new circle physics body with generic parameters +PHYSACDEF PhysicsBody CreatePhysicsBodyCircle(Vector2 pos, float radius, float density) +{ + PhysicsBody body = CreatePhysicsBodyPolygon(pos, radius, PHYSAC_DEFAULT_CIRCLE_VERTICES, density); + return body; +} + +// Creates a new rectangle physics body with generic parameters +PHYSACDEF PhysicsBody CreatePhysicsBodyRectangle(Vector2 pos, float width, float height, float density) +{ + // NOTE: Make sure body data is initialized to 0 + PhysicsBody body = (PhysicsBody)PHYSAC_CALLOC(sizeof(PhysicsBodyData), 1); + usedMemory += sizeof(PhysicsBodyData); + + int id = FindAvailableBodyIndex(); + if (id != -1) + { + // Initialize new body with generic values + body->id = id; + body->enabled = true; + body->position = pos; + body->shape.type = PHYSICS_POLYGON; + body->shape.body = body; + body->shape.transform = MathMatFromRadians(0.0f); + body->shape.vertexData = CreateRectanglePolygon(pos, CLITERAL(Vector2){ width, height }); + + // Calculate centroid and moment of inertia + Vector2 center = { 0.0f, 0.0f }; + float area = 0.0f; + float inertia = 0.0f; + + for (unsigned int i = 0; i < body->shape.vertexData.vertexCount; i++) + { + // Triangle vertices, third vertex implied as (0, 0) + Vector2 p1 = body->shape.vertexData.positions[i]; + unsigned int nextIndex = (((i + 1) < body->shape.vertexData.vertexCount) ? (i + 1) : 0); + Vector2 p2 = body->shape.vertexData.positions[nextIndex]; + + float D = MathVector2CrossProduct(p1, p2); + float triangleArea = D/2; + + area += triangleArea; + + // Use area to weight the centroid average, not just vertex position + center.x += triangleArea*PHYSAC_K*(p1.x + p2.x); + center.y += triangleArea*PHYSAC_K*(p1.y + p2.y); + + float intx2 = p1.x*p1.x + p2.x*p1.x + p2.x*p2.x; + float inty2 = p1.y*p1.y + p2.y*p1.y + p2.y*p2.y; + inertia += (0.25f*PHYSAC_K*D)*(intx2 + inty2); + } + + center.x *= 1.0f/area; + center.y *= 1.0f/area; + + // Translate vertices to centroid (make the centroid (0, 0) for the polygon in model space) + // Note: this is not really necessary + for (unsigned int i = 0; i < body->shape.vertexData.vertexCount; i++) + { + body->shape.vertexData.positions[i].x -= center.x; + body->shape.vertexData.positions[i].y -= center.y; + } + + body->mass = density*area; + body->inverseMass = ((body->mass != 0.0f) ? 1.0f/body->mass : 0.0f); + body->inertia = density*inertia; + body->inverseInertia = ((body->inertia != 0.0f) ? 1.0f/body->inertia : 0.0f); + body->staticFriction = 0.4f; + body->dynamicFriction = 0.2f; + body->restitution = 0.0f; + body->useGravity = true; + body->isGrounded = false; + body->freezeOrient = false; + + // Add new body to bodies pointers array and update bodies count + bodies[physicsBodiesCount] = body; + physicsBodiesCount++; + + TRACELOG("[PHYSAC] Physic body created successfully (id: %i)\n", body->id); + } + else TRACELOG("[PHYSAC] Physic body could not be created, PHYSAC_MAX_BODIES reached\n"); + + return body; +} + +// Creates a new polygon physics body with generic parameters +PHYSACDEF PhysicsBody CreatePhysicsBodyPolygon(Vector2 pos, float radius, int sides, float density) +{ + PhysicsBody body = (PhysicsBody)PHYSAC_MALLOC(sizeof(PhysicsBodyData)); + usedMemory += sizeof(PhysicsBodyData); + + int id = FindAvailableBodyIndex(); + if (id != -1) + { + // Initialize new body with generic values + body->id = id; + body->enabled = true; + body->position = pos; + body->velocity = PHYSAC_VECTOR_ZERO; + body->force = PHYSAC_VECTOR_ZERO; + body->angularVelocity = 0.0f; + body->torque = 0.0f; + body->orient = 0.0f; + body->shape.type = PHYSICS_POLYGON; + body->shape.body = body; + body->shape.transform = MathMatFromRadians(0.0f); + body->shape.vertexData = CreateDefaultPolygon(radius, sides); + + // Calculate centroid and moment of inertia + Vector2 center = { 0.0f, 0.0f }; + float area = 0.0f; + float inertia = 0.0f; + + for (unsigned int i = 0; i < body->shape.vertexData.vertexCount; i++) + { + // Triangle vertices, third vertex implied as (0, 0) + Vector2 position1 = body->shape.vertexData.positions[i]; + unsigned int nextIndex = (((i + 1) < body->shape.vertexData.vertexCount) ? (i + 1) : 0); + Vector2 position2 = body->shape.vertexData.positions[nextIndex]; + + float cross = MathVector2CrossProduct(position1, position2); + float triangleArea = cross/2; + + area += triangleArea; + + // Use area to weight the centroid average, not just vertex position + center.x += triangleArea*PHYSAC_K*(position1.x + position2.x); + center.y += triangleArea*PHYSAC_K*(position1.y + position2.y); + + float intx2 = position1.x*position1.x + position2.x*position1.x + position2.x*position2.x; + float inty2 = position1.y*position1.y + position2.y*position1.y + position2.y*position2.y; + inertia += (0.25f*PHYSAC_K*cross)*(intx2 + inty2); + } + + center.x *= 1.0f/area; + center.y *= 1.0f/area; + + // Translate vertices to centroid (make the centroid (0, 0) for the polygon in model space) + // Note: this is not really necessary + for (unsigned int i = 0; i < body->shape.vertexData.vertexCount; i++) + { + body->shape.vertexData.positions[i].x -= center.x; + body->shape.vertexData.positions[i].y -= center.y; + } + + body->mass = density*area; + body->inverseMass = ((body->mass != 0.0f) ? 1.0f/body->mass : 0.0f); + body->inertia = density*inertia; + body->inverseInertia = ((body->inertia != 0.0f) ? 1.0f/body->inertia : 0.0f); + body->staticFriction = 0.4f; + body->dynamicFriction = 0.2f; + body->restitution = 0.0f; + body->useGravity = true; + body->isGrounded = false; + body->freezeOrient = false; + + // Add new body to bodies pointers array and update bodies count + bodies[physicsBodiesCount] = body; + physicsBodiesCount++; + + TRACELOG("[PHYSAC] Physic body created successfully (id: %i)\n", body->id); + } + else TRACELOG("[PHYSAC] Physics body could not be created, PHYSAC_MAX_BODIES reached\n"); + + return body; +} + +// Adds a force to a physics body +PHYSACDEF void PhysicsAddForce(PhysicsBody body, Vector2 force) +{ + if (body != NULL) body->force = MathVector2Add(body->force, force); +} + +// Adds an angular force to a physics body +PHYSACDEF void PhysicsAddTorque(PhysicsBody body, float amount) +{ + if (body != NULL) body->torque += amount; +} + +// Shatters a polygon shape physics body to little physics bodies with explosion force +PHYSACDEF void PhysicsShatter(PhysicsBody body, Vector2 position, float force) +{ + if (body != NULL) + { + if (body->shape.type == PHYSICS_POLYGON) + { + PhysicsVertexData vertexData = body->shape.vertexData; + bool collision = false; + + for (unsigned int i = 0; i < vertexData.vertexCount; i++) + { + Vector2 positionA = body->position; + Vector2 positionB = MathMatVector2Product(body->shape.transform, MathVector2Add(body->position, vertexData.positions[i])); + unsigned int nextIndex = (((i + 1) < vertexData.vertexCount) ? (i + 1) : 0); + Vector2 positionC = MathMatVector2Product(body->shape.transform, MathVector2Add(body->position, vertexData.positions[nextIndex])); + + // Check collision between each triangle + float alpha = ((positionB.y - positionC.y)*(position.x - positionC.x) + (positionC.x - positionB.x)*(position.y - positionC.y))/ + ((positionB.y - positionC.y)*(positionA.x - positionC.x) + (positionC.x - positionB.x)*(positionA.y - positionC.y)); + + float beta = ((positionC.y - positionA.y)*(position.x - positionC.x) + (positionA.x - positionC.x)*(position.y - positionC.y))/ + ((positionB.y - positionC.y)*(positionA.x - positionC.x) + (positionC.x - positionB.x)*(positionA.y - positionC.y)); + + float gamma = 1.0f - alpha - beta; + + if ((alpha > 0.0f) && (beta > 0.0f) & (gamma > 0.0f)) + { + collision = true; + break; + } + } + + if (collision) + { + int count = vertexData.vertexCount; + Vector2 bodyPos = body->position; + Vector2 *vertices = (Vector2 *)PHYSAC_MALLOC(sizeof(Vector2)*count); + Matrix2x2 trans = body->shape.transform; + for (int i = 0; i < count; i++) vertices[i] = vertexData.positions[i]; + + // Destroy shattered physics body + DestroyPhysicsBody(body); + + for (int i = 0; i < count; i++) + { + int nextIndex = (((i + 1) < count) ? (i + 1) : 0); + Vector2 center = MathTriangleBarycenter(vertices[i], vertices[nextIndex], PHYSAC_VECTOR_ZERO); + center = MathVector2Add(bodyPos, center); + Vector2 offset = MathVector2Subtract(center, bodyPos); + + PhysicsBody body = CreatePhysicsBodyPolygon(center, 10, 3, 10); // Create polygon physics body with relevant values + + PhysicsVertexData vertexData = { 0 }; + vertexData.vertexCount = 3; + + vertexData.positions[0] = MathVector2Subtract(vertices[i], offset); + vertexData.positions[1] = MathVector2Subtract(vertices[nextIndex], offset); + vertexData.positions[2] = MathVector2Subtract(position, center); + + // Separate vertices to avoid unnecessary physics collisions + vertexData.positions[0].x *= 0.95f; + vertexData.positions[0].y *= 0.95f; + vertexData.positions[1].x *= 0.95f; + vertexData.positions[1].y *= 0.95f; + vertexData.positions[2].x *= 0.95f; + vertexData.positions[2].y *= 0.95f; + + // Calculate polygon faces normals + for (unsigned int j = 0; j < vertexData.vertexCount; j++) + { + unsigned int nextVertex = (((j + 1) < vertexData.vertexCount) ? (j + 1) : 0); + Vector2 face = MathVector2Subtract(vertexData.positions[nextVertex], vertexData.positions[j]); + + vertexData.normals[j] = CLITERAL(Vector2){ face.y, -face.x }; + MathVector2Normalize(&vertexData.normals[j]); + } + + // Apply computed vertex data to new physics body shape + body->shape.vertexData = vertexData; + body->shape.transform = trans; + + // Calculate centroid and moment of inertia + center = PHYSAC_VECTOR_ZERO; + float area = 0.0f; + float inertia = 0.0f; + + for (unsigned int j = 0; j < body->shape.vertexData.vertexCount; j++) + { + // Triangle vertices, third vertex implied as (0, 0) + Vector2 p1 = body->shape.vertexData.positions[j]; + unsigned int nextVertex = (((j + 1) < body->shape.vertexData.vertexCount) ? (j + 1) : 0); + Vector2 p2 = body->shape.vertexData.positions[nextVertex]; + + float D = MathVector2CrossProduct(p1, p2); + float triangleArea = D/2; + + area += triangleArea; + + // Use area to weight the centroid average, not just vertex position + center.x += triangleArea*PHYSAC_K*(p1.x + p2.x); + center.y += triangleArea*PHYSAC_K*(p1.y + p2.y); + + float intx2 = p1.x*p1.x + p2.x*p1.x + p2.x*p2.x; + float inty2 = p1.y*p1.y + p2.y*p1.y + p2.y*p2.y; + inertia += (0.25f*PHYSAC_K*D)*(intx2 + inty2); + } + + center.x *= 1.0f/area; + center.y *= 1.0f/area; + + body->mass = area; + body->inverseMass = ((body->mass != 0.0f) ? 1.0f/body->mass : 0.0f); + body->inertia = inertia; + body->inverseInertia = ((body->inertia != 0.0f) ? 1.0f/body->inertia : 0.0f); + + // Calculate explosion force direction + Vector2 pointA = body->position; + Vector2 pointB = MathVector2Subtract(vertexData.positions[1], vertexData.positions[0]); + pointB.x /= 2.0f; + pointB.y /= 2.0f; + Vector2 forceDirection = MathVector2Subtract(MathVector2Add(pointA, MathVector2Add(vertexData.positions[0], pointB)), body->position); + MathVector2Normalize(&forceDirection); + forceDirection.x *= force; + forceDirection.y *= force; + + // Apply force to new physics body + PhysicsAddForce(body, forceDirection); + } + + PHYSAC_FREE(vertices); + } + } + } + else TRACELOG("[PHYSAC] WARNING: PhysicsShatter: NULL physic body\n"); +} + +// Returns the current amount of created physics bodies +PHYSACDEF int GetPhysicsBodiesCount(void) +{ + return physicsBodiesCount; +} + +// Returns a physics body of the bodies pool at a specific index +PHYSACDEF PhysicsBody GetPhysicsBody(int index) +{ + PhysicsBody body = NULL; + + if (index < (int)physicsBodiesCount) + { + body = bodies[index]; + + if (body == NULL) TRACELOG("[PHYSAC] WARNING: GetPhysicsBody: NULL physic body\n"); + } + else TRACELOG("[PHYSAC] WARNING: Physic body index is out of bounds\n"); + + return body; +} + +// Returns the physics body shape type (PHYSICS_CIRCLE or PHYSICS_POLYGON) +PHYSACDEF int GetPhysicsShapeType(int index) +{ + int result = -1; + + if (index < (int)physicsBodiesCount) + { + PhysicsBody body = bodies[index]; + + if (body != NULL) result = body->shape.type; + else TRACELOG("[PHYSAC] WARNING: GetPhysicsShapeType: NULL physic body\n"); + } + else TRACELOG("[PHYSAC] WARNING: Physic body index is out of bounds\n"); + + return result; +} + +// Returns the amount of vertices of a physics body shape +PHYSACDEF int GetPhysicsShapeVerticesCount(int index) +{ + int result = 0; + + if (index < (int)physicsBodiesCount) + { + PhysicsBody body = bodies[index]; + + if (body != NULL) + { + switch (body->shape.type) + { + case PHYSICS_CIRCLE: result = PHYSAC_DEFAULT_CIRCLE_VERTICES; break; + case PHYSICS_POLYGON: result = body->shape.vertexData.vertexCount; break; + default: break; + } + } + else TRACELOG("[PHYSAC] WARNING: GetPhysicsShapeVerticesCount: NULL physic body\n"); + } + else TRACELOG("[PHYSAC] WARNING: Physic body index is out of bounds\n"); + + return result; +} + +// Returns transformed position of a body shape (body position + vertex transformed position) +PHYSACDEF Vector2 GetPhysicsShapeVertex(PhysicsBody body, int vertex) +{ + Vector2 position = { 0.0f, 0.0f }; + + if (body != NULL) + { + switch (body->shape.type) + { + case PHYSICS_CIRCLE: + { + position.x = body->position.x + cosf(360.0f/PHYSAC_DEFAULT_CIRCLE_VERTICES*vertex*PHYSAC_DEG2RAD)*body->shape.radius; + position.y = body->position.y + sinf(360.0f/PHYSAC_DEFAULT_CIRCLE_VERTICES*vertex*PHYSAC_DEG2RAD)*body->shape.radius; + } break; + case PHYSICS_POLYGON: + { + PhysicsVertexData vertexData = body->shape.vertexData; + position = MathVector2Add(body->position, MathMatVector2Product(body->shape.transform, vertexData.positions[vertex])); + } break; + default: break; + } + } + else TRACELOG("[PHYSAC] WARNING: GetPhysicsShapeVertex: NULL physic body\n"); + + return position; +} + +// Sets physics body shape transform based on radians parameter +PHYSACDEF void SetPhysicsBodyRotation(PhysicsBody body, float radians) +{ + if (body != NULL) + { + body->orient = radians; + + if (body->shape.type == PHYSICS_POLYGON) body->shape.transform = MathMatFromRadians(radians); + } +} + +// Unitializes and destroys a physics body +PHYSACDEF void DestroyPhysicsBody(PhysicsBody body) +{ + if (body != NULL) + { + int id = body->id; + int index = -1; + + for (unsigned int i = 0; i < physicsBodiesCount; i++) + { + if (bodies[i]->id == id) + { + index = i; + break; + } + } + + if (index == -1) + { + TRACELOG("[PHYSAC] WARNING: Requested body (id: %i) can not be found\n", id); + return; // Prevent access to index -1 + } + + // Free body allocated memory + PHYSAC_FREE(body); + usedMemory -= sizeof(PhysicsBodyData); + bodies[index] = NULL; + + // Reorder physics bodies pointers array and its catched index + for (unsigned int i = index; i < physicsBodiesCount; i++) + { + if ((i + 1) < physicsBodiesCount) bodies[i] = bodies[i + 1]; + } + + // Update physics bodies count + physicsBodiesCount--; + + TRACELOG("[PHYSAC] Physic body destroyed successfully (id: %i)\n", id); + } + else TRACELOG("[PHYSAC] WARNING: DestroyPhysicsBody: NULL physic body\n"); +} + +// Destroys created physics bodies and manifolds and resets global values +PHYSACDEF void ResetPhysics(void) +{ + if (physicsBodiesCount > 0) + { + // Unitialize physics bodies dynamic memory allocations + for (int i = physicsBodiesCount - 1; i >= 0; i--) + { + PhysicsBody body = bodies[i]; + + if (body != NULL) + { + PHYSAC_FREE(body); + bodies[i] = NULL; + usedMemory -= sizeof(PhysicsBodyData); + } + } + + physicsBodiesCount = 0; + } + + if (physicsManifoldsCount > 0) + { + // Unitialize physics manifolds dynamic memory allocations + for (int i = physicsManifoldsCount - 1; i >= 0; i--) + { + PhysicsManifold manifold = contacts[i]; + + if (manifold != NULL) + { + PHYSAC_FREE(manifold); + contacts[i] = NULL; + usedMemory -= sizeof(PhysicsManifoldData); + } + } + + physicsManifoldsCount = 0; + } + + TRACELOG("[PHYSAC] Physics module reseted successfully\n"); +} + +// Unitializes physics pointers and exits physics loop thread +PHYSACDEF void ClosePhysics(void) +{ + // Unitialize physics manifolds dynamic memory allocations + if (physicsManifoldsCount > 0) + { + for (unsigned int i = physicsManifoldsCount - 1; i >= 0; i--) + DestroyPhysicsManifold(contacts[i]); + } + + // Unitialize physics bodies dynamic memory allocations + if (physicsBodiesCount > 0) + { + for (unsigned int i = physicsBodiesCount - 1; i >= 0; i--) + DestroyPhysicsBody(bodies[i]); + } + + // Trace log info + if ((physicsBodiesCount > 0) || (usedMemory != 0)) + { + TRACELOG("[PHYSAC] WARNING: Physics module closed with unallocated bodies (BODIES: %i, MEMORY: %i bytes)\n", physicsBodiesCount, usedMemory); + } + else if ((physicsManifoldsCount > 0) || (usedMemory != 0)) + { + TRACELOG("[PHYSAC] WARNING: Pysics module closed with unallocated manifolds (MANIFOLDS: %i, MEMORY: %i bytes)\n", physicsManifoldsCount, usedMemory); + } + else TRACELOG("[PHYSAC] Physics module closed successfully\n"); +} + +//---------------------------------------------------------------------------------- +// Module Internal Functions Definition +//---------------------------------------------------------------------------------- +// Finds a valid index for a new physics body initialization +static int FindAvailableBodyIndex() +{ + int index = -1; + for (int i = 0; i < PHYSAC_MAX_BODIES; i++) + { + int currentId = i; + + // Check if current id already exist in other physics body + for (unsigned int k = 0; k < physicsBodiesCount; k++) + { + if (bodies[k]->id == currentId) + { + currentId++; + break; + } + } + + // If it is not used, use it as new physics body id + if (currentId == (int)i) + { + index = (int)i; + break; + } + } + + return index; +} + +// Creates a default polygon shape with max vertex distance from polygon pivot +static PhysicsVertexData CreateDefaultPolygon(float radius, int sides) +{ + PhysicsVertexData data = { 0 }; + data.vertexCount = sides; + + // Calculate polygon vertices positions + for (unsigned int i = 0; i < data.vertexCount; i++) + { + data.positions[i].x = (float)cosf(360.0f/sides*i*PHYSAC_DEG2RAD)*radius; + data.positions[i].y = (float)sinf(360.0f/sides*i*PHYSAC_DEG2RAD)*radius; + } + + // Calculate polygon faces normals + for (int i = 0; i < (int)data.vertexCount; i++) + { + int nextIndex = (((i + 1) < sides) ? (i + 1) : 0); + Vector2 face = MathVector2Subtract(data.positions[nextIndex], data.positions[i]); + + data.normals[i] = CLITERAL(Vector2){ face.y, -face.x }; + MathVector2Normalize(&data.normals[i]); + } + + return data; +} + +// Creates a rectangle polygon shape based on a min and max positions +static PhysicsVertexData CreateRectanglePolygon(Vector2 pos, Vector2 size) +{ + PhysicsVertexData data = { 0 }; + data.vertexCount = 4; + + // Calculate polygon vertices positions + data.positions[0] = CLITERAL(Vector2){ pos.x + size.x/2, pos.y - size.y/2 }; + data.positions[1] = CLITERAL(Vector2){ pos.x + size.x/2, pos.y + size.y/2 }; + data.positions[2] = CLITERAL(Vector2){ pos.x - size.x/2, pos.y + size.y/2 }; + data.positions[3] = CLITERAL(Vector2){ pos.x - size.x/2, pos.y - size.y/2 }; + + // Calculate polygon faces normals + for (unsigned int i = 0; i < data.vertexCount; i++) + { + int nextIndex = (((i + 1) < data.vertexCount) ? (i + 1) : 0); + Vector2 face = MathVector2Subtract(data.positions[nextIndex], data.positions[i]); + + data.normals[i] = CLITERAL(Vector2){ face.y, -face.x }; + MathVector2Normalize(&data.normals[i]); + } + + return data; +} + +// Update physics step (dynamics, collisions and position corrections) +void UpdatePhysicsStep(void) +{ + // Clear previous generated collisions information + for (int i = (int)physicsManifoldsCount - 1; i >= 0; i--) + { + PhysicsManifold manifold = contacts[i]; + if (manifold != NULL) DestroyPhysicsManifold(manifold); + } + + // Reset physics bodies grounded state + for (unsigned int i = 0; i < physicsBodiesCount; i++) + { + PhysicsBody body = bodies[i]; + body->isGrounded = false; + } + + // Generate new collision information + for (unsigned int i = 0; i < physicsBodiesCount; i++) + { + PhysicsBody bodyA = bodies[i]; + + if (bodyA != NULL) + { + for (unsigned int j = i + 1; j < physicsBodiesCount; j++) + { + PhysicsBody bodyB = bodies[j]; + + if (bodyB != NULL) + { + if ((bodyA->inverseMass == 0) && (bodyB->inverseMass == 0)) continue; + + PhysicsManifold manifold = CreatePhysicsManifold(bodyA, bodyB); + SolvePhysicsManifold(manifold); + + if (manifold->contactsCount > 0) + { + // Create a new manifold with same information as previously solved manifold and add it to the manifolds pool last slot + PhysicsManifold manifold = CreatePhysicsManifold(bodyA, bodyB); + manifold->penetration = manifold->penetration; + manifold->normal = manifold->normal; + manifold->contacts[0] = manifold->contacts[0]; + manifold->contacts[1] = manifold->contacts[1]; + manifold->contactsCount = manifold->contactsCount; + manifold->restitution = manifold->restitution; + manifold->dynamicFriction = manifold->dynamicFriction; + manifold->staticFriction = manifold->staticFriction; + } + } + } + } + } + + // Integrate forces to physics bodies + for (unsigned int i = 0; i < physicsBodiesCount; i++) + { + PhysicsBody body = bodies[i]; + if (body != NULL) IntegratePhysicsForces(body); + } + + // Initialize physics manifolds to solve collisions + for (unsigned int i = 0; i < physicsManifoldsCount; i++) + { + PhysicsManifold manifold = contacts[i]; + if (manifold != NULL) InitializePhysicsManifolds(manifold); + } + + // Integrate physics collisions impulses to solve collisions + for (unsigned int i = 0; i < PHYSAC_COLLISION_ITERATIONS; i++) + { + for (unsigned int j = 0; j < physicsManifoldsCount; j++) + { + PhysicsManifold manifold = contacts[i]; + if (manifold != NULL) IntegratePhysicsImpulses(manifold); + } + } + + // Integrate velocity to physics bodies + for (unsigned int i = 0; i < physicsBodiesCount; i++) + { + PhysicsBody body = bodies[i]; + if (body != NULL) IntegratePhysicsVelocity(body); + } + + // Correct physics bodies positions based on manifolds collision information + for (unsigned int i = 0; i < physicsManifoldsCount; i++) + { + PhysicsManifold manifold = contacts[i]; + if (manifold != NULL) CorrectPhysicsPositions(manifold); + } + + // Clear physics bodies forces + for (unsigned int i = 0; i < physicsBodiesCount; i++) + { + PhysicsBody body = bodies[i]; + if (body != NULL) + { + body->force = PHYSAC_VECTOR_ZERO; + body->torque = 0.0f; + } + } +} + +// Update physics system +// Physics steps are launched at a fixed time step if enabled +PHYSACDEF void UpdatePhysics(void) +{ +#if !defined(PHYSAC_AVOID_TIMMING_SYSTEM) + static double deltaTimeAccumulator = 0.0; + + // Calculate current time (ms) + currentTime = GetCurrentTime(); + + // Calculate current delta time (ms) + const double delta = currentTime - startTime; + + // Store the time elapsed since the last frame began + deltaTimeAccumulator += delta; + + // Fixed time stepping loop + while (deltaTimeAccumulator >= deltaTime) + { + UpdatePhysicsStep(); + deltaTimeAccumulator -= deltaTime; + } + + // Record the starting of this frame + startTime = currentTime; +#else + UpdatePhysicsStep(); +#endif +} + +PHYSACDEF void SetPhysicsTimeStep(double delta) +{ + deltaTime = delta; +} + +// Finds a valid index for a new manifold initialization +static int FindAvailableManifoldIndex() +{ + int index = -1; + for (int i = 0; i < PHYSAC_MAX_MANIFOLDS; i++) + { + int currentId = i; + + // Check if current id already exist in other physics body + for (unsigned int k = 0; k < physicsManifoldsCount; k++) + { + if (contacts[k]->id == currentId) + { + currentId++; + break; + } + } + + // If it is not used, use it as new physics body id + if (currentId == i) + { + index = i; + break; + } + } + + return index; +} + +// Creates a new physics manifold to solve collision +static PhysicsManifold CreatePhysicsManifold(PhysicsBody a, PhysicsBody b) +{ + PhysicsManifold manifold = (PhysicsManifold)PHYSAC_MALLOC(sizeof(PhysicsManifoldData)); + usedMemory += sizeof(PhysicsManifoldData); + + int id = FindAvailableManifoldIndex(); + if (id != -1) + { + // Initialize new manifold with generic values + manifold->id = id; + manifold->bodyA = a; + manifold->bodyB = b; + manifold->penetration = 0; + manifold->normal = PHYSAC_VECTOR_ZERO; + manifold->contacts[0] = PHYSAC_VECTOR_ZERO; + manifold->contacts[1] = PHYSAC_VECTOR_ZERO; + manifold->contactsCount = 0; + manifold->restitution = 0.0f; + manifold->dynamicFriction = 0.0f; + manifold->staticFriction = 0.0f; + + // Add new body to bodies pointers array and update bodies count + contacts[physicsManifoldsCount] = manifold; + physicsManifoldsCount++; + } + else TRACELOG("[PHYSAC] Physic manifold could not be created, PHYSAC_MAX_MANIFOLDS reached\n"); + + return manifold; +} + +// Unitializes and destroys a physics manifold +static void DestroyPhysicsManifold(PhysicsManifold manifold) +{ + if (manifold != NULL) + { + int id = manifold->id; + int index = -1; + + for (unsigned int i = 0; i < physicsManifoldsCount; i++) + { + if (contacts[i]->id == id) + { + index = i; + break; + } + } + + if (index == -1) return; // Prevent access to index -1 + + // Free manifold allocated memory + PHYSAC_FREE(manifold); + usedMemory -= sizeof(PhysicsManifoldData); + contacts[index] = NULL; + + // Reorder physics manifolds pointers array and its catched index + for (unsigned int i = index; i < physicsManifoldsCount; i++) + { + if ((i + 1) < physicsManifoldsCount) contacts[i] = contacts[i + 1]; + } + + // Update physics manifolds count + physicsManifoldsCount--; + } + else TRACELOG("[PHYSAC] WARNING: DestroyPhysicsManifold: NULL physic manifold\n"); +} + +// Solves a created physics manifold between two physics bodies +static void SolvePhysicsManifold(PhysicsManifold manifold) +{ + switch (manifold->bodyA->shape.type) + { + case PHYSICS_CIRCLE: + { + switch (manifold->bodyB->shape.type) + { + case PHYSICS_CIRCLE: SolveCircleToCircle(manifold); break; + case PHYSICS_POLYGON: SolveCircleToPolygon(manifold); break; + default: break; + } + } break; + case PHYSICS_POLYGON: + { + switch (manifold->bodyB->shape.type) + { + case PHYSICS_CIRCLE: SolvePolygonToCircle(manifold); break; + case PHYSICS_POLYGON: SolvePolygonToPolygon(manifold); break; + default: break; + } + } break; + default: break; + } + + // Update physics body grounded state if normal direction is down and grounded state is not set yet in previous manifolds + if (!manifold->bodyB->isGrounded) manifold->bodyB->isGrounded = (manifold->normal.y < 0); +} + +// Solves collision between two circle shape physics bodies +static void SolveCircleToCircle(PhysicsManifold manifold) +{ + PhysicsBody bodyA = manifold->bodyA; + PhysicsBody bodyB = manifold->bodyB; + + if ((bodyA == NULL) || (bodyB == NULL)) return; + + // Calculate translational vector, which is normal + Vector2 normal = MathVector2Subtract(bodyB->position, bodyA->position); + + float distSqr = MathVector2SqrLen(normal); + float radius = bodyA->shape.radius + bodyB->shape.radius; + + // Check if circles are not in contact + if (distSqr >= radius*radius) + { + manifold->contactsCount = 0; + return; + } + + float distance = sqrtf(distSqr); + manifold->contactsCount = 1; + + if (distance == 0.0f) + { + manifold->penetration = bodyA->shape.radius; + manifold->normal = CLITERAL(Vector2){ 1.0f, 0.0f }; + manifold->contacts[0] = bodyA->position; + } + else + { + manifold->penetration = radius - distance; + manifold->normal = CLITERAL(Vector2){ normal.x/distance, normal.y/distance }; // Faster than using MathVector2Normalize() due to sqrt is already performed + manifold->contacts[0] = CLITERAL(Vector2){ manifold->normal.x*bodyA->shape.radius + bodyA->position.x, manifold->normal.y*bodyA->shape.radius + bodyA->position.y }; + } + + // Update physics body grounded state if normal direction is down + if (!bodyA->isGrounded) bodyA->isGrounded = (manifold->normal.y < 0); +} + +// Solves collision between a circle to a polygon shape physics bodies +static void SolveCircleToPolygon(PhysicsManifold manifold) +{ + PhysicsBody bodyA = manifold->bodyA; + PhysicsBody bodyB = manifold->bodyB; + + if ((bodyA == NULL) || (bodyB == NULL)) return; + + manifold->contactsCount = 0; + + // Transform circle center to polygon transform space + Vector2 center = bodyA->position; + center = MathMatVector2Product(MathMatTranspose(bodyB->shape.transform), MathVector2Subtract(center, bodyB->position)); + + // Find edge with minimum penetration + // It is the same concept as using support points in SolvePolygonToPolygon + float separation = -PHYSAC_FLT_MAX; + int faceNormal = 0; + PhysicsVertexData vertexData = bodyB->shape.vertexData; + + for (unsigned int i = 0; i < vertexData.vertexCount; i++) + { + float currentSeparation = MathVector2DotProduct(vertexData.normals[i], MathVector2Subtract(center, vertexData.positions[i])); + + if (currentSeparation > bodyA->shape.radius) return; + + if (currentSeparation > separation) + { + separation = currentSeparation; + faceNormal = i; + } + } + + // Grab face's vertices + Vector2 v1 = vertexData.positions[faceNormal]; + int nextIndex = (((faceNormal + 1) < (int)vertexData.vertexCount) ? (faceNormal + 1) : 0); + Vector2 v2 = vertexData.positions[nextIndex]; + + // Check to see if center is within polygon + if (separation < PHYSAC_EPSILON) + { + manifold->contactsCount = 1; + Vector2 normal = MathMatVector2Product(bodyB->shape.transform, vertexData.normals[faceNormal]); + manifold->normal = CLITERAL(Vector2){ -normal.x, -normal.y }; + manifold->contacts[0] = CLITERAL(Vector2){ manifold->normal.x*bodyA->shape.radius + bodyA->position.x, manifold->normal.y*bodyA->shape.radius + bodyA->position.y }; + manifold->penetration = bodyA->shape.radius; + return; + } + + // Determine which voronoi region of the edge center of circle lies within + float dot1 = MathVector2DotProduct(MathVector2Subtract(center, v1), MathVector2Subtract(v2, v1)); + float dot2 = MathVector2DotProduct(MathVector2Subtract(center, v2), MathVector2Subtract(v1, v2)); + manifold->penetration = bodyA->shape.radius - separation; + + if (dot1 <= 0.0f) // Closest to v1 + { + if (MathVector2SqrDistance(center, v1) > bodyA->shape.radius*bodyA->shape.radius) return; + + manifold->contactsCount = 1; + Vector2 normal = MathVector2Subtract(v1, center); + normal = MathMatVector2Product(bodyB->shape.transform, normal); + MathVector2Normalize(&normal); + manifold->normal = normal; + v1 = MathMatVector2Product(bodyB->shape.transform, v1); + v1 = MathVector2Add(v1, bodyB->position); + manifold->contacts[0] = v1; + } + else if (dot2 <= 0.0f) // Closest to v2 + { + if (MathVector2SqrDistance(center, v2) > bodyA->shape.radius*bodyA->shape.radius) return; + + manifold->contactsCount = 1; + Vector2 normal = MathVector2Subtract(v2, center); + v2 = MathMatVector2Product(bodyB->shape.transform, v2); + v2 = MathVector2Add(v2, bodyB->position); + manifold->contacts[0] = v2; + normal = MathMatVector2Product(bodyB->shape.transform, normal); + MathVector2Normalize(&normal); + manifold->normal = normal; + } + else // Closest to face + { + Vector2 normal = vertexData.normals[faceNormal]; + + if (MathVector2DotProduct(MathVector2Subtract(center, v1), normal) > bodyA->shape.radius) return; + + normal = MathMatVector2Product(bodyB->shape.transform, normal); + manifold->normal = CLITERAL(Vector2){ -normal.x, -normal.y }; + manifold->contacts[0] = CLITERAL(Vector2){ manifold->normal.x*bodyA->shape.radius + bodyA->position.x, manifold->normal.y*bodyA->shape.radius + bodyA->position.y }; + manifold->contactsCount = 1; + } +} + +// Solves collision between a polygon to a circle shape physics bodies +static void SolvePolygonToCircle(PhysicsManifold manifold) +{ + PhysicsBody bodyA = manifold->bodyA; + PhysicsBody bodyB = manifold->bodyB; + + if ((bodyA == NULL) || (bodyB == NULL)) return; + + manifold->bodyA = bodyB; + manifold->bodyB = bodyA; + SolveCircleToPolygon(manifold); + + manifold->normal.x *= -1.0f; + manifold->normal.y *= -1.0f; +} + +// Solves collision between two polygons shape physics bodies +static void SolvePolygonToPolygon(PhysicsManifold manifold) +{ + if ((manifold->bodyA == NULL) || (manifold->bodyB == NULL)) return; + + PhysicsShape bodyA = manifold->bodyA->shape; + PhysicsShape bodyB = manifold->bodyB->shape; + manifold->contactsCount = 0; + + // Check for separating axis with A shape's face planes + int faceA = 0; + float penetrationA = FindAxisLeastPenetration(&faceA, bodyA, bodyB); + if (penetrationA >= 0.0f) return; + + // Check for separating axis with B shape's face planes + int faceB = 0; + float penetrationB = FindAxisLeastPenetration(&faceB, bodyB, bodyA); + if (penetrationB >= 0.0f) return; + + int referenceIndex = 0; + bool flip = false; // Always point from A shape to B shape + + PhysicsShape refPoly; // Reference + PhysicsShape incPoly; // Incident + + // Determine which shape contains reference face + // Checking bias range for penetration + if (penetrationA >= (penetrationB*0.95f + penetrationA*0.01f)) + { + refPoly = bodyA; + incPoly = bodyB; + referenceIndex = faceA; + } + else + { + refPoly = bodyB; + incPoly = bodyA; + referenceIndex = faceB; + flip = true; + } + + // World space incident face + Vector2 incidentFace[2]; + FindIncidentFace(&incidentFace[0], &incidentFace[1], refPoly, incPoly, referenceIndex); + + // Setup reference face vertices + PhysicsVertexData refData = refPoly.vertexData; + Vector2 v1 = refData.positions[referenceIndex]; + referenceIndex = (((referenceIndex + 1) < (int)refData.vertexCount) ? (referenceIndex + 1) : 0); + Vector2 v2 = refData.positions[referenceIndex]; + + // Transform vertices to world space + v1 = MathMatVector2Product(refPoly.transform, v1); + v1 = MathVector2Add(v1, refPoly.body->position); + v2 = MathMatVector2Product(refPoly.transform, v2); + v2 = MathVector2Add(v2, refPoly.body->position); + + // Calculate reference face side normal in world space + Vector2 sidePlaneNormal = MathVector2Subtract(v2, v1); + MathVector2Normalize(&sidePlaneNormal); + + // Orthogonalize + Vector2 refFaceNormal = { sidePlaneNormal.y, -sidePlaneNormal.x }; + float refC = MathVector2DotProduct(refFaceNormal, v1); + float negSide = MathVector2DotProduct(sidePlaneNormal, v1)*-1; + float posSide = MathVector2DotProduct(sidePlaneNormal, v2); + + // MathVector2Clip incident face to reference face side planes (due to floating point error, possible to not have required points + if (MathVector2Clip(CLITERAL(Vector2){ -sidePlaneNormal.x, -sidePlaneNormal.y }, &incidentFace[0], &incidentFace[1], negSide) < 2) return; + if (MathVector2Clip(sidePlaneNormal, &incidentFace[0], &incidentFace[1], posSide) < 2) return; + + // Flip normal if required + manifold->normal = (flip ? CLITERAL(Vector2){ -refFaceNormal.x, -refFaceNormal.y } : refFaceNormal); + + // Keep points behind reference face + int currentPoint = 0; // MathVector2Clipped points behind reference face + float separation = MathVector2DotProduct(refFaceNormal, incidentFace[0]) - refC; + if (separation <= 0.0f) + { + manifold->contacts[currentPoint] = incidentFace[0]; + manifold->penetration = -separation; + currentPoint++; + } + else manifold->penetration = 0.0f; + + separation = MathVector2DotProduct(refFaceNormal, incidentFace[1]) - refC; + + if (separation <= 0.0f) + { + manifold->contacts[currentPoint] = incidentFace[1]; + manifold->penetration += -separation; + currentPoint++; + + // Calculate total penetration average + manifold->penetration /= currentPoint; + } + + manifold->contactsCount = currentPoint; +} + +// Integrates physics forces into velocity +static void IntegratePhysicsForces(PhysicsBody body) +{ + if ((body == NULL) || (body->inverseMass == 0.0f) || !body->enabled) return; + + body->velocity.x += (float)((body->force.x*body->inverseMass)*(deltaTime/2.0)); + body->velocity.y += (float)((body->force.y*body->inverseMass)*(deltaTime/2.0)); + + if (body->useGravity) + { + body->velocity.x += (float)(gravityForce.x*(deltaTime/1000/2.0)); + body->velocity.y += (float)(gravityForce.y*(deltaTime/1000/2.0)); + } + + if (!body->freezeOrient) body->angularVelocity += (float)(body->torque*body->inverseInertia*(deltaTime/2.0)); +} + +// Initializes physics manifolds to solve collisions +static void InitializePhysicsManifolds(PhysicsManifold manifold) +{ + PhysicsBody bodyA = manifold->bodyA; + PhysicsBody bodyB = manifold->bodyB; + + if ((bodyA == NULL) || (bodyB == NULL)) return; + + // Calculate average restitution, static and dynamic friction + manifold->restitution = sqrtf(bodyA->restitution*bodyB->restitution); + manifold->staticFriction = sqrtf(bodyA->staticFriction*bodyB->staticFriction); + manifold->dynamicFriction = sqrtf(bodyA->dynamicFriction*bodyB->dynamicFriction); + + for (unsigned int i = 0; i < manifold->contactsCount; i++) + { + // Caculate radius from center of mass to contact + Vector2 radiusA = MathVector2Subtract(manifold->contacts[i], bodyA->position); + Vector2 radiusB = MathVector2Subtract(manifold->contacts[i], bodyB->position); + + Vector2 crossA = MathVector2Product(radiusA, bodyA->angularVelocity); + Vector2 crossB = MathVector2Product(radiusB, bodyB->angularVelocity); + + Vector2 radiusV = { 0.0f, 0.0f }; + radiusV.x = bodyB->velocity.x + crossB.x - bodyA->velocity.x - crossA.x; + radiusV.y = bodyB->velocity.y + crossB.y - bodyA->velocity.y - crossA.y; + + // Determine if we should perform a resting collision or not; + // The idea is if the only thing moving this object is gravity, then the collision should be performed without any restitution + if (MathVector2SqrLen(radiusV) < (MathVector2SqrLen(CLITERAL(Vector2){ (float)(gravityForce.x*deltaTime/1000), (float)(gravityForce.y*deltaTime/1000) }) + PHYSAC_EPSILON)) manifold->restitution = 0; + } +} + +// Integrates physics collisions impulses to solve collisions +static void IntegratePhysicsImpulses(PhysicsManifold manifold) +{ + PhysicsBody bodyA = manifold->bodyA; + PhysicsBody bodyB = manifold->bodyB; + + if ((bodyA == NULL) || (bodyB == NULL)) return; + + // Early out and positional correct if both objects have infinite mass + if (fabs(bodyA->inverseMass + bodyB->inverseMass) <= PHYSAC_EPSILON) + { + bodyA->velocity = PHYSAC_VECTOR_ZERO; + bodyB->velocity = PHYSAC_VECTOR_ZERO; + return; + } + + for (unsigned int i = 0; i < manifold->contactsCount; i++) + { + // Calculate radius from center of mass to contact + Vector2 radiusA = MathVector2Subtract(manifold->contacts[i], bodyA->position); + Vector2 radiusB = MathVector2Subtract(manifold->contacts[i], bodyB->position); + + // Calculate relative velocity + Vector2 radiusV = { 0.0f, 0.0f }; + radiusV.x = bodyB->velocity.x + MathVector2Product(radiusB, bodyB->angularVelocity).x - bodyA->velocity.x - MathVector2Product(radiusA, bodyA->angularVelocity).x; + radiusV.y = bodyB->velocity.y + MathVector2Product(radiusB, bodyB->angularVelocity).y - bodyA->velocity.y - MathVector2Product(radiusA, bodyA->angularVelocity).y; + + // Relative velocity along the normal + float contactVelocity = MathVector2DotProduct(radiusV, manifold->normal); + + // Do not resolve if velocities are separating + if (contactVelocity > 0.0f) return; + + float raCrossN = MathVector2CrossProduct(radiusA, manifold->normal); + float rbCrossN = MathVector2CrossProduct(radiusB, manifold->normal); + + float inverseMassSum = bodyA->inverseMass + bodyB->inverseMass + (raCrossN*raCrossN)*bodyA->inverseInertia + (rbCrossN*rbCrossN)*bodyB->inverseInertia; + + // Calculate impulse scalar value + float impulse = -(1.0f + manifold->restitution)*contactVelocity; + impulse /= inverseMassSum; + impulse /= (float)manifold->contactsCount; + + // Apply impulse to each physics body + Vector2 impulseV = { manifold->normal.x*impulse, manifold->normal.y*impulse }; + + if (bodyA->enabled) + { + bodyA->velocity.x += bodyA->inverseMass*(-impulseV.x); + bodyA->velocity.y += bodyA->inverseMass*(-impulseV.y); + if (!bodyA->freezeOrient) bodyA->angularVelocity += bodyA->inverseInertia*MathVector2CrossProduct(radiusA, CLITERAL(Vector2){ -impulseV.x, -impulseV.y }); + } + + if (bodyB->enabled) + { + bodyB->velocity.x += bodyB->inverseMass*(impulseV.x); + bodyB->velocity.y += bodyB->inverseMass*(impulseV.y); + if (!bodyB->freezeOrient) bodyB->angularVelocity += bodyB->inverseInertia*MathVector2CrossProduct(radiusB, impulseV); + } + + // Apply friction impulse to each physics body + radiusV.x = bodyB->velocity.x + MathVector2Product(radiusB, bodyB->angularVelocity).x - bodyA->velocity.x - MathVector2Product(radiusA, bodyA->angularVelocity).x; + radiusV.y = bodyB->velocity.y + MathVector2Product(radiusB, bodyB->angularVelocity).y - bodyA->velocity.y - MathVector2Product(radiusA, bodyA->angularVelocity).y; + + Vector2 tangent = { radiusV.x - (manifold->normal.x*MathVector2DotProduct(radiusV, manifold->normal)), radiusV.y - (manifold->normal.y*MathVector2DotProduct(radiusV, manifold->normal)) }; + MathVector2Normalize(&tangent); + + // Calculate impulse tangent magnitude + float impulseTangent = -MathVector2DotProduct(radiusV, tangent); + impulseTangent /= inverseMassSum; + impulseTangent /= (float)manifold->contactsCount; + + float absImpulseTangent = (float)fabs(impulseTangent); + + // Don't apply tiny friction impulses + if (absImpulseTangent <= PHYSAC_EPSILON) return; + + // Apply coulumb's law + Vector2 tangentImpulse = { 0.0f, 0.0f }; + if (absImpulseTangent < impulse*manifold->staticFriction) tangentImpulse = CLITERAL(Vector2){ tangent.x*impulseTangent, tangent.y*impulseTangent }; + else tangentImpulse = CLITERAL(Vector2){ tangent.x*-impulse*manifold->dynamicFriction, tangent.y*-impulse*manifold->dynamicFriction }; + + // Apply friction impulse + if (bodyA->enabled) + { + bodyA->velocity.x += bodyA->inverseMass*(-tangentImpulse.x); + bodyA->velocity.y += bodyA->inverseMass*(-tangentImpulse.y); + + if (!bodyA->freezeOrient) bodyA->angularVelocity += bodyA->inverseInertia*MathVector2CrossProduct(radiusA, CLITERAL(Vector2){ -tangentImpulse.x, -tangentImpulse.y }); + } + + if (bodyB->enabled) + { + bodyB->velocity.x += bodyB->inverseMass*(tangentImpulse.x); + bodyB->velocity.y += bodyB->inverseMass*(tangentImpulse.y); + + if (!bodyB->freezeOrient) bodyB->angularVelocity += bodyB->inverseInertia*MathVector2CrossProduct(radiusB, tangentImpulse); + } + } +} + +// Integrates physics velocity into position and forces +static void IntegratePhysicsVelocity(PhysicsBody body) +{ + if ((body == NULL) ||!body->enabled) return; + + body->position.x += (float)(body->velocity.x*deltaTime); + body->position.y += (float)(body->velocity.y*deltaTime); + + if (!body->freezeOrient) body->orient += (float)(body->angularVelocity*deltaTime); + body->shape.transform = MathMatFromRadians(body->orient); + + IntegratePhysicsForces(body); +} + +// Corrects physics bodies positions based on manifolds collision information +static void CorrectPhysicsPositions(PhysicsManifold manifold) +{ + PhysicsBody bodyA = manifold->bodyA; + PhysicsBody bodyB = manifold->bodyB; + + if ((bodyA == NULL) || (bodyB == NULL)) return; + + Vector2 correction = { 0.0f, 0.0f }; + correction.x = (PHYSAC_MAX(manifold->penetration - PHYSAC_PENETRATION_ALLOWANCE, 0.0f)/(bodyA->inverseMass + bodyB->inverseMass))*manifold->normal.x*PHYSAC_PENETRATION_CORRECTION; + correction.y = (PHYSAC_MAX(manifold->penetration - PHYSAC_PENETRATION_ALLOWANCE, 0.0f)/(bodyA->inverseMass + bodyB->inverseMass))*manifold->normal.y*PHYSAC_PENETRATION_CORRECTION; + + if (bodyA->enabled) + { + bodyA->position.x -= correction.x*bodyA->inverseMass; + bodyA->position.y -= correction.y*bodyA->inverseMass; + } + + if (bodyB->enabled) + { + bodyB->position.x += correction.x*bodyB->inverseMass; + bodyB->position.y += correction.y*bodyB->inverseMass; + } +} + +// Returns the extreme point along a direction within a polygon +static Vector2 GetSupport(PhysicsShape shape, Vector2 dir) +{ + float bestProjection = -PHYSAC_FLT_MAX; + Vector2 bestVertex = { 0.0f, 0.0f }; + PhysicsVertexData data = shape.vertexData; + + for (unsigned int i = 0; i < data.vertexCount; i++) + { + Vector2 vertex = data.positions[i]; + float projection = MathVector2DotProduct(vertex, dir); + + if (projection > bestProjection) + { + bestVertex = vertex; + bestProjection = projection; + } + } + + return bestVertex; +} + +// Finds polygon shapes axis least penetration +static float FindAxisLeastPenetration(int *faceIndex, PhysicsShape shapeA, PhysicsShape shapeB) +{ + float bestDistance = -PHYSAC_FLT_MAX; + int bestIndex = 0; + + PhysicsVertexData dataA = shapeA.vertexData; + //PhysicsVertexData dataB = shapeB.vertexData; + + for (unsigned int i = 0; i < dataA.vertexCount; i++) + { + // Retrieve a face normal from A shape + Vector2 normal = dataA.normals[i]; + Vector2 transNormal = MathMatVector2Product(shapeA.transform, normal); + + // Transform face normal into B shape's model space + Matrix2x2 buT = MathMatTranspose(shapeB.transform); + normal = MathMatVector2Product(buT, transNormal); + + // Retrieve support point from B shape along -n + Vector2 support = GetSupport(shapeB, CLITERAL(Vector2){ -normal.x, -normal.y }); + + // Retrieve vertex on face from A shape, transform into B shape's model space + Vector2 vertex = dataA.positions[i]; + vertex = MathMatVector2Product(shapeA.transform, vertex); + vertex = MathVector2Add(vertex, shapeA.body->position); + vertex = MathVector2Subtract(vertex, shapeB.body->position); + vertex = MathMatVector2Product(buT, vertex); + + // Compute penetration distance in B shape's model space + float distance = MathVector2DotProduct(normal, MathVector2Subtract(support, vertex)); + + // Store greatest distance + if (distance > bestDistance) + { + bestDistance = distance; + bestIndex = i; + } + } + + *faceIndex = bestIndex; + return bestDistance; +} + +// Finds two polygon shapes incident face +static void FindIncidentFace(Vector2 *v0, Vector2 *v1, PhysicsShape ref, PhysicsShape inc, int index) +{ + PhysicsVertexData refData = ref.vertexData; + PhysicsVertexData incData = inc.vertexData; + + Vector2 referenceNormal = refData.normals[index]; + + // Calculate normal in incident's frame of reference + referenceNormal = MathMatVector2Product(ref.transform, referenceNormal); // To world space + referenceNormal = MathMatVector2Product(MathMatTranspose(inc.transform), referenceNormal); // To incident's model space + + // Find most anti-normal face on polygon + int incidentFace = 0; + float minDot = PHYSAC_FLT_MAX; + + for (unsigned int i = 0; i < incData.vertexCount; i++) + { + float dot = MathVector2DotProduct(referenceNormal, incData.normals[i]); + + if (dot < minDot) + { + minDot = dot; + incidentFace = i; + } + } + + // Assign face vertices for incident face + *v0 = MathMatVector2Product(inc.transform, incData.positions[incidentFace]); + *v0 = MathVector2Add(*v0, inc.body->position); + incidentFace = (((incidentFace + 1) < (int)incData.vertexCount) ? (incidentFace + 1) : 0); + *v1 = MathMatVector2Product(inc.transform, incData.positions[incidentFace]); + *v1 = MathVector2Add(*v1, inc.body->position); +} + +// Returns clipping value based on a normal and two faces +static int MathVector2Clip(Vector2 normal, Vector2 *faceA, Vector2 *faceB, float clip) +{ + int sp = 0; + Vector2 out[2] = { *faceA, *faceB }; + + // Retrieve distances from each endpoint to the line + float distanceA = MathVector2DotProduct(normal, *faceA) - clip; + float distanceB = MathVector2DotProduct(normal, *faceB) - clip; + + // If negative (behind plane) + if (distanceA <= 0.0f) out[sp++] = *faceA; + if (distanceB <= 0.0f) out[sp++] = *faceB; + + // If the points are on different sides of the plane + if ((distanceA*distanceB) < 0.0f) + { + // Push intersection point + float alpha = distanceA/(distanceA - distanceB); + out[sp] = *faceA; + Vector2 delta = MathVector2Subtract(*faceB, *faceA); + delta.x *= alpha; + delta.y *= alpha; + out[sp] = MathVector2Add(out[sp], delta); + sp++; + } + + // Assign the new converted values + *faceA = out[0]; + *faceB = out[1]; + + return sp; +} + +// Returns the barycenter of a triangle given by 3 points +static Vector2 MathTriangleBarycenter(Vector2 v1, Vector2 v2, Vector2 v3) +{ + Vector2 result = { 0.0f, 0.0f }; + + result.x = (v1.x + v2.x + v3.x)/3; + result.y = (v1.y + v2.y + v3.y)/3; + + return result; +} + +#if !defined(PHYSAC_AVOID_TIMMING_SYSTEM) +// Initializes hi-resolution MONOTONIC timer +static void InitTimer(void) +{ +#if defined(_WIN32) + QueryPerformanceFrequency((unsigned long long int *) &frequency); +#endif + +#if defined(__EMSCRIPTEN__) || defined(__linux__) + struct timespec now; + if (clock_gettime(CLOCK_MONOTONIC, &now) == 0) frequency = 1000000000; +#endif + +#if defined(__APPLE__) + mach_timebase_info_data_t timebase; + mach_timebase_info(&timebase); + frequency = (timebase.denom*1e9)/timebase.numer; +#endif + + baseClockTicks = (double)GetClockTicks(); // Get MONOTONIC clock time offset + startTime = GetCurrentTime(); // Get current time in milliseconds +} + +// Get hi-res MONOTONIC time measure in clock ticks +static unsigned long long int GetClockTicks(void) +{ + unsigned long long int value = 0; + +#if defined(_WIN32) + QueryPerformanceCounter((unsigned long long int *) &value); +#endif + +#if defined(__linux__) + struct timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); + value = (unsigned long long int)now.tv_sec*(unsigned long long int)1000000000 + (unsigned long long int)now.tv_nsec; +#endif + +#if defined(__APPLE__) + value = mach_absolute_time(); +#endif + + return value; +} + +// Get current time in milliseconds +static double GetCurrentTime(void) +{ + return (double)(GetClockTicks() - baseClockTicks)/frequency*1000; +} +#endif // !PHYSAC_AVOID_TIMMING_SYSTEM + + +// Returns the cross product of a vector and a value +static inline Vector2 MathVector2Product(Vector2 vector, float value) +{ + Vector2 result = { -value*vector.y, value*vector.x }; + return result; +} + +// Returns the cross product of two vectors +static inline float MathVector2CrossProduct(Vector2 v1, Vector2 v2) +{ + return (v1.x*v2.y - v1.y*v2.x); +} + +// Returns the len square root of a vector +static inline float MathVector2SqrLen(Vector2 vector) +{ + return (vector.x*vector.x + vector.y*vector.y); +} + +// Returns the dot product of two vectors +static inline float MathVector2DotProduct(Vector2 v1, Vector2 v2) +{ + return (v1.x*v2.x + v1.y*v2.y); +} + +// Returns the square root of distance between two vectors +static inline float MathVector2SqrDistance(Vector2 v1, Vector2 v2) +{ + Vector2 dir = MathVector2Subtract(v1, v2); + return MathVector2DotProduct(dir, dir); +} + +// Returns the normalized values of a vector +static void MathVector2Normalize(Vector2 *vector) +{ + float length, ilength; + + Vector2 aux = *vector; + length = sqrtf(aux.x*aux.x + aux.y*aux.y); + + if (length == 0) length = 1.0f; + + ilength = 1.0f/length; + + vector->x *= ilength; + vector->y *= ilength; +} + +// Returns the sum of two given vectors +static inline Vector2 MathVector2Add(Vector2 v1, Vector2 v2) +{ + Vector2 result = { v1.x + v2.x, v1.y + v2.y }; + return result; +} + +// Returns the subtract of two given vectors +static inline Vector2 MathVector2Subtract(Vector2 v1, Vector2 v2) +{ + Vector2 result = { v1.x - v2.x, v1.y - v2.y }; + return result; +} + +// Creates a matrix 2x2 from a given radians value +static Matrix2x2 MathMatFromRadians(float radians) +{ + float cos = cosf(radians); + float sin = sinf(radians); + + Matrix2x2 result = { cos, -sin, sin, cos }; + return result; +} + +// Returns the transpose of a given matrix 2x2 +static inline Matrix2x2 MathMatTranspose(Matrix2x2 matrix) +{ + Matrix2x2 result = { matrix.m00, matrix.m10, matrix.m01, matrix.m11 }; + return result; +} + +// Multiplies a vector by a matrix 2x2 +static inline Vector2 MathMatVector2Product(Matrix2x2 matrix, Vector2 vector) +{ + Vector2 result = { matrix.m00*vector.x + matrix.m01*vector.y, matrix.m10*vector.x + matrix.m11*vector.y }; + return result; +} + +#endif // PHYSAC_IMPLEMENTATION diff --git a/src/extras/rmem.h b/src/extras/rmem.h new file mode 100644 index 00000000..dbf417fe --- /dev/null +++ b/src/extras/rmem.h @@ -0,0 +1,739 @@ +/********************************************************************************************** +* +* rmem - raylib memory pool and objects pool +* +* A quick, efficient, and minimal free list and arena-based allocator +* +* PURPOSE: +* - A quicker, efficient memory allocator alternative to 'malloc' and friends. +* - Reduce the possibilities of memory leaks for beginner developers using Raylib. +* - Being able to flexibly range check memory if necessary. +* +* CONFIGURATION: +* +* #define RMEM_IMPLEMENTATION +* Generates the implementation of the library into the included file. +* If not defined, the library is in header only mode and can be included in other headers +* or source files without problems. But only ONE file should hold the implementation. +* +* +* LICENSE: zlib/libpng +* +* Copyright (c) 2019 Kevin 'Assyrianic' Yonan (@assyrianic) and reviewed by Ramon Santamaria (@raysan5) +* +* This software is provided "as-is", without any express or implied warranty. In no event +* will the authors be held liable for any damages arising from the use of this software. +* +* Permission is granted to anyone to use this software for any purpose, including commercial +* applications, and to alter it and redistribute it freely, subject to the following restrictions: +* +* 1. The origin of this software must not be misrepresented; you must not claim that you +* wrote the original software. If you use this software in a product, an acknowledgment +* in the product documentation would be appreciated but is not required. +* +* 2. Altered source versions must be plainly marked as such, and must not be misrepresented +* as being the original software. +* +* 3. This notice may not be removed or altered from any source distribution. +* +**********************************************************************************************/ + +#ifndef RMEM_H +#define RMEM_H + +#include +#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/src/extras/rnet.h b/src/extras/rnet.h new file mode 100644 index 00000000..439b105a --- /dev/null +++ b/src/extras/rnet.h @@ -0,0 +1,2256 @@ +/********************************************************************************************** +* +* rnet - A simple and easy-to-use network module for raylib +* +* FEATURES: +* - Provides a simple and (hopefully) easy to use wrapper around the Berkeley socket API +* +* INSPIRED BY: +* SFML Sockets - https://www.sfml-dev.org/documentation/2.5.1/classsf_1_1Socket.php +* SDL_net - https://www.libsdl.org/projects/SDL_net/ +* BSD Sockets - https://www.gnu.org/software/libc/manual/html_node/Sockets.html +* BEEJ - https://beej.us/guide/bgnet/html/single/bgnet.html +* Winsock2 - https://docs.microsoft.com/en-us/windows/desktop/api/winsock2 +* +* CONTRIBUTORS: +* Jak Barnes (github: @syphonx) (Feb. 2019) - Initial version +* +* +* LICENSE: zlib/libpng +* +* Copyright (c) 2019-2020 Jak Barnes (@syphonx) and Ramon Santamaria (@raysan5) +* +* This software is provided "as-is", without any express or implied warranty. In no event +* will the authors be held liable for any damages arising from the use of this software. +* +* Permission is granted to anyone to use this software for any purpose, including commercial +* applications, and to alter it and redistribute it freely, subject to the following restrictions: +* +* 1. The origin of this software must not be misrepresented; you must not claim that you +* wrote the original software. If you use this software in a product, an acknowledgment +* in the product documentation would be appreciated but is not required. +* +* 2. Altered source versions must be plainly marked as such, and must not be misrepresented +* as being the original software. +* +* 3. This notice may not be removed or altered from any source distribution. +* +**********************************************************************************************/ + +#ifndef RNET_H +#define RNET_H + +#include // 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 +#ifdef _WIN32 + #include +#else +#if defined(__STDC__) && __STDC_VERSION__ >= 199901L + #include +#elif !defined(__cplusplus) && !defined(bool) + typedef enum { false, true } bool; +#endif +#endif + +typedef enum { + SOCKET_TCP = 0, // SOCK_STREAM + SOCKET_UDP = 1 // SOCK_DGRAM +} SocketType; + +// Network typedefs +typedef uint32_t SocketChannel; +typedef struct _AddressInformation *AddressInformation; +typedef struct _SocketAddress *SocketAddress; +typedef struct _SocketAddressIPv4 *SocketAddressIPv4; +typedef struct _SocketAddressIPv6 *SocketAddressIPv6; +typedef struct _SocketAddressStorage *SocketAddressStorage; + +// IPAddress definition (in network byte order) +typedef struct IPAddress { + unsigned long host; // 32-bit IPv4 host address + unsigned short port; // 16-bit protocol port +} IPAddress; + +typedef struct UDPChannel { + int numbound; // The total number of addresses this channel is bound to + IPAddress address[SOCKET_MAX_UDPADDRESSES]; // The list of remote addresses this channel is bound to +} UDPChannel; + +// An option ID, value, sizeof(value) tuple for setsockopt(2). +typedef struct SocketOpt { + int id; // Socked option id + int valueLen; // Socked option value len + void *value; // Socked option value data +} SocketOpt; + +typedef struct Socket { + int ready; // Is the socket ready? i.e. has information + int status; // The last status code to have occured using this socket + bool isServer; // Is this socket a server socket (i.e. TCP/UDP Listen Server) + SocketChannel channel; // The socket handle id + SocketType type; // Is this socket a TCP or UDP socket? + + bool isIPv6; // Is this socket address an ipv6 address? + SocketAddressIPv4 addripv4; // The host/target IPv4 for this socket (in network byte order) + SocketAddressIPv6 addripv6; // The host/target IPv6 for this socket (in network byte order) + + struct UDPChannel binding[SOCKET_MAX_UDPCHANNELS]; // The amount of channels (if UDP) this socket is bound to +} Socket; + +// Configuration for a socket +typedef struct SocketConfig { + SocketType type; // The type of socket, TCP/UDP + char *host; // The host address in xxx.xxx.xxx.xxx form + char *port; // The target port/service in the form "http" or "25565" + bool server; // Listen for incoming clients? + bool nonblocking; // non-blocking operation? + int backlog_size; // set a custom backlog size + SocketOpt sockopts[SOCKET_MAX_SOCK_OPTS]; +} SocketConfig; + +typedef struct SocketDataPacket { + IPAddress address; // The source/dest address of an incoming/outgoing packet + int channel; // The src/dst channel of the packet + int maxlen; // The size of the data buffer + int status; // Packet status after sending + unsigned int len; // The length of the packet data + unsigned char *data; // The packet data +} SocketDataPacket; + +// Result from calling open with a given config +typedef struct SocketResult { + int status; // Socket result state + Socket *socket; // Socket ref +} SocketResult; + +typedef struct SocketSet { + int numsockets; // Socket set count + int maxsockets; // Socket set max + struct Socket **sockets; // Sockets array +} SocketSet; + +// Packet type +typedef struct Packet { + uint32_t size; // The total size of bytes in data + uint32_t offs; // The offset to data access + uint32_t maxs; // The max size of data + uint8_t *data; // Data stored in network byte order +} Packet; + + +#ifdef __cplusplus +extern "C" { // Prevents name mangling of functions +#endif + +//---------------------------------------------------------------------------------- +// Global Variables Definition +//---------------------------------------------------------------------------------- +//... + +//---------------------------------------------------------------------------------- +// Module Functions Declaration +//---------------------------------------------------------------------------------- + +// Initialisation and cleanup +bool InitNetworkDevice(void); +void CloseNetworkDevice(void); + +// Address API +void ResolveIP(const char *ip, const char *service, int flags, char *outhost, char *outserv); +int ResolveHost(const char *address, const char *service, int addressType, int flags, AddressInformation *outAddr); +int GetAddressFamily(AddressInformation address); +int GetAddressSocketType(AddressInformation address); +int GetAddressProtocol(AddressInformation address); +char *GetAddressCanonName(AddressInformation address); +char *GetAddressHostAndPort(AddressInformation address, char *outhost, unsigned short *outport); + +// Address Memory API +AddressInformation LoadAddress(void); +void UnloadAddress(AddressInformation *addressInfo); +AddressInformation *LoadAddressList(int size); + +// Socket API +bool SocketCreate(SocketConfig *config, SocketResult *result); +bool SocketBind(SocketConfig *config, SocketResult *result); +bool SocketListen(SocketConfig *config, SocketResult *result); +bool SocketConnect(SocketConfig *config, SocketResult *result); +Socket *SocketAccept(Socket *server, SocketConfig *config); + +// General Socket API +int SocketSend(Socket *sock, const void *datap, int len); +int SocketReceive(Socket *sock, void *data, int maxlen); +SocketAddressStorage SocketGetPeerAddress(Socket *sock); +const char *GetSocketAddressHost(SocketAddressStorage storage); +short GetSocketAddressPort(SocketAddressStorage storage); +void SocketClose(Socket *sock); + +// UDP Socket API +int SocketSetChannel(Socket *socket, int channel, const IPAddress *address); +void SocketUnsetChannel(Socket *socket, int channel); + +// UDP DataPacket API +SocketDataPacket *AllocPacket(int size); +int ResizePacket(SocketDataPacket *packet, int newsize); +void FreePacket(SocketDataPacket *packet); +SocketDataPacket **AllocPacketList(int count, int size); +void FreePacketList(SocketDataPacket **packets); + +// Socket Memory API +Socket *LoadSocket(void); +void UnloadSocket(Socket **sock); +SocketResult *LoadSocketResult(void); +void UnloadSocketResult(SocketResult **result); +SocketSet *LoadSocketSet(int max); +void UnloadSocketSet(SocketSet *sockset); + +// Socket I/O API +bool IsSocketReady(Socket *sock); +bool IsSocketConnected(Socket *sock); +int AddSocket(SocketSet *set, Socket *sock); +int RemoveSocket(SocketSet *set, Socket *sock); +int CheckSockets(SocketSet *set, unsigned int timeout); + +// Packet API +void PacketSend(Packet *packet); +void PacketReceive(Packet *packet); +void PacketWrite8(Packet *packet, uint16_t value); +void PacketWrite16(Packet *packet, uint16_t value); +void PacketWrite32(Packet *packet, uint32_t value); +void PacketWrite64(Packet *packet, uint64_t value); +uint16_t PacketRead8(Packet *packet); +uint16_t PacketRead16(Packet *packet); +uint32_t PacketRead32(Packet *packet); +uint64_t PacketRead64(Packet *packet); + +#ifdef __cplusplus +} +#endif + +#endif // RNET_H + +/*********************************************************************************** +* +* RNET IMPLEMENTATION +* +************************************************************************************/ + +#if defined(RNET_IMPLEMENTATION) + +#include // 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 = (unsigned short)(hport); + + // TODO: Changed the code to avoid the usage of inet_pton and inet_ntop replacing them with getnameinfo (that should have a better support on windows). + + //inet_pton(AF_INET, config->host, &ip4addr.sin_addr); + int connect_result = connect(result->socket->channel, (struct sockaddr *)&ip4addr, sizeof(ip4addr)); + + if (connect_result == SOCKET_ERROR) + { + result->socket->status = SocketGetLastError(); + SocketSetLastError(0); + + switch (result->socket->status) + { + case WSAEWOULDBLOCK: success = true; break; + default: + { + TRACELOG(LOG_WARNING, "Socket Error: %s", SocketErrorCodeToString(result->socket->status)); + success = false; + } break; + } + } + else + { + TRACELOG(LOG_INFO, "Successfully connected to socket."); + success = true; + } + } + else + { + if (IsIPv6Address(config->host)) + { + struct sockaddr_in6 ip6addr; + ip6addr.sin6_family = AF_INET6; + unsigned long hport; + hport = strtoul(config->port, NULL, 0); + ip6addr.sin6_port = htons((unsigned short)hport); + //inet_pton(AF_INET6, config->host, &ip6addr.sin6_addr); // TODO. + int connect_result = connect(result->socket->channel, (struct sockaddr *)&ip6addr, sizeof(ip6addr)); + + if (connect_result == SOCKET_ERROR) + { + result->socket->status = SocketGetLastError(); + SocketSetLastError(0); + + switch (result->socket->status) + { + case WSAEWOULDBLOCK: success = true; break; + default: + { + TRACELOG(LOG_WARNING, "Socket Error: %s", SocketErrorCodeToString(result->socket->status)); + success = false; + } break; + } + } + else + { + TRACELOG(LOG_INFO, "Successfully connected to socket."); + success = true; + } + } + } + } + + if (success) + { + result->status = RESULT_SUCCESS; + result->socket->ready = 0; + result->socket->status = 0; + } + + return success; +} + +// Closes an existing socket +// +// SocketChannel socket - The id of the socket to close +void SocketClose(Socket *sock) +{ + if (sock != NULL) + { + if (sock->channel != INVALID_SOCKET) closesocket(sock->channel); + } +} + +// Returns the sockaddress for a specific socket in a generic storage struct +SocketAddressStorage SocketGetPeerAddress(Socket *sock) +{ + // TODO. + /* + if (sock->isServer) return NULL; + if (sock->isIPv6) return sock->addripv6; + else return sock->addripv4; + */ + + return NULL; +} + +// Return the address-type appropriate host portion of a socket address +const char *GetSocketAddressHost(SocketAddressStorage storage) +{ + assert(storage->address.ss_family == AF_INET || storage->address.ss_family == AF_INET6); + return SocketAddressToString((struct sockaddr_storage *)storage); +} + +// Return the address-type appropriate port(service) portion of a socket address +short GetSocketAddressPort(SocketAddressStorage storage) +{ + //return ntohs(GetSocketPortPtr(storage)); // TODO. + + return 0; +} + +// The accept function permits an incoming connection attempt on a socket. +// +// SocketChannel listener - The socket to listen for incoming connections on (i.e. server) +// SocketResult* out - The result of this function (if any, including errors) +// +// e.g. +// +// SocketResult connection; +// bool connected = false; +// if (!connected) +// { +// if (SocketAccept(server_res.socket.channel, &connection)) +// { +// connected = true; +// } +// } +Socket *SocketAccept(Socket *server, SocketConfig *config) +{ + if (!server->isServer || server->type == SOCKET_UDP) return NULL; + + struct sockaddr_storage sock_addr; + socklen_t sock_alen; + Socket *sock = LoadSocket(); + server->ready = 0; + sock_alen = sizeof(sock_addr); + sock->channel = accept(server->channel, (struct sockaddr *)&sock_addr, &sock_alen); + + if (sock->channel == INVALID_SOCKET) + { + sock->status = SocketGetLastError(); + TRACELOG(LOG_WARNING, "Socket Error: %s", SocketErrorCodeToString(sock->status)); + SocketSetLastError(0); + SocketClose(sock); + + return NULL; + } + + (config->nonblocking) ? SocketSetNonBlocking(sock) : SocketSetBlocking(sock); + sock->isServer = false; + sock->ready = 0; + sock->type = server->type; + + switch (sock_addr.ss_family) + { + case AF_INET: + { + struct sockaddr_in *s = ((struct sockaddr_in *)&sock_addr); + sock->addripv4 = (struct _SocketAddressIPv4 *)RNET_MALLOC(sizeof(*sock->addripv4)); + + if (sock->addripv4 != NULL) + { + memset(sock->addripv4, 0, sizeof(*sock->addripv4)); + memcpy(&sock->addripv4->address, (struct sockaddr_in *)&s->sin_addr, sizeof(struct sockaddr_in)); + TRACELOG(LOG_INFO, "Server: Got connection from %s::%hu", SocketAddressToString((struct sockaddr_storage *)s), ntohs(sock->addripv4->address.sin_port)); + } + } break; + case AF_INET6: + { + struct sockaddr_in6 *s = ((struct sockaddr_in6 *)&sock_addr); + sock->addripv6 = (struct _SocketAddressIPv6 *)RNET_MALLOC(sizeof(*sock->addripv6)); + + if (sock->addripv6 != NULL) + { + memset(sock->addripv6, 0, sizeof(*sock->addripv6)); + memcpy(&sock->addripv6->address, (struct sockaddr_in6 *)&s->sin6_addr, sizeof(struct sockaddr_in6)); + TRACELOG(LOG_INFO, "Server: Got connection from %s::%hu", SocketAddressToString((struct sockaddr_storage *)s), ntohs(sock->addripv6->address.sin6_port)); + } + } break; + } + + return sock; +} + +// Verify that the channel is in the valid range +static int ValidChannel(int channel) +{ + if ((channel < 0) || (channel >= SOCKET_MAX_UDPCHANNELS)) + { + TRACELOG(LOG_WARNING, "Invalid channel"); + return 0; + } + + return 1; +} + +// Set the socket channel +int SocketSetChannel(Socket *socket, int channel, const IPAddress *address) +{ + struct UDPChannel *binding; + + if (socket == NULL) + { + TRACELOG(LOG_WARNING, "Passed a NULL socket"); + return (-1); + } + + if (channel == -1) + { + for (channel = 0; channel < SOCKET_MAX_UDPCHANNELS; ++channel) + { + binding = &socket->binding[channel]; + if (binding->numbound < SOCKET_MAX_UDPADDRESSES) break; + } + } + else + { + if (!ValidChannel(channel)) return (-1); + + binding = &socket->binding[channel]; + } + + if (binding->numbound == SOCKET_MAX_UDPADDRESSES) + { + TRACELOG(LOG_WARNING, "No room for new addresses"); + return (-1); + } + + binding->address[binding->numbound++] = *address; + + return (channel); +} + +// Remove the socket channel +void SocketUnsetChannel(Socket *socket, int channel) +{ + if ((channel >= 0) && (channel < SOCKET_MAX_UDPCHANNELS)) socket->binding[channel].numbound = 0; +} + +/* Allocate/free a single UDP packet 'size' bytes long. + The new packet is returned, or NULL if the function ran out of memory. + */ +SocketDataPacket *AllocPacket(int size) +{ + SocketDataPacket *packet = (SocketDataPacket *)RNET_MALLOC(sizeof(*packet)); + int error = 1; + + if (packet != NULL) + { + packet->maxlen = size; + packet->data = (uint8_t *)RNET_MALLOC(size); + if (packet->data != NULL) + { + error = 0; + } + } + + if (error) + { + FreePacket(packet); + packet = NULL; + } + + return (packet); +} + +int ResizePacket(SocketDataPacket *packet, int newsize) +{ + uint8_t *newdata = (uint8_t *)RNET_MALLOC(newsize); + + if (newdata != NULL) + { + RNET_FREE(packet->data); + packet->data = newdata; + packet->maxlen = newsize; + } + + return (packet->maxlen); +} + +void FreePacket(SocketDataPacket *packet) +{ + if (packet) + { + RNET_FREE(packet->data); + RNET_FREE(packet); + } +} + +// Allocate/Free a UDP packet vector (array of packets) of 'howmany' packets, each 'size' bytes long. +// A pointer to the packet array is returned, or NULL if the function ran out of memory. +SocketDataPacket **AllocPacketList(int howmany, int size) +{ + SocketDataPacket **packetV = (SocketDataPacket **)RNET_MALLOC((howmany + 1) * sizeof(*packetV)); + + if (packetV != NULL) + { + int i; + for (i = 0; i < howmany; ++i) + { + packetV[i] = AllocPacket(size); + if (packetV[i] == NULL) + { + break; + } + } + packetV[i] = NULL; + + if (i != howmany) + { + FreePacketList(packetV); + packetV = NULL; + } + } + + return (packetV); +} + +void FreePacketList(SocketDataPacket **packetV) +{ + if (packetV) + { + for (int i = 0; packetV[i]; ++i) FreePacket(packetV[i]); + RNET_FREE(packetV); + } +} + +// Send 'len' bytes of 'data' over the non-server socket 'sock' +int SocketSend(Socket *sock, const void *datap, int length) +{ + int sent = 0; + int left = length; + int status = -1; + int numsent = 0; + const unsigned char *data = (const unsigned char *)datap; + + // Server sockets are for accepting connections only + if (sock->isServer) + { + TRACELOG(LOG_WARNING, "Cannot send information on a server socket"); + return -1; + } + + // Which socket are we trying to send data on + switch (sock->type) + { + case SOCKET_TCP: + { + SocketSetLastError(0); + do + { + length = send(sock->channel, (const char *)data, left, 0); + if (length > 0) + { + sent += length; + left -= length; + data += length; + } + } while ((left > 0) && // While we still have bytes left to send + ((length > 0) || // The amount of bytes we actually sent is > 0 + (SocketGetLastError() == WSAEINTR)) // The socket was interupted + ); + + if (length == SOCKET_ERROR) + { + sock->status = SocketGetLastError(); + TRACELOG(LOG_DEBUG, "Socket Error: %s", SocketErrorCodeToString(sock->status)); + SocketSetLastError(0); + } + else TRACELOG(LOG_DEBUG, "Successfully sent \"%s\" (%d bytes)", datap, sent); + + return sent; + } break; + case SOCKET_UDP: + { + SocketSetLastError(0); + + if (sock->isIPv6) status = sendto(sock->channel, (const char *)data, left, 0, (struct sockaddr *)&sock->addripv6->address, sizeof(sock->addripv6->address)); + else status = sendto(sock->channel, (const char *)data, left, 0, (struct sockaddr *)&sock->addripv4->address, sizeof(sock->addripv4->address)); + + if (sent >= 0) + { + sock->status = 0; + ++numsent; + TRACELOG(LOG_DEBUG, "Successfully sent \"%s\" (%d bytes)", datap, status); + } + else + { + sock->status = SocketGetLastError(); + TRACELOG(LOG_DEBUG, "Socket Error: %s", SocketGetLastErrorString(sock->status)); + SocketSetLastError(0); + return 0; + } + + return numsent; + } break; + default: break; + } + + return -1; +} + +// Receive up to 'maxlen' bytes of data over the non-server socket 'sock', +// and store them in the buffer pointed to by 'data'. +// This function returns the actual amount of data received. If the return +// value is less than or equal to zero, then either the remote connection was +// closed, or an unknown socket error occurred. +int SocketReceive(Socket *sock, void *data, int maxlen) +{ + int len = 0; + int numrecv = 0; + int status = 0; + socklen_t sock_len; + struct sockaddr_storage sock_addr; + //char ip[INET6_ADDRSTRLEN]; + + // Server sockets are for accepting connections only + if (sock->isServer && (sock->type == SOCKET_TCP)) + { + sock->status = SocketGetLastError(); + TRACELOG(LOG_DEBUG, "Socket Error: %s", "Server sockets cannot be used to receive data"); + SocketSetLastError(0); + return 0; + } + + // Which socket are we trying to send data on + switch (sock->type) + { + case SOCKET_TCP: + { + SocketSetLastError(0); + do + { + len = recv(sock->channel, (char *)data, maxlen, 0); + } while (SocketGetLastError() == WSAEINTR); + + if (len > 0) + { + // Who sent the packet? + if (sock->type == SOCKET_UDP) + { + //TRACELOG(LOG_DEBUG, "Received data from: %s", inet_ntop(sock_addr.ss_family, GetSocketAddressPtr((struct sockaddr *)&sock_addr), ip, sizeof(ip))); + } + + ((unsigned char *)data)[len] = '\0'; // Add null terminating character to the end of the stream + TRACELOG(LOG_DEBUG, "Received \"%s\" (%d bytes)", data, len); + } + + sock->ready = 0; + return len; + } break; + case SOCKET_UDP: + { + SocketSetLastError(0); + sock_len = sizeof(sock_addr); + status = recvfrom(sock->channel, // The receving channel + data, // A pointer to the data buffer to fill + maxlen, // The max length of the data to fill + 0, // Flags + (struct sockaddr *)&sock_addr, // The address of the recevied data + &sock_len // The length of the received data address + ); + + if (status >= 0) ++numrecv; + else + { + sock->status = SocketGetLastError(); + + switch (sock->status) + { + case WSAEWOULDBLOCK: break; + default: TRACELOG(LOG_WARNING, "Socket Error: %s", SocketErrorCodeToString(sock->status)); break; + } + + SocketSetLastError(0); + return 0; + } + + sock->ready = 0; + return numrecv; + } break; + default: break; + } + + return -1; +} + +// Does the socket have it's 'ready' flag set? +bool IsSocketReady(Socket *sock) +{ + return (sock != NULL) && (sock->ready); +} + +// Check if the socket is considered connected +bool IsSocketConnected(Socket *sock) +{ +#if defined(_WIN32) + FD_SET writefds; + FD_ZERO(&writefds); + FD_SET(sock->channel, &writefds); + struct timeval timeout; + timeout.tv_sec = 1; + timeout.tv_usec = 1000000000UL; + int total = select(0, NULL, &writefds, NULL, &timeout); + + if (total == -1) + { // Error + sock->status = SocketGetLastError(); + TRACELOG(LOG_WARNING, "Socket Error: %s", SocketErrorCodeToString(sock->status)); + SocketSetLastError(0); + } + else if (total == 0) return false; // Timeout + else if (FD_ISSET(sock->channel, &writefds)) return true; + + return false; +#else + return true; +#endif +} + +// Allocate and return a SocketResult struct +SocketResult *LoadSocketResult(void) +{ + struct SocketResult *res = (struct SocketResult *)RNET_MALLOC(sizeof(*res)); + + if (res != NULL) + { + memset(res, 0, sizeof(*res)); + if ((res->socket = LoadSocket()) == NULL) + { + RNET_FREE(res); + res = NULL; + } + } + + return res; +} + +// Free an allocated SocketResult +void UnloadSocketResult(SocketResult **result) +{ + if (*result != NULL) + { + if ((*result)->socket != NULL) UnloadSocket(&((*result)->socket)); + + RNET_FREE(*result); + *result = NULL; + } +} + +// Allocate a Socket +Socket *LoadSocket(void) +{ + struct Socket *sock; + sock = (Socket *)RNET_MALLOC(sizeof(*sock)); + + if (sock != NULL) memset(sock, 0, sizeof(*sock)); + else + { + TRACELOG(LOG_WARNING, "Ran out of memory attempting to allocate a socket"); + SocketClose(sock); + RNET_FREE(sock); + sock = NULL; + } + + return sock; +} + +// Free an allocated Socket +void UnloadSocket(Socket **sock) +{ + if (*sock != NULL) + { + RNET_FREE(*sock); + *sock = NULL; + } +} + +// Allocate a SocketSet +SocketSet *LoadSocketSet(int max) +{ + struct SocketSet *set = (struct SocketSet *)RNET_MALLOC(sizeof(*set)); + + if (set != NULL) + { + set->numsockets = 0; + set->maxsockets = max; + set->sockets = (struct Socket **)RNET_MALLOC(max * sizeof(*set->sockets)); + if (set->sockets != NULL) + { + for (int i = 0; i < max; ++i) set->sockets[i] = NULL; + } + else + { + RNET_FREE(set); + set = NULL; + } + } + + return (set); +} + +// Free an allocated SocketSet +void UnloadSocketSet(SocketSet *set) +{ + if (set) + { + RNET_FREE(set->sockets); + RNET_FREE(set); + } +} + +// Add a Socket "sock" to the SocketSet "set" +int AddSocket(SocketSet *set, Socket *sock) +{ + if (sock != NULL) + { + if (set->numsockets == set->maxsockets) + { + TRACELOG(LOG_DEBUG, "Socket Error: %s", "SocketSet is full"); + SocketSetLastError(0); + return (-1); + } + set->sockets[set->numsockets++] = (struct Socket *)sock; + } + else + { + TRACELOG(LOG_DEBUG, "Socket Error: %s", "Socket was null"); + SocketSetLastError(0); + return (-1); + } + + return (set->numsockets); +} + +// Remove a Socket "sock" to the SocketSet "set" +int RemoveSocket(SocketSet *set, Socket *sock) +{ + if (sock != NULL) + { + int i = 0; + for (i = 0; i < set->numsockets; ++i) + { + if (set->sockets[i] == (struct Socket *)sock) break; + } + + if (i == set->numsockets) + { + TRACELOG(LOG_DEBUG, "Socket Error: %s", "Socket not found"); + SocketSetLastError(0); + return (-1); + } + + --set->numsockets; + for (; i < set->numsockets; ++i) set->sockets[i] = set->sockets[i + 1]; + } + + return (set->numsockets); +} + +// Check the sockets in the socket set for pending information +int CheckSockets(SocketSet *set, unsigned int timeout) +{ + int i; + SOCKET maxfd; + int retval; + struct timeval tv; + fd_set mask; + + /* Find the largest file descriptor */ + maxfd = 0; + for (i = set->numsockets - 1; i >= 0; --i) + { + if (set->sockets[i]->channel > maxfd) + { + maxfd = set->sockets[i]->channel; + } + } + + // Check the file descriptors for available data + do + { + SocketSetLastError(0); + + // Set up the mask of file descriptors + FD_ZERO(&mask); + for (i = set->numsockets - 1; i >= 0; --i) + { + FD_SET(set->sockets[i]->channel, &mask); + } // Set up the timeout + + tv.tv_sec = timeout / 1000; + tv.tv_usec = (timeout % 1000) * 1000; + + /* Look! */ + retval = select(maxfd + 1, &mask, NULL, NULL, &tv); + } while (SocketGetLastError() == WSAEINTR); + + // Mark all file descriptors ready that have data available + if (retval > 0) + { + for (i = set->numsockets - 1; i >= 0; --i) + { + if (FD_ISSET(set->sockets[i]->channel, &mask)) set->sockets[i]->ready = 1; + } + } + + return retval; +} + +// Allocate an AddressInformation +AddressInformation LoadAddress(void) +{ + AddressInformation addressInfo = NULL; + addressInfo = (AddressInformation)RNET_CALLOC(1, sizeof(*addressInfo)); + + if (addressInfo != NULL) + { + addressInfo->addr.ai_addr = (struct sockaddr *)RNET_CALLOC(1, sizeof(struct sockaddr)); + if (addressInfo->addr.ai_addr == NULL) TRACELOG(LOG_WARNING, "Failed to allocate memory for \"struct sockaddr\""); + } + else TRACELOG(LOG_WARNING, "Failed to allocate memory for \"struct AddressInformation\""); + + return addressInfo; +} + +// Free an AddressInformation struct +void UnloadAddress(AddressInformation *addressInfo) +{ + if (*addressInfo != NULL) + { + if ((*addressInfo)->addr.ai_addr != NULL) + { + RNET_FREE((*addressInfo)->addr.ai_addr); + (*addressInfo)->addr.ai_addr = NULL; + } + + RNET_FREE(*addressInfo); + *addressInfo = NULL; + } +} + +// Allocate a list of AddressInformation +AddressInformation *LoadAddressList(int size) +{ + AddressInformation *addr; + addr = (AddressInformation *)RNET_MALLOC(size * sizeof(AddressInformation)); + return addr; +} + +// Opaque datatype accessor addrinfo->ai_family +int GetAddressFamily(AddressInformation address) +{ + return address->addr.ai_family; +} + +// Opaque datatype accessor addrinfo->ai_socktype +int GetAddressSocketType(AddressInformation address) +{ + return address->addr.ai_socktype; +} + +// Opaque datatype accessor addrinfo->ai_protocol +int GetAddressProtocol(AddressInformation address) +{ + return address->addr.ai_protocol; +} + +// Opaque datatype accessor addrinfo->ai_canonname +char *GetAddressCanonName(AddressInformation address) +{ + return address->addr.ai_canonname; +} + +// Opaque datatype accessor addrinfo->ai_addr +char *GetAddressHostAndPort(AddressInformation address, char *outhost, unsigned short *outport) +{ + //char *ip[INET6_ADDRSTRLEN]; + char *result = NULL; + struct sockaddr_storage *storage = (struct sockaddr_storage *)address->addr.ai_addr; + + switch (storage->ss_family) + { + case AF_INET: + { + struct sockaddr_in *s = ((struct sockaddr_in *)address->addr.ai_addr); + //result = inet_ntop(AF_INET, &s->sin_addr, ip, INET_ADDRSTRLEN); // TODO. + *outport = ntohs(s->sin_port); + } break; + case AF_INET6: + { + struct sockaddr_in6 *s = ((struct sockaddr_in6 *)address->addr.ai_addr); + //result = inet_ntop(AF_INET6, &s->sin6_addr, ip, INET6_ADDRSTRLEN); // TODO. + *outport = ntohs(s->sin6_port); + } break; + default: break; + } + + if (result == NULL) + { + TRACELOG(LOG_WARNING, "Socket Error: %s", SocketErrorCodeToString(SocketGetLastError())); + SocketSetLastError(0); + } + else + { + strcpy(outhost, result); + } + return result; +} + +// +void PacketSend(Packet *packet) +{ + TRACELOG(LOG_INFO, "Sending packet (%s) with size %d\n", packet->data, packet->size); +} + +// +void PacketReceive(Packet *packet) +{ + TRACELOG(LOG_INFO, "Receiving packet (%s) with size %d\n", packet->data, packet->size); +} + +// +void PacketWrite16(Packet *packet, uint16_t value) +{ + TRACELOG(LOG_INFO, "Original: 0x%04" PRIX16 " - %" PRIu16 "\n", value, value); + uint8_t *data = packet->data + packet->offs; + *data++ = (uint8_t)(value >> 8); + *data++ = (uint8_t)(value); + packet->size += sizeof(uint16_t); + packet->offs += sizeof(uint16_t); + TRACELOG(LOG_INFO, "Network: 0x%04" PRIX16 " - %" PRIu16 "\n", (uint16_t) *data, (uint16_t) *data); +} + +// +void PacketWrite32(Packet *packet, uint32_t value) +{ + TRACELOG(LOG_INFO, "Original: 0x%08" PRIX32 " - %" PRIu32 "\n", value, value); + uint8_t *data = packet->data + packet->offs; + *data++ = (uint8_t)(value >> 24); + *data++ = (uint8_t)(value >> 16); + *data++ = (uint8_t)(value >> 8); + *data++ = (uint8_t)(value); + packet->size += sizeof(uint32_t); + packet->offs += sizeof(uint32_t); + + TRACELOG(LOG_INFO, "Network: 0x%08" PRIX32 " - %" PRIu32 "\n", + (uint32_t)(((intptr_t) packet->data) - packet->offs), + (uint32_t)(((intptr_t) packet->data) - packet->offs)); +} + +// +void PacketWrite64(Packet *packet, uint64_t value) +{ + TRACELOG(LOG_INFO, "Original: 0x%016" PRIX64 " - %" PRIu64 "\n", value, value); + + uint8_t *data = packet->data + packet->offs; + *data++ = (uint8_t)(value >> 56); + *data++ = (uint8_t)(value >> 48); + *data++ = (uint8_t)(value >> 40); + *data++ = (uint8_t)(value >> 32); + *data++ = (uint8_t)(value >> 24); + *data++ = (uint8_t)(value >> 16); + *data++ = (uint8_t)(value >> 8); + *data++ = (uint8_t)(value); + packet->size += sizeof(uint64_t); + packet->offs += sizeof(uint64_t); + + TRACELOG(LOG_INFO, "Network: 0x%016" PRIX64 " - %" PRIu64 "\n", (uint64_t)(packet->data - packet->offs), (uint64_t)(packet->data - packet->offs)); +} + +// +uint16_t PacketRead16(Packet *packet) +{ + uint8_t *data = packet->data + packet->offs; + packet->size += sizeof(uint16_t); + packet->offs += sizeof(uint16_t); + uint16_t value = ((uint16_t) data[0] << 8) | data[1]; + TRACELOG(LOG_INFO, "Original: 0x%04" PRIX16 " - %" PRIu16 "\n", value, value); + + return value; +} + +// +uint32_t PacketRead32(Packet *packet) +{ + uint8_t *data = packet->data + packet->offs; + packet->size += sizeof(uint32_t); + packet->offs += sizeof(uint32_t); + uint32_t value = ((uint32_t) data[0] << 24) | ((uint32_t) data[1] << 16) | ((uint32_t) data[2] << 8) | data[3]; + TRACELOG(LOG_INFO, "Original: 0x%08" PRIX32 " - %" PRIu32 "\n", value, value); + + return value; +} + +// +uint64_t PacketRead64(Packet *packet) +{ + uint8_t *data = packet->data + packet->offs; + packet->size += sizeof(uint64_t); + packet->offs += sizeof(uint64_t); + uint64_t value = ((uint64_t) data[0] << 56) | ((uint64_t) data[1] << 48) | ((uint64_t) data[2] << 40) | ((uint64_t) data[3] << 32) | ((uint64_t) data[4] << 24) | ((uint64_t) data[5] << 16) | ((uint64_t) data[6] << 8) | data[7]; + TRACELOG(LOG_INFO, "Original: 0x%016" PRIX64 " - %" PRIu64 "\n", value, value); + + return value; +} + +#endif // RNET_IMPLEMENTATION \ No newline at end of file diff --git a/src/physac.h b/src/physac.h deleted file mode 100644 index 676a9695..00000000 --- a/src/physac.h +++ /dev/null @@ -1,1988 +0,0 @@ -/********************************************************************************************** -* -* Physac v1.1 - 2D Physics library for videogames -* -* DESCRIPTION: -* -* Physac is a small 2D physics engine written in pure C. The engine uses a fixed time-step thread loop -* to simluate physics. A physics step contains the following phases: get collision information, -* apply dynamics, collision solving and position correction. It uses a very simple struct for physic -* bodies with a position vector to be used in any 3D rendering API. -* -* CONFIGURATION: -* -* #define PHYSAC_IMPLEMENTATION -* Generates the implementation of the library into the included file. -* If not defined, the library is in header only mode and can be included in other headers -* or source files without problems. But only ONE file should hold the implementation. -* -* #define PHYSAC_STATIC (defined by default) -* The generated implementation will stay private inside implementation file and all -* internal symbols and functions will only be visible inside that file. -* -* #define PHYSAC_DEBUG -* Show debug traces log messages about physic bodies creation/destruction, physic system errors, -* some calculations results and NULL reference exceptions -* -* #define PHYSAC_DEFINE_VECTOR2_TYPE -* Forces library to define struct Vector2 data type (float x; float y) -* -* #define PHYSAC_AVOID_TIMMING_SYSTEM -* Disables internal timming system, used by UpdatePhysics() to launch timmed physic steps, -* it allows just running UpdatePhysics() automatically on a separate thread at a desired time step. -* In case physics steps update needs to be controlled by user with a custom timming mechanism, -* just define this flag and the internal timming mechanism will be avoided, in that case, -* timming libraries are neither required by the module. -* -* #define PHYSAC_MALLOC() -* #define PHYSAC_CALLOC() -* #define PHYSAC_FREE() -* You can define your own malloc/free implementation replacing stdlib.h malloc()/free() functions. -* Otherwise it will include stdlib.h and use the C standard library malloc()/free() function. -* -* COMPILATION: -* -* Use the following code to compile with GCC: -* gcc -o $(NAME_PART).exe $(FILE_NAME) -s -static -lraylib -lopengl32 -lgdi32 -lwinmm -std=c99 -* -* VERSIONS HISTORY: -* 1.1 (20-Jan-2021) @raysan5: Library general revision -* Removed threading system (up to the user) -* Support MSVC C++ compilation using CLITERAL() -* Review DEBUG mechanism for TRACELOG() and all TRACELOG() messages -* Review internal variables/functions naming for consistency -* Allow option to avoid internal timming system, to allow app manage the steps -* 1.0 (12-Jun-2017) First release of the library -* -* -* LICENSE: zlib/libpng -* -* Copyright (c) 2016-2021 Victor Fisac (@victorfisac) and Ramon Santamaria (@raysan5) -* -* This software is provided "as-is", without any express or implied warranty. In no event -* will the authors be held liable for any damages arising from the use of this software. -* -* Permission is granted to anyone to use this software for any purpose, including commercial -* applications, and to alter it and redistribute it freely, subject to the following restrictions: -* -* 1. The origin of this software must not be misrepresented; you must not claim that you -* wrote the original software. If you use this software in a product, an acknowledgment -* in the product documentation would be appreciated but is not required. -* -* 2. Altered source versions must be plainly marked as such, and must not be misrepresented -* as being the original software. -* -* 3. This notice may not be removed or altered from any source distribution. -* -**********************************************************************************************/ - -#if !defined(PHYSAC_H) -#define PHYSAC_H - -#if defined(PHYSAC_STATIC) - #define PHYSACDEF static // Functions just visible to module including this file -#else - #if defined(__cplusplus) - #define PHYSACDEF extern "C" // Functions visible from other files (no name mangling of functions in C++) - #else - #define PHYSACDEF extern // Functions visible from other files - #endif -#endif - -// Allow custom memory allocators -#ifndef PHYSAC_MALLOC - #define PHYSAC_MALLOC(size) malloc(size) -#endif -#ifndef PHYSAC_CALLOC - #define PHYSAC_CALLOC(size, n) calloc(size, n) -#endif -#ifndef PHYSAC_FREE - #define PHYSAC_FREE(ptr) free(ptr) -#endif - -//---------------------------------------------------------------------------------- -// Defines and Macros -//---------------------------------------------------------------------------------- -#define PHYSAC_MAX_BODIES 64 // Maximum number of physic bodies supported -#define PHYSAC_MAX_MANIFOLDS 4096 // Maximum number of physic bodies interactions (64x64) -#define PHYSAC_MAX_VERTICES 24 // Maximum number of vertex for polygons shapes -#define PHYSAC_DEFAULT_CIRCLE_VERTICES 24 // Default number of vertices for circle shapes - -#define PHYSAC_COLLISION_ITERATIONS 100 -#define PHYSAC_PENETRATION_ALLOWANCE 0.05f -#define PHYSAC_PENETRATION_CORRECTION 0.4f - -#define PHYSAC_PI 3.14159265358979323846f -#define PHYSAC_DEG2RAD (PHYSAC_PI/180.0f) - -//---------------------------------------------------------------------------------- -// Data Types Structure Definition -//---------------------------------------------------------------------------------- -#if defined(__STDC__) && __STDC_VERSION__ >= 199901L - #include -#endif - -typedef enum PhysicsShapeType { PHYSICS_CIRCLE = 0, PHYSICS_POLYGON } PhysicsShapeType; - -// Previously defined to be used in PhysicsShape struct as circular dependencies -typedef struct PhysicsBodyData *PhysicsBody; - -#if defined(PHYSAC_DEFINE_VECTOR2_TYPE) -// Vector2 type -typedef struct Vector2 { - float x; - float y; -} Vector2; -#endif - -// Matrix2x2 type (used for polygon shape rotation matrix) -typedef struct Matrix2x2 { - float m00; - float m01; - float m10; - float m11; -} Matrix2x2; - -typedef struct PhysicsVertexData { - unsigned int vertexCount; // Vertex count (positions and normals) - Vector2 positions[PHYSAC_MAX_VERTICES]; // Vertex positions vectors - Vector2 normals[PHYSAC_MAX_VERTICES]; // Vertex normals vectors -} PhysicsVertexData; - -typedef struct PhysicsShape { - PhysicsShapeType type; // Shape type (circle or polygon) - PhysicsBody body; // Shape physics body data pointer - PhysicsVertexData vertexData; // Shape vertices data (used for polygon shapes) - float radius; // Shape radius (used for circle shapes) - Matrix2x2 transform; // Vertices transform matrix 2x2 -} PhysicsShape; - -typedef struct PhysicsBodyData { - unsigned int id; // Unique identifier - bool enabled; // Enabled dynamics state (collisions are calculated anyway) - Vector2 position; // Physics body shape pivot - Vector2 velocity; // Current linear velocity applied to position - Vector2 force; // Current linear force (reset to 0 every step) - float angularVelocity; // Current angular velocity applied to orient - float torque; // Current angular force (reset to 0 every step) - float orient; // Rotation in radians - float inertia; // Moment of inertia - float inverseInertia; // Inverse value of inertia - float mass; // Physics body mass - float inverseMass; // Inverse value of mass - float staticFriction; // Friction when the body has not movement (0 to 1) - float dynamicFriction; // Friction when the body has movement (0 to 1) - float restitution; // Restitution coefficient of the body (0 to 1) - bool useGravity; // Apply gravity force to dynamics - bool isGrounded; // Physics grounded on other body state - bool freezeOrient; // Physics rotation constraint - PhysicsShape shape; // Physics body shape information (type, radius, vertices, transform) -} PhysicsBodyData; - -typedef struct PhysicsManifoldData { - unsigned int id; // Unique identifier - PhysicsBody bodyA; // Manifold first physics body reference - PhysicsBody bodyB; // Manifold second physics body reference - float penetration; // Depth of penetration from collision - Vector2 normal; // Normal direction vector from 'a' to 'b' - Vector2 contacts[2]; // Points of contact during collision - unsigned int contactsCount; // Current collision number of contacts - float restitution; // Mixed restitution during collision - float dynamicFriction; // Mixed dynamic friction during collision - float staticFriction; // Mixed static friction during collision -} PhysicsManifoldData, *PhysicsManifold; - -#if defined(__cplusplus) -extern "C" { // Prevents name mangling of functions -#endif - -//---------------------------------------------------------------------------------- -// Module Functions Declaration -//---------------------------------------------------------------------------------- -// Physics system management -PHYSACDEF void InitPhysics(void); // Initializes physics system -PHYSACDEF void UpdatePhysics(void); // Update physics system -PHYSACDEF void ResetPhysics(void); // Reset physics system (global variables) -PHYSACDEF void ClosePhysics(void); // Close physics system and unload used memory -PHYSACDEF void SetPhysicsTimeStep(double delta); // Sets physics fixed time step in milliseconds. 1.666666 by default -PHYSACDEF void SetPhysicsGravity(float x, float y); // Sets physics global gravity force - -// Physic body creation/destroy -PHYSACDEF PhysicsBody CreatePhysicsBodyCircle(Vector2 pos, float radius, float density); // Creates a new circle physics body with generic parameters -PHYSACDEF PhysicsBody CreatePhysicsBodyRectangle(Vector2 pos, float width, float height, float density); // Creates a new rectangle physics body with generic parameters -PHYSACDEF PhysicsBody CreatePhysicsBodyPolygon(Vector2 pos, float radius, int sides, float density); // Creates a new polygon physics body with generic parameters -PHYSACDEF void DestroyPhysicsBody(PhysicsBody body); // Destroy a physics body - -// Physic body forces -PHYSACDEF void PhysicsAddForce(PhysicsBody body, Vector2 force); // Adds a force to a physics body -PHYSACDEF void PhysicsAddTorque(PhysicsBody body, float amount); // Adds an angular force to a physics body -PHYSACDEF void PhysicsShatter(PhysicsBody body, Vector2 position, float force); // Shatters a polygon shape physics body to little physics bodies with explosion force -PHYSACDEF void SetPhysicsBodyRotation(PhysicsBody body, float radians); // Sets physics body shape transform based on radians parameter - -// Query physics info -PHYSACDEF PhysicsBody GetPhysicsBody(int index); // Returns a physics body of the bodies pool at a specific index -PHYSACDEF int GetPhysicsBodiesCount(void); // Returns the current amount of created physics bodies -PHYSACDEF int GetPhysicsShapeType(int index); // Returns the physics body shape type (PHYSICS_CIRCLE or PHYSICS_POLYGON) -PHYSACDEF int GetPhysicsShapeVerticesCount(int index); // Returns the amount of vertices of a physics body shape -PHYSACDEF Vector2 GetPhysicsShapeVertex(PhysicsBody body, int vertex); // Returns transformed position of a body shape (body position + vertex transformed position) - -#if defined(__cplusplus) -} -#endif - -#endif // PHYSAC_H - -/*********************************************************************************** -* -* PHYSAC IMPLEMENTATION -* -************************************************************************************/ - -#if defined(PHYSAC_IMPLEMENTATION) - -// Support TRACELOG macros -#if defined(PHYSAC_DEBUG) - #include // Required for: printf() - #define TRACELOG(...) printf(__VA_ARGS__) -#else - #define TRACELOG(...) (void)0; -#endif - -#include // Required for: malloc(), calloc(), free() -#include // Required for: cosf(), sinf(), fabs(), sqrtf() - -#if !defined(PHYSAC_AVOID_TIMMING_SYSTEM) - // 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); - #endif - #if defined(__linux__) || defined(__FreeBSD__) - #if _POSIX_C_SOURCE < 199309L - #undef _POSIX_C_SOURCE - #define _POSIX_C_SOURCE 199309L // Required for CLOCK_MONOTONIC if compiled with c99 without gnu ext. - #endif - #include // Required for: timespec - #endif - #if defined(__APPLE__) // macOS also defines __MACH__ - #include // Required for: mach_absolute_time() - #endif -#endif - -// NOTE: MSVC C++ compiler does not support compound literals (C99 feature) -// Plain structures in C++ (without constructors) can be initialized from { } initializers. -#if defined(__cplusplus) - #define CLITERAL(type) type -#else - #define CLITERAL(type) (type) -#endif - -//---------------------------------------------------------------------------------- -// Defines and Macros -//---------------------------------------------------------------------------------- -#define PHYSAC_MIN(a,b) (((a)<(b))?(a):(b)) -#define PHYSAC_MAX(a,b) (((a)>(b))?(a):(b)) -#define PHYSAC_FLT_MAX 3.402823466e+38f -#define PHYSAC_EPSILON 0.000001f -#define PHYSAC_K 1.0f/3.0f -#define PHYSAC_VECTOR_ZERO CLITERAL(Vector2){ 0.0f, 0.0f } - -//---------------------------------------------------------------------------------- -// Global Variables Definition -//---------------------------------------------------------------------------------- -static double deltaTime = 1.0/60.0/10.0 * 1000; // Delta time in milliseconds used for physics steps - -#if !defined(PHYSAC_AVOID_TIMMING_SYSTEM) -// Time measure variables -static double baseClockTicks = 0.0; // Offset clock ticks for MONOTONIC clock -static unsigned long long int frequency = 0; // Hi-res clock frequency -static double startTime = 0.0; // Start time in milliseconds -static double currentTime = 0.0; // Current time in milliseconds -#endif - -// Physics system configuration -static PhysicsBody bodies[PHYSAC_MAX_BODIES]; // Physics bodies pointers array -static unsigned int physicsBodiesCount = 0; // Physics world current bodies counter -static PhysicsManifold contacts[PHYSAC_MAX_MANIFOLDS]; // Physics bodies pointers array -static unsigned int physicsManifoldsCount = 0; // Physics world current manifolds counter - -static Vector2 gravityForce = { 0.0f, 9.81f }; // Physics world gravity force - -// Utilities variables -static unsigned int usedMemory = 0; // Total allocated dynamic memory - -//---------------------------------------------------------------------------------- -// Module Internal Functions Declaration -//---------------------------------------------------------------------------------- -#if !defined(PHYSAC_AVOID_TIMMING_SYSTEM) -// Timming measure functions -static void InitTimer(void); // Initializes hi-resolution MONOTONIC timer -static unsigned long long int GetClockTicks(void); // Get hi-res MONOTONIC time measure in mseconds -static double GetCurrentTime(void); // Get current time measure in milliseconds -#endif - -static void UpdatePhysicsStep(void); // Update physics step (dynamics, collisions and position corrections) - -static int FindAvailableBodyIndex(); // Finds a valid index for a new physics body initialization -static int FindAvailableManifoldIndex(); // Finds a valid index for a new manifold initialization -static PhysicsVertexData CreateDefaultPolygon(float radius, int sides); // Creates a random polygon shape with max vertex distance from polygon pivot -static PhysicsVertexData CreateRectanglePolygon(Vector2 pos, Vector2 size); // Creates a rectangle polygon shape based on a min and max positions - -static void InitializePhysicsManifolds(PhysicsManifold manifold); // Initializes physics manifolds to solve collisions -static PhysicsManifold CreatePhysicsManifold(PhysicsBody a, PhysicsBody b); // Creates a new physics manifold to solve collision -static void DestroyPhysicsManifold(PhysicsManifold manifold); // Unitializes and destroys a physics manifold - -static void SolvePhysicsManifold(PhysicsManifold manifold); // Solves a created physics manifold between two physics bodies -static void SolveCircleToCircle(PhysicsManifold manifold); // Solves collision between two circle shape physics bodies -static void SolveCircleToPolygon(PhysicsManifold manifold); // Solves collision between a circle to a polygon shape physics bodies -static void SolvePolygonToCircle(PhysicsManifold manifold); // Solves collision between a polygon to a circle shape physics bodies -static void SolvePolygonToPolygon(PhysicsManifold manifold); // Solves collision between two polygons shape physics bodies -static void IntegratePhysicsForces(PhysicsBody body); // Integrates physics forces into velocity -static void IntegratePhysicsVelocity(PhysicsBody body); // Integrates physics velocity into position and forces -static void IntegratePhysicsImpulses(PhysicsManifold manifold); // Integrates physics collisions impulses to solve collisions -static void CorrectPhysicsPositions(PhysicsManifold manifold); // Corrects physics bodies positions based on manifolds collision information -static void FindIncidentFace(Vector2 *v0, Vector2 *v1, PhysicsShape ref, PhysicsShape inc, int index); // Finds two polygon shapes incident face -static float FindAxisLeastPenetration(int *faceIndex, PhysicsShape shapeA, PhysicsShape shapeB); // Finds polygon shapes axis least penetration - -// Math required functions -static Vector2 MathVector2Product(Vector2 vector, float value); // Returns the product of a vector and a value -static float MathVector2CrossProduct(Vector2 v1, Vector2 v2); // Returns the cross product of two vectors -static float MathVector2SqrLen(Vector2 vector); // Returns the len square root of a vector -static float MathVector2DotProduct(Vector2 v1, Vector2 v2); // Returns the dot product of two vectors -static inline float MathVector2SqrDistance(Vector2 v1, Vector2 v2); // Returns the square root of distance between two vectors -static void MathVector2Normalize(Vector2 *vector); // Returns the normalized values of a vector -static Vector2 MathVector2Add(Vector2 v1, Vector2 v2); // Returns the sum of two given vectors -static Vector2 MathVector2Subtract(Vector2 v1, Vector2 v2); // Returns the subtract of two given vectors -static Matrix2x2 MathMatFromRadians(float radians); // Returns a matrix 2x2 from a given radians value -static inline Matrix2x2 MathMatTranspose(Matrix2x2 matrix); // Returns the transpose of a given matrix 2x2 -static inline Vector2 MathMatVector2Product(Matrix2x2 matrix, Vector2 vector); // Returns product between matrix 2x2 and vector -static int MathVector2Clip(Vector2 normal, Vector2 *faceA, Vector2 *faceB, float clip); // Returns clipping value based on a normal and two faces -static Vector2 MathTriangleBarycenter(Vector2 v1, Vector2 v2, Vector2 v3); // Returns the barycenter of a triangle given by 3 points - -//---------------------------------------------------------------------------------- -// Module Functions Definition -//---------------------------------------------------------------------------------- - -// Initializes physics values, pointers and creates physics loop thread -PHYSACDEF void InitPhysics(void) -{ -#if !defined(PHYSAC_AVOID_TIMMING_SYSTEM) - // Initialize high resolution timer - InitTimer(); -#endif - - TRACELOG("[PHYSAC] Physics module initialized successfully\n"); -} - -// Sets physics global gravity force -PHYSACDEF void SetPhysicsGravity(float x, float y) -{ - gravityForce.x = x; - gravityForce.y = y; -} - -// Creates a new circle physics body with generic parameters -PHYSACDEF PhysicsBody CreatePhysicsBodyCircle(Vector2 pos, float radius, float density) -{ - PhysicsBody body = CreatePhysicsBodyPolygon(pos, radius, PHYSAC_DEFAULT_CIRCLE_VERTICES, density); - return body; -} - -// Creates a new rectangle physics body with generic parameters -PHYSACDEF PhysicsBody CreatePhysicsBodyRectangle(Vector2 pos, float width, float height, float density) -{ - // NOTE: Make sure body data is initialized to 0 - PhysicsBody body = (PhysicsBody)PHYSAC_CALLOC(sizeof(PhysicsBodyData), 1); - usedMemory += sizeof(PhysicsBodyData); - - int id = FindAvailableBodyIndex(); - if (id != -1) - { - // Initialize new body with generic values - body->id = id; - body->enabled = true; - body->position = pos; - body->shape.type = PHYSICS_POLYGON; - body->shape.body = body; - body->shape.transform = MathMatFromRadians(0.0f); - body->shape.vertexData = CreateRectanglePolygon(pos, CLITERAL(Vector2){ width, height }); - - // Calculate centroid and moment of inertia - Vector2 center = { 0.0f, 0.0f }; - float area = 0.0f; - float inertia = 0.0f; - - for (unsigned int i = 0; i < body->shape.vertexData.vertexCount; i++) - { - // Triangle vertices, third vertex implied as (0, 0) - Vector2 p1 = body->shape.vertexData.positions[i]; - unsigned int nextIndex = (((i + 1) < body->shape.vertexData.vertexCount) ? (i + 1) : 0); - Vector2 p2 = body->shape.vertexData.positions[nextIndex]; - - float D = MathVector2CrossProduct(p1, p2); - float triangleArea = D/2; - - area += triangleArea; - - // Use area to weight the centroid average, not just vertex position - center.x += triangleArea*PHYSAC_K*(p1.x + p2.x); - center.y += triangleArea*PHYSAC_K*(p1.y + p2.y); - - float intx2 = p1.x*p1.x + p2.x*p1.x + p2.x*p2.x; - float inty2 = p1.y*p1.y + p2.y*p1.y + p2.y*p2.y; - inertia += (0.25f*PHYSAC_K*D)*(intx2 + inty2); - } - - center.x *= 1.0f/area; - center.y *= 1.0f/area; - - // Translate vertices to centroid (make the centroid (0, 0) for the polygon in model space) - // Note: this is not really necessary - for (unsigned int i = 0; i < body->shape.vertexData.vertexCount; i++) - { - body->shape.vertexData.positions[i].x -= center.x; - body->shape.vertexData.positions[i].y -= center.y; - } - - body->mass = density*area; - body->inverseMass = ((body->mass != 0.0f) ? 1.0f/body->mass : 0.0f); - body->inertia = density*inertia; - body->inverseInertia = ((body->inertia != 0.0f) ? 1.0f/body->inertia : 0.0f); - body->staticFriction = 0.4f; - body->dynamicFriction = 0.2f; - body->restitution = 0.0f; - body->useGravity = true; - body->isGrounded = false; - body->freezeOrient = false; - - // Add new body to bodies pointers array and update bodies count - bodies[physicsBodiesCount] = body; - physicsBodiesCount++; - - TRACELOG("[PHYSAC] Physic body created successfully (id: %i)\n", body->id); - } - else TRACELOG("[PHYSAC] Physic body could not be created, PHYSAC_MAX_BODIES reached\n"); - - return body; -} - -// Creates a new polygon physics body with generic parameters -PHYSACDEF PhysicsBody CreatePhysicsBodyPolygon(Vector2 pos, float radius, int sides, float density) -{ - PhysicsBody body = (PhysicsBody)PHYSAC_MALLOC(sizeof(PhysicsBodyData)); - usedMemory += sizeof(PhysicsBodyData); - - int id = FindAvailableBodyIndex(); - if (id != -1) - { - // Initialize new body with generic values - body->id = id; - body->enabled = true; - body->position = pos; - body->velocity = PHYSAC_VECTOR_ZERO; - body->force = PHYSAC_VECTOR_ZERO; - body->angularVelocity = 0.0f; - body->torque = 0.0f; - body->orient = 0.0f; - body->shape.type = PHYSICS_POLYGON; - body->shape.body = body; - body->shape.transform = MathMatFromRadians(0.0f); - body->shape.vertexData = CreateDefaultPolygon(radius, sides); - - // Calculate centroid and moment of inertia - Vector2 center = { 0.0f, 0.0f }; - float area = 0.0f; - float inertia = 0.0f; - - for (unsigned int i = 0; i < body->shape.vertexData.vertexCount; i++) - { - // Triangle vertices, third vertex implied as (0, 0) - Vector2 position1 = body->shape.vertexData.positions[i]; - unsigned int nextIndex = (((i + 1) < body->shape.vertexData.vertexCount) ? (i + 1) : 0); - Vector2 position2 = body->shape.vertexData.positions[nextIndex]; - - float cross = MathVector2CrossProduct(position1, position2); - float triangleArea = cross/2; - - area += triangleArea; - - // Use area to weight the centroid average, not just vertex position - center.x += triangleArea*PHYSAC_K*(position1.x + position2.x); - center.y += triangleArea*PHYSAC_K*(position1.y + position2.y); - - float intx2 = position1.x*position1.x + position2.x*position1.x + position2.x*position2.x; - float inty2 = position1.y*position1.y + position2.y*position1.y + position2.y*position2.y; - inertia += (0.25f*PHYSAC_K*cross)*(intx2 + inty2); - } - - center.x *= 1.0f/area; - center.y *= 1.0f/area; - - // Translate vertices to centroid (make the centroid (0, 0) for the polygon in model space) - // Note: this is not really necessary - for (unsigned int i = 0; i < body->shape.vertexData.vertexCount; i++) - { - body->shape.vertexData.positions[i].x -= center.x; - body->shape.vertexData.positions[i].y -= center.y; - } - - body->mass = density*area; - body->inverseMass = ((body->mass != 0.0f) ? 1.0f/body->mass : 0.0f); - body->inertia = density*inertia; - body->inverseInertia = ((body->inertia != 0.0f) ? 1.0f/body->inertia : 0.0f); - body->staticFriction = 0.4f; - body->dynamicFriction = 0.2f; - body->restitution = 0.0f; - body->useGravity = true; - body->isGrounded = false; - body->freezeOrient = false; - - // Add new body to bodies pointers array and update bodies count - bodies[physicsBodiesCount] = body; - physicsBodiesCount++; - - TRACELOG("[PHYSAC] Physic body created successfully (id: %i)\n", body->id); - } - else TRACELOG("[PHYSAC] Physics body could not be created, PHYSAC_MAX_BODIES reached\n"); - - return body; -} - -// Adds a force to a physics body -PHYSACDEF void PhysicsAddForce(PhysicsBody body, Vector2 force) -{ - if (body != NULL) body->force = MathVector2Add(body->force, force); -} - -// Adds an angular force to a physics body -PHYSACDEF void PhysicsAddTorque(PhysicsBody body, float amount) -{ - if (body != NULL) body->torque += amount; -} - -// Shatters a polygon shape physics body to little physics bodies with explosion force -PHYSACDEF void PhysicsShatter(PhysicsBody body, Vector2 position, float force) -{ - if (body != NULL) - { - if (body->shape.type == PHYSICS_POLYGON) - { - PhysicsVertexData vertexData = body->shape.vertexData; - bool collision = false; - - for (unsigned int i = 0; i < vertexData.vertexCount; i++) - { - Vector2 positionA = body->position; - Vector2 positionB = MathMatVector2Product(body->shape.transform, MathVector2Add(body->position, vertexData.positions[i])); - unsigned int nextIndex = (((i + 1) < vertexData.vertexCount) ? (i + 1) : 0); - Vector2 positionC = MathMatVector2Product(body->shape.transform, MathVector2Add(body->position, vertexData.positions[nextIndex])); - - // Check collision between each triangle - float alpha = ((positionB.y - positionC.y)*(position.x - positionC.x) + (positionC.x - positionB.x)*(position.y - positionC.y))/ - ((positionB.y - positionC.y)*(positionA.x - positionC.x) + (positionC.x - positionB.x)*(positionA.y - positionC.y)); - - float beta = ((positionC.y - positionA.y)*(position.x - positionC.x) + (positionA.x - positionC.x)*(position.y - positionC.y))/ - ((positionB.y - positionC.y)*(positionA.x - positionC.x) + (positionC.x - positionB.x)*(positionA.y - positionC.y)); - - float gamma = 1.0f - alpha - beta; - - if ((alpha > 0.0f) && (beta > 0.0f) & (gamma > 0.0f)) - { - collision = true; - break; - } - } - - if (collision) - { - int count = vertexData.vertexCount; - Vector2 bodyPos = body->position; - Vector2 *vertices = (Vector2 *)PHYSAC_MALLOC(sizeof(Vector2)*count); - Matrix2x2 trans = body->shape.transform; - for (int i = 0; i < count; i++) vertices[i] = vertexData.positions[i]; - - // Destroy shattered physics body - DestroyPhysicsBody(body); - - for (int i = 0; i < count; i++) - { - int nextIndex = (((i + 1) < count) ? (i + 1) : 0); - Vector2 center = MathTriangleBarycenter(vertices[i], vertices[nextIndex], PHYSAC_VECTOR_ZERO); - center = MathVector2Add(bodyPos, center); - Vector2 offset = MathVector2Subtract(center, bodyPos); - - PhysicsBody body = CreatePhysicsBodyPolygon(center, 10, 3, 10); // Create polygon physics body with relevant values - - PhysicsVertexData vertexData = { 0 }; - vertexData.vertexCount = 3; - - vertexData.positions[0] = MathVector2Subtract(vertices[i], offset); - vertexData.positions[1] = MathVector2Subtract(vertices[nextIndex], offset); - vertexData.positions[2] = MathVector2Subtract(position, center); - - // Separate vertices to avoid unnecessary physics collisions - vertexData.positions[0].x *= 0.95f; - vertexData.positions[0].y *= 0.95f; - vertexData.positions[1].x *= 0.95f; - vertexData.positions[1].y *= 0.95f; - vertexData.positions[2].x *= 0.95f; - vertexData.positions[2].y *= 0.95f; - - // Calculate polygon faces normals - for (unsigned int j = 0; j < vertexData.vertexCount; j++) - { - unsigned int nextVertex = (((j + 1) < vertexData.vertexCount) ? (j + 1) : 0); - Vector2 face = MathVector2Subtract(vertexData.positions[nextVertex], vertexData.positions[j]); - - vertexData.normals[j] = CLITERAL(Vector2){ face.y, -face.x }; - MathVector2Normalize(&vertexData.normals[j]); - } - - // Apply computed vertex data to new physics body shape - body->shape.vertexData = vertexData; - body->shape.transform = trans; - - // Calculate centroid and moment of inertia - center = PHYSAC_VECTOR_ZERO; - float area = 0.0f; - float inertia = 0.0f; - - for (unsigned int j = 0; j < body->shape.vertexData.vertexCount; j++) - { - // Triangle vertices, third vertex implied as (0, 0) - Vector2 p1 = body->shape.vertexData.positions[j]; - unsigned int nextVertex = (((j + 1) < body->shape.vertexData.vertexCount) ? (j + 1) : 0); - Vector2 p2 = body->shape.vertexData.positions[nextVertex]; - - float D = MathVector2CrossProduct(p1, p2); - float triangleArea = D/2; - - area += triangleArea; - - // Use area to weight the centroid average, not just vertex position - center.x += triangleArea*PHYSAC_K*(p1.x + p2.x); - center.y += triangleArea*PHYSAC_K*(p1.y + p2.y); - - float intx2 = p1.x*p1.x + p2.x*p1.x + p2.x*p2.x; - float inty2 = p1.y*p1.y + p2.y*p1.y + p2.y*p2.y; - inertia += (0.25f*PHYSAC_K*D)*(intx2 + inty2); - } - - center.x *= 1.0f/area; - center.y *= 1.0f/area; - - body->mass = area; - body->inverseMass = ((body->mass != 0.0f) ? 1.0f/body->mass : 0.0f); - body->inertia = inertia; - body->inverseInertia = ((body->inertia != 0.0f) ? 1.0f/body->inertia : 0.0f); - - // Calculate explosion force direction - Vector2 pointA = body->position; - Vector2 pointB = MathVector2Subtract(vertexData.positions[1], vertexData.positions[0]); - pointB.x /= 2.0f; - pointB.y /= 2.0f; - Vector2 forceDirection = MathVector2Subtract(MathVector2Add(pointA, MathVector2Add(vertexData.positions[0], pointB)), body->position); - MathVector2Normalize(&forceDirection); - forceDirection.x *= force; - forceDirection.y *= force; - - // Apply force to new physics body - PhysicsAddForce(body, forceDirection); - } - - PHYSAC_FREE(vertices); - } - } - } - else TRACELOG("[PHYSAC] WARNING: PhysicsShatter: NULL physic body\n"); -} - -// Returns the current amount of created physics bodies -PHYSACDEF int GetPhysicsBodiesCount(void) -{ - return physicsBodiesCount; -} - -// Returns a physics body of the bodies pool at a specific index -PHYSACDEF PhysicsBody GetPhysicsBody(int index) -{ - PhysicsBody body = NULL; - - if (index < (int)physicsBodiesCount) - { - body = bodies[index]; - - if (body == NULL) TRACELOG("[PHYSAC] WARNING: GetPhysicsBody: NULL physic body\n"); - } - else TRACELOG("[PHYSAC] WARNING: Physic body index is out of bounds\n"); - - return body; -} - -// Returns the physics body shape type (PHYSICS_CIRCLE or PHYSICS_POLYGON) -PHYSACDEF int GetPhysicsShapeType(int index) -{ - int result = -1; - - if (index < (int)physicsBodiesCount) - { - PhysicsBody body = bodies[index]; - - if (body != NULL) result = body->shape.type; - else TRACELOG("[PHYSAC] WARNING: GetPhysicsShapeType: NULL physic body\n"); - } - else TRACELOG("[PHYSAC] WARNING: Physic body index is out of bounds\n"); - - return result; -} - -// Returns the amount of vertices of a physics body shape -PHYSACDEF int GetPhysicsShapeVerticesCount(int index) -{ - int result = 0; - - if (index < (int)physicsBodiesCount) - { - PhysicsBody body = bodies[index]; - - if (body != NULL) - { - switch (body->shape.type) - { - case PHYSICS_CIRCLE: result = PHYSAC_DEFAULT_CIRCLE_VERTICES; break; - case PHYSICS_POLYGON: result = body->shape.vertexData.vertexCount; break; - default: break; - } - } - else TRACELOG("[PHYSAC] WARNING: GetPhysicsShapeVerticesCount: NULL physic body\n"); - } - else TRACELOG("[PHYSAC] WARNING: Physic body index is out of bounds\n"); - - return result; -} - -// Returns transformed position of a body shape (body position + vertex transformed position) -PHYSACDEF Vector2 GetPhysicsShapeVertex(PhysicsBody body, int vertex) -{ - Vector2 position = { 0.0f, 0.0f }; - - if (body != NULL) - { - switch (body->shape.type) - { - case PHYSICS_CIRCLE: - { - position.x = body->position.x + cosf(360.0f/PHYSAC_DEFAULT_CIRCLE_VERTICES*vertex*PHYSAC_DEG2RAD)*body->shape.radius; - position.y = body->position.y + sinf(360.0f/PHYSAC_DEFAULT_CIRCLE_VERTICES*vertex*PHYSAC_DEG2RAD)*body->shape.radius; - } break; - case PHYSICS_POLYGON: - { - PhysicsVertexData vertexData = body->shape.vertexData; - position = MathVector2Add(body->position, MathMatVector2Product(body->shape.transform, vertexData.positions[vertex])); - } break; - default: break; - } - } - else TRACELOG("[PHYSAC] WARNING: GetPhysicsShapeVertex: NULL physic body\n"); - - return position; -} - -// Sets physics body shape transform based on radians parameter -PHYSACDEF void SetPhysicsBodyRotation(PhysicsBody body, float radians) -{ - if (body != NULL) - { - body->orient = radians; - - if (body->shape.type == PHYSICS_POLYGON) body->shape.transform = MathMatFromRadians(radians); - } -} - -// Unitializes and destroys a physics body -PHYSACDEF void DestroyPhysicsBody(PhysicsBody body) -{ - if (body != NULL) - { - int id = body->id; - int index = -1; - - for (unsigned int i = 0; i < physicsBodiesCount; i++) - { - if (bodies[i]->id == id) - { - index = i; - break; - } - } - - if (index == -1) - { - TRACELOG("[PHYSAC] WARNING: Requested body (id: %i) can not be found\n", id); - return; // Prevent access to index -1 - } - - // Free body allocated memory - PHYSAC_FREE(body); - usedMemory -= sizeof(PhysicsBodyData); - bodies[index] = NULL; - - // Reorder physics bodies pointers array and its catched index - for (unsigned int i = index; i < physicsBodiesCount; i++) - { - if ((i + 1) < physicsBodiesCount) bodies[i] = bodies[i + 1]; - } - - // Update physics bodies count - physicsBodiesCount--; - - TRACELOG("[PHYSAC] Physic body destroyed successfully (id: %i)\n", id); - } - else TRACELOG("[PHYSAC] WARNING: DestroyPhysicsBody: NULL physic body\n"); -} - -// Destroys created physics bodies and manifolds and resets global values -PHYSACDEF void ResetPhysics(void) -{ - if (physicsBodiesCount > 0) - { - // Unitialize physics bodies dynamic memory allocations - for (int i = physicsBodiesCount - 1; i >= 0; i--) - { - PhysicsBody body = bodies[i]; - - if (body != NULL) - { - PHYSAC_FREE(body); - bodies[i] = NULL; - usedMemory -= sizeof(PhysicsBodyData); - } - } - - physicsBodiesCount = 0; - } - - if (physicsManifoldsCount > 0) - { - // Unitialize physics manifolds dynamic memory allocations - for (int i = physicsManifoldsCount - 1; i >= 0; i--) - { - PhysicsManifold manifold = contacts[i]; - - if (manifold != NULL) - { - PHYSAC_FREE(manifold); - contacts[i] = NULL; - usedMemory -= sizeof(PhysicsManifoldData); - } - } - - physicsManifoldsCount = 0; - } - - TRACELOG("[PHYSAC] Physics module reseted successfully\n"); -} - -// Unitializes physics pointers and exits physics loop thread -PHYSACDEF void ClosePhysics(void) -{ - // Unitialize physics manifolds dynamic memory allocations - if (physicsManifoldsCount > 0) - { - for (unsigned int i = physicsManifoldsCount - 1; i >= 0; i--) - DestroyPhysicsManifold(contacts[i]); - } - - // Unitialize physics bodies dynamic memory allocations - if (physicsBodiesCount > 0) - { - for (unsigned int i = physicsBodiesCount - 1; i >= 0; i--) - DestroyPhysicsBody(bodies[i]); - } - - // Trace log info - if ((physicsBodiesCount > 0) || (usedMemory != 0)) - { - TRACELOG("[PHYSAC] WARNING: Physics module closed with unallocated bodies (BODIES: %i, MEMORY: %i bytes)\n", physicsBodiesCount, usedMemory); - } - else if ((physicsManifoldsCount > 0) || (usedMemory != 0)) - { - TRACELOG("[PHYSAC] WARNING: Pysics module closed with unallocated manifolds (MANIFOLDS: %i, MEMORY: %i bytes)\n", physicsManifoldsCount, usedMemory); - } - else TRACELOG("[PHYSAC] Physics module closed successfully\n"); -} - -//---------------------------------------------------------------------------------- -// Module Internal Functions Definition -//---------------------------------------------------------------------------------- -// Finds a valid index for a new physics body initialization -static int FindAvailableBodyIndex() -{ - int index = -1; - for (int i = 0; i < PHYSAC_MAX_BODIES; i++) - { - int currentId = i; - - // Check if current id already exist in other physics body - for (unsigned int k = 0; k < physicsBodiesCount; k++) - { - if (bodies[k]->id == currentId) - { - currentId++; - break; - } - } - - // If it is not used, use it as new physics body id - if (currentId == (int)i) - { - index = (int)i; - break; - } - } - - return index; -} - -// Creates a default polygon shape with max vertex distance from polygon pivot -static PhysicsVertexData CreateDefaultPolygon(float radius, int sides) -{ - PhysicsVertexData data = { 0 }; - data.vertexCount = sides; - - // Calculate polygon vertices positions - for (unsigned int i = 0; i < data.vertexCount; i++) - { - data.positions[i].x = (float)cosf(360.0f/sides*i*PHYSAC_DEG2RAD)*radius; - data.positions[i].y = (float)sinf(360.0f/sides*i*PHYSAC_DEG2RAD)*radius; - } - - // Calculate polygon faces normals - for (int i = 0; i < (int)data.vertexCount; i++) - { - int nextIndex = (((i + 1) < sides) ? (i + 1) : 0); - Vector2 face = MathVector2Subtract(data.positions[nextIndex], data.positions[i]); - - data.normals[i] = CLITERAL(Vector2){ face.y, -face.x }; - MathVector2Normalize(&data.normals[i]); - } - - return data; -} - -// Creates a rectangle polygon shape based on a min and max positions -static PhysicsVertexData CreateRectanglePolygon(Vector2 pos, Vector2 size) -{ - PhysicsVertexData data = { 0 }; - data.vertexCount = 4; - - // Calculate polygon vertices positions - data.positions[0] = CLITERAL(Vector2){ pos.x + size.x/2, pos.y - size.y/2 }; - data.positions[1] = CLITERAL(Vector2){ pos.x + size.x/2, pos.y + size.y/2 }; - data.positions[2] = CLITERAL(Vector2){ pos.x - size.x/2, pos.y + size.y/2 }; - data.positions[3] = CLITERAL(Vector2){ pos.x - size.x/2, pos.y - size.y/2 }; - - // Calculate polygon faces normals - for (unsigned int i = 0; i < data.vertexCount; i++) - { - int nextIndex = (((i + 1) < data.vertexCount) ? (i + 1) : 0); - Vector2 face = MathVector2Subtract(data.positions[nextIndex], data.positions[i]); - - data.normals[i] = CLITERAL(Vector2){ face.y, -face.x }; - MathVector2Normalize(&data.normals[i]); - } - - return data; -} - -// Update physics step (dynamics, collisions and position corrections) -void UpdatePhysicsStep(void) -{ - // Clear previous generated collisions information - for (int i = (int)physicsManifoldsCount - 1; i >= 0; i--) - { - PhysicsManifold manifold = contacts[i]; - if (manifold != NULL) DestroyPhysicsManifold(manifold); - } - - // Reset physics bodies grounded state - for (unsigned int i = 0; i < physicsBodiesCount; i++) - { - PhysicsBody body = bodies[i]; - body->isGrounded = false; - } - - // Generate new collision information - for (unsigned int i = 0; i < physicsBodiesCount; i++) - { - PhysicsBody bodyA = bodies[i]; - - if (bodyA != NULL) - { - for (unsigned int j = i + 1; j < physicsBodiesCount; j++) - { - PhysicsBody bodyB = bodies[j]; - - if (bodyB != NULL) - { - if ((bodyA->inverseMass == 0) && (bodyB->inverseMass == 0)) continue; - - PhysicsManifold manifold = CreatePhysicsManifold(bodyA, bodyB); - SolvePhysicsManifold(manifold); - - if (manifold->contactsCount > 0) - { - // Create a new manifold with same information as previously solved manifold and add it to the manifolds pool last slot - PhysicsManifold manifold = CreatePhysicsManifold(bodyA, bodyB); - manifold->penetration = manifold->penetration; - manifold->normal = manifold->normal; - manifold->contacts[0] = manifold->contacts[0]; - manifold->contacts[1] = manifold->contacts[1]; - manifold->contactsCount = manifold->contactsCount; - manifold->restitution = manifold->restitution; - manifold->dynamicFriction = manifold->dynamicFriction; - manifold->staticFriction = manifold->staticFriction; - } - } - } - } - } - - // Integrate forces to physics bodies - for (unsigned int i = 0; i < physicsBodiesCount; i++) - { - PhysicsBody body = bodies[i]; - if (body != NULL) IntegratePhysicsForces(body); - } - - // Initialize physics manifolds to solve collisions - for (unsigned int i = 0; i < physicsManifoldsCount; i++) - { - PhysicsManifold manifold = contacts[i]; - if (manifold != NULL) InitializePhysicsManifolds(manifold); - } - - // Integrate physics collisions impulses to solve collisions - for (unsigned int i = 0; i < PHYSAC_COLLISION_ITERATIONS; i++) - { - for (unsigned int j = 0; j < physicsManifoldsCount; j++) - { - PhysicsManifold manifold = contacts[i]; - if (manifold != NULL) IntegratePhysicsImpulses(manifold); - } - } - - // Integrate velocity to physics bodies - for (unsigned int i = 0; i < physicsBodiesCount; i++) - { - PhysicsBody body = bodies[i]; - if (body != NULL) IntegratePhysicsVelocity(body); - } - - // Correct physics bodies positions based on manifolds collision information - for (unsigned int i = 0; i < physicsManifoldsCount; i++) - { - PhysicsManifold manifold = contacts[i]; - if (manifold != NULL) CorrectPhysicsPositions(manifold); - } - - // Clear physics bodies forces - for (unsigned int i = 0; i < physicsBodiesCount; i++) - { - PhysicsBody body = bodies[i]; - if (body != NULL) - { - body->force = PHYSAC_VECTOR_ZERO; - body->torque = 0.0f; - } - } -} - -// Update physics system -// Physics steps are launched at a fixed time step if enabled -PHYSACDEF void UpdatePhysics(void) -{ -#if !defined(PHYSAC_AVOID_TIMMING_SYSTEM) - static double deltaTimeAccumulator = 0.0; - - // Calculate current time (ms) - currentTime = GetCurrentTime(); - - // Calculate current delta time (ms) - const double delta = currentTime - startTime; - - // Store the time elapsed since the last frame began - deltaTimeAccumulator += delta; - - // Fixed time stepping loop - while (deltaTimeAccumulator >= deltaTime) - { - UpdatePhysicsStep(); - deltaTimeAccumulator -= deltaTime; - } - - // Record the starting of this frame - startTime = currentTime; -#else - UpdatePhysicsStep(); -#endif -} - -PHYSACDEF void SetPhysicsTimeStep(double delta) -{ - deltaTime = delta; -} - -// Finds a valid index for a new manifold initialization -static int FindAvailableManifoldIndex() -{ - int index = -1; - for (int i = 0; i < PHYSAC_MAX_MANIFOLDS; i++) - { - int currentId = i; - - // Check if current id already exist in other physics body - for (unsigned int k = 0; k < physicsManifoldsCount; k++) - { - if (contacts[k]->id == currentId) - { - currentId++; - break; - } - } - - // If it is not used, use it as new physics body id - if (currentId == i) - { - index = i; - break; - } - } - - return index; -} - -// Creates a new physics manifold to solve collision -static PhysicsManifold CreatePhysicsManifold(PhysicsBody a, PhysicsBody b) -{ - PhysicsManifold manifold = (PhysicsManifold)PHYSAC_MALLOC(sizeof(PhysicsManifoldData)); - usedMemory += sizeof(PhysicsManifoldData); - - int id = FindAvailableManifoldIndex(); - if (id != -1) - { - // Initialize new manifold with generic values - manifold->id = id; - manifold->bodyA = a; - manifold->bodyB = b; - manifold->penetration = 0; - manifold->normal = PHYSAC_VECTOR_ZERO; - manifold->contacts[0] = PHYSAC_VECTOR_ZERO; - manifold->contacts[1] = PHYSAC_VECTOR_ZERO; - manifold->contactsCount = 0; - manifold->restitution = 0.0f; - manifold->dynamicFriction = 0.0f; - manifold->staticFriction = 0.0f; - - // Add new body to bodies pointers array and update bodies count - contacts[physicsManifoldsCount] = manifold; - physicsManifoldsCount++; - } - else TRACELOG("[PHYSAC] Physic manifold could not be created, PHYSAC_MAX_MANIFOLDS reached\n"); - - return manifold; -} - -// Unitializes and destroys a physics manifold -static void DestroyPhysicsManifold(PhysicsManifold manifold) -{ - if (manifold != NULL) - { - int id = manifold->id; - int index = -1; - - for (unsigned int i = 0; i < physicsManifoldsCount; i++) - { - if (contacts[i]->id == id) - { - index = i; - break; - } - } - - if (index == -1) return; // Prevent access to index -1 - - // Free manifold allocated memory - PHYSAC_FREE(manifold); - usedMemory -= sizeof(PhysicsManifoldData); - contacts[index] = NULL; - - // Reorder physics manifolds pointers array and its catched index - for (unsigned int i = index; i < physicsManifoldsCount; i++) - { - if ((i + 1) < physicsManifoldsCount) contacts[i] = contacts[i + 1]; - } - - // Update physics manifolds count - physicsManifoldsCount--; - } - else TRACELOG("[PHYSAC] WARNING: DestroyPhysicsManifold: NULL physic manifold\n"); -} - -// Solves a created physics manifold between two physics bodies -static void SolvePhysicsManifold(PhysicsManifold manifold) -{ - switch (manifold->bodyA->shape.type) - { - case PHYSICS_CIRCLE: - { - switch (manifold->bodyB->shape.type) - { - case PHYSICS_CIRCLE: SolveCircleToCircle(manifold); break; - case PHYSICS_POLYGON: SolveCircleToPolygon(manifold); break; - default: break; - } - } break; - case PHYSICS_POLYGON: - { - switch (manifold->bodyB->shape.type) - { - case PHYSICS_CIRCLE: SolvePolygonToCircle(manifold); break; - case PHYSICS_POLYGON: SolvePolygonToPolygon(manifold); break; - default: break; - } - } break; - default: break; - } - - // Update physics body grounded state if normal direction is down and grounded state is not set yet in previous manifolds - if (!manifold->bodyB->isGrounded) manifold->bodyB->isGrounded = (manifold->normal.y < 0); -} - -// Solves collision between two circle shape physics bodies -static void SolveCircleToCircle(PhysicsManifold manifold) -{ - PhysicsBody bodyA = manifold->bodyA; - PhysicsBody bodyB = manifold->bodyB; - - if ((bodyA == NULL) || (bodyB == NULL)) return; - - // Calculate translational vector, which is normal - Vector2 normal = MathVector2Subtract(bodyB->position, bodyA->position); - - float distSqr = MathVector2SqrLen(normal); - float radius = bodyA->shape.radius + bodyB->shape.radius; - - // Check if circles are not in contact - if (distSqr >= radius*radius) - { - manifold->contactsCount = 0; - return; - } - - float distance = sqrtf(distSqr); - manifold->contactsCount = 1; - - if (distance == 0.0f) - { - manifold->penetration = bodyA->shape.radius; - manifold->normal = CLITERAL(Vector2){ 1.0f, 0.0f }; - manifold->contacts[0] = bodyA->position; - } - else - { - manifold->penetration = radius - distance; - manifold->normal = CLITERAL(Vector2){ normal.x/distance, normal.y/distance }; // Faster than using MathVector2Normalize() due to sqrt is already performed - manifold->contacts[0] = CLITERAL(Vector2){ manifold->normal.x*bodyA->shape.radius + bodyA->position.x, manifold->normal.y*bodyA->shape.radius + bodyA->position.y }; - } - - // Update physics body grounded state if normal direction is down - if (!bodyA->isGrounded) bodyA->isGrounded = (manifold->normal.y < 0); -} - -// Solves collision between a circle to a polygon shape physics bodies -static void SolveCircleToPolygon(PhysicsManifold manifold) -{ - PhysicsBody bodyA = manifold->bodyA; - PhysicsBody bodyB = manifold->bodyB; - - if ((bodyA == NULL) || (bodyB == NULL)) return; - - manifold->contactsCount = 0; - - // Transform circle center to polygon transform space - Vector2 center = bodyA->position; - center = MathMatVector2Product(MathMatTranspose(bodyB->shape.transform), MathVector2Subtract(center, bodyB->position)); - - // Find edge with minimum penetration - // It is the same concept as using support points in SolvePolygonToPolygon - float separation = -PHYSAC_FLT_MAX; - int faceNormal = 0; - PhysicsVertexData vertexData = bodyB->shape.vertexData; - - for (unsigned int i = 0; i < vertexData.vertexCount; i++) - { - float currentSeparation = MathVector2DotProduct(vertexData.normals[i], MathVector2Subtract(center, vertexData.positions[i])); - - if (currentSeparation > bodyA->shape.radius) return; - - if (currentSeparation > separation) - { - separation = currentSeparation; - faceNormal = i; - } - } - - // Grab face's vertices - Vector2 v1 = vertexData.positions[faceNormal]; - int nextIndex = (((faceNormal + 1) < (int)vertexData.vertexCount) ? (faceNormal + 1) : 0); - Vector2 v2 = vertexData.positions[nextIndex]; - - // Check to see if center is within polygon - if (separation < PHYSAC_EPSILON) - { - manifold->contactsCount = 1; - Vector2 normal = MathMatVector2Product(bodyB->shape.transform, vertexData.normals[faceNormal]); - manifold->normal = CLITERAL(Vector2){ -normal.x, -normal.y }; - manifold->contacts[0] = CLITERAL(Vector2){ manifold->normal.x*bodyA->shape.radius + bodyA->position.x, manifold->normal.y*bodyA->shape.radius + bodyA->position.y }; - manifold->penetration = bodyA->shape.radius; - return; - } - - // Determine which voronoi region of the edge center of circle lies within - float dot1 = MathVector2DotProduct(MathVector2Subtract(center, v1), MathVector2Subtract(v2, v1)); - float dot2 = MathVector2DotProduct(MathVector2Subtract(center, v2), MathVector2Subtract(v1, v2)); - manifold->penetration = bodyA->shape.radius - separation; - - if (dot1 <= 0.0f) // Closest to v1 - { - if (MathVector2SqrDistance(center, v1) > bodyA->shape.radius*bodyA->shape.radius) return; - - manifold->contactsCount = 1; - Vector2 normal = MathVector2Subtract(v1, center); - normal = MathMatVector2Product(bodyB->shape.transform, normal); - MathVector2Normalize(&normal); - manifold->normal = normal; - v1 = MathMatVector2Product(bodyB->shape.transform, v1); - v1 = MathVector2Add(v1, bodyB->position); - manifold->contacts[0] = v1; - } - else if (dot2 <= 0.0f) // Closest to v2 - { - if (MathVector2SqrDistance(center, v2) > bodyA->shape.radius*bodyA->shape.radius) return; - - manifold->contactsCount = 1; - Vector2 normal = MathVector2Subtract(v2, center); - v2 = MathMatVector2Product(bodyB->shape.transform, v2); - v2 = MathVector2Add(v2, bodyB->position); - manifold->contacts[0] = v2; - normal = MathMatVector2Product(bodyB->shape.transform, normal); - MathVector2Normalize(&normal); - manifold->normal = normal; - } - else // Closest to face - { - Vector2 normal = vertexData.normals[faceNormal]; - - if (MathVector2DotProduct(MathVector2Subtract(center, v1), normal) > bodyA->shape.radius) return; - - normal = MathMatVector2Product(bodyB->shape.transform, normal); - manifold->normal = CLITERAL(Vector2){ -normal.x, -normal.y }; - manifold->contacts[0] = CLITERAL(Vector2){ manifold->normal.x*bodyA->shape.radius + bodyA->position.x, manifold->normal.y*bodyA->shape.radius + bodyA->position.y }; - manifold->contactsCount = 1; - } -} - -// Solves collision between a polygon to a circle shape physics bodies -static void SolvePolygonToCircle(PhysicsManifold manifold) -{ - PhysicsBody bodyA = manifold->bodyA; - PhysicsBody bodyB = manifold->bodyB; - - if ((bodyA == NULL) || (bodyB == NULL)) return; - - manifold->bodyA = bodyB; - manifold->bodyB = bodyA; - SolveCircleToPolygon(manifold); - - manifold->normal.x *= -1.0f; - manifold->normal.y *= -1.0f; -} - -// Solves collision between two polygons shape physics bodies -static void SolvePolygonToPolygon(PhysicsManifold manifold) -{ - if ((manifold->bodyA == NULL) || (manifold->bodyB == NULL)) return; - - PhysicsShape bodyA = manifold->bodyA->shape; - PhysicsShape bodyB = manifold->bodyB->shape; - manifold->contactsCount = 0; - - // Check for separating axis with A shape's face planes - int faceA = 0; - float penetrationA = FindAxisLeastPenetration(&faceA, bodyA, bodyB); - if (penetrationA >= 0.0f) return; - - // Check for separating axis with B shape's face planes - int faceB = 0; - float penetrationB = FindAxisLeastPenetration(&faceB, bodyB, bodyA); - if (penetrationB >= 0.0f) return; - - int referenceIndex = 0; - bool flip = false; // Always point from A shape to B shape - - PhysicsShape refPoly; // Reference - PhysicsShape incPoly; // Incident - - // Determine which shape contains reference face - // Checking bias range for penetration - if (penetrationA >= (penetrationB*0.95f + penetrationA*0.01f)) - { - refPoly = bodyA; - incPoly = bodyB; - referenceIndex = faceA; - } - else - { - refPoly = bodyB; - incPoly = bodyA; - referenceIndex = faceB; - flip = true; - } - - // World space incident face - Vector2 incidentFace[2]; - FindIncidentFace(&incidentFace[0], &incidentFace[1], refPoly, incPoly, referenceIndex); - - // Setup reference face vertices - PhysicsVertexData refData = refPoly.vertexData; - Vector2 v1 = refData.positions[referenceIndex]; - referenceIndex = (((referenceIndex + 1) < (int)refData.vertexCount) ? (referenceIndex + 1) : 0); - Vector2 v2 = refData.positions[referenceIndex]; - - // Transform vertices to world space - v1 = MathMatVector2Product(refPoly.transform, v1); - v1 = MathVector2Add(v1, refPoly.body->position); - v2 = MathMatVector2Product(refPoly.transform, v2); - v2 = MathVector2Add(v2, refPoly.body->position); - - // Calculate reference face side normal in world space - Vector2 sidePlaneNormal = MathVector2Subtract(v2, v1); - MathVector2Normalize(&sidePlaneNormal); - - // Orthogonalize - Vector2 refFaceNormal = { sidePlaneNormal.y, -sidePlaneNormal.x }; - float refC = MathVector2DotProduct(refFaceNormal, v1); - float negSide = MathVector2DotProduct(sidePlaneNormal, v1)*-1; - float posSide = MathVector2DotProduct(sidePlaneNormal, v2); - - // MathVector2Clip incident face to reference face side planes (due to floating point error, possible to not have required points - if (MathVector2Clip(CLITERAL(Vector2){ -sidePlaneNormal.x, -sidePlaneNormal.y }, &incidentFace[0], &incidentFace[1], negSide) < 2) return; - if (MathVector2Clip(sidePlaneNormal, &incidentFace[0], &incidentFace[1], posSide) < 2) return; - - // Flip normal if required - manifold->normal = (flip ? CLITERAL(Vector2){ -refFaceNormal.x, -refFaceNormal.y } : refFaceNormal); - - // Keep points behind reference face - int currentPoint = 0; // MathVector2Clipped points behind reference face - float separation = MathVector2DotProduct(refFaceNormal, incidentFace[0]) - refC; - if (separation <= 0.0f) - { - manifold->contacts[currentPoint] = incidentFace[0]; - manifold->penetration = -separation; - currentPoint++; - } - else manifold->penetration = 0.0f; - - separation = MathVector2DotProduct(refFaceNormal, incidentFace[1]) - refC; - - if (separation <= 0.0f) - { - manifold->contacts[currentPoint] = incidentFace[1]; - manifold->penetration += -separation; - currentPoint++; - - // Calculate total penetration average - manifold->penetration /= currentPoint; - } - - manifold->contactsCount = currentPoint; -} - -// Integrates physics forces into velocity -static void IntegratePhysicsForces(PhysicsBody body) -{ - if ((body == NULL) || (body->inverseMass == 0.0f) || !body->enabled) return; - - body->velocity.x += (float)((body->force.x*body->inverseMass)*(deltaTime/2.0)); - body->velocity.y += (float)((body->force.y*body->inverseMass)*(deltaTime/2.0)); - - if (body->useGravity) - { - body->velocity.x += (float)(gravityForce.x*(deltaTime/1000/2.0)); - body->velocity.y += (float)(gravityForce.y*(deltaTime/1000/2.0)); - } - - if (!body->freezeOrient) body->angularVelocity += (float)(body->torque*body->inverseInertia*(deltaTime/2.0)); -} - -// Initializes physics manifolds to solve collisions -static void InitializePhysicsManifolds(PhysicsManifold manifold) -{ - PhysicsBody bodyA = manifold->bodyA; - PhysicsBody bodyB = manifold->bodyB; - - if ((bodyA == NULL) || (bodyB == NULL)) return; - - // Calculate average restitution, static and dynamic friction - manifold->restitution = sqrtf(bodyA->restitution*bodyB->restitution); - manifold->staticFriction = sqrtf(bodyA->staticFriction*bodyB->staticFriction); - manifold->dynamicFriction = sqrtf(bodyA->dynamicFriction*bodyB->dynamicFriction); - - for (unsigned int i = 0; i < manifold->contactsCount; i++) - { - // Caculate radius from center of mass to contact - Vector2 radiusA = MathVector2Subtract(manifold->contacts[i], bodyA->position); - Vector2 radiusB = MathVector2Subtract(manifold->contacts[i], bodyB->position); - - Vector2 crossA = MathVector2Product(radiusA, bodyA->angularVelocity); - Vector2 crossB = MathVector2Product(radiusB, bodyB->angularVelocity); - - Vector2 radiusV = { 0.0f, 0.0f }; - radiusV.x = bodyB->velocity.x + crossB.x - bodyA->velocity.x - crossA.x; - radiusV.y = bodyB->velocity.y + crossB.y - bodyA->velocity.y - crossA.y; - - // Determine if we should perform a resting collision or not; - // The idea is if the only thing moving this object is gravity, then the collision should be performed without any restitution - if (MathVector2SqrLen(radiusV) < (MathVector2SqrLen(CLITERAL(Vector2){ (float)(gravityForce.x*deltaTime/1000), (float)(gravityForce.y*deltaTime/1000) }) + PHYSAC_EPSILON)) manifold->restitution = 0; - } -} - -// Integrates physics collisions impulses to solve collisions -static void IntegratePhysicsImpulses(PhysicsManifold manifold) -{ - PhysicsBody bodyA = manifold->bodyA; - PhysicsBody bodyB = manifold->bodyB; - - if ((bodyA == NULL) || (bodyB == NULL)) return; - - // Early out and positional correct if both objects have infinite mass - if (fabs(bodyA->inverseMass + bodyB->inverseMass) <= PHYSAC_EPSILON) - { - bodyA->velocity = PHYSAC_VECTOR_ZERO; - bodyB->velocity = PHYSAC_VECTOR_ZERO; - return; - } - - for (unsigned int i = 0; i < manifold->contactsCount; i++) - { - // Calculate radius from center of mass to contact - Vector2 radiusA = MathVector2Subtract(manifold->contacts[i], bodyA->position); - Vector2 radiusB = MathVector2Subtract(manifold->contacts[i], bodyB->position); - - // Calculate relative velocity - Vector2 radiusV = { 0.0f, 0.0f }; - radiusV.x = bodyB->velocity.x + MathVector2Product(radiusB, bodyB->angularVelocity).x - bodyA->velocity.x - MathVector2Product(radiusA, bodyA->angularVelocity).x; - radiusV.y = bodyB->velocity.y + MathVector2Product(radiusB, bodyB->angularVelocity).y - bodyA->velocity.y - MathVector2Product(radiusA, bodyA->angularVelocity).y; - - // Relative velocity along the normal - float contactVelocity = MathVector2DotProduct(radiusV, manifold->normal); - - // Do not resolve if velocities are separating - if (contactVelocity > 0.0f) return; - - float raCrossN = MathVector2CrossProduct(radiusA, manifold->normal); - float rbCrossN = MathVector2CrossProduct(radiusB, manifold->normal); - - float inverseMassSum = bodyA->inverseMass + bodyB->inverseMass + (raCrossN*raCrossN)*bodyA->inverseInertia + (rbCrossN*rbCrossN)*bodyB->inverseInertia; - - // Calculate impulse scalar value - float impulse = -(1.0f + manifold->restitution)*contactVelocity; - impulse /= inverseMassSum; - impulse /= (float)manifold->contactsCount; - - // Apply impulse to each physics body - Vector2 impulseV = { manifold->normal.x*impulse, manifold->normal.y*impulse }; - - if (bodyA->enabled) - { - bodyA->velocity.x += bodyA->inverseMass*(-impulseV.x); - bodyA->velocity.y += bodyA->inverseMass*(-impulseV.y); - if (!bodyA->freezeOrient) bodyA->angularVelocity += bodyA->inverseInertia*MathVector2CrossProduct(radiusA, CLITERAL(Vector2){ -impulseV.x, -impulseV.y }); - } - - if (bodyB->enabled) - { - bodyB->velocity.x += bodyB->inverseMass*(impulseV.x); - bodyB->velocity.y += bodyB->inverseMass*(impulseV.y); - if (!bodyB->freezeOrient) bodyB->angularVelocity += bodyB->inverseInertia*MathVector2CrossProduct(radiusB, impulseV); - } - - // Apply friction impulse to each physics body - radiusV.x = bodyB->velocity.x + MathVector2Product(radiusB, bodyB->angularVelocity).x - bodyA->velocity.x - MathVector2Product(radiusA, bodyA->angularVelocity).x; - radiusV.y = bodyB->velocity.y + MathVector2Product(radiusB, bodyB->angularVelocity).y - bodyA->velocity.y - MathVector2Product(radiusA, bodyA->angularVelocity).y; - - Vector2 tangent = { radiusV.x - (manifold->normal.x*MathVector2DotProduct(radiusV, manifold->normal)), radiusV.y - (manifold->normal.y*MathVector2DotProduct(radiusV, manifold->normal)) }; - MathVector2Normalize(&tangent); - - // Calculate impulse tangent magnitude - float impulseTangent = -MathVector2DotProduct(radiusV, tangent); - impulseTangent /= inverseMassSum; - impulseTangent /= (float)manifold->contactsCount; - - float absImpulseTangent = (float)fabs(impulseTangent); - - // Don't apply tiny friction impulses - if (absImpulseTangent <= PHYSAC_EPSILON) return; - - // Apply coulumb's law - Vector2 tangentImpulse = { 0.0f, 0.0f }; - if (absImpulseTangent < impulse*manifold->staticFriction) tangentImpulse = CLITERAL(Vector2){ tangent.x*impulseTangent, tangent.y*impulseTangent }; - else tangentImpulse = CLITERAL(Vector2){ tangent.x*-impulse*manifold->dynamicFriction, tangent.y*-impulse*manifold->dynamicFriction }; - - // Apply friction impulse - if (bodyA->enabled) - { - bodyA->velocity.x += bodyA->inverseMass*(-tangentImpulse.x); - bodyA->velocity.y += bodyA->inverseMass*(-tangentImpulse.y); - - if (!bodyA->freezeOrient) bodyA->angularVelocity += bodyA->inverseInertia*MathVector2CrossProduct(radiusA, CLITERAL(Vector2){ -tangentImpulse.x, -tangentImpulse.y }); - } - - if (bodyB->enabled) - { - bodyB->velocity.x += bodyB->inverseMass*(tangentImpulse.x); - bodyB->velocity.y += bodyB->inverseMass*(tangentImpulse.y); - - if (!bodyB->freezeOrient) bodyB->angularVelocity += bodyB->inverseInertia*MathVector2CrossProduct(radiusB, tangentImpulse); - } - } -} - -// Integrates physics velocity into position and forces -static void IntegratePhysicsVelocity(PhysicsBody body) -{ - if ((body == NULL) ||!body->enabled) return; - - body->position.x += (float)(body->velocity.x*deltaTime); - body->position.y += (float)(body->velocity.y*deltaTime); - - if (!body->freezeOrient) body->orient += (float)(body->angularVelocity*deltaTime); - body->shape.transform = MathMatFromRadians(body->orient); - - IntegratePhysicsForces(body); -} - -// Corrects physics bodies positions based on manifolds collision information -static void CorrectPhysicsPositions(PhysicsManifold manifold) -{ - PhysicsBody bodyA = manifold->bodyA; - PhysicsBody bodyB = manifold->bodyB; - - if ((bodyA == NULL) || (bodyB == NULL)) return; - - Vector2 correction = { 0.0f, 0.0f }; - correction.x = (PHYSAC_MAX(manifold->penetration - PHYSAC_PENETRATION_ALLOWANCE, 0.0f)/(bodyA->inverseMass + bodyB->inverseMass))*manifold->normal.x*PHYSAC_PENETRATION_CORRECTION; - correction.y = (PHYSAC_MAX(manifold->penetration - PHYSAC_PENETRATION_ALLOWANCE, 0.0f)/(bodyA->inverseMass + bodyB->inverseMass))*manifold->normal.y*PHYSAC_PENETRATION_CORRECTION; - - if (bodyA->enabled) - { - bodyA->position.x -= correction.x*bodyA->inverseMass; - bodyA->position.y -= correction.y*bodyA->inverseMass; - } - - if (bodyB->enabled) - { - bodyB->position.x += correction.x*bodyB->inverseMass; - bodyB->position.y += correction.y*bodyB->inverseMass; - } -} - -// Returns the extreme point along a direction within a polygon -static Vector2 GetSupport(PhysicsShape shape, Vector2 dir) -{ - float bestProjection = -PHYSAC_FLT_MAX; - Vector2 bestVertex = { 0.0f, 0.0f }; - PhysicsVertexData data = shape.vertexData; - - for (unsigned int i = 0; i < data.vertexCount; i++) - { - Vector2 vertex = data.positions[i]; - float projection = MathVector2DotProduct(vertex, dir); - - if (projection > bestProjection) - { - bestVertex = vertex; - bestProjection = projection; - } - } - - return bestVertex; -} - -// Finds polygon shapes axis least penetration -static float FindAxisLeastPenetration(int *faceIndex, PhysicsShape shapeA, PhysicsShape shapeB) -{ - float bestDistance = -PHYSAC_FLT_MAX; - int bestIndex = 0; - - PhysicsVertexData dataA = shapeA.vertexData; - //PhysicsVertexData dataB = shapeB.vertexData; - - for (unsigned int i = 0; i < dataA.vertexCount; i++) - { - // Retrieve a face normal from A shape - Vector2 normal = dataA.normals[i]; - Vector2 transNormal = MathMatVector2Product(shapeA.transform, normal); - - // Transform face normal into B shape's model space - Matrix2x2 buT = MathMatTranspose(shapeB.transform); - normal = MathMatVector2Product(buT, transNormal); - - // Retrieve support point from B shape along -n - Vector2 support = GetSupport(shapeB, CLITERAL(Vector2){ -normal.x, -normal.y }); - - // Retrieve vertex on face from A shape, transform into B shape's model space - Vector2 vertex = dataA.positions[i]; - vertex = MathMatVector2Product(shapeA.transform, vertex); - vertex = MathVector2Add(vertex, shapeA.body->position); - vertex = MathVector2Subtract(vertex, shapeB.body->position); - vertex = MathMatVector2Product(buT, vertex); - - // Compute penetration distance in B shape's model space - float distance = MathVector2DotProduct(normal, MathVector2Subtract(support, vertex)); - - // Store greatest distance - if (distance > bestDistance) - { - bestDistance = distance; - bestIndex = i; - } - } - - *faceIndex = bestIndex; - return bestDistance; -} - -// Finds two polygon shapes incident face -static void FindIncidentFace(Vector2 *v0, Vector2 *v1, PhysicsShape ref, PhysicsShape inc, int index) -{ - PhysicsVertexData refData = ref.vertexData; - PhysicsVertexData incData = inc.vertexData; - - Vector2 referenceNormal = refData.normals[index]; - - // Calculate normal in incident's frame of reference - referenceNormal = MathMatVector2Product(ref.transform, referenceNormal); // To world space - referenceNormal = MathMatVector2Product(MathMatTranspose(inc.transform), referenceNormal); // To incident's model space - - // Find most anti-normal face on polygon - int incidentFace = 0; - float minDot = PHYSAC_FLT_MAX; - - for (unsigned int i = 0; i < incData.vertexCount; i++) - { - float dot = MathVector2DotProduct(referenceNormal, incData.normals[i]); - - if (dot < minDot) - { - minDot = dot; - incidentFace = i; - } - } - - // Assign face vertices for incident face - *v0 = MathMatVector2Product(inc.transform, incData.positions[incidentFace]); - *v0 = MathVector2Add(*v0, inc.body->position); - incidentFace = (((incidentFace + 1) < (int)incData.vertexCount) ? (incidentFace + 1) : 0); - *v1 = MathMatVector2Product(inc.transform, incData.positions[incidentFace]); - *v1 = MathVector2Add(*v1, inc.body->position); -} - -// Returns clipping value based on a normal and two faces -static int MathVector2Clip(Vector2 normal, Vector2 *faceA, Vector2 *faceB, float clip) -{ - int sp = 0; - Vector2 out[2] = { *faceA, *faceB }; - - // Retrieve distances from each endpoint to the line - float distanceA = MathVector2DotProduct(normal, *faceA) - clip; - float distanceB = MathVector2DotProduct(normal, *faceB) - clip; - - // If negative (behind plane) - if (distanceA <= 0.0f) out[sp++] = *faceA; - if (distanceB <= 0.0f) out[sp++] = *faceB; - - // If the points are on different sides of the plane - if ((distanceA*distanceB) < 0.0f) - { - // Push intersection point - float alpha = distanceA/(distanceA - distanceB); - out[sp] = *faceA; - Vector2 delta = MathVector2Subtract(*faceB, *faceA); - delta.x *= alpha; - delta.y *= alpha; - out[sp] = MathVector2Add(out[sp], delta); - sp++; - } - - // Assign the new converted values - *faceA = out[0]; - *faceB = out[1]; - - return sp; -} - -// Returns the barycenter of a triangle given by 3 points -static Vector2 MathTriangleBarycenter(Vector2 v1, Vector2 v2, Vector2 v3) -{ - Vector2 result = { 0.0f, 0.0f }; - - result.x = (v1.x + v2.x + v3.x)/3; - result.y = (v1.y + v2.y + v3.y)/3; - - return result; -} - -#if !defined(PHYSAC_AVOID_TIMMING_SYSTEM) -// Initializes hi-resolution MONOTONIC timer -static void InitTimer(void) -{ -#if defined(_WIN32) - QueryPerformanceFrequency((unsigned long long int *) &frequency); -#endif - -#if defined(__EMSCRIPTEN__) || defined(__linux__) - struct timespec now; - if (clock_gettime(CLOCK_MONOTONIC, &now) == 0) frequency = 1000000000; -#endif - -#if defined(__APPLE__) - mach_timebase_info_data_t timebase; - mach_timebase_info(&timebase); - frequency = (timebase.denom*1e9)/timebase.numer; -#endif - - baseClockTicks = (double)GetClockTicks(); // Get MONOTONIC clock time offset - startTime = GetCurrentTime(); // Get current time in milliseconds -} - -// Get hi-res MONOTONIC time measure in clock ticks -static unsigned long long int GetClockTicks(void) -{ - unsigned long long int value = 0; - -#if defined(_WIN32) - QueryPerformanceCounter((unsigned long long int *) &value); -#endif - -#if defined(__linux__) - struct timespec now; - clock_gettime(CLOCK_MONOTONIC, &now); - value = (unsigned long long int)now.tv_sec*(unsigned long long int)1000000000 + (unsigned long long int)now.tv_nsec; -#endif - -#if defined(__APPLE__) - value = mach_absolute_time(); -#endif - - return value; -} - -// Get current time in milliseconds -static double GetCurrentTime(void) -{ - return (double)(GetClockTicks() - baseClockTicks)/frequency*1000; -} -#endif // !PHYSAC_AVOID_TIMMING_SYSTEM - - -// Returns the cross product of a vector and a value -static inline Vector2 MathVector2Product(Vector2 vector, float value) -{ - Vector2 result = { -value*vector.y, value*vector.x }; - return result; -} - -// Returns the cross product of two vectors -static inline float MathVector2CrossProduct(Vector2 v1, Vector2 v2) -{ - return (v1.x*v2.y - v1.y*v2.x); -} - -// Returns the len square root of a vector -static inline float MathVector2SqrLen(Vector2 vector) -{ - return (vector.x*vector.x + vector.y*vector.y); -} - -// Returns the dot product of two vectors -static inline float MathVector2DotProduct(Vector2 v1, Vector2 v2) -{ - return (v1.x*v2.x + v1.y*v2.y); -} - -// Returns the square root of distance between two vectors -static inline float MathVector2SqrDistance(Vector2 v1, Vector2 v2) -{ - Vector2 dir = MathVector2Subtract(v1, v2); - return MathVector2DotProduct(dir, dir); -} - -// Returns the normalized values of a vector -static void MathVector2Normalize(Vector2 *vector) -{ - float length, ilength; - - Vector2 aux = *vector; - length = sqrtf(aux.x*aux.x + aux.y*aux.y); - - if (length == 0) length = 1.0f; - - ilength = 1.0f/length; - - vector->x *= ilength; - vector->y *= ilength; -} - -// Returns the sum of two given vectors -static inline Vector2 MathVector2Add(Vector2 v1, Vector2 v2) -{ - Vector2 result = { v1.x + v2.x, v1.y + v2.y }; - return result; -} - -// Returns the subtract of two given vectors -static inline Vector2 MathVector2Subtract(Vector2 v1, Vector2 v2) -{ - Vector2 result = { v1.x - v2.x, v1.y - v2.y }; - return result; -} - -// Creates a matrix 2x2 from a given radians value -static Matrix2x2 MathMatFromRadians(float radians) -{ - float cos = cosf(radians); - float sin = sinf(radians); - - Matrix2x2 result = { cos, -sin, sin, cos }; - return result; -} - -// Returns the transpose of a given matrix 2x2 -static inline Matrix2x2 MathMatTranspose(Matrix2x2 matrix) -{ - Matrix2x2 result = { matrix.m00, matrix.m10, matrix.m01, matrix.m11 }; - return result; -} - -// Multiplies a vector by a matrix 2x2 -static inline Vector2 MathMatVector2Product(Matrix2x2 matrix, Vector2 vector) -{ - Vector2 result = { matrix.m00*vector.x + matrix.m01*vector.y, matrix.m10*vector.x + matrix.m11*vector.y }; - return result; -} - -#endif // PHYSAC_IMPLEMENTATION diff --git a/src/rmem.h b/src/rmem.h deleted file mode 100644 index dbf417fe..00000000 --- a/src/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/src/rnet.h b/src/rnet.h deleted file mode 100644 index 7ed29b15..00000000 --- a/src/rnet.h +++ /dev/null @@ -1,2256 +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 -#ifdef _WIN32 - #include -#else -#if defined(__STDC__) && __STDC_VERSION__ >= 199901L - #include -#elif !defined(__cplusplus) && !defined(bool) - typedef enum { false, true } bool; -#endif -#endif - -typedef enum { - SOCKET_TCP = 0, // SOCK_STREAM - SOCKET_UDP = 1 // SOCK_DGRAM -} SocketType; - -// Network typedefs -typedef uint32_t SocketChannel; -typedef struct _AddressInformation *AddressInformation; -typedef struct _SocketAddress *SocketAddress; -typedef struct _SocketAddressIPv4 *SocketAddressIPv4; -typedef struct _SocketAddressIPv6 *SocketAddressIPv6; -typedef struct _SocketAddressStorage *SocketAddressStorage; - -// IPAddress definition (in network byte order) -typedef struct IPAddress { - unsigned long host; // 32-bit IPv4 host address - unsigned short port; // 16-bit protocol port -} IPAddress; - -typedef struct UDPChannel { - int numbound; // The total number of addresses this channel is bound to - IPAddress address[SOCKET_MAX_UDPADDRESSES]; // The list of remote addresses this channel is bound to -} UDPChannel; - -// An option ID, value, sizeof(value) tuple for setsockopt(2). -typedef struct SocketOpt { - int id; // Socked option id - int valueLen; // Socked option value len - void *value; // Socked option value data -} SocketOpt; - -typedef struct Socket { - int ready; // Is the socket ready? i.e. has information - int status; // The last status code to have occured using this socket - bool isServer; // Is this socket a server socket (i.e. TCP/UDP Listen Server) - SocketChannel channel; // The socket handle id - SocketType type; // Is this socket a TCP or UDP socket? - - bool isIPv6; // Is this socket address an ipv6 address? - SocketAddressIPv4 addripv4; // The host/target IPv4 for this socket (in network byte order) - SocketAddressIPv6 addripv6; // The host/target IPv6 for this socket (in network byte order) - - struct UDPChannel binding[SOCKET_MAX_UDPCHANNELS]; // The amount of channels (if UDP) this socket is bound to -} Socket; - -// Configuration for a socket -typedef struct SocketConfig { - SocketType type; // The type of socket, TCP/UDP - char *host; // The host address in xxx.xxx.xxx.xxx form - char *port; // The target port/service in the form "http" or "25565" - bool server; // Listen for incoming clients? - bool nonblocking; // non-blocking operation? - int backlog_size; // set a custom backlog size - SocketOpt sockopts[SOCKET_MAX_SOCK_OPTS]; -} SocketConfig; - -typedef struct SocketDataPacket { - IPAddress address; // The source/dest address of an incoming/outgoing packet - int channel; // The src/dst channel of the packet - int maxlen; // The size of the data buffer - int status; // Packet status after sending - unsigned int len; // The length of the packet data - unsigned char *data; // The packet data -} SocketDataPacket; - -// Result from calling open with a given config -typedef struct SocketResult { - int status; // Socket result state - Socket *socket; // Socket ref -} SocketResult; - -typedef struct SocketSet { - int numsockets; // Socket set count - int maxsockets; // Socket set max - struct Socket **sockets; // Sockets array -} SocketSet; - -// Packet type -typedef struct Packet { - uint32_t size; // The total size of bytes in data - uint32_t offs; // The offset to data access - uint32_t maxs; // The max size of data - uint8_t *data; // Data stored in network byte order -} Packet; - - -#ifdef __cplusplus -extern "C" { // Prevents name mangling of functions -#endif - -//---------------------------------------------------------------------------------- -// Global Variables Definition -//---------------------------------------------------------------------------------- -//... - -//---------------------------------------------------------------------------------- -// Module Functions Declaration -//---------------------------------------------------------------------------------- - -// Initialisation and cleanup -bool InitNetworkDevice(void); -void CloseNetworkDevice(void); - -// Address API -void ResolveIP(const char *ip, const char *service, int flags, char *outhost, char *outserv); -int ResolveHost(const char *address, const char *service, int addressType, int flags, AddressInformation *outAddr); -int GetAddressFamily(AddressInformation address); -int GetAddressSocketType(AddressInformation address); -int GetAddressProtocol(AddressInformation address); -char *GetAddressCanonName(AddressInformation address); -char *GetAddressHostAndPort(AddressInformation address, char *outhost, unsigned short *outport); - -// Address Memory API -AddressInformation LoadAddress(void); -void UnloadAddress(AddressInformation *addressInfo); -AddressInformation *LoadAddressList(int size); - -// Socket API -bool SocketCreate(SocketConfig *config, SocketResult *result); -bool SocketBind(SocketConfig *config, SocketResult *result); -bool SocketListen(SocketConfig *config, SocketResult *result); -bool SocketConnect(SocketConfig *config, SocketResult *result); -Socket *SocketAccept(Socket *server, SocketConfig *config); - -// General Socket API -int SocketSend(Socket *sock, const void *datap, int len); -int SocketReceive(Socket *sock, void *data, int maxlen); -SocketAddressStorage SocketGetPeerAddress(Socket *sock); -const char *GetSocketAddressHost(SocketAddressStorage storage); -short GetSocketAddressPort(SocketAddressStorage storage); -void SocketClose(Socket *sock); - -// UDP Socket API -int SocketSetChannel(Socket *socket, int channel, const IPAddress *address); -void SocketUnsetChannel(Socket *socket, int channel); - -// UDP DataPacket API -SocketDataPacket *AllocPacket(int size); -int ResizePacket(SocketDataPacket *packet, int newsize); -void FreePacket(SocketDataPacket *packet); -SocketDataPacket **AllocPacketList(int count, int size); -void FreePacketList(SocketDataPacket **packets); - -// Socket Memory API -Socket *LoadSocket(void); -void UnloadSocket(Socket **sock); -SocketResult *LoadSocketResult(void); -void UnloadSocketResult(SocketResult **result); -SocketSet *LoadSocketSet(int max); -void UnloadSocketSet(SocketSet *sockset); - -// Socket I/O API -bool IsSocketReady(Socket *sock); -bool IsSocketConnected(Socket *sock); -int AddSocket(SocketSet *set, Socket *sock); -int RemoveSocket(SocketSet *set, Socket *sock); -int CheckSockets(SocketSet *set, unsigned int timeout); - -// Packet API -void PacketSend(Packet *packet); -void PacketReceive(Packet *packet); -void PacketWrite8(Packet *packet, uint16_t value); -void PacketWrite16(Packet *packet, uint16_t value); -void PacketWrite32(Packet *packet, uint32_t value); -void PacketWrite64(Packet *packet, uint64_t value); -uint16_t PacketRead8(Packet *packet); -uint16_t PacketRead16(Packet *packet); -uint32_t PacketRead32(Packet *packet); -uint64_t PacketRead64(Packet *packet); - -#ifdef __cplusplus -} -#endif - -#endif // RNET_H - -/*********************************************************************************** -* -* RNET IMPLEMENTATION -* -************************************************************************************/ - -#if defined(RNET_IMPLEMENTATION) - -#include // 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 = (unsigned short)(hport); - - // TODO: Changed the code to avoid the usage of inet_pton and inet_ntop replacing them with getnameinfo (that should have a better support on windows). - - //inet_pton(AF_INET, config->host, &ip4addr.sin_addr); - int connect_result = connect(result->socket->channel, (struct sockaddr *)&ip4addr, sizeof(ip4addr)); - - if (connect_result == SOCKET_ERROR) - { - result->socket->status = SocketGetLastError(); - SocketSetLastError(0); - - switch (result->socket->status) - { - case WSAEWOULDBLOCK: success = true; break; - default: - { - TRACELOG(LOG_WARNING, "Socket Error: %s", SocketErrorCodeToString(result->socket->status)); - success = false; - } break; - } - } - else - { - TRACELOG(LOG_INFO, "Successfully connected to socket."); - success = true; - } - } - else - { - if (IsIPv6Address(config->host)) - { - struct sockaddr_in6 ip6addr; - ip6addr.sin6_family = AF_INET6; - unsigned long hport; - hport = strtoul(config->port, NULL, 0); - ip6addr.sin6_port = htons((unsigned short)hport); - //inet_pton(AF_INET6, config->host, &ip6addr.sin6_addr); // TODO. - int connect_result = connect(result->socket->channel, (struct sockaddr *)&ip6addr, sizeof(ip6addr)); - - if (connect_result == SOCKET_ERROR) - { - result->socket->status = SocketGetLastError(); - SocketSetLastError(0); - - switch (result->socket->status) - { - case WSAEWOULDBLOCK: success = true; break; - default: - { - TRACELOG(LOG_WARNING, "Socket Error: %s", SocketErrorCodeToString(result->socket->status)); - success = false; - } break; - } - } - else - { - TRACELOG(LOG_INFO, "Successfully connected to socket."); - success = true; - } - } - } - } - - if (success) - { - result->status = RESULT_SUCCESS; - result->socket->ready = 0; - result->socket->status = 0; - } - - return success; -} - -// Closes an existing socket -// -// SocketChannel socket - The id of the socket to close -void SocketClose(Socket *sock) -{ - if (sock != NULL) - { - if (sock->channel != INVALID_SOCKET) closesocket(sock->channel); - } -} - -// Returns the sockaddress for a specific socket in a generic storage struct -SocketAddressStorage SocketGetPeerAddress(Socket *sock) -{ - // TODO. - /* - if (sock->isServer) return NULL; - if (sock->isIPv6) return sock->addripv6; - else return sock->addripv4; - */ - - return NULL; -} - -// Return the address-type appropriate host portion of a socket address -const char *GetSocketAddressHost(SocketAddressStorage storage) -{ - assert(storage->address.ss_family == AF_INET || storage->address.ss_family == AF_INET6); - return SocketAddressToString((struct sockaddr_storage *)storage); -} - -// Return the address-type appropriate port(service) portion of a socket address -short GetSocketAddressPort(SocketAddressStorage storage) -{ - //return ntohs(GetSocketPortPtr(storage)); // TODO. - - return 0; -} - -// The accept function permits an incoming connection attempt on a socket. -// -// SocketChannel listener - The socket to listen for incoming connections on (i.e. server) -// SocketResult* out - The result of this function (if any, including errors) -// -// e.g. -// -// SocketResult connection; -// bool connected = false; -// if (!connected) -// { -// if (SocketAccept(server_res.socket.channel, &connection)) -// { -// connected = true; -// } -// } -Socket *SocketAccept(Socket *server, SocketConfig *config) -{ - if (!server->isServer || server->type == SOCKET_UDP) return NULL; - - struct sockaddr_storage sock_addr; - socklen_t sock_alen; - Socket *sock = LoadSocket(); - server->ready = 0; - sock_alen = sizeof(sock_addr); - sock->channel = accept(server->channel, (struct sockaddr *)&sock_addr, &sock_alen); - - if (sock->channel == INVALID_SOCKET) - { - sock->status = SocketGetLastError(); - TRACELOG(LOG_WARNING, "Socket Error: %s", SocketErrorCodeToString(sock->status)); - SocketSetLastError(0); - SocketClose(sock); - - return NULL; - } - - (config->nonblocking) ? SocketSetNonBlocking(sock) : SocketSetBlocking(sock); - sock->isServer = false; - sock->ready = 0; - sock->type = server->type; - - switch (sock_addr.ss_family) - { - case AF_INET: - { - struct sockaddr_in *s = ((struct sockaddr_in *)&sock_addr); - sock->addripv4 = (struct _SocketAddressIPv4 *)RNET_MALLOC(sizeof(*sock->addripv4)); - - if (sock->addripv4 != NULL) - { - memset(sock->addripv4, 0, sizeof(*sock->addripv4)); - memcpy(&sock->addripv4->address, (struct sockaddr_in *)&s->sin_addr, sizeof(struct sockaddr_in)); - TRACELOG(LOG_INFO, "Server: Got connection from %s::%hu", SocketAddressToString((struct sockaddr_storage *)s), ntohs(sock->addripv4->address.sin_port)); - } - } break; - case AF_INET6: - { - struct sockaddr_in6 *s = ((struct sockaddr_in6 *)&sock_addr); - sock->addripv6 = (struct _SocketAddressIPv6 *)RNET_MALLOC(sizeof(*sock->addripv6)); - - if (sock->addripv6 != NULL) - { - memset(sock->addripv6, 0, sizeof(*sock->addripv6)); - memcpy(&sock->addripv6->address, (struct sockaddr_in6 *)&s->sin6_addr, sizeof(struct sockaddr_in6)); - TRACELOG(LOG_INFO, "Server: Got connection from %s::%hu", SocketAddressToString((struct sockaddr_storage *)s), ntohs(sock->addripv6->address.sin6_port)); - } - } break; - } - - return sock; -} - -// Verify that the channel is in the valid range -static int ValidChannel(int channel) -{ - if ((channel < 0) || (channel >= SOCKET_MAX_UDPCHANNELS)) - { - TRACELOG(LOG_WARNING, "Invalid channel"); - return 0; - } - - return 1; -} - -// Set the socket channel -int SocketSetChannel(Socket *socket, int channel, const IPAddress *address) -{ - struct UDPChannel *binding; - - if (socket == NULL) - { - TRACELOG(LOG_WARNING, "Passed a NULL socket"); - return (-1); - } - - if (channel == -1) - { - for (channel = 0; channel < SOCKET_MAX_UDPCHANNELS; ++channel) - { - binding = &socket->binding[channel]; - if (binding->numbound < SOCKET_MAX_UDPADDRESSES) break; - } - } - else - { - if (!ValidChannel(channel)) return (-1); - - binding = &socket->binding[channel]; - } - - if (binding->numbound == SOCKET_MAX_UDPADDRESSES) - { - TRACELOG(LOG_WARNING, "No room for new addresses"); - return (-1); - } - - binding->address[binding->numbound++] = *address; - - return (channel); -} - -// Remove the socket channel -void SocketUnsetChannel(Socket *socket, int channel) -{ - if ((channel >= 0) && (channel < SOCKET_MAX_UDPCHANNELS)) socket->binding[channel].numbound = 0; -} - -/* Allocate/free a single UDP packet 'size' bytes long. - The new packet is returned, or NULL if the function ran out of memory. - */ -SocketDataPacket *AllocPacket(int size) -{ - SocketDataPacket *packet = (SocketDataPacket *)RNET_MALLOC(sizeof(*packet)); - int error = 1; - - if (packet != NULL) - { - packet->maxlen = size; - packet->data = (uint8_t *)RNET_MALLOC(size); - if (packet->data != NULL) - { - error = 0; - } - } - - if (error) - { - FreePacket(packet); - packet = NULL; - } - - return (packet); -} - -int ResizePacket(SocketDataPacket *packet, int newsize) -{ - uint8_t *newdata = (uint8_t *)RNET_MALLOC(newsize); - - if (newdata != NULL) - { - RNET_FREE(packet->data); - packet->data = newdata; - packet->maxlen = newsize; - } - - return (packet->maxlen); -} - -void FreePacket(SocketDataPacket *packet) -{ - if (packet) - { - RNET_FREE(packet->data); - RNET_FREE(packet); - } -} - -// Allocate/Free a UDP packet vector (array of packets) of 'howmany' packets, each 'size' bytes long. -// A pointer to the packet array is returned, or NULL if the function ran out of memory. -SocketDataPacket **AllocPacketList(int howmany, int size) -{ - SocketDataPacket **packetV = (SocketDataPacket **)RNET_MALLOC((howmany + 1) * sizeof(*packetV)); - - if (packetV != NULL) - { - int i; - for (i = 0; i < howmany; ++i) - { - packetV[i] = AllocPacket(size); - if (packetV[i] == NULL) - { - break; - } - } - packetV[i] = NULL; - - if (i != howmany) - { - FreePacketList(packetV); - packetV = NULL; - } - } - - return (packetV); -} - -void FreePacketList(SocketDataPacket **packetV) -{ - if (packetV) - { - for (int i = 0; packetV[i]; ++i) FreePacket(packetV[i]); - RNET_FREE(packetV); - } -} - -// Send 'len' bytes of 'data' over the non-server socket 'sock' -int SocketSend(Socket *sock, const void *datap, int length) -{ - int sent = 0; - int left = length; - int status = -1; - int numsent = 0; - const unsigned char *data = (const unsigned char *)datap; - - // Server sockets are for accepting connections only - if (sock->isServer) - { - TRACELOG(LOG_WARNING, "Cannot send information on a server socket"); - return -1; - } - - // Which socket are we trying to send data on - switch (sock->type) - { - case SOCKET_TCP: - { - SocketSetLastError(0); - do - { - length = send(sock->channel, (const char *)data, left, 0); - if (length > 0) - { - sent += length; - left -= length; - data += length; - } - } while ((left > 0) && // While we still have bytes left to send - ((length > 0) || // The amount of bytes we actually sent is > 0 - (SocketGetLastError() == WSAEINTR)) // The socket was interupted - ); - - if (length == SOCKET_ERROR) - { - sock->status = SocketGetLastError(); - TRACELOG(LOG_DEBUG, "Socket Error: %s", SocketErrorCodeToString(sock->status)); - SocketSetLastError(0); - } - else TRACELOG(LOG_DEBUG, "Successfully sent \"%s\" (%d bytes)", datap, sent); - - return sent; - } break; - case SOCKET_UDP: - { - SocketSetLastError(0); - - if (sock->isIPv6) status = sendto(sock->channel, (const char *)data, left, 0, (struct sockaddr *)&sock->addripv6->address, sizeof(sock->addripv6->address)); - else status = sendto(sock->channel, (const char *)data, left, 0, (struct sockaddr *)&sock->addripv4->address, sizeof(sock->addripv4->address)); - - if (sent >= 0) - { - sock->status = 0; - ++numsent; - TRACELOG(LOG_DEBUG, "Successfully sent \"%s\" (%d bytes)", datap, status); - } - else - { - sock->status = SocketGetLastError(); - TRACELOG(LOG_DEBUG, "Socket Error: %s", SocketGetLastErrorString(sock->status)); - SocketSetLastError(0); - return 0; - } - - return numsent; - } break; - default: break; - } - - return -1; -} - -// Receive up to 'maxlen' bytes of data over the non-server socket 'sock', -// and store them in the buffer pointed to by 'data'. -// This function returns the actual amount of data received. If the return -// value is less than or equal to zero, then either the remote connection was -// closed, or an unknown socket error occurred. -int SocketReceive(Socket *sock, void *data, int maxlen) -{ - int len = 0; - int numrecv = 0; - int status = 0; - socklen_t sock_len; - struct sockaddr_storage sock_addr; - //char ip[INET6_ADDRSTRLEN]; - - // Server sockets are for accepting connections only - if (sock->isServer && (sock->type == SOCKET_TCP)) - { - sock->status = SocketGetLastError(); - TRACELOG(LOG_DEBUG, "Socket Error: %s", "Server sockets cannot be used to receive data"); - SocketSetLastError(0); - return 0; - } - - // Which socket are we trying to send data on - switch (sock->type) - { - case SOCKET_TCP: - { - SocketSetLastError(0); - do - { - len = recv(sock->channel, (char *)data, maxlen, 0); - } while (SocketGetLastError() == WSAEINTR); - - if (len > 0) - { - // Who sent the packet? - if (sock->type == SOCKET_UDP) - { - //TRACELOG(LOG_DEBUG, "Received data from: %s", inet_ntop(sock_addr.ss_family, GetSocketAddressPtr((struct sockaddr *)&sock_addr), ip, sizeof(ip))); - } - - ((unsigned char *)data)[len] = '\0'; // Add null terminating character to the end of the stream - TRACELOG(LOG_DEBUG, "Received \"%s\" (%d bytes)", data, len); - } - - sock->ready = 0; - return len; - } break; - case SOCKET_UDP: - { - SocketSetLastError(0); - sock_len = sizeof(sock_addr); - status = recvfrom(sock->channel, // The receving channel - data, // A pointer to the data buffer to fill - maxlen, // The max length of the data to fill - 0, // Flags - (struct sockaddr *)&sock_addr, // The address of the recevied data - &sock_len // The length of the received data address - ); - - if (status >= 0) ++numrecv; - else - { - sock->status = SocketGetLastError(); - - switch (sock->status) - { - case WSAEWOULDBLOCK: break; - default: TRACELOG(LOG_WARNING, "Socket Error: %s", SocketErrorCodeToString(sock->status)); break; - } - - SocketSetLastError(0); - return 0; - } - - sock->ready = 0; - return numrecv; - } break; - default: break; - } - - return -1; -} - -// Does the socket have it's 'ready' flag set? -bool IsSocketReady(Socket *sock) -{ - return (sock != NULL) && (sock->ready); -} - -// Check if the socket is considered connected -bool IsSocketConnected(Socket *sock) -{ -#if defined(_WIN32) - FD_SET writefds; - FD_ZERO(&writefds); - FD_SET(sock->channel, &writefds); - struct timeval timeout; - timeout.tv_sec = 1; - timeout.tv_usec = 1000000000UL; - int total = select(0, NULL, &writefds, NULL, &timeout); - - if (total == -1) - { // Error - sock->status = SocketGetLastError(); - TRACELOG(LOG_WARNING, "Socket Error: %s", SocketErrorCodeToString(sock->status)); - SocketSetLastError(0); - } - else if (total == 0) return false; // Timeout - else if (FD_ISSET(sock->channel, &writefds)) return true; - - return false; -#else - return true; -#endif -} - -// Allocate and return a SocketResult struct -SocketResult *LoadSocketResult(void) -{ - struct SocketResult *res = (struct SocketResult *)RNET_MALLOC(sizeof(*res)); - - if (res != NULL) - { - memset(res, 0, sizeof(*res)); - if ((res->socket = LoadSocket()) == NULL) - { - RNET_FREE(res); - res = NULL; - } - } - - return res; -} - -// Free an allocated SocketResult -void UnloadSocketResult(SocketResult **result) -{ - if (*result != NULL) - { - if ((*result)->socket != NULL) UnloadSocket(&((*result)->socket)); - - RNET_FREE(*result); - *result = NULL; - } -} - -// Allocate a Socket -Socket *LoadSocket(void) -{ - struct Socket *sock; - sock = (Socket *)RNET_MALLOC(sizeof(*sock)); - - if (sock != NULL) memset(sock, 0, sizeof(*sock)); - else - { - TRACELOG(LOG_WARNING, "Ran out of memory attempting to allocate a socket"); - SocketClose(sock); - RNET_FREE(sock); - sock = NULL; - } - - return sock; -} - -// Free an allocated Socket -void UnloadSocket(Socket **sock) -{ - if (*sock != NULL) - { - RNET_FREE(*sock); - *sock = NULL; - } -} - -// Allocate a SocketSet -SocketSet *LoadSocketSet(int max) -{ - struct SocketSet *set = (struct SocketSet *)RNET_MALLOC(sizeof(*set)); - - if (set != NULL) - { - set->numsockets = 0; - set->maxsockets = max; - set->sockets = (struct Socket **)RNET_MALLOC(max * sizeof(*set->sockets)); - if (set->sockets != NULL) - { - for (int i = 0; i < max; ++i) set->sockets[i] = NULL; - } - else - { - RNET_FREE(set); - set = NULL; - } - } - - return (set); -} - -// Free an allocated SocketSet -void UnloadSocketSet(SocketSet *set) -{ - if (set) - { - RNET_FREE(set->sockets); - RNET_FREE(set); - } -} - -// Add a Socket "sock" to the SocketSet "set" -int AddSocket(SocketSet *set, Socket *sock) -{ - if (sock != NULL) - { - if (set->numsockets == set->maxsockets) - { - TRACELOG(LOG_DEBUG, "Socket Error: %s", "SocketSet is full"); - SocketSetLastError(0); - return (-1); - } - set->sockets[set->numsockets++] = (struct Socket *)sock; - } - else - { - TRACELOG(LOG_DEBUG, "Socket Error: %s", "Socket was null"); - SocketSetLastError(0); - return (-1); - } - - return (set->numsockets); -} - -// Remove a Socket "sock" to the SocketSet "set" -int RemoveSocket(SocketSet *set, Socket *sock) -{ - if (sock != NULL) - { - int i = 0; - for (i = 0; i < set->numsockets; ++i) - { - if (set->sockets[i] == (struct Socket *)sock) break; - } - - if (i == set->numsockets) - { - TRACELOG(LOG_DEBUG, "Socket Error: %s", "Socket not found"); - SocketSetLastError(0); - return (-1); - } - - --set->numsockets; - for (; i < set->numsockets; ++i) set->sockets[i] = set->sockets[i + 1]; - } - - return (set->numsockets); -} - -// Check the sockets in the socket set for pending information -int CheckSockets(SocketSet *set, unsigned int timeout) -{ - int i; - SOCKET maxfd; - int retval; - struct timeval tv; - fd_set mask; - - /* Find the largest file descriptor */ - maxfd = 0; - for (i = set->numsockets - 1; i >= 0; --i) - { - if (set->sockets[i]->channel > maxfd) - { - maxfd = set->sockets[i]->channel; - } - } - - // Check the file descriptors for available data - do - { - SocketSetLastError(0); - - // Set up the mask of file descriptors - FD_ZERO(&mask); - for (i = set->numsockets - 1; i >= 0; --i) - { - FD_SET(set->sockets[i]->channel, &mask); - } // Set up the timeout - - tv.tv_sec = timeout / 1000; - tv.tv_usec = (timeout % 1000) * 1000; - - /* Look! */ - retval = select(maxfd + 1, &mask, NULL, NULL, &tv); - } while (SocketGetLastError() == WSAEINTR); - - // Mark all file descriptors ready that have data available - if (retval > 0) - { - for (i = set->numsockets - 1; i >= 0; --i) - { - if (FD_ISSET(set->sockets[i]->channel, &mask)) set->sockets[i]->ready = 1; - } - } - - return retval; -} - -// Allocate an AddressInformation -AddressInformation LoadAddress(void) -{ - AddressInformation addressInfo = NULL; - addressInfo = (AddressInformation)RNET_CALLOC(1, sizeof(*addressInfo)); - - if (addressInfo != NULL) - { - addressInfo->addr.ai_addr = (struct sockaddr *)RNET_CALLOC(1, sizeof(struct sockaddr)); - if (addressInfo->addr.ai_addr == NULL) TRACELOG(LOG_WARNING, "Failed to allocate memory for \"struct sockaddr\""); - } - else TRACELOG(LOG_WARNING, "Failed to allocate memory for \"struct AddressInformation\""); - - return addressInfo; -} - -// Free an AddressInformation struct -void UnloadAddress(AddressInformation *addressInfo) -{ - if (*addressInfo != NULL) - { - if ((*addressInfo)->addr.ai_addr != NULL) - { - RNET_FREE((*addressInfo)->addr.ai_addr); - (*addressInfo)->addr.ai_addr = NULL; - } - - RNET_FREE(*addressInfo); - *addressInfo = NULL; - } -} - -// Allocate a list of AddressInformation -AddressInformation *LoadAddressList(int size) -{ - AddressInformation *addr; - addr = (AddressInformation *)RNET_MALLOC(size * sizeof(AddressInformation)); - return addr; -} - -// Opaque datatype accessor addrinfo->ai_family -int GetAddressFamily(AddressInformation address) -{ - return address->addr.ai_family; -} - -// Opaque datatype accessor addrinfo->ai_socktype -int GetAddressSocketType(AddressInformation address) -{ - return address->addr.ai_socktype; -} - -// Opaque datatype accessor addrinfo->ai_protocol -int GetAddressProtocol(AddressInformation address) -{ - return address->addr.ai_protocol; -} - -// Opaque datatype accessor addrinfo->ai_canonname -char *GetAddressCanonName(AddressInformation address) -{ - return address->addr.ai_canonname; -} - -// Opaque datatype accessor addrinfo->ai_addr -char *GetAddressHostAndPort(AddressInformation address, char *outhost, unsigned short *outport) -{ - //char *ip[INET6_ADDRSTRLEN]; - char *result = NULL; - struct sockaddr_storage *storage = (struct sockaddr_storage *)address->addr.ai_addr; - - switch (storage->ss_family) - { - case AF_INET: - { - struct sockaddr_in *s = ((struct sockaddr_in *)address->addr.ai_addr); - //result = inet_ntop(AF_INET, &s->sin_addr, ip, INET_ADDRSTRLEN); // TODO. - *outport = ntohs(s->sin_port); - } break; - case AF_INET6: - { - struct sockaddr_in6 *s = ((struct sockaddr_in6 *)address->addr.ai_addr); - //result = inet_ntop(AF_INET6, &s->sin6_addr, ip, INET6_ADDRSTRLEN); // TODO. - *outport = ntohs(s->sin6_port); - } break; - default: break; - } - - if (result == NULL) - { - TRACELOG(LOG_WARNING, "Socket Error: %s", SocketErrorCodeToString(SocketGetLastError())); - SocketSetLastError(0); - } - else - { - strcpy(outhost, result); - } - return result; -} - -// -void PacketSend(Packet *packet) -{ - TRACELOG(LOG_INFO, "Sending packet (%s) with size %d\n", packet->data, packet->size); -} - -// -void PacketReceive(Packet *packet) -{ - TRACELOG(LOG_INFO, "Receiving packet (%s) with size %d\n", packet->data, packet->size); -} - -// -void PacketWrite16(Packet *packet, uint16_t value) -{ - TRACELOG(LOG_INFO, "Original: 0x%04" PRIX16 " - %" PRIu16 "\n", value, value); - uint8_t *data = packet->data + packet->offs; - *data++ = (uint8_t)(value >> 8); - *data++ = (uint8_t)(value); - packet->size += sizeof(uint16_t); - packet->offs += sizeof(uint16_t); - TRACELOG(LOG_INFO, "Network: 0x%04" PRIX16 " - %" PRIu16 "\n", (uint16_t) *data, (uint16_t) *data); -} - -// -void PacketWrite32(Packet *packet, uint32_t value) -{ - TRACELOG(LOG_INFO, "Original: 0x%08" PRIX32 " - %" PRIu32 "\n", value, value); - uint8_t *data = packet->data + packet->offs; - *data++ = (uint8_t)(value >> 24); - *data++ = (uint8_t)(value >> 16); - *data++ = (uint8_t)(value >> 8); - *data++ = (uint8_t)(value); - packet->size += sizeof(uint32_t); - packet->offs += sizeof(uint32_t); - - TRACELOG(LOG_INFO, "Network: 0x%08" PRIX32 " - %" PRIu32 "\n", - (uint32_t)(((intptr_t) packet->data) - packet->offs), - (uint32_t)(((intptr_t) packet->data) - packet->offs)); -} - -// -void PacketWrite64(Packet *packet, uint64_t value) -{ - TRACELOG(LOG_INFO, "Original: 0x%016" PRIX64 " - %" PRIu64 "\n", value, value); - - uint8_t *data = packet->data + packet->offs; - *data++ = (uint8_t)(value >> 56); - *data++ = (uint8_t)(value >> 48); - *data++ = (uint8_t)(value >> 40); - *data++ = (uint8_t)(value >> 32); - *data++ = (uint8_t)(value >> 24); - *data++ = (uint8_t)(value >> 16); - *data++ = (uint8_t)(value >> 8); - *data++ = (uint8_t)(value); - packet->size += sizeof(uint64_t); - packet->offs += sizeof(uint64_t); - - TRACELOG(LOG_INFO, "Network: 0x%016" PRIX64 " - %" PRIu64 "\n", (uint64_t)(packet->data - packet->offs), (uint64_t)(packet->data - packet->offs)); -} - -// -uint16_t PacketRead16(Packet *packet) -{ - uint8_t *data = packet->data + packet->offs; - packet->size += sizeof(uint16_t); - packet->offs += sizeof(uint16_t); - uint16_t value = ((uint16_t) data[0] << 8) | data[1]; - TRACELOG(LOG_INFO, "Original: 0x%04" PRIX16 " - %" PRIu16 "\n", value, value); - - return value; -} - -// -uint32_t PacketRead32(Packet *packet) -{ - uint8_t *data = packet->data + packet->offs; - packet->size += sizeof(uint32_t); - packet->offs += sizeof(uint32_t); - uint32_t value = ((uint32_t) data[0] << 24) | ((uint32_t) data[1] << 16) | ((uint32_t) data[2] << 8) | data[3]; - TRACELOG(LOG_INFO, "Original: 0x%08" PRIX32 " - %" PRIu32 "\n", value, value); - - return value; -} - -// -uint64_t PacketRead64(Packet *packet) -{ - uint8_t *data = packet->data + packet->offs; - packet->size += sizeof(uint64_t); - packet->offs += sizeof(uint64_t); - uint64_t value = ((uint64_t) data[0] << 56) | ((uint64_t) data[1] << 48) | ((uint64_t) data[2] << 40) | ((uint64_t) data[3] << 32) | ((uint64_t) data[4] << 24) | ((uint64_t) data[5] << 16) | ((uint64_t) data[6] << 8) | data[7]; - TRACELOG(LOG_INFO, "Original: 0x%016" PRIX64 " - %" PRIu64 "\n", value, value); - - return value; -} - -#endif // RNET_IMPLEMENTATION \ No newline at end of file -- cgit v1.2.3