code:
#ifndef __khrplatform_h_
#define __khrplatform_h_

/*
** Copyright (c) 2008-2018 The Khronos Group Inc.
**
** Permission is hereby granted, free of charge, to any person obtaining a
** copy of this software and/or associated documentation files (the
** "Materials"), to deal in the Materials without restriction, including
** without limitation the rights to use, copy, modify, merge, publish,
** distribute, sublicense, and/or sell copies of the Materials, and to
** permit persons to whom the Materials are furnished to do so, subject to
** the following conditions:
**
** The above copyright notice and this permission notice shall be included
** in all copies or substantial portions of the Materials.
**
** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
*/

/* Khronos platform-specific types and definitions.
 *
 * The master copy of khrplatform.h is maintained in the Khronos EGL
 * Registry repository at https://github.com/KhronosGroup/EGL-Registry
 * The last semantic modification to khrplatform.h was at commit ID:
 *      67a3e0864c2d75ea5287b9f3d2eb74a745936692
 *
 * Adopters may modify this file to suit their platform. Adopters are
 * encouraged to submit platform specific modifications to the Khronos
 * group so that they can be included in future versions of this file.
 * Please submit changes by filing pull requests or issues on
 * the EGL Registry repository linked above.
 *
 *
 * See the Implementer's Guidelines for information about where this file
 * should be located on your system and for more details of its use:
 *    http://www.khronos.org/registry/implementers_guide.pdf
 *
 * This file should be included as
 *        #include <KHR/khrplatform.h>
 * by Khronos client API header files that use its types and defines.
 *
 * The types in khrplatform.h should only be used to define API-specific types.
 *
 * Types defined in khrplatform.h:
 *    khronos_int8_t              signed   8  bit
 *    khronos_uint8_t             unsigned 8  bit
 *    khronos_int16_t             signed   16 bit
 *    khronos_uint16_t            unsigned 16 bit
 *    khronos_int32_t             signed   32 bit
 *    khronos_uint32_t            unsigned 32 bit
 *    khronos_int64_t             signed   64 bit
 *    khronos_uint64_t            unsigned 64 bit
 *    khronos_intptr_t            signed   same number of bits as a pointer
 *    khronos_uintptr_t           unsigned same number of bits as a pointer
 *    khronos_ssize_t             signed   size
 *    khronos_usize_t             unsigned size
 *    khronos_float_t             signed   32 bit floating point
 *    khronos_time_ns_t           unsigned 64 bit time in nanoseconds
 *    khronos_utime_nanoseconds_t unsigned time interval or absolute time in
 *                                         nanoseconds
 *    khronos_stime_nanoseconds_t signed time interval in nanoseconds
 *    khronos_boolean_enum_t      enumerated boolean type. This should
 *      only be used as a base type when a client API's boolean type is
 *      an enum. Client APIs which use an integer or other type for
 *      booleans cannot use this as the base type for their boolean.
 *
 * Tokens defined in khrplatform.h:
 *
 *    KHRONOS_FALSE, KHRONOS_TRUE Enumerated boolean false/true values.
 *
 *    KHRONOS_SUPPORT_INT64 is 1 if 64 bit integers are supported; otherwise 0.
 *    KHRONOS_SUPPORT_FLOAT is 1 if floats are supported; otherwise 0.
 *
 * Calling convention macros defined in this file:
 *    KHRONOS_APICALL
 *    KHRONOS_APIENTRY
 *    KHRONOS_APIATTRIBUTES
 *
 * These may be used in function prototypes as:
 *
 *      KHRONOS_APICALL void KHRONOS_APIENTRY funcname(
 *                                  int arg1,
 *                                  int arg2) KHRONOS_APIATTRIBUTES;
 */

/*-------------------------------------------------------------------------
 * Definition of KHRONOS_APICALL
 *-------------------------------------------------------------------------
 * This precedes the return type of the function in the function prototype.
 */
#if defined(_WIN32) && !defined(__SCITECH_SNAP__)
#   define KHRONOS_APICALL __declspec(dllimport)
#elif defined (__SYMBIAN32__)
#   define KHRONOS_APICALL IMPORT_C
#elif defined(__ANDROID__)
#   define KHRONOS_APICALL __attribute__((visibility("default")))
#else
#   define KHRONOS_APICALL
#endif

/*-------------------------------------------------------------------------
 * Definition of KHRONOS_APIENTRY
 *-------------------------------------------------------------------------
 * This follows the return type of the function  and precedes the function
 * name in the function prototype.
 */
#if defined(_WIN32) && !defined(_WIN32_WCE) && !defined(__SCITECH_SNAP__)
    /* Win32 but not WinCE */
#   define KHRONOS_APIENTRY __stdcall
#else
#   define KHRONOS_APIENTRY
#endif

/*-------------------------------------------------------------------------
 * Definition of KHRONOS_APIATTRIBUTES
 *-------------------------------------------------------------------------
 * This follows the closing parenthesis of the function prototype arguments.
 */
#if defined (__ARMCC_2__)
#define KHRONOS_APIATTRIBUTES __softfp
#else
#define KHRONOS_APIATTRIBUTES
#endif

/*-------------------------------------------------------------------------
 * basic type definitions
 *-----------------------------------------------------------------------*/
#if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(__GNUC__) || defined(__SCO__) || defined(__USLC__)

/*
 * Using <stdint.h>
 */
#include <stdint.h>
typedef int32_t                 khronos_int32_t;
typedef uint32_t                khronos_uint32_t;
typedef int64_t                 khronos_int64_t;
typedef uint64_t                khronos_uint64_t;
#define KHRONOS_SUPPORT_INT64   1
#define KHRONOS_SUPPORT_FLOAT   1

#elif defined(__VMS ) || defined(__sgi)

/*
 * Using <inttypes.h>
 */
#include <inttypes.h>
typedef int32_t                 khronos_int32_t;
typedef uint32_t                khronos_uint32_t;
typedef int64_t                 khronos_int64_t;
typedef uint64_t                khronos_uint64_t;
#define KHRONOS_SUPPORT_INT64   1
#define KHRONOS_SUPPORT_FLOAT   1

#elif defined(_WIN32) && !defined(__SCITECH_SNAP__)

/*
 * Win32
 */
typedef __int32                 khronos_int32_t;
typedef unsigned __int32        khronos_uint32_t;
typedef __int64                 khronos_int64_t;
typedef unsigned __int64        khronos_uint64_t;
#define KHRONOS_SUPPORT_INT64   1
#define KHRONOS_SUPPORT_FLOAT   1

#elif defined(__sun__) || defined(__digital__)

/*
 * Sun or Digital
 */
typedef int                     khronos_int32_t;
typedef unsigned int            khronos_uint32_t;
#if defined(__arch64__) || defined(_LP64)
typedef long int                khronos_int64_t;
typedef unsigned long int       khronos_uint64_t;
#else
typedef long long int           khronos_int64_t;
typedef unsigned long long int  khronos_uint64_t;
#endif /* __arch64__ */
#define KHRONOS_SUPPORT_INT64   1
#define KHRONOS_SUPPORT_FLOAT   1

#elif 0

/*
 * Hypothetical platform with no float or int64 support
 */
typedef int                     khronos_int32_t;
typedef unsigned int            khronos_uint32_t;
#define KHRONOS_SUPPORT_INT64   0
#define KHRONOS_SUPPORT_FLOAT   0

#else

/*
 * Generic fallback
 */
#include <stdint.h>
typedef int32_t                 khronos_int32_t;
typedef uint32_t                khronos_uint32_t;
typedef int64_t                 khronos_int64_t;
typedef uint64_t                khronos_uint64_t;
#define KHRONOS_SUPPORT_INT64   1
#define KHRONOS_SUPPORT_FLOAT   1

#endif

/*
 * Types that are (so far) the same on all platforms
 */
typedef signed   char          khronos_int8_t;
typedef unsigned char          khronos_uint8_t;
typedef signed   short int     khronos_int16_t;
typedef unsigned short int     khronos_uint16_t;

/*
 * Types that differ between LLP64 and LP64 architectures - in LLP64,
 * pointers are 64 bits, but 'long' is still 32 bits. Win64 appears
 * to be the only LLP64 architecture in current use.
 */
#ifdef _WIN64
typedef signed   long long int khronos_intptr_t;
typedef unsigned long long int khronos_uintptr_t;
typedef signed   long long int khronos_ssize_t;
typedef unsigned long long int khronos_usize_t;
#else
typedef signed   long  int     khronos_intptr_t;
typedef unsigned long  int     khronos_uintptr_t;
typedef signed   long  int     khronos_ssize_t;
typedef unsigned long  int     khronos_usize_t;
#endif

#if KHRONOS_SUPPORT_FLOAT
/*
 * Float type
 */
typedef          float         khronos_float_t;
#endif

#if KHRONOS_SUPPORT_INT64
/* Time types
 *
 * These types can be used to represent a time interval in nanoseconds or
 * an absolute Unadjusted System Time.  Unadjusted System Time is the number
 * of nanoseconds since some arbitrary system event (e.g. since the last
 * time the system booted).  The Unadjusted System Time is an unsigned
 * 64 bit value that wraps back to 0 every 584 years.  Time intervals
 * may be either signed or unsigned.
 */
typedef khronos_uint64_t       khronos_utime_nanoseconds_t;
typedef khronos_int64_t        khronos_stime_nanoseconds_t;
#endif

/*
 * Dummy value used to pad enum types to 32 bits.
 */
#ifndef KHRONOS_MAX_ENUM
#define KHRONOS_MAX_ENUM 0x7FFFFFFF
#endif

/*
 * Enumerated boolean type
 *
 * Values other than zero should be considered to be true.  Therefore
 * comparisons should not be made against KHRONOS_TRUE.
 */
typedef enum {
    KHRONOS_FALSE = 0,
    KHRONOS_TRUE  = 1,
    KHRONOS_BOOLEAN_ENUM_FORCE_SIZE = KHRONOS_MAX_ENUM
} khronos_boolean_enum_t;

#endif /* __khrplatform_h_ */

repo_name: AstroImpact/AstroImpact

license: mit


code:
/*
    December 2022

    Requires vec.h: https://gist.github.com/mrbid/77a92019e1ab8b86109bf103166bd04e

    Credits:
    Aaftab Munshi, Dan Ginsburg, Dave Shreiner, James William Fletcher, Intel, Gabriel Cramer
*/

#ifndef MAT_H
#define MAT_H

#include "vec.h"

typedef struct
{
    float m[4][4];
} mat;

void mIdent(mat *m);
void mCopy(mat *restrict r, const mat *restrict v);
void mMul(mat *restrict r, const mat *restrict a, const mat *restrict b);
void mMulP(vec *restrict r, const mat *restrict a, const float x, const float y, const float z);
void mMulV(vec *restrict r, const mat *restrict a, const vec v);
void mScale(mat *r, const float x, const float y, const float z);
void mTranslate(mat *r, const float x, const float y, const float z);
void mRotate(mat *r, const float radians, float x, float y, float z); // rotate axis (rotate X to move Y up and down)
void mRotX(mat *r, const float radians); // rotate on axis
void mRotY(mat *r, const float radians); // rotate on axis
void mRotZ(mat *r, const float radians); // rotate on axis
void mFrustum(mat *r, const float left, const float right, const float bottom, const float top, const float nearZ, const float farZ);
void mPerspective(mat *r, const float fovy, const float aspect, const float nearZ, const float farZ);
void mOrtho(mat *r, const float left, const float right, const float bottom, const float top, const float nearZ, const float farZ);
void mLookAt(mat *r, const vec origin, const vec unit_dir);
void mInvert(float *restrict dst, const float *restrict mat);
void mTranspose(mat *restrict r, const mat *restrict m);
void mSetViewDir(mat *r, const vec dir_norm, const vec up_norm);
void mGetViewDir(vec *r, const mat matrix); // returns normal/unit vector
void mGetDirX(vec *r, const mat matrix);
void mGetDirY(vec *r, const mat matrix);
void mGetDirZ(vec *r, const mat matrix);
void mGetPos(vec *r, const mat matrix);

//

void mIdent(mat *m)
{
    memset(m, 0x0, sizeof(mat));
    m->m[0][0] = 1.0f;
    m->m[1][1] = 1.0f;
    m->m[2][2] = 1.0f;
    m->m[3][3] = 1.0f;
}

void mCopy(mat *restrict r, const mat *restrict v)
{
    memcpy(r, v, sizeof(mat));
}

void mMul(mat *restrict r, const mat *restrict a, const mat *restrict b)
{
    mat tmp;
    for(int i = 0; i < 4; i++)
    {
        tmp.m[i][0] =	(a->m[i][0] * b->m[0][0]) +
                        (a->m[i][1] * b->m[1][0]) +
                        (a->m[i][2] * b->m[2][0]) +
                        (a->m[i][3] * b->m[3][0]) ;

        tmp.m[i][1] =	(a->m[i][0] * b->m[0][1]) +
                        (a->m[i][1] * b->m[1][1]) +
                        (a->m[i][2] * b->m[2][1]) +
                        (a->m[i][3] * b->m[3][1]) ;

        tmp.m[i][2] =	(a->m[i][0] * b->m[0][2]) +
                        (a->m[i][1] * b->m[1][2]) +
                        (a->m[i][2] * b->m[2][2]) +
                        (a->m[i][3] * b->m[3][2]) ;

        tmp.m[i][3] =	(a->m[i][0] * b->m[0][3]) +
                        (a->m[i][1] * b->m[1][3]) +
                        (a->m[i][2] * b->m[2][3]) +
                        (a->m[i][3] * b->m[3][3]) ;
    }
    memcpy(r, &tmp, sizeof(mat));
}

void mMulP(vec *restrict r, const mat *restrict a, const float x, const float y, const float z)
{
    r->x =  (a->m[0][0] * x) +
            (a->m[0][1] * x) +
            (a->m[0][2] * x) +
            (a->m[0][3] * x) ;

    r->y =  (a->m[1][0] * y) +
            (a->m[1][1] * y) +
            (a->m[1][2] * y) +
            (a->m[1][3] * y) ;

    r->z =  (a->m[2][0] * z) +
            (a->m[2][1] * z) +
            (a->m[2][2] * z) +
            (a->m[2][3] * z) ;
}

void mMulV(vec *restrict r, const mat *restrict a, const vec v)
{
    r->x =  (a->m[0][0] * v.x) +
            (a->m[0][1] * v.x) +
            (a->m[0][2] * v.x) +
            (a->m[0][3] * v.x) ;

    r->y =  (a->m[1][0] * v.y) +
            (a->m[1][1] * v.y) +
            (a->m[1][2] * v.y) +
            (a->m[1][3] * v.y) ;

    r->z =  (a->m[2][0] * v.z) +
            (a->m[2][1] * v.z) +
            (a->m[2][2] * v.z) +
            (a->m[2][3] * v.z) ;

    r->w =  (a->m[3][0] * v.w) +
            (a->m[3][1] * v.w) +
            (a->m[3][2] * v.w) +
            (a->m[3][3] * v.w) ;
}

void mScale(mat *r, const float x, const float y, const float z)
{
    r->m[0][0] *= x;
    r->m[0][1] *= x;
    r->m[0][2] *= x;
    r->m[0][3] *= x;

    r->m[1][0] *= y;
    r->m[1][1] *= y;
    r->m[1][2] *= y;
    r->m[1][3] *= y;

    r->m[2][0] *= z;
    r->m[2][1] *= z;
    r->m[2][2] *= z;
    r->m[2][3] *= z;
}

void mTranslate(mat *r, const float x, const float y, const float z)
{
    r->m[3][0] += (r->m[0][0] * x + r->m[1][0] * y + r->m[2][0] * z);
    r->m[3][1] += (r->m[0][1] * x + r->m[1][1] * y + r->m[2][1] * z);
    r->m[3][2] += (r->m[0][2] * x + r->m[1][2] * y + r->m[2][2] * z);
    r->m[3][3] += (r->m[0][3] * x + r->m[1][3] * y + r->m[2][3] * z);
}

void mRotate(mat *r, const float radians, float x, float y, float z)
{
    const float mag = rsqrtss(x * x + y * y + z * z);
    const float sinAngle = sinf(radians);
    const float cosAngle = cosf(radians);
    if(mag > 0.0f)
    {
        x *= mag;
        y *= mag;
        z *= mag;

        const float xx = x * x;
        const float yy = y * y;
        const float zz = z * z;
        const float xy = x * y;
        const float yz = y * z;
        const float zx = z * x;
        const float xs = x * sinAngle;
        const float ys = y * sinAngle;
        const float zs = z * sinAngle;
        const float oneMinusCos = 1.0f - cosAngle;

        mat rotMat;
        rotMat.m[0][0] = (oneMinusCos * xx) + cosAngle;
        rotMat.m[0][1] = (oneMinusCos * xy) - zs;
        rotMat.m[0][2] = (oneMinusCos * zx) + ys;
        rotMat.m[0][3] = 0.0F;

        rotMat.m[1][0] = (oneMinusCos * xy) + zs;
        rotMat.m[1][1] = (oneMinusCos * yy) + cosAngle;
        rotMat.m[1][2] = (oneMinusCos * yz) - xs;
        rotMat.m[1][3] = 0.0F;

        rotMat.m[2][0] = (oneMinusCos * zx) - ys;
        rotMat.m[2][1] = (oneMinusCos * yz) + xs;
        rotMat.m[2][2] = (oneMinusCos * zz) + cosAngle;
        rotMat.m[2][3] = 0.0F;

        rotMat.m[3][0] = 0.0F;
        rotMat.m[3][1] = 0.0F;
        rotMat.m[3][2] = 0.0F;
        rotMat.m[3][3] = 1.0F;

        mMul(r, &rotMat, r);
    }
}

void mRotX(mat *r, const float radians)
{
    const float s = sinf(radians);
    const float c = cosf(radians);
    const mat t = { c, 0.f, s, 0.f,
                    0.f, 1.f, 0.f, 0.f,
                    -s, 0.f, c, 0.f,
                    0.f, 0.f, 0.f, 1.f };
    mMul(r, &t, r);
}

void mRotY(mat *r, const float radians)
{
    const float s = sinf(radians);
    const float c = cosf(radians);
    const mat t = { 1.f, 0.f, 0.f, 0.f,
                    0.f, c, -s, 0.f,
                    0.f, s, c, 0.f,
                    0.f, 0.f, 0.f, 1.f };
    mMul(r, &t, r);
}

void mRotZ(mat *r, const float radians)
{
    const float s = sinf(radians);
    const float c = cosf(radians);
    const mat t = { c, -s, 0.f, 0.f,
                    s, c, 0.f, 0.f,
                    0.f, 0.f, 1.f, 0.f,
                    0.f, 0.f, 0.f, 1.f };
    mMul(r, &t, r);
}

void mFrustum(mat *r, const float left, const float right, const float bottom, const float top, const float nearZ, const float farZ)
{
    const float dX = right - left;
    const float dY = top - bottom;
    const float dZ = farZ - nearZ;
    const float rdX = 1.f/dX;
    const float rdY = 1.f/dY;
    const float rdZ = 1.f/dZ;
    mat frust;

    if(nearZ <= 0.0f || farZ <= 0.0f || dX <= 0.0f || dY <= 0.0f || dZ <= 0.0f)
        return;

    frust.m[0][0] = 2.0f * nearZ * rdX;
    frust.m[0][1] = frust.m[0][2] = frust.m[0][3] = 0.0f;

    frust.m[1][1] = 2.0f * nearZ * rdY;
    frust.m[1][0] = frust.m[1][2] = frust.m[1][3] = 0.0f;

    frust.m[2][0] = (right + left) * rdX;
    frust.m[2][1] = (top + bottom) * rdY;
    frust.m[2][2] = -(nearZ + farZ) * rdZ;
    frust.m[2][3] = -1.0f;

    frust.m[3][2] = -2.0f * nearZ * farZ * rdZ;
    frust.m[3][0] = frust.m[3][1] = frust.m[3][3] = 0.0f;

    mMul(r, &frust, r);
}

void mPerspective(mat *r, const float fovy, const float aspect, const float nearZ, const float farZ)
{
   float frustumW, frustumH;
   frustumH = tanf(fovy * 0.002777778078f * PI ) * nearZ; // 0x3b360b62
   frustumW = frustumH * aspect;
   mFrustum(r, -frustumW, frustumW, -frustumH, frustumH, nearZ, farZ);
}

void mOrtho(mat *r, const float left, const float right, const float bottom, const float top, const float nearZ, const float farZ)
{
    const float dX = right - left;
    const float dY = top - bottom;
    const float dZ = farZ - nearZ;
    const float rdX = 1.f/dX;
    const float rdY = 1.f/dY;
    const float rdZ = 1.f/dZ;
    mat ortho;

    if(dX == 0.0f || dY == 0.0f || dZ == 0.0f)
        return;

    mIdent(&ortho);
    ortho.m[0][0] = 2.0f * rdX;
    ortho.m[3][0] = -(right + left) * rdX;
    ortho.m[1][1] = 2.0f * rdY;
    ortho.m[3][1] = -(top + bottom) * rdY;
    ortho.m[2][2] = -2.0f * rdZ;
    ortho.m[3][2] = -(nearZ + farZ) * rdZ;

    mMul(r, &ortho, r);
}

void mLookAt(mat *r, const vec origin, const vec unit_dir)
{
    vec up;
    up.x = 0.f, up.y = 1.f, up.z = 0.f;

    vec dirn;
    dirn.x = unit_dir.x;
    dirn.y = unit_dir.y;
    dirn.z = unit_dir.z;

    vec c;
    vCross(&c, up, dirn);
    vNorm(&c);

    vec rup;
    vCross(&rup, dirn, c);

    r->m[0][0] = c.x;
    r->m[0][1] = c.y;
    r->m[0][2] = c.z;

    r->m[1][0] = rup.x;
    r->m[1][1] = rup.y;
    r->m[1][2] = rup.z;

    r->m[2][0] = dirn.x;
    r->m[2][1] = dirn.y;
    r->m[2][2] = dirn.z;

    r->m[3][0] = origin.x;
    r->m[3][1] = origin.y;
    r->m[3][2] = origin.z;
}

void mInvert(float *restrict dst, const float *restrict src)
{
    // original source: ftp://download.intel.com/design/PentiumIII/sml/24504301.pdf
    // mirrored: https://github.com/esAux/esAux-Menger/raw/main/SIMD%20Matrix%20Inverse.pdf

#ifdef NOSSE
    // Cramer Invert
    float tmp[12]; /* temp array for pairs */
    float tsrc[16]; /* array of transpose source matrix */
    float det; /* determinant */

    /* transpose matrix */
    for(int i = 0; i < 4; i++)
    {
        tsrc[i] = src[i*4];
        tsrc[i + 4] = src[i*4 + 1];
        tsrc[i + 8] = src[i*4 + 2];
        tsrc[i + 12] = src[i*4 + 3];
    }

    /* calculate pairs for first 8 elements (cofactors) */
    tmp[0] = tsrc[10] * tsrc[15];
    tmp[1] = tsrc[11] * tsrc[14];
    tmp[2] = tsrc[9] * tsrc[15];
    tmp[3] = tsrc[11] * tsrc[13];
    tmp[4] = tsrc[9] * tsrc[14];
    tmp[5] = tsrc[10] * tsrc[13];
    tmp[6] = tsrc[8] * tsrc[15];
    tmp[7] = tsrc[11] * tsrc[12];
    tmp[8] = tsrc[8] * tsrc[14];
    tmp[9] = tsrc[10] * tsrc[12];
    tmp[10] = tsrc[8] * tsrc[13];
    tmp[11] = tsrc[9] * tsrc[12];

    /* calculate first 8 elements (cofactors) */
    dst[0] = tmp[0]*tsrc[5] + tmp[3]*tsrc[6] + tmp[4]*tsrc[7];
    dst[0] -= tmp[1]*tsrc[5] + tmp[2]*tsrc[6] + tmp[5]*tsrc[7];
    dst[1] = tmp[1]*tsrc[4] + tmp[6]*tsrc[6] + tmp[9]*tsrc[7];
    dst[1] -= tmp[0]*tsrc[4] + tmp[7]*tsrc[6] + tmp[8]*tsrc[7];
    dst[2] = tmp[2]*tsrc[4] + tmp[7]*tsrc[5] + tmp[10]*tsrc[7];
    dst[2] -= tmp[3]*tsrc[4] + tmp[6]*tsrc[5] + tmp[11]*tsrc[7];
    dst[3] = tmp[5]*tsrc[4] + tmp[8]*tsrc[5] + tmp[11]*tsrc[6];
    dst[3] -= tmp[4]*tsrc[4] + tmp[9]*tsrc[5] + tmp[10]*tsrc[6];
    dst[4] = tmp[1]*tsrc[1] + tmp[2]*tsrc[2] + tmp[5]*tsrc[3];
    dst[4] -= tmp[0]*tsrc[1] + tmp[3]*tsrc[2] + tmp[4]*tsrc[3];
    dst[5] = tmp[0]*tsrc[0] + tmp[7]*tsrc[2] + tmp[8]*tsrc[3];
    dst[5] -= tmp[1]*tsrc[0] + tmp[6]*tsrc[2] + tmp[9]*tsrc[3];
    dst[6] = tmp[3]*tsrc[0] + tmp[6]*tsrc[1] + tmp[11]*tsrc[3];
    dst[6] -= tmp[2]*tsrc[0] + tmp[7]*tsrc[1] + tmp[10]*tsrc[3];
    dst[7] = tmp[4]*tsrc[0] + tmp[9]*tsrc[1] + tmp[10]*tsrc[2];
    dst[7] -= tmp[5]*tsrc[0] + tmp[8]*tsrc[1] + tmp[11]*tsrc[2];

    /* calculate pairs for second 8 elements (cofactors) */
    tmp[0] = tsrc[2]*tsrc[7];
    tmp[1] = tsrc[3]*tsrc[6];
    tmp[2] = tsrc[1]*tsrc[7];
    tmp[3] = tsrc[3]*tsrc[5];
    tmp[4] = tsrc[1]*tsrc[6];
    tmp[5] = tsrc[2]*tsrc[5];
    tmp[6] = tsrc[0]*tsrc[7];
    tmp[7] = tsrc[3]*tsrc[4];
    tmp[8] = tsrc[0]*tsrc[6];
    tmp[9] = tsrc[2]*tsrc[4];
    tmp[10] = tsrc[0]*tsrc[5];
    tmp[11] = tsrc[1]*tsrc[4];

    /* calculate second 8 elements (cofactors) */
    dst[8] = tmp[0]*tsrc[13] + tmp[3]*tsrc[14] + tmp[4]*tsrc[15];
    dst[8] -= tmp[1]*tsrc[13] + tmp[2]*tsrc[14] + tmp[5]*tsrc[15];
    dst[9] = tmp[1]*tsrc[12] + tmp[6]*tsrc[14] + tmp[9]*tsrc[15];
    dst[9] -= tmp[0]*tsrc[12] + tmp[7]*tsrc[14] + tmp[8]*tsrc[15];
    dst[10] = tmp[2]*tsrc[12] + tmp[7]*tsrc[13] + tmp[10]*tsrc[15];
    dst[10]-= tmp[3]*tsrc[12] + tmp[6]*tsrc[13] + tmp[11]*tsrc[15];
    dst[11] = tmp[5]*tsrc[12] + tmp[8]*tsrc[13] + tmp[11]*tsrc[14];
    dst[11]-= tmp[4]*tsrc[12] + tmp[9]*tsrc[13] + tmp[10]*tsrc[14];
    dst[12] = tmp[2]*tsrc[10] + tmp[5]*tsrc[11] + tmp[1]*tsrc[9];
    dst[12]-= tmp[4]*tsrc[11] + tmp[0]*tsrc[9] + tmp[3]*tsrc[10];
    dst[13] = tmp[8]*tsrc[11] + tmp[0]*tsrc[8] + tmp[7]*tsrc[10];
    dst[13]-= tmp[6]*tsrc[10] + tmp[9]*tsrc[11] + tmp[1]*tsrc[8];
    dst[14] = tmp[6]*tsrc[9] + tmp[11]*tsrc[11] + tmp[3]*tsrc[8];
    dst[14]-= tmp[10]*tsrc[11] + tmp[2]*tsrc[8] + tmp[7]*tsrc[9];
    dst[15] = tmp[10]*tsrc[10] + tmp[4]*tsrc[8] + tmp[9]*tsrc[9];
    dst[15]-= tmp[8]*tsrc[9] + tmp[11]*tsrc[10] + tmp[5]*tsrc[8];

    /* calculate determinant */
    det = tsrc[0]*dst[0]+tsrc[1]*dst[1]+tsrc[2]*dst[2]+tsrc[3]*dst[3];

    /* calculate matrix inverse */
    det = 1.0f/det;
    for(int j = 0; j < 16; j++){dst[j] *= det;}
#else
    memcpy(dst, src, sizeof(mat));

    __m128 minor0, minor1, minor2, minor3, det;
    __m128 tmp1 = _mm_setzero_ps();
    __m128 row0 = _mm_setzero_ps();
    __m128 row1 = _mm_setzero_ps();
    __m128 row2 = _mm_setzero_ps();
    __m128 row3 = _mm_setzero_ps();

    tmp1 = _mm_loadh_pi(_mm_loadl_pi(tmp1, (__m64*)(dst)), (__m64*)(dst+ 4));
    row1 = _mm_loadh_pi(_mm_loadl_pi(row1, (__m64*)(dst+8)), (__m64*)(dst+12));
    row0 = _mm_shuffle_ps(tmp1, row1, 0x88);
    row1 = _mm_shuffle_ps(row1, tmp1, 0xDD);
    tmp1 = _mm_loadh_pi(_mm_loadl_pi(tmp1, (__m64*)(dst+ 2)), (__m64*)(dst+ 6));
    row3 = _mm_loadh_pi(_mm_loadl_pi(row3, (__m64*)(dst+10)), (__m64*)(dst+14));
    row2 = _mm_shuffle_ps(tmp1, row3, 0x88);
    row3 = _mm_shuffle_ps(row3, tmp1, 0xDD);

    tmp1 = _mm_mul_ps(row2, row3);
    tmp1 = _mm_shuffle_ps(tmp1, tmp1, 0xB1);
    minor0 = _mm_mul_ps(row1, tmp1);
    minor1 = _mm_mul_ps(row0, tmp1);
    tmp1 = _mm_shuffle_ps(tmp1, tmp1, 0x4E);
    minor0 = _mm_sub_ps(_mm_mul_ps(row1, tmp1), minor0);
    minor1 = _mm_sub_ps(_mm_mul_ps(row0, tmp1), minor1);
    minor1 = _mm_shuffle_ps(minor1, minor1, 0x4E);

    tmp1 = _mm_mul_ps(row1, row2);
    tmp1 = _mm_shuffle_ps(tmp1, tmp1, 0xB1);
    minor0 = _mm_add_ps(_mm_mul_ps(row3, tmp1), minor0);
    minor3 = _mm_mul_ps(row0, tmp1);
    tmp1 = _mm_shuffle_ps(tmp1, tmp1, 0x4E);
    minor0 = _mm_sub_ps(minor0, _mm_mul_ps(row3, tmp1));
    minor3 = _mm_sub_ps(_mm_mul_ps(row0, tmp1), minor3);
    minor3 = _mm_shuffle_ps(minor3, minor3, 0x4E);

    tmp1 = _mm_mul_ps(_mm_shuffle_ps(row1, row1, 0x4E), row3);
    tmp1 = _mm_shuffle_ps(tmp1, tmp1, 0xB1);
    row2 = _mm_shuffle_ps(row2, row2, 0x4E);
    minor0 = _mm_add_ps(_mm_mul_ps(row2, tmp1), minor0);
    minor2 = _mm_mul_ps(row0, tmp1);
    tmp1 = _mm_shuffle_ps(tmp1, tmp1, 0x4E);
    minor0 = _mm_sub_ps(minor0, _mm_mul_ps(row2, tmp1));
    minor2 = _mm_sub_ps(_mm_mul_ps(row0, tmp1), minor2);
    minor2 = _mm_shuffle_ps(minor2, minor2, 0x4E);

    tmp1 = _mm_mul_ps(row0, row1);
    tmp1 = _mm_shuffle_ps(tmp1, tmp1, 0xB1);
    minor2 = _mm_add_ps(_mm_mul_ps(row3, tmp1), minor2);
    minor3 = _mm_sub_ps(_mm_mul_ps(row2, tmp1), minor3);
    tmp1 = _mm_shuffle_ps(tmp1, tmp1, 0x4E);
    minor2 = _mm_sub_ps(_mm_mul_ps(row3, tmp1), minor2);
    minor3 = _mm_sub_ps(minor3, _mm_mul_ps(row2, tmp1));

    tmp1 = _mm_mul_ps(row0, row3);
    tmp1 = _mm_shuffle_ps(tmp1, tmp1, 0xB1);
    minor1 = _mm_sub_ps(minor1, _mm_mul_ps(row2, tmp1));
    minor2 = _mm_add_ps(_mm_mul_ps(row1, tmp1), minor2);
    tmp1 = _mm_shuffle_ps(tmp1, tmp1, 0x4E);
    minor1 = _mm_add_ps(_mm_mul_ps(row2, tmp1), minor1);
    minor2 = _mm_sub_ps(minor2, _mm_mul_ps(row1, tmp1));

    tmp1 = _mm_mul_ps(row0, row2);
    tmp1 = _mm_shuffle_ps(tmp1, tmp1, 0xB1);
    minor1 = _mm_add_ps(_mm_mul_ps(row3, tmp1), minor1);
    minor3 = _mm_sub_ps(minor3, _mm_mul_ps(row1, tmp1));
    tmp1 = _mm_shuffle_ps(tmp1, tmp1, 0x4E);
    minor1 = _mm_sub_ps(minor1, _mm_mul_ps(row3, tmp1));
    minor3 = _mm_add_ps(_mm_mul_ps(row1, tmp1), minor3);

    det = _mm_mul_ps(row0, minor0);
    det = _mm_add_ps(_mm_shuffle_ps(det, det, 0x4E), det);
    det = _mm_add_ss(_mm_shuffle_ps(det, det, 0xB1), det);
    tmp1 = _mm_rcp_ss(det);
    det = _mm_sub_ss(_mm_add_ss(tmp1, tmp1), _mm_mul_ss(det, _mm_mul_ss(tmp1, tmp1)));
    det = _mm_shuffle_ps(det, det, 0x00);
    minor0 = _mm_mul_ps(det, minor0);
    _mm_storel_pi((__m64*)(dst), minor0);
    _mm_storeh_pi((__m64*)(dst+2), minor0);
    minor1 = _mm_mul_ps(det, minor1);
    _mm_storel_pi((__m64*)(dst+4), minor1);
    _mm_storeh_pi((__m64*)(dst+6), minor1);
    minor2 = _mm_mul_ps(det, minor2);
    _mm_storel_pi((__m64*)(dst+ 8), minor2);
    _mm_storeh_pi((__m64*)(dst+10), minor2);
    minor3 = _mm_mul_ps(det, minor3);
    _mm_storel_pi((__m64*)(dst+12), minor3);
    _mm_storeh_pi((__m64*)(dst+14), minor3);
#endif
}

void mTranspose(mat *r, const mat *restrict m)
{
    r->m[0][0] = m->m[0][0];
    r->m[1][0] = m->m[0][1];
    r->m[2][0] = m->m[0][2];
    r->m[3][0] = m->m[0][3];

    r->m[0][1] = m->m[1][0];
    r->m[1][1] = m->m[1][1];
    r->m[2][1] = m->m[1][2];
    r->m[3][1] = m->m[1][3];

    r->m[0][2] = m->m[2][0];
    r->m[1][2] = m->m[2][1];
    r->m[2][2] = m->m[2][2];
    r->m[3][2] = m->m[2][3];

    r->m[0][3] = m->m[3][0];
    r->m[1][3] = m->m[3][1];
    r->m[2][3] = m->m[3][2];
    r->m[3][3] = m->m[3][3];
}

void mSetViewDir(mat *r, const vec dir_norm, const vec up_norm)
{
    vec c;
    vCross(&c, up_norm, dir_norm);
    vNorm(&c);

    vec rup;
    vCross(&rup, dir_norm, c);

    r->m[0][0] = c.x;
    r->m[0][1] = c.y;
    r->m[0][2] = c.z;

    r->m[1][0] = rup.x;
    r->m[1][1] = rup.y;
    r->m[1][2] = rup.z;

    r->m[2][0] = dir_norm.x;
    r->m[2][1] = dir_norm.y;
    r->m[2][2] = dir_norm.z;
}

void mGetViewDir(vec *r, const mat matrix)
{
    r->x = -matrix.m[0][2];
    r->y = -matrix.m[1][2];
    r->z = -matrix.m[2][2];
}

void mGetDirX(vec *r, const mat matrix)
{
    r->x = matrix.m[0][0];
    r->y = matrix.m[0][1];
    r->z = matrix.m[0][2];
}

void mGetDirY(vec *r, const mat matrix)
{
    r->x = matrix.m[1][0];
    r->y = matrix.m[1][1];
    r->z = matrix.m[1][2];
}

void mGetDirZ(vec *r, const mat matrix)
{
    r->x = matrix.m[2][0];
    r->y = matrix.m[2][1];
    r->z = matrix.m[2][2];
}

void mGetPos(vec *r, const mat matrix)
{
    r->x = matrix.m[3][0];
    r->y = matrix.m[3][1];
    r->z = matrix.m[3][2];
}

#endif


code:
[section .text]
global munmap_and_exit
munmap_and_exit:
mov rax, 11
syscall

mov rax, 60
xor rdi, rdi
syscall


code: void munmap_and_exit(uint8_t *arg, uint64_t len);


code:
#include <unistd.h>
#include <sys/syscall.h>
#include <stdint.h>
#include <linux/futex.h>

static inline void mutex_lock(uint32_t *futex) {
	uint32_t val;
	while (val = __sync_lock_test_and_set(futex, 1))
		syscall(SYS_futex, futex, FUTEX_WAIT, val, 0, 0, 0);
}

static inline void mutex_unlock(uint32_t *futex) {
	__sync_lock_release(futex);
	syscall(SYS_futex, futex, FUTEX_WAKE, 1, 0, 0, 0);
}


code:
#include <math.h>
#include "protocol.h"

uint32_t HTOLE32F(f32 f) {
	int exp;
	f32 fm = frexpf(f, &exp);
	uint32_t m = (uint32_t)ldexpf(fm, 24);
	return 0;
}

f32 LE32FTOH(uint32_t i) {
	return 0.f;
}


code:
#include <endian.h>
#include <stdint.h>

#include "inc/gl.h"
#define f32 GLfloat

#if defined(__BYTE_ORDER) && __BYTE_ORDER == __LITTLE_ENDIAN
	#define SERVER_PORT 0x961F
	#define PROTOCOL_ID 0x75F6073677E10C44

	#define HTOLE16 // no htole* called when we're already little-endian
	#define HTOLE32
	#define HTOLE64

	#define LE16TOH // no letoh*
	#define LE32TOH
	#define LE64TOH

	#define HTOBE16 htobe16
	#define HTOBE32 htobe32
	#define HTOBE64 htobe64

	#define BE16TOH be16toh
	#define BE32TOH be32toh
	#define BE64TOH be64toh
#elif defined(__BYTE_ORDER) && __BYTE_ORDER == __BIG_ENDIAN
	#define SERVER_PORT 0x1F96
	#define PROTOCOL_ID 0x440CE1773607F675

	#define HTOLE16 htole16
	#define HTOLE32 htole32
	#define HTOLE64 htole64

	#define LE16TOH le16toh
	#define LE32TOH le32toh
	#define LE64TOH le64toh

	#define HTOBE16 // no htobe*
	#define HTOBE32
	#define HTOBE64

	#define BE16TOH // no betoh*
	#define BE32TOH
	#define BE64TOH
#else
	#define SERVER_PORT htobe16(0x1F96)
	#define PROTOCOL_ID htole64(0x75F6073677E10C44)

	#define HTOLE16 htole16
	#define HTOLE32 htole32
	#define HTOLE64 htole64

	#define LE16TOH le16toh
	#define LE32TOH le32toh
	#define LE64TOH le64toh

	#define HTOBE16 htobe16
	#define HTOBE32 htobe32
	#define HTOBE64 htobe64

	#define BE16TOH be16toh
	#define BE32TOH be32toh
	#define BE64TOH be64toh
#endif

//uint32_t HTOLE32F(f32 f);
//f32 LE32FTOH(uint32_t i);
#define HTOLE32F
#define LE32FTOH

#define MSG_TYPE_ASTEROID_POS (uint8_t) 0x00

#define MSG_TYPE_PLAYER_POS (uint8_t) 0x01

#define MSG_TYPE_SUN_POS (uint8_t) 0x02
#define MSG_SIZE_SUN_POS (uint16_t) 21

#define MSG_TYPE_ASTEROID_DESTROYED (uint8_t) 0x03

#define MSG_TYPE_EXO_HIT (uint8_t) 0x04
#define MSG_SIZE_EXO_HIT (uint16_t) 89

#define MSG_TYPE_PLAYER_DISCONNECTED (uint8_t) 0x05

#define MSG_TYPE_EXO_HIT_RECVD (uint8_t) 0x80

#define MSG_TYPE_ASTEROID_DESTROYED_RECVD (uint8_t) 0x81

#define MSG_TYPE_PLAYER_DISCONNECTED_RECVD (uint8_t) 0x82

#define MSG_TYPE_REGISTER_ACCEPTED (uint8_t) 0xC0

#define MSG_TYPE_BAD_REGISTER_VALUE (uint8_t) 0xFE

#define MSG_TYPE_REGISTER (uint8_t) 0xFF


code:
/*
    James William Fletcher (github.com/mrbid)
        September 2021

    Portable floating-point Vec3 lib with basic SSE support
*/

#if !defined(__linux__) || defined(NOSSE)
    #define SEIR_RAND
#endif

#ifdef SEIR_RAND
int srandfq = 74235;
#else
__int64_t srandfq = 74235;
#endif


code:
/*
    James William Fletcher (github.com/mrbid)
        September 2021

    Portable floating-point Vec3 lib with basic SSE support
*/

#ifndef VEC_H
#define VEC_H

#include <math.h>
#include <string.h>

// #define NOSSE
#if !defined(__linux__) || defined(NOSSE)
    #define SEIR_RAND
#endif

#ifndef NOSSE
    #include <x86intrin.h>
#endif

#define PI 3.141592741f         // PI
#define x2PI 6.283185482f       // PI * 2
#define d2PI 1.570796371f       // PI / 2
#define DEGREE 57.29578018f     // 1 Radian as Degrees
#define RADIAN 0.01745329238f   // PI / 180 (1 Degree as Radians)
#define RAD2DEG DEGREE
#define DEG2RAD RADIAN

#define FLOAT_MAX 9223372036854775807.0f
#define INV_FLOAT_MAX 1.084202172e-19F

typedef struct{
    float x,y,z,w;
} vec;

static inline float rsqrtss(float f);
static inline float sqrtps(float f);
static inline float randf();  // uniform [0 to 1]
static inline float randfc(); // uniform [-1 to 1]
static inline float randfn(); // box-muller normal [bi-directional]
static inline int vec_ftoi(float f); // float to integer quantise

// normalising the result is optional / at the callers responsibility
static inline void vRuv(vec* v);   // Random Unit Vector
static inline void vRuvN(vec* v);  // Normal Random Unit Vector
static inline void vRuvBT(vec* v); // Brian Tung Random Unit Vector (on surface of unit sphere)
static inline void vRuvTA(vec* v); // T.Davison Trial & Error (inside unit sphere)
static inline void vRuvTD(vec* v); // T.Davison Random Unit Vector Sphere

static inline void  vCross(vec* r, const vec v1, const vec v2);
static inline float vDot(const vec v1, const vec v2);
static inline float vSum(const vec v);
static inline void  vReflect(vec* r, const vec v, const vec n);

static inline int  vEqualTol(const vec a, const vec b, const float tol);
static inline int  vEqualInt(const vec a, const vec b);
static inline void vMin(vec* r, const vec v1, const vec v2);
static inline void vMax(vec* r, const vec v1, const vec v2);

static inline void  vNorm(vec* v);
static inline float vDist(const vec v1, const vec v2);
static inline float vDistSq(const vec a, const vec b);
static inline float vDistMh(const vec a, const vec b); // manhattan
static inline float vDistLa(const vec a, const vec b); // longest axis
static inline float vMod(const vec v); // modulus
static inline float vMag(const vec v); // magnitude
static inline void  vInv(vec* v); // invert
static inline void  vCopy(vec* r, const vec v);
static inline void  vDir(vec* r, const vec v1, const vec v2); // direction vector from v1 to v2

static inline void vRotX(vec* v, const float radians);
static inline void vRotY(vec* v, const float radians);
static inline void vRotZ(vec* v, const float radians);

static inline void vAdd(vec* r, const vec v1, const vec v2);
static inline void vSub(vec* r, const vec v1, const vec v2);
static inline void vDiv(vec* r, const vec numerator, const vec denominator);
static inline void vMul(vec* r, const vec v1, const vec v2);

static inline void vAddS(vec* r, const vec v1, const float v2);
static inline void vSubS(vec* r, const vec v1, const float v2);
static inline void vDivS(vec* r, const vec v1, const float v2);
static inline void vMulS(vec* r, const vec v1, const float v2);

//

static inline float rsqrtss(float f)
{
#ifdef NOSSE
    return 1.f/sqrtf(f);
#else
    return _mm_cvtss_f32(_mm_rsqrt_ss(_mm_set_ss(f)));
#endif
}

static inline float sqrtps(float f)
{
#ifdef NOSSE
    return sqrtf(f);
#else
    return _mm_cvtss_f32(_mm_sqrt_ps(_mm_set_ss(f)));
#endif
}

#ifdef SEIR_RAND

// https://www.musicdsp.org/en/latest/Other/273-fast-float-random-numbers.html
// moc.liamg@seir.kinimod

extern int srandfq;
static inline void srandf(const int seed)
{
    srandfq = seed;
}

static inline float randf()
{
    srandfq *= 16807;
    return (float)(srandfq & 0x7FFFFFFF) * 4.6566129e-010f;
}

static inline float randfc()
{
    srandfq *= 16807;
    return ((float)(srandfq)) * 4.6566129e-010f;
}

#else

// adapted from ogre3d asm_math.h
// https://www.flipcode.com/archives/07-15-2002.shtml
// https://www.cs.cmu.edu/afs/andrew/scs/cs/oldfiles/15-494-sp09/dst/A/sw/ogre-1.6.4/OgreMain/include/asm_math.h
// https://gist.github.com/mrbid/9a050ee747a9188bc0aa849385bef865#file-rand_float_normal_bench-c-L63

extern __int64_t srandfq;
static inline void srandf(const __int64_t seed)
{
    srandfq = seed;
}

static inline float randf()
{
    __m64 mm0 = _mm_cvtsi64_m64(srandfq);
    __m64 mm1 = _m_pshufw(mm0, 0x1E);
    mm0 = _mm_add_pi32(mm0, mm1);
    srandfq = _m_to_int64(mm0);
    _m_empty();
    return fabsf((float)srandfq) * INV_FLOAT_MAX;
}

static inline float randfc()
{
    __m64 mm0 = _mm_cvtsi64_m64(srandfq);
    __m64 mm1 = _m_pshufw(mm0, 0x1E);
    mm0 = _mm_add_pi32(mm0, mm1);
    srandfq = _m_to_int64(mm0);
    _m_empty();
    return ((float)srandfq) * INV_FLOAT_MAX;
}

#endif

static inline float randfn()
{
    float u = randfc();
    float v = randfc();
    float r = u * u + v * v;
    while(r == 0.f || r > 1.f)
    {
        u = randfc();
        v = randfc();
        r = u * u + v * v;
    }
    return u * sqrtps(-2.f * logf(r) / r);
}

static inline void vRuv(vec* v)
{
    v->x = randfc();
    v->y = randfc();
    v->z = randfc();
}

static inline void vRuvN(vec* v)
{
    v->x = randfn();
    v->y = randfn();
    v->z = randfn();
}

static inline void vRuvBT(vec* v)
{
    // https://math.stackexchange.com/a/1586185
    // or should I have called this vRuvLR()
    // https://mathworld.wolfram.com/SpherePointPicking.html
    const float y = acosf(randfc()) - d2PI;
    const float p = x2PI * randf();
    v->x = cosf(y) * cosf(p);
    v->y = cosf(y) * sinf(p);
    v->z = sinf(y);
}

static inline void vRuvTA(vec* v)
{
    // T.P.Davison@tees.ac.uk
    while(1)
    {
        v->x = randfc();
        v->y = randfc();
        v->z = randfc();
        const float len = vMag(*v);
        if(len <= 1.0f){return;}
    }
}

static inline void vRuvTD(vec* v)
{
    // T.P.Davison@tees.ac.uk
    v->x = sinf((randf() * x2PI) - PI);
    v->y = cosf((randf() * x2PI) - PI);
    v->z = randfc();
}

static inline void vCross(vec* r, const vec v1, const vec v2)
{
    r->x = (v1.y * v2.z) - (v2.y * v1.z);
    r->y = -((v1.x * v2.z) - (v2.x * v1.z));
    r->z = (v1.x * v2.y) - (v2.x * v1.y);
}

static inline float vDot(const vec v1, const vec v2)
{
    return (v1.x * v2.x) + (v1.y * v2.y) + (v1.z * v2.z);
}

static inline float vSum(const vec v)
{
    return v.x + v.y + v.z;
}

static inline void vInv(vec* v)
{
    v->x = -v->x;
    v->y = -v->y;
    v->z = -v->z;
}

static inline void vNorm(vec* v)
{
    const float len = rsqrtss(v->x*v->x + v->y*v->y + v->z*v->z);
    v->x *= len;
    v->y *= len;
    v->z *= len;
}

static inline float vDist(const vec v1, const vec v2)
{
    const float xm = (v1.x - v2.x);
    const float ym = (v1.y - v2.y);
    const float zm = (v1.z - v2.z);
    return sqrtps(xm*xm + ym*ym + zm*zm);
}

static inline float vDistSq(const vec a, const vec b)
{
    const float xm = (a.x - b.x);
    const float ym = (a.y - b.y);
    const float zm = (a.z - b.z);
    return xm*xm + ym*ym + zm*zm;
}

static inline float vDistMh(const vec a, const vec b)
{
    return (a.x - b.x) + (a.y - b.y) + (a.z - b.z);
}

static inline float vDistLa(const vec v1, const vec v2)
{
    const float xm = fabsf(v1.x - v2.x);
    const float ym = fabsf(v1.y - v2.y);
    const float zm = fabsf(v1.z - v2.z);

    float dist = xm;
    if(ym > dist)
        dist = ym;
    if(zm > dist)
        dist = zm;

    return dist;
}

static inline void vReflect(vec* r, const vec v, const vec n)
{
    const float angle = vDot(v, n);
    r->x = v.x - (2.f * n.x) * angle;
    r->y = v.y - (2.f * n.y) * angle;
    r->z = v.z - (2.f * n.z) * angle;
}

static inline int vEqualTol(const vec a, const vec b, const float tol)
{
    if( a.x >= b.x - tol && a.x <= b.x + tol &&
        a.y >= b.y - tol && a.y <= b.y + tol &&
        a.z >= b.z - tol && a.z <= b.z + tol )
        return 1;
    else
        return 0;
}

static inline void vMin(vec* r, const vec v1, const vec v2)
{
    if(v1.x < v2.x && v1.y < v2.y && v1.z < v2.z)
    {
        r->x = v1.x;
        r->y = v1.y;
        r->z = v1.z;
    }

    r->x = v2.x;
    r->y = v2.y;
    r->z = v2.z;
}

static inline void vMax(vec* r, const vec v1, const vec v2)
{
    if(v1.x > v2.x && v1.y > v2.y && v1.z > v2.z)
    {
        r->x = v1.x;
        r->y = v1.y;
        r->z = v1.z;
    }

    r->x = v2.x;
    r->y = v2.y;
    r->z = v2.z;
}

static inline int vec_ftoi(float f)
{
    if(f < 0.f)
        f -= 0.5f;
    else
        f += 0.5f;
    return (int)f;
}

static inline int vEqualInt(const vec a, const vec b)
{
    if(vec_ftoi(a.x) == vec_ftoi(b.x) && vec_ftoi(a.y) == vec_ftoi(b.y) && vec_ftoi(a.z) == vec_ftoi(b.z))
        return 1;
    else
        return 0;
}

static inline float vMod(const vec v)
{
    return sqrtps(v.x*v.x + v.y*v.y + v.z*v.z);
}

static inline float vMag(const vec v)
{
    return v.x*v.x + v.y*v.y + v.z*v.z;
}

static inline void vCopy(vec* r, const vec v)
{
    memcpy(r, &v, sizeof(vec));
}

static inline void vDir(vec* r, const vec v1, const vec v2)
{
    vSub(r, v2, v1);
    vNorm(r);
}

static inline void vRotX(vec* v, const float radians)
{
    v->y = v->y * cosf(radians) + v->z * sinf(radians);
    v->z = v->y * sinf(radians) - v->z * cosf(radians);
}

static inline void vRotY(vec* v, const float radians)
{
    v->x = v->z * sinf(radians) - v->x * cosf(radians);
    v->z = v->z * cosf(radians) + v->x * sinf(radians);
}

static inline void vRotZ(vec* v, const float radians)
{
    v->x = v->x * cosf(radians) + v->y * sinf(radians);
    v->y = v->x * sinf(radians) - v->y * cosf(radians);
}

static inline void vAdd(vec* r, const vec v1, const vec v2)
{
    r->x = v1.x + v2.x;
    r->y = v1.y + v2.y;
    r->z = v1.z + v2.z;
}

static inline void vSub(vec* r, const vec v1, const vec v2)
{
    r->x = v1.x - v2.x;
    r->y = v1.y - v2.y;
    r->z = v1.z - v2.z;
}

static inline void vDiv(vec* r, const vec numerator, const vec denominator)
{
    r->x = numerator.x / denominator.x;
    r->y = numerator.y / denominator.y;
    r->z = numerator.z / denominator.z;
}

static inline void vMul(vec* r, const vec v1, const vec v2)
{
    r->x = v1.x * v2.x;
    r->y = v1.y * v2.y;
    r->z = v1.z * v2.z;
}

static inline void vAddS(vec* r, const vec v1, const float v2)
{
    r->x = v1.x + v2;
    r->y = v1.y + v2;
    r->z = v1.z + v2;
}

static inline void vSubS(vec* r, const vec v1, const float v2)
{
    r->x = v1.x - v2;
    r->y = v1.y - v2;
    r->z = v1.z - v2;
}

static inline void vDivS(vec* r, const vec v1, const float v2)
{
    r->x = v1.x / v2;
    r->y = v1.y / v2;
    r->z = v1.z / v2;
}

static inline void vMulS(vec* r, const vec v1, const float v2)
{
    r->x = v1.x * v2;
    r->y = v1.y * v2;
    r->z = v1.z * v2;
}

#endif


code:
qmake flan.pro -spec linux-clang
make
strip --strip-unneeded flan
upx --lzma --best flan


code:
QT       += core gui

greaterThan(QT_MAJOR_VERSION, 4): QT += widgets

CONFIG += c++17

# You can make your code fail to compile if it uses deprecated APIs.
# In order to do so, uncomment the following line.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000    # disables all the APIs deprecated before Qt 6.0.0

SOURCES += \
    main.cpp \
    mainwindow.cpp

HEADERS += \
    mainwindow.h

FORMS += \
    mainwindow.ui

# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target

RESOURCES += \
    res.qrc


code:
#include "mainwindow.h"

#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    w.show();
    return a.exec();
}


code:
#include <QSettings>
#include <unistd.h>

#include "mainwindow.h"
#include "./ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    this->setFixedSize(this->size());
    ui->epoch->setText(QString::number(((((time_t)(((double)time(0) / 60.0) / 3.0)+1)*3)*60)));
    ui->msaa->setValue(16);
    ui->asteroids->setValue(64);

    QSettings settings;
    settings.beginGroup(this->objectName());
    QVariant center = settings.value("center");
    if(!center.isNull())
    {
        ui->center->setCheckState(Qt::CheckState(center.toInt()));
        ui->tilt->setCheckState(Qt::CheckState(settings.value("tilt").toInt()));
        ui->msaa->setValue(settings.value("msaa").toInt());
        ui->asteroids->setValue(settings.value("asteroids").toInt());
        ui->hostname->setText(settings.value("hostname").toString());
    }
    settings.endGroup();

    timerId = startTimer(1);
    ui->join->setFocus();
}

MainWindow::~MainWindow()
{
    killTimer(timerId);
    QSettings settings;
    settings.beginGroup(this->objectName());
    settings.setValue("center", QString::number(ui->center->checkState()));
    settings.setValue("tilt", QString::number(ui->tilt->checkState()));
    settings.setValue("msaa", QString::number(ui->msaa->value()));
    settings.setValue("asteroids", QString::number(ui->asteroids->value()));
    settings.setValue("hostname", ui->hostname->text());
    settings.endGroup();
    delete ui;
}

void MainWindow::timerEvent(QTimerEvent *event)
{
    if(event->timerId() == timerId)
    {
        long long eta = ui->epoch->text().toULong()-time(0);
        ui->eta->setText("ETA " + QString::number(eta) + "s");
        if(eta <= 33)
        {
            ui->eta->setStyleSheet("color:#dc3545");
            if(eta <= 0){ui->eta->setText("Missed Epoch");}
        }
        else
        {
            ui->eta->setStyleSheet("color:#20c997");
            if(eta >= 1800){ui->eta->setText("A long time");}
        }
    }
}

void MainWindow::on_quit_clicked()
{
    QApplication::quit();
}

void MainWindow::on_join_clicked()
{
    QSettings settings;
    settings.beginGroup(this->objectName());
    settings.setValue("center", QString::number(ui->center->checkState()));
    settings.setValue("tilt", QString::number(ui->tilt->checkState()));
    settings.setValue("msaa", QString::number(ui->msaa->value()));
    settings.setValue("asteroids", QString::number(ui->asteroids->value()));
    settings.setValue("hostname", ui->hostname->text());
    settings.endGroup();
    settings.sync();

    execl("bin/fat", "bin/fat", ui->epoch->text().toStdString().c_str(), ui->hostname->text().toStdString().c_str(), QString::number(ui->asteroids->value()).toStdString().c_str(), QString::number(ui->msaa->value()).toStdString().c_str(), QString::number(ui->tilt->isChecked()).toStdString().c_str(), QString::number(ui->center->isChecked()).toStdString().c_str(), (char*)NULL);
}

void MainWindow::on_reset_clicked()
{
    ui->epoch->setText(QString::number(((((time_t)(((double)time(0) / 60.0) / 3.0)+1)*3)*60)));
    ui->msaa->setValue(16);
    ui->asteroids->setValue(64);
    ui->center->setCheckState(Qt::CheckState(2));
    ui->tilt->setCheckState(Qt::CheckState(2));
    ui->hostname->setText("vfcash.co.uk");
}

void MainWindow::on_maxast_clicked()
{
    if(ui->asteroids->value() == 65535)
        ui->asteroids->setValue(16);
    else
        ui->asteroids->setValue(65535);
}

void MainWindow::on_newepoch_clicked()
{
    ui->epoch->setText(QString::number(time(0)+180));
}


code:
#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>

QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

private slots:
    void on_quit_clicked();

    void on_join_clicked();

    void on_reset_clicked();

    void on_maxast_clicked();

    void on_newepoch_clicked();

private:
    Ui::MainWindow *ui;
    void timerEvent(QTimerEvent *event);
    int timerId;
};
#endif // MAINWINDOW_H


code:
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>MainWindow</class>
 <widget class="QMainWindow" name="MainWindow">
  <property name="windowModality">
   <enum>Qt::NonModal</enum>
  </property>
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>282</width>
    <height>202</height>
   </rect>
  </property>
  <property name="sizePolicy">
   <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
    <horstretch>0</horstretch>
    <verstretch>0</verstretch>
   </sizepolicy>
  </property>
  <property name="windowTitle">
   <string>FAT Online Launcher</string>
  </property>
  <property name="windowIcon">
   <iconset resource="res.qrc">
    <normaloff>:/new/prefix1/icon.png</normaloff>:/new/prefix1/icon.png</iconset>
  </property>
  <property name="statusTip">
   <string/>
  </property>
  <widget class="QWidget" name="centralwidget">
   <widget class="QSpinBox" name="asteroids">
    <property name="geometry">
     <rect>
      <x>70</x>
      <y>80</y>
      <width>81</width>
      <height>26</height>
     </rect>
    </property>
    <property name="minimum">
     <number>16</number>
    </property>
    <property name="maximum">
     <number>65535</number>
    </property>
   </widget>
   <widget class="QLabel" name="label">
    <property name="geometry">
     <rect>
      <x>10</x>
      <y>110</y>
      <width>121</width>
      <height>21</height>
     </rect>
    </property>
    <property name="text">
     <string>Host Name or IPv4:</string>
    </property>
   </widget>
   <widget class="QLabel" name="label_2">
    <property name="geometry">
     <rect>
      <x>70</x>
      <y>60</y>
      <width>61</width>
      <height>17</height>
     </rect>
    </property>
    <property name="toolTip">
     <string>number of asteorids that will be bombarding the planet</string>
    </property>
    <property name="text">
     <string>Asteroids</string>
    </property>
   </widget>
   <widget class="QPushButton" name="join">
    <property name="geometry">
     <rect>
      <x>190</x>
      <y>160</y>
      <width>80</width>
      <height>25</height>
     </rect>
    </property>
    <property name="font">

      <bold>true</bold>

    </property>
    <property name="text">
     <string>Join Game</string>
    </property>
   </widget>
   <widget class="QLineEdit" name="hostname">
    <property name="geometry">
     <rect>
      <x>10</x>
      <y>130</y>
      <width>261</width>
      <height>25</height>
     </rect>
    </property>
    <property name="text">
     <string>vfcash.co.uk</string>
    </property>
    <property name="placeholderText">
     <string>vfcash.co.uk</string>
    </property>
   </widget>
   <widget class="QLineEdit" name="epoch">
    <property name="geometry">
     <rect>
      <x>160</x>
      <y>80</y>
      <width>111</width>
      <height>25</height>
     </rect>
    </property>
   </widget>
   <widget class="QLabel" name="label_3">
    <property name="geometry">
     <rect>
      <x>160</x>
      <y>60</y>
      <width>71</width>
      <height>20</height>
     </rect>
    </property>
    <property name="toolTip">
     <string>game start time as unix epoch</string>
    </property>
    <property name="text">
     <string>Start Epoch</string>
    </property>
   </widget>
   <widget class="QPushButton" name="quit">
    <property name="geometry">
     <rect>
      <x>10</x>
      <y>160</y>
      <width>80</width>
      <height>25</height>
     </rect>
    </property>
    <property name="text">
     <string>Quit</string>
    </property>
   </widget>
   <widget class="QFrame" name="frame">
    <property name="geometry">
     <rect>
      <x>10</x>
      <y>10</y>
      <width>261</width>
      <height>41</height>
     </rect>
    </property>
    <property name="accessibleName">
     <string/>
    </property>
    <property name="frameShape">
     <enum>QFrame::StyledPanel</enum>
    </property>
    <property name="frameShadow">
     <enum>QFrame::Raised</enum>
    </property>
    <widget class="QCheckBox" name="tilt">
     <property name="geometry">
      <rect>
       <x>170</x>
       <y>10</y>
       <width>81</width>
       <height>23</height>
      </rect>
     </property>
     <property name="toolTip">
      <string><html><head/><body><p>enable auto tilt/roll when near to the planet</p><p>can be toggled in game by pressing the 'R' key</p></body></html></string>
     </property>
     <property name="text">
      <string>Auto-Tilt</string>
     </property>
     <property name="checked">
      <bool>true</bool>
     </property>
    </widget>
    <widget class="QCheckBox" name="center">
     <property name="geometry">
      <rect>
       <x>10</x>
       <y>10</y>
       <width>121</width>
       <height>23</height>
      </rect>
     </property>
     <property name="toolTip">
      <string>start game window as centered</string>
     </property>
     <property name="text">
      <string>Center Window</string>
     </property>
     <property name="checked">
      <bool>true</bool>
     </property>
    </widget>
   </widget>
   <widget class="QSpinBox" name="msaa">
    <property name="geometry">
     <rect>
      <x>10</x>
      <y>80</y>
      <width>51</width>
      <height>26</height>
     </rect>
    </property>
    <property name="maximum">
     <number>16</number>
    </property>
    <property name="value">
     <number>16</number>
    </property>
   </widget>
   <widget class="QLabel" name="label_6">
    <property name="geometry">
     <rect>
      <x>10</x>
      <y>60</y>
      <width>41</width>
      <height>17</height>
     </rect>
    </property>
    <property name="toolTip">
     <string><html><head/><body><p>Multisample anti-aliasing for smooth edges<br/>(higher is better)</p></body></html></string>
    </property>
    <property name="text">
     <string>MSAA</string>
    </property>
   </widget>
   <widget class="QPushButton" name="reset">
    <property name="geometry">
     <rect>
      <x>100</x>
      <y>160</y>
      <width>80</width>
      <height>25</height>
     </rect>
    </property>
    <property name="text">
     <string>Reset</string>
    </property>
   </widget>
   <widget class="QPushButton" name="maxast">
    <property name="geometry">
     <rect>
      <x>130</x>
      <y>60</y>
      <width>20</width>
      <height>16</height>
     </rect>
    </property>
    <property name="toolTip">
     <string>max/min</string>
    </property>
    <property name="text">
     <string>^</string>
    </property>
   </widget>
   <widget class="QPushButton" name="newepoch">
    <property name="geometry">
     <rect>
      <x>250</x>
      <y>60</y>
      <width>20</width>
      <height>16</height>
     </rect>
    </property>
    <property name="toolTip">
     <string>new epoch</string>
    </property>
    <property name="text">
     <string>^</string>
    </property>
   </widget>
   <widget class="QLabel" name="eta">
    <property name="geometry">
     <rect>
      <x>160</x>
      <y>104</y>
      <width>111</width>
      <height>20</height>
     </rect>
    </property>
    <property name="font">

      <italic>false</italic>
      <bold>true</bold>

    </property>
    <property name="toolTip">
     <string>time until game start</string>
    </property>
    <property name="styleSheet">
     <string notr="true">color:#20c997;</string>
    </property>
    <property name="text">
     <string>ETA: 0</string>
    </property>
    <property name="alignment">
     <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
    </property>
   </widget>
  </widget>
  <widget class="QMenuBar" name="menubar">
   <property name="geometry">
    <rect>
     <x>0</x>
     <y>0</y>
     <width>282</width>
     <height>22</height>
    </rect>
   </property>
  </widget>
 </widget>
 <resources>
  <include location="res.qrc"/>
 </resources>
 <connections/>
</ui>


code:
<RCC>
    <qresource prefix="/new/prefix1">
        <file>icon.png</file>
    </qresource>
</RCC>


code:
INCLUDEFLAGS = -I.

CFLAGS += $(INCLUDEFLAGS) -Ofast -DSEIR_RAND

ifneq ($(native), false)
	CFLAGS += -march=native
endif

ifeq ($(debug), true)
	CFLAGS += -DVERBOSE -DDEBUG_GL
endif

ifeq ($(test), true)
	CFLAGS += -DFAST_START
endif

LDFLAGS = -lm

DEPS = $(shell $(CC) $(INCLUDEFLAGS) -MM -MT $(1).o $(1).c | sed -z 's/\\\n //g')

.PHONY: all clean cleanall release
all: bin bin/fat bin/fatserver

bin:
	mkdir bin

$(call DEPS,client/main)
	$(CC) $(CFLAGS) -c $< -o $@

$(call DEPS,client/glad_gl)
	$(CC) $(CFLAGS) -c $< -o $@

# processing models.c is too slow, hardcode
#$(call DEPS,assets/models)
#	$(CC) $(CFLAGS) -c $< -o $@
assets/client_models.o: assets/models.c assets/models.h
	$(CC) $(CFLAGS) -c $< -o $@

assets/images.o: assets/images.c assets/images.h
	$(CC) $(CFLAGS) -c $< -o $@

$(call DEPS,inc/vec)
	$(CC) $(CFLAGS) -c $< -o $@

#$(call DEPS,inc/protocol)
#	$(CC) $(CFLAGS) -c $< -o $@

inc/munmap.o: inc/munmap.asm
	nasm -f elf64 $< -o $@

bin/fat: client/main.o client/glad_gl.o assets/client_models.o assets/images.o inc/vec.o
	$(CC) $^ $(LDFLAGS) -lglfw -lpthread -o $@

$(call DEPS,server/main)
	$(CC) $(CFLAGS) -c $< -o $@

$(call DEPS,server/utils)
	$(CC) $(CFLAGS) -c $< -o $@

assets/server_models.o: assets/models.c assets/models.h
	$(CC) $(CFLAGS) -c $< -o $@ -DEXO_VERTICES_ONLY

bin/fatserver: server/main.o server/utils.o assets/server_models.o inc/vec.o inc/munmap.o
	$(CC) $^ $(LDFLAGS) -o $@ -static

clean:
	$(RM) bin/fat bin/fatserver client/*.o server/*.o inc/*.o bin/*.upx

cleanall: clean
	$(RM) assets/*.o

release: bin/fat
	strip --strip-unneeded bin/fat
	upx --lzma --best bin/fat

fat.deb: deb/usr/games/fat deb/usr/games/fatserver
	dpkg-deb --build deb fat.deb

deb/usr/games/fat: bin/fat
	mkdir -p deb/usr/games
	cp bin/fat deb/usr/games/fat

deb/usr/games/fatserver: bin/fatserver
	mkdir -p deb/usr/games
	cp bin/fatserver deb/usr/games/fatserver

install:
	cp bin/fat $(DESTDIR)/fractalattackonline

uninstall:
	rm $(DESTDIR)/fractalattackonline

path: makefile


code:
message types:
	asteroid_pos (0x00):
		uint64_t microtimestamp;
		for each asteroid:
			uint16_t id;
			vec vel, pos;
			f32 rot, scale;

	player_pos (0x01):
		server to client:
			uint64_t microtimestamp; // only useful with prediction
			for each player:
				uint8_t id;
				vec pos;
				vec vel; // only useful with prediction
		client to server:
			uint64_t microtimestamp; // only useful with prediction
			vec pos;
			vec vel; // only useful with prediction

	sun_pos (0x02):
		uint64_t microtimestamp;
		f32 location;

	asteroid_destroyed (0x03):
		server to client: // reliable
			uint64_t microtimestamp;
			uint16_t id;
			vec newvel, newpos;
			f32 rot, scale;
		client to server: // confirmed by the server echoing the details back
			uint64_t microtimestamp;
			uint16_t id;

	exo_hit (0x04): // reliable
		uint64_t microtimestamp;
		uint16_t hit_id;
		uint16_t asteroid_id;
		vec oldvel, oldpos, newvel, newpos;
		f32 oldrot, oldscale, newrot, newscale;
		f32 damage_ratio; // > 10.f if the game should end

	player_disconnected (0x05): // reliable
			uint8_t id;

	exo_hit_recvd (0x80):
		uint16_t hid_id;

	asteroid_destroyed_recvd (0x81):
		uint16_t id;

	player_disconnected_recvd (0x82):
		uint8_t id;

	register_accepted (0xC0):
		uint8_t your_id;
		uint16_t num_asteroids; // 0 = 0x10000

	bad_register_value (0xFE): // will eventually add an asteroids count to the register type and make it dynamic
		<no data>

	register (0xFF):
		uint64_t epoch;
		uint16_t num_asteroids; // 0 = 0x10000

message format:
	uint64_t protocol_id (0x75F6073677E10C44); // don't want random udp packets
	uint8_t message type;
	<message-specific data>;


code:
make clean
make release -j`nproc` native=false


code:
#define EXO_IMPACT_RADIUS 1.14f
#define EXO_TARGET_RADIUS 1.f // if you don't want asteroids scraping the edge, set this to somewhere less than EXO_IMPACT_RADIUS

#define MIN_PACKET_SIZE 11
#define MAX_PACKET_SIZE 512

#define PLAYER_SEND_UTIME 50000 // 20hz
#define ASTEROID_SEND_UTIME 1000000 // 1hz

#define USABLE_STACK_SIZE 128*4096	// 512K, there's not really much stack in usage with these threads
#define BLOCKED_STACK_SIZE 16*4096	// 64K, this is the area below the stack to map as PROT_NONE -
					// segfault if we run out rather than overflowing into whatever may have happened to be there originally

#define TOTAL_STACK_SIZE BLOCKED_STACK_SIZE + USABLE_STACK_SIZE

#define CLONE_FLAGS CLONE_FILES | CLONE_FS | CLONE_IO | CLONE_VM | SIGCHLD


code:
#define _GNU_SOURCE
#include <stdio.h>
#include <sys/time.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdint.h>
#include <arpa/inet.h>
#include <endian.h>
#include <time.h>
#include <signal.h>
#include <sys/mman.h>
#include <sched.h>

#include "main.h"
#include "types.h"
#include "utils.h"
#include "inc/protocol.h"

#define EXO_VERTICES_ONLY
#include "assets/models.h"

#include "config.h"

#include "inc/mutex.h"
#include "inc/munmap.h"

int fd;

uint32_t main_lock = 0;
struct state_holder *states = 0;
uint64_t num_states = 0;

struct client_holder *all_clients = 0;
uint64_t num_clients = 0;
uint64_t min_time_to_impact;

int main_loop_world(void *tmp) {
	struct current_state *state = tmp; // annoying type checking
	char *my_stack = state->world_stack;

	{
		uint64_t t = state->epoch - 1;
		for (uint64_t t1 = time(0); t1 < t; t1 = time(0)) // for loop abuse
			sleep(t - t1);

		t = state->epoch * 1000000;
		for (uint64_t t1 = microtime(); t1 < t; t1 = microtime())
			usleep(t - t1);
	}

	printf("Epoch reached! Epoch: %ld, Current time: %ld\n(microepoch: %ld, microtime: %ld)\n",
			state->epoch, time(0), state->epoch * 1000000, microtime());

	rand_all_asteroids(state, state->epoch * 1000000);

	uint8_t buffer[MAX_PACKET_SIZE];
	*((uint64_t*)buffer) = PROTOCOL_ID;
	buffer[8] = MSG_TYPE_EXO_HIT; // only message we'll want to send from here

	uint64_t last_time = microtime();
	uint16_t hit_id = 0; // arbitrary number
	mutex_lock(&(state->lock));
	while (1) {
		uint16_t asteroid_id = 0;
		uint64_t soonest = state->asteroids[0].impact_time;
		for (uint32_t i = 1; i <= (uint32_t)state->max_asteroid_id; i++) {
			if (state->asteroids[i].impact_time < soonest) {
				asteroid_id = i;
				soonest = state->asteroids[i].impact_time;
			}
		}

		if (soonest <= microtime()) {
			struct asteroid *asteroid = &(state->asteroids[asteroid_id]);
			uint64_t current_time = asteroid->impact_time;
			f32 time_delta = (f32)(current_time - last_time) * 0.000001f;
			last_time = asteroid->impact_time;

			uint8_t end_game = do_exo_impact(state, asteroid->impact_pos, (asteroid->scale+(vMod(asteroid->vel)*0.1f))*1.2f);

			*((uint64_t*)(buffer+9)) = HTOLE64(current_time);
			*((uint16_t*)(buffer+17)) = HTOLE16(hit_id);
			*((uint16_t*)(buffer+19)) = HTOLE16(asteroid_id);
			*((f32*)(buffer+21)) = HTOLE32F(asteroid->vel.x);
			*((f32*)(buffer+25)) = HTOLE32F(asteroid->vel.y);
			*((f32*)(buffer+29)) = HTOLE32F(asteroid->vel.z);
			*((f32*)(buffer+33)) = HTOLE32F(asteroid->impact_pos.x);
			*((f32*)(buffer+37)) = HTOLE32F(asteroid->impact_pos.y);
			*((f32*)(buffer+41)) = HTOLE32F(asteroid->impact_pos.z);
			// @+45
			// 2 more vecs, +24
			*((f32*)(buffer+69)) = HTOLE32F(asteroid->rot);
			*((f32*)(buffer+73)) = HTOLE32F(asteroid->scale);

			rand_asteroid(asteroid, current_time);

			//+45 to +64
			*((f32*)(buffer+45)) = HTOLE32F(asteroid->vel.x);
			*((f32*)(buffer+49)) = HTOLE32F(asteroid->vel.y);
			*((f32*)(buffer+53)) = HTOLE32F(asteroid->vel.z);
			*((f32*)(buffer+57)) = HTOLE32F(asteroid->start_pos.x);
			*((f32*)(buffer+61)) = HTOLE32F(asteroid->start_pos.y);
			*((f32*)(buffer+65)) = HTOLE32F(asteroid->start_pos.z);
			// @+69
			// 2 f32s, +8
			*((f32*)(buffer+77)) = HTOLE32F(asteroid->rot);
			*((f32*)(buffer+81)) = HTOLE32F(asteroid->scale);
			if (end_game)
				*((f32*)(buffer+85)) = HTOLE32F(20.f);
			else
				*((f32*)(buffer+85)) = HTOLE32F((f32)state->damage / (exo_numvert/2));
			// total size: 89

			for (uint16_t pid = 0; pid <= (uint16_t)state->max_player_id; pid++)
				if (state->players[pid].valid)
					sendto(fd, buffer, MSG_SIZE_EXO_HIT, 0, (struct sockaddr*)&state->players[pid].address, sizeof(struct sockaddr));

			hit_id++;

			if (end_game) {
				mutex_unlock(&(state->lock));
				mutex_lock(&main_lock);
				mutex_lock(&(state->lock)); // ordering to avoid deadlocks

				kill(state->player_thread, SIGKILL);
				munmap(state->player_stack, TOTAL_STACK_SIZE);
				kill(state->asteroid_thread, SIGKILL);
				munmap(state->asteroid_stack, TOTAL_STACK_SIZE);
				// don't kill self :P

				for (uint16_t pid = 0; pid <= (uint16_t)state->max_player_id; pid++)
					if (state->players[pid].valid)
						all_clients[state->players[pid].client_index].valid = 0;

				printf("World %lu finished.\n", state->epoch);
				states[state->state_index].epoch = 0;

				free(state->players);
				free(state->asteroids);
				free(state->damage_index);
				free(state);

				mutex_unlock(&main_lock);

				// signals between unmapping and exiting will segfault and kill the main thread, don't listen to them
				sigset_t sigset;
				sigfillset(&sigset);
				sigprocmask(SIG_SETMASK, &sigset, 0);

				munmap_and_exit(my_stack, TOTAL_STACK_SIZE);
			}
		} else {
			mutex_unlock(&(state->lock));

			{
				uint64_t min_time = min_time_to_impact+microtime();
				if (soonest > min_time)
					soonest = min_time;
			}

			for (uint64_t t1 = microtime(); t1 < (soonest - 1000000); t1 = microtime())
				sleep((soonest - t1) / 1000000);

			for (uint64_t t1 = microtime(); t1 < soonest; t1 = microtime())
				usleep(soonest - t1);

			mutex_lock(&(state->lock));
		}
	}

	return 0;
}

int main_loop_send_asteroids(void *tmp) {
	struct current_state *state = tmp; // annoying type checking

	uint64_t current_time;
	uint64_t last_time = microtime();

	uint8_t buffer[MAX_PACKET_SIZE];
	*((uint64_t*)buffer) = PROTOCOL_ID;
	while (1) {
		current_time = microtime();
		mutex_lock(&(state->lock));

		buffer[8] = MSG_TYPE_ASTEROID_POS;
		*((uint64_t*)(buffer+9)) = HTOLE64(current_time);
		uint16_t size = 17;
		for (uint32_t asteroid_id = 0; asteroid_id <= (uint32_t)state->max_asteroid_id; asteroid_id++) {
			f32 time_diff = (f32)(int64_t)(current_time - state->asteroids[asteroid_id].gen_time) * 0.000001f;

			vec pos = {
				state->asteroids[asteroid_id].start_pos.x + state->asteroids[asteroid_id].vel.x * time_diff,
				state->asteroids[asteroid_id].start_pos.y + state->asteroids[asteroid_id].vel.y * time_diff,
				state->asteroids[asteroid_id].start_pos.z + state->asteroids[asteroid_id].vel.z * time_diff
			};

			*((uint16_t*)(buffer+size)) = HTOLE16((uint16_t)asteroid_id);
			*((f32*)(buffer+size+2)) = HTOLE32F(state->asteroids[asteroid_id].vel.x);
			*((f32*)(buffer+size+6)) = HTOLE32F(state->asteroids[asteroid_id].vel.y);
			*((f32*)(buffer+size+10)) = HTOLE32F(state->asteroids[asteroid_id].vel.z);
			*((f32*)(buffer+size+14)) = HTOLE32F(pos.x);
			*((f32*)(buffer+size+18)) = HTOLE32F(pos.y);
			*((f32*)(buffer+size+22)) = HTOLE32F(pos.z);
			*((f32*)(buffer+size+26)) = HTOLE32F(state->asteroids[asteroid_id].rot);
			*((f32*)(buffer+size+30)) = HTOLE32F(state->asteroids[asteroid_id].scale);
			size += 34;

			if (size+34 > MAX_PACKET_SIZE || (uint16_t)asteroid_id == state->max_asteroid_id) {
				for (uint16_t pid = 0; pid <= (uint16_t)state->max_player_id; pid++)
					if (state->players[pid].valid)
						sendto(fd, buffer, size, 0, (struct sockaddr*)&state->players[pid].address, sizeof(struct sockaddr));

				size = 17;
			}
		}

		state->sun_pos += (f32)(current_time - last_time) * 0.000001f * 0.03f;
		{
			int64_t tmp = (int64_t)(state->sun_pos / (PI * 4.f)); // only want whole numbers
			if (tmp > 0)
				state->sun_pos = state->sun_pos - (PI * 4.f * (f32)tmp);
		}

		buffer[8] = MSG_TYPE_SUN_POS;
		*((f32*)(buffer+17)) = HTOLE32F(state->sun_pos);
		for (uint16_t pid = 0; pid <= (uint16_t)state->max_player_id; pid++)
			if (state->players[pid].valid)
				sendto(fd, buffer, MSG_SIZE_SUN_POS, 0, (struct sockaddr*)&state->players[pid].address, sizeof(struct sockaddr));

		mutex_unlock(&(state->lock));
		last_time = current_time;
		register uint64_t t = microtime();
		if (t < last_time + ASTEROID_SEND_UTIME)
			usleep(last_time + ASTEROID_SEND_UTIME - t);
		else
			fprintf(stderr, "WARNING: Asteroid send took too long, skipping sleep! (%luus)\n", t-last_time);
	}

	return 0;
}

int main_loop_send_players(void *tmp) {
	struct current_state *state = tmp; // annoying type checking

	uint64_t current_time;
	uint64_t last_time = microtime();

	uint8_t buffer[MAX_PACKET_SIZE];
	*((uint64_t*)buffer) = PROTOCOL_ID;
	buffer[8] = MSG_TYPE_PLAYER_POS;
	while (1) {
		current_time = microtime();
		mutex_lock(&(state->lock));

		f32 scale = (f32)(current_time - last_time) * 0.000001f;

		*((uint64_t*)(buffer+9)) = HTOLE64(current_time);
		uint16_t size = 17;
		for (uint16_t player_id = 0; player_id <= (uint16_t)state->max_player_id; player_id++) {
			if (state->players[player_id].valid) {
				*((uint8_t*)(buffer+size)) = player_id;

				state->players[player_id].pos.x += state->players[player_id].vel.x * scale;
				state->players[player_id].pos.y += state->players[player_id].vel.y * scale;
				state->players[player_id].pos.z += state->players[player_id].vel.z * scale;

				*((f32*)(buffer+size+1)) = HTOLE32F(state->players[player_id].pos.x);
				*((f32*)(buffer+size+5)) = HTOLE32F(state->players[player_id].pos.y);
				*((f32*)(buffer+size+9)) = HTOLE32F(state->players[player_id].pos.z);
				*((f32*)(buffer+size+13)) = HTOLE32F(state->players[player_id].vel.x);
				*((f32*)(buffer+size+17)) = HTOLE32F(state->players[player_id].vel.y);
				*((f32*)(buffer+size+21)) = HTOLE32F(state->players[player_id].vel.z);
				size += 25;
			}

			if (size+25 > MAX_PACKET_SIZE || (uint8_t)player_id == state->max_player_id) {
				for (uint16_t pid = 0; pid <= (uint16_t)state->max_player_id; pid++)
					if (state->players[pid].valid)
						sendto(fd, buffer, size, 0, (struct sockaddr*)&state->players[pid].address, sizeof(struct sockaddr));

				size = 17;
			}
		}

		state->last_player_update = current_time;

		mutex_unlock(&(state->lock));
		last_time = current_time;
		register uint64_t t = microtime();
		if (t < last_time + PLAYER_SEND_UTIME)
			usleep(last_time + PLAYER_SEND_UTIME - t);
		else
			fprintf(stderr, "WARNING: Player send took too long, skipping sleep! (%luus)\n", t-last_time);
	}

	return 0;
}

#define GFX_SCALE 0.01f
void scaleBuffer(GLfloat* b, GLsizeiptr s) {
	for(GLsizeiptr i = 0; i < s; i++)
		b[i] *= GFX_SCALE * 1.025;
}

int main(int argc, char **argv) {
	{
		struct sigaction ignore_sigchld;
		ignore_sigchld.sa_handler = SIG_IGN;
		if (sigaction(SIGCHLD, &ignore_sigchld, 0)) {
			fputs("ERROR: Unable to setup signal handler!\n(this should never actually happen)\n", stderr);
			return 1;
		}
	}

	min_time_to_impact = min_asteroid_time();

	scaleBuffer(exo_vertices, exo_numvert*3);

	fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
	if (fd < 0) {
		fputs("ERROR: Unable to create socket!\n", stderr);
		return 1;
	}

	{ // no reason to keep `server` and `len` around
		struct sockaddr_in server;
		server.sin_family = AF_INET;
		server.sin_addr.s_addr = INADDR_ANY;

		if (argc > 1) {
			uint32_t port;
			if (strlen(argv[1]) > 5 || sscanf(argv[1], "%d", &port) != 1 || port > 65535) {
				fprintf(stderr, "ERROR: Invalid syntax! Syntax: %s [port]\n", argv[0]);
				return 2;
			} else if (port == 0) {
				fputs("WARNING: Port 0 is automatically chosen by the OS; if you do not intend this, restart this program and select a different port.\n", stderr);
			}
			server.sin_port = HTOBE16((uint16_t)port);
		} else {
			server.sin_port = SERVER_PORT;
		}

		if (bind(fd, (struct sockaddr*)&server, sizeof(server)) != 0) {
			fputs("ERROR: Unable to bind socket!\n", stderr);
			return 1;
		}

		socklen_t len = sizeof(server);
		if (getsockname(fd, (struct sockaddr*)&server, &len) == 0)
			printf("Using port %d.\n", BE16TOH(server.sin_port));
	}

	uint8_t buffer[MAX_PACKET_SIZE];
	while (1) {
		// TODO: Resend stuff that should be reliable here

		struct sockaddr_in source;
		socklen_t source_len = sizeof(source);
		ssize_t msg_len = recvfrom(fd, buffer, MAX_PACKET_SIZE, 0, (struct sockaddr*)&source, &source_len);
		uint8_t *addr = (uint8_t*)&source.sin_addr.s_addr;
		if (msg_len < MIN_PACKET_SIZE) {
			fputs("Packet too small!\n", stderr);
			continue;
		} else if (((uint64_t*)buffer)[0] != PROTOCOL_ID) {
			fputs("Invalid ID given!\n", stderr);
			continue;
		}

		uint64_t current_time = microtime();

		switch (buffer[8]) {
		case MSG_TYPE_PLAYER_POS:
			if (msg_len < 17+(4*6)) {
				fputs("Packet too small for its type!\n", stderr);
				break;
			}

			mutex_lock(&main_lock);

			uint64_t sent_time = LE64TOH(*((uint64_t*)(buffer+9)));

			for (uint64_t i = 0; i < num_clients; i++) {
				if (all_clients[i].address.sin_addr.s_addr == source.sin_addr.s_addr && all_clients[i].address.sin_port == source.sin_port && all_clients[i].valid) {
					mutex_lock(&(states[all_clients[i].state_index].state->lock));

					vec pos, vel;

					f32 scale = (f32)(int64_t)(states[all_clients[i].state_index].state->last_player_update - sent_time) * 0.000001f;

					pos.x = LE32FTOH(*((f32*)(buffer+17)));
					pos.y = LE32FTOH(*((f32*)(buffer+21)));
					pos.z = LE32FTOH(*((f32*)(buffer+25)));

					vel.x = LE32FTOH(*((f32*)(buffer+29)));
					vel.y = LE32FTOH(*((f32*)(buffer+33)));
					vel.z = LE32FTOH(*((f32*)(buffer+37)));

					pos.x += vel.x*scale;
					pos.y += vel.y*scale;
					pos.z += vel.z*scale;

					states[all_clients[i].state_index].state->players[all_clients[i].player_id].pos.x = pos.x;
					states[all_clients[i].state_index].state->players[all_clients[i].player_id].pos.y = pos.y;
					states[all_clients[i].state_index].state->players[all_clients[i].player_id].pos.z = pos.z;

					states[all_clients[i].state_index].state->players[all_clients[i].player_id].vel.x = vel.x;
					states[all_clients[i].state_index].state->players[all_clients[i].player_id].vel.y = vel.y;
					states[all_clients[i].state_index].state->players[all_clients[i].player_id].vel.z = vel.z;

					mutex_unlock(&(states[all_clients[i].state_index].state->lock));

					all_clients[i].last_recvd = current_time;
					break;
				}
			}

			mutex_unlock(&main_lock);
			break;
		case MSG_TYPE_ASTEROID_DESTROYED:
			if (msg_len < 19) {
				fputs("Packet too small for its type!\n", stderr);
				break;
			}

			mutex_lock(&main_lock);

			for (uint64_t i = 0; i < num_clients; i++) {
				if (all_clients[i].address.sin_addr.s_addr == source.sin_addr.s_addr && all_clients[i].address.sin_port == source.sin_port && all_clients[i].valid) {
					mutex_lock(&(states[all_clients[i].state_index].state->lock));

					all_clients[i].last_recvd = current_time;

					uint64_t microtimestamp = LE64TOH(*((uint64_t*)(buffer+9)));
					uint16_t asteroid_id = LE16TOH(*((uint16_t*)(buffer+17)));
					if (asteroid_id > states[all_clients[i].state_index].state->max_asteroid_id) {
						fputs("Client attempted to destroy invalid asteroid!\n", stderr);
						goto skip_asteroid_destroyed;
					}

					struct asteroid *asteroid = &(states[all_clients[i].state_index].state->asteroids[asteroid_id]);

					uint8_t send_all;
					if (states[all_clients[i].state_index].state->asteroids[asteroid_id].gen_time < microtimestamp) {
						rand_asteroid(asteroid, microtimestamp);
						send_all = 1;
					} else {
						send_all = 0;
					}

					buffer[8] = MSG_TYPE_ASTEROID_DESTROYED;
					*((uint64_t*)(buffer+9)) = HTOLE64(asteroid->gen_time);
					*((uint16_t*)(buffer+17)) = HTOLE16(asteroid_id);
					*((f32*)(buffer+19)) = HTOLE32F(asteroid->vel.x);
					*((f32*)(buffer+23)) = HTOLE32F(asteroid->vel.y);
					*((f32*)(buffer+27)) = HTOLE32F(asteroid->vel.z);
					*((f32*)(buffer+31)) = HTOLE32F(asteroid->start_pos.x);
					*((f32*)(buffer+35)) = HTOLE32F(asteroid->start_pos.y);
					*((f32*)(buffer+39)) = HTOLE32F(asteroid->start_pos.z);
					*((f32*)(buffer+43)) = HTOLE32F(asteroid->rot);
					*((f32*)(buffer+47)) = HTOLE32F(asteroid->scale);

					if (send_all) {
						for (uint16_t pid = 0; pid <= (uint16_t)states[all_clients[i].state_index].state->max_player_id; pid++) {
							if (states[all_clients[i].state_index].state->players[pid].valid)
								sendto(fd, buffer, 51, 0, (struct sockaddr*)&states[all_clients[i].state_index].state->players[pid].address, sizeof(struct sockaddr_in));
						}
					} else {
						sendto(fd, buffer, 51, 0, (struct sockaddr*)&source, source_len);
					}

					skip_asteroid_destroyed:
					mutex_unlock(&(states[all_clients[i].state_index].state->lock));
					break;
				}
			}

			mutex_unlock(&main_lock);
			break;
		case MSG_TYPE_EXO_HIT_RECVD:
			break;
		case MSG_TYPE_ASTEROID_DESTROYED_RECVD:
			break;
		case MSG_TYPE_PLAYER_DISCONNECTED_RECVD:
			break;
		case MSG_TYPE_REGISTER:
			if (msg_len < 19) {
				fputs("Packet to small for its type!\n", stderr);
				break;
			}

			uint64_t requested_epoch = LE64TOH(*((uint64_t*) &(buffer[9])));
			uint32_t num_asteroids = LE16TOH(*((uint16_t*) &(buffer[17])));
			if (num_asteroids == 0)
				num_asteroids = 0x10000;
			else if (num_asteroids < 0x10)
				goto register_error;

			if (current_time >= requested_epoch * 1000000) // TODO: cap the upper limit, not wait 5 years for a game?
				goto register_error;

			uint8_t found = 0;
			uint64_t client_index;
			uint64_t state_index;
			uint64_t available = 0;

			mutex_lock(&main_lock);

			for (uint64_t i = 0; i < num_clients; i++) {
				if (all_clients[i].address.sin_addr.s_addr == source.sin_addr.s_addr && all_clients[i].address.sin_port == source.sin_port && all_clients[i].valid) {
					if (all_clients[i].epoch != requested_epoch) { // client is already registered with a different epoch...
						goto register_error_unlock;
					} else {
						state_index = all_clients[i].state_index;
						goto send_id;
					}
				} else if (!all_clients[i].valid && available) {
					available = 1;
					client_index = i;
				}
			}

			if (available) {
				available = 0;
			} else {
				void *tmp = realloc(all_clients, sizeof(*all_clients) * (num_clients + 1));
				if (tmp == 0)
					goto register_error_unlock;

				all_clients = tmp;
				client_index = num_clients;
				num_clients++;
			}

			for (uint64_t i = 0; i < num_states; i++) {
				if (states[i].epoch == requested_epoch) {
					found = 1;
					state_index = i;
					break;
				} else if (states[i].epoch == 0 && !available) {
					available = 1;
					state_index = i;
				}
			}

			struct current_state *state;
			uint64_t player_index;
			if (found) {
				state = states[state_index].state;
				mutex_lock(&(state->lock));
				for (uint16_t i = 0; i <= (uint16_t) state->max_player_id; i++) {
					if (!state->players[i].valid) {
						available = 1;
						player_index = i;
						break;
					}
				}

				if (!available) {
					if (state->max_player_id == 0xFF) {
						goto register_error_unlock;
					}

					void *tmp = realloc(state->players, sizeof(*(state->players)) * (state->max_player_id + 2));
					if (tmp == 0) {
						goto register_error_unlock;
					}
					state->players = tmp;

					state->max_player_id++;
					player_index = state->max_player_id;
				}
			} else {
				player_index = 0;
				if (!available) {
					void *tmp = realloc(states, sizeof(*states) * (num_states + 1));
					if (tmp == 0)
						goto register_error_unlock;

					states = tmp;
					state_index = num_states;
					num_states++;
				}

				state = malloc(sizeof(*state));
				if (state == 0) // AAAAAAA OOM
					goto register_error_unlock;

				states[state_index].state = state;
				states[state_index].epoch = requested_epoch;

				memset(state, 0, sizeof(*state));

				state->players = malloc(sizeof(*(state->players)));
				if (state->players == 0) {
					goto register_error_clear;
				}
				state->max_player_id = 0;

				state->asteroids = malloc(sizeof(*(state->asteroids)) * num_asteroids);
				if (state->asteroids == 0) {
					free(state->players);

					goto register_error_clear;
				}

				state->max_asteroid_id = num_asteroids - 1;
				memset(state->asteroids, 0, sizeof(*(state->asteroids)) * num_asteroids);
				state->damage = 0;

				state->damage_index = malloc(sizeof(*state->damage_index) * exo_numvert);
				if (state->damage_index == 0) {
					free(state->players);
					free(state->asteroids);

					goto register_error_clear;
				}
				memset(state->damage_index, 0, sizeof(*state->damage_index) * exo_numvert);

				state->epoch = requested_epoch;
				state->state_index = state_index;

				mutex_lock(&(state->lock));

				state->world_stack = mmap(0, TOTAL_STACK_SIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
				if (state->world_stack == 0) {
					goto register_error_clear_all;
				}
				mprotect(state->world_stack, BLOCKED_STACK_SIZE, PROT_NONE);

				state->asteroid_stack = mmap(0, TOTAL_STACK_SIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
				if (state->world_stack == 0) {
					munmap(state->world_stack, TOTAL_STACK_SIZE);
					goto register_error_clear_all;
				}
				mprotect(state->asteroid_stack, BLOCKED_STACK_SIZE, PROT_NONE);

				state->player_stack = mmap(0, TOTAL_STACK_SIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
				if (state->world_stack == 0) {
					munmap(state->world_stack, TOTAL_STACK_SIZE);
					munmap(state->asteroid_stack, TOTAL_STACK_SIZE);
					goto register_error_clear_all;
				}
				mprotect(state->player_stack, BLOCKED_STACK_SIZE, PROT_NONE);

				state->world_thread = clone(&main_loop_world, state->world_stack+TOTAL_STACK_SIZE, CLONE_FLAGS, state);
				state->asteroid_thread = clone(&main_loop_send_asteroids, state->asteroid_stack+TOTAL_STACK_SIZE, CLONE_FLAGS, state);
				state->player_thread = clone(&main_loop_send_players, state->player_stack+TOTAL_STACK_SIZE, CLONE_FLAGS, state);
			}

			state->players[player_index].address.sin_family = AF_INET;
			state->players[player_index].address.sin_addr.s_addr = source.sin_addr.s_addr;
			state->players[player_index].address.sin_port = source.sin_port;
			state->players[player_index].client_index = client_index;
			state->players[player_index].pos.x = 0.f;
			state->players[player_index].pos.y = 0.f;
			state->players[player_index].pos.z = 0.f;
			state->players[player_index].vel.x = 0.f;
			state->players[player_index].vel.y = 0.f;
			state->players[player_index].vel.z = 0.f;
			state->players[player_index].valid = 1;

			all_clients[client_index].state_index = state_index;
			all_clients[client_index].address.sin_addr.s_addr = source.sin_addr.s_addr;
			all_clients[client_index].address.sin_port = source.sin_port;
			all_clients[client_index].last_recvd = current_time;
			all_clients[client_index].player_id = player_index;
			all_clients[client_index].epoch = requested_epoch;
			all_clients[client_index].valid = 1;

			state->sun_pos = 0.f;

			mutex_unlock(&(state->lock));

			send_id:
			*((uint16_t*)(buffer+10)) = HTOLE16(states[state_index].max_asteroid_id + 1);

			mutex_unlock(&main_lock);
			buffer[8] = MSG_TYPE_REGISTER_ACCEPTED;
			buffer[9] = (uint8_t)player_index;
			sendto(fd, buffer, 10, 0, (struct sockaddr*)&source, source_len);

			break;

			register_error_clear_all:
			// not really all (stacks untouched), TODO: rename

			free(states[state_index].state->damage_index);
			free(states[state_index].state->players);
			free(states[state_index].state->asteroids);

			register_error_clear:
			free(states[state_index].state);
			states[state_index].epoch = 0;

			register_error_unlock:
			mutex_unlock(&main_lock);

			register_error:
			buffer[8] = MSG_TYPE_BAD_REGISTER_VALUE;
			sendto(fd, buffer, 9, 0, (struct sockaddr*)&source, source_len);
			break;

		default:
			fputs("Invalid message type!\n", stderr);
			break;
		}
	}

	return 0;
}


code:
#pragma once

extern int fd;
extern uint32_t main_lock;
extern struct state_holder *states;
extern uint64_t num_states;
extern struct client_holder *clients;
extern uint64_t num_clients;


code:
#pragma once

#include <stdint.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>

#include "inc/gl.h"
#include "inc/vec.h"

#define f32 GLfloat

struct asteroid {
	vec vel, start_pos, impact_pos;
	f32 rot, scale;
	uint64_t gen_time;
	uint64_t impact_time;
};

struct player {
	uint8_t valid;
	vec vel, pos;
	struct sockaddr_in address;
	uint64_t client_index;
};

struct current_state {
	struct asteroid *asteroids;
	uint16_t max_asteroid_id;
	struct player *players;
	uint8_t max_player_id;
	f32 sun_pos;
	uint64_t damage;
	uint8_t *damage_index;
	uint64_t epoch;
	uint64_t state_index;
	uint32_t lock;

	pid_t world_thread;
	pid_t asteroid_thread;
	pid_t player_thread;

	uint8_t *world_stack;
	uint8_t *asteroid_stack;
	uint8_t *player_stack;

	uint64_t last_player_update;
};

struct state_holder {
	uint64_t epoch;
	uint16_t max_asteroid_id;
	struct current_state *state;
};

struct client_holder {
	struct sockaddr_in address;
	uint64_t epoch;
	uint64_t last_recvd;
	uint8_t valid;
	uint64_t state_index;
	uint8_t player_id;
};


code:
#include <sys/time.h>
#include <stdint.h>

#include <stdio.h>

#include "main.h"
#include "utils.h"
#include "types.h"
#include "assets/models.h"
#include "inc/vec.h"
#include "config.h"

uint64_t microtime(void) {
	struct timeval tv;
	struct timezone tz = {0};

	gettimeofday(&tv, &tz);
	return (tv.tv_sec * 1000000) + tv.tv_usec;
}

uint8_t do_exo_impact(struct current_state *state, vec pos, float f) {
	for (GLsizeiptr i = 0; i < exo_numvert; i++) {
		if (!state->damage_index[i]) {
			vec v = {exo_vertices[i*3], exo_vertices[(i*3)+1], exo_vertices[(i*3)+2]};
			if (vDistSq(v, pos) < f*f) {
				state->damage++;
				state->damage_index[i] = 1;
			}
		}
	}

	return state->damage >= (exo_numvert/2);
}

//void rand_asteroid(struct asteroid *asteroid) {
//	vRuvBT(&(asteroid->pos));
//	vMulS(&(asteroid->pos), asteroid->pos, 10.f);
//
//	vec dp;
//	vRuvTA(&dp);
//	vSub(&(asteroid->vel), asteroid->pos, dp);
//	vNorm(&(asteroid->vel));
//	vInv(&(asteroid->vel));
//
//	vec of = asteroid->vel;
//	vInv(&of);
//	vMulS(&of, of, randf() * 6.f);
//	vAdd(&(asteroid->pos), asteroid->pos, of);
//
//	asteroid->rot = randf() * 300.f;
//	asteroid->scale = 0.01f + (randf() * 0.07f);
//	f32 speed = 0.16f + (randf() * 0.08f);
//	asteroid->vel.x = asteroid->vel.x * speed;
//	asteroid->vel.y = asteroid->vel.y * speed;
//	asteroid->vel.z = asteroid->vel.z * speed;
//}

void rand_asteroid(struct asteroid *asteroid, uint64_t timestamp) {
	asteroid->gen_time = timestamp;

	asteroid->rot = randf() * 300.f;
	asteroid->scale = 0.01f + (randf() * 0.07f);
	f32 speed = 0.16f + (randf() * 0.08f);

	vRuv(&asteroid->start_pos);
	while (vSum(asteroid->start_pos) == 0.f) // can't use null vectors
		vRuv(&asteroid->start_pos);

	vNorm(&asteroid->start_pos);

	f32 dist = randfc()+10.f;
	asteroid->start_pos.x *= dist;
	asteroid->start_pos.y *= dist;
	asteroid->start_pos.z *= dist;

	vec impactpos;
	vRuv(&impactpos);
	while (vSum(impactpos) == 0.f)
		vRuv(&impactpos);

	vNorm(&impactpos);
	impactpos.x *= EXO_TARGET_RADIUS;
	impactpos.y *= EXO_TARGET_RADIUS;
	impactpos.z *= EXO_TARGET_RADIUS;

	asteroid->vel = (vec){impactpos.x - asteroid->start_pos.x, impactpos.y - asteroid->start_pos.y, impactpos.z - asteroid->start_pos.z};

	vNorm(&asteroid->vel);

	f32 a = vMag(asteroid->vel);
	f32 b = 2.f*vDot(asteroid->vel, asteroid->start_pos);
	f32 c = vMag(asteroid->start_pos) - (EXO_IMPACT_RADIUS * EXO_IMPACT_RADIUS);

	f32 dis = b*b - 4.f*a*c;
	if (dis < 0.f) // safety, it *will* impact, but it might just scrape the edge (and float inaccuracies result in a negative number)
		dis = 0.f;

	f32 dist_to_impact = (-b - sqrtps(dis))/(2.f*a); // negative root for near end
	asteroid->impact_pos = (vec){
		asteroid->start_pos.x + asteroid->vel.x*dist_to_impact,
		asteroid->start_pos.y + asteroid->vel.y*dist_to_impact,
		asteroid->start_pos.z + asteroid->vel.z*dist_to_impact
	};

	asteroid->vel.x *= speed;
	asteroid->vel.y *= speed;
	asteroid->vel.z *= speed;

	speed = vMod(asteroid->vel);
	asteroid->impact_time = timestamp + (uint64_t)(dist_to_impact * 1000000.f / speed);
}

uint64_t min_asteroid_time(void) {
	f32 speed = 0.24f;

	vec pos = {1.f,0.f,0.f};

	vNorm(&pos);

	f32 dist = 9.f;
	pos.x *= dist;
	pos.y *= dist;
	pos.z *= dist;

	vec impactpos = {1.f,0.f,0.f};

	vNorm(&impactpos);
	impactpos.x *= EXO_TARGET_RADIUS;
	impactpos.y *= EXO_TARGET_RADIUS;
	impactpos.z *= EXO_TARGET_RADIUS;

	vec vel = {impactpos.x - pos.x, impactpos.y - pos.y, impactpos.z - pos.z};

	vNorm(&vel);

	f32 a = vMag(vel);
	f32 b = 2.f*vDot(vel, pos);
	f32 c = vMag(pos) - (EXO_IMPACT_RADIUS * EXO_IMPACT_RADIUS);

	f32 dis = b*b - 4.f*a*c;
	if (dis < 0.f) // safety, it *will* impact, but it might just scrape the edge (and float inaccuracies result in a negative number)
		dis = 0.f;

	f32 dist_to_impact = (-b - sqrtps(dis))/(2.f*a); // negative root for near end

	vel.x *= speed;
	vel.y *= speed;
	vel.z *= speed;

	speed = vMod(vel);

	return (uint64_t)(dist_to_impact * 1000000.f / speed);
}

void rand_all_asteroids(struct current_state *state, uint64_t timestamp) {
	for (uint32_t i = 0; i <= (uint32_t)state->max_asteroid_id; i++) {
		rand_asteroid(&(state->asteroids[i]), timestamp);
	}
}


code:
#pragma once

#include <stdint.h>

#include "types.h"
#include "inc/vec.h"

uint64_t microtime(void);
uint8_t do_exo_impact(struct current_state *state, vec pos, float f);
void rand_asteroid(struct asteroid *asteroid, uint64_t timestamp);
uint64_t min_asteroid_time(void);
void rand_all_asteroids(struct current_state *state, uint64_t timestamp);


code:
[Desktop Entry]
Encoding=UTF-8
Name=Fractal Attack Online
Comment=Play online with friends to save all the martians before their home planet is destoyed!
StartupNotify=false
Exec=fractalattackonline
Icon=${SNAP}/meta/gui/fractalattackonline.png
Terminal=false
Type=Application
Categories=Application;Game;Simulation;


code:
name: fractalattackonline
base: core20
version: '1.0'
summary: Play online with friends to save all the martians before their home planet is destoyed!
description: |
  Play online with friends to save all the martians before their home planet is destoyed!

  Use W,A,S,D,Q,E,SPACE & LEFT SHIFT to move around.
  L-CTRL / Right Click to Brake.
  Escape / Left Click to free mouse focus.
  R = Toggle auto-tilt/roll around planet.
  F = FPS to console.

grade: stable
confinement: strict

architectures:
  - build-on: amd64
    run-on: amd64
  - build-on: arm64
    run-on: arm64
  - build-on: armhf
    run-on: armhf

apps:
  fractalattackonline:
    extensions: [gnome-3-38]
    command: fractalattackonline
    plugs:
    - desktop
    - desktop-legacy
    - gsettings
    - opengl
    - wayland
    - x11
    - network

parts:
  fractalattackonline:
    source: https://notabug.org/FractalAttack/FractalAttackOnlineUDP
    source-type: git
    plugin: make
    build-packages:
    - libglfw3-dev
    stage-packages:
    - libglfw3
    - libgles2


code:
avideo/
temp.txt
tmp-output
Patches/__pycache__/*

repo_name: GPast/avideo

path: .gitignore


code:
2017.9.27:
*Updated to latest youtube-dl release

2017.8.0:
*Updated bade youtube-dl release

2017.7.139:
*Add publication to PyPI
*Enhance apt metadata

2017.7.67:
*Fix bugs in apt repository install/documentation

2017.7.41:
*Set up Apt Repositories
*Add mention of rain1's research

2017.6.830:
*Add support for use as a Python library (very cheekily done, I might add)
*Formalize movement towards GNU compliance

2017.6.827:
*Yet another bug patch for the updater
*Sink versions in the RIGHT direction- down, not up to 828

2017.6.828:
*Integrated auto-merge to master
*Actually, really, prepared for release
*Made sure master and series were pushed with archive release (GPL-compliance and what-not)

2017.6.591:
*Ensured update system actually works
*Introduced update tests
*Added notes about JS issue/history to README

2017.6.580:
*Minor Update Mechanism Fix
*Hash verification now included for executable zipfile
*Make links in installer hard, and point to the actual file

2017.6.579:
*Fix the update mechanism

2017.6.85:
*Edit tests to avoid downloads reliant on proprietary JS
*Fix non-existent links in the User Docs

2017.6.60:
*Fix directory paths& symlinks in Deb package

2017.6.0:
*First Release!

path: Patches/Files/ChangeLog


path: Patches/Files/DebianPkg/conffiles


code:
Depends: python-pkg-resources, python (>= 2.7), python (<< 2.8), python:any (>= 2.7.1-0ubuntu2)
Recommends: libav-tools | ffmpeg, libav-tools | ffmpeg (>= 4:0.6) | ffprobe, mplayer2 | mplayer, rtmpdump
Conflicts: youtube-dl
Section: web
Priority: extra
Homepage: http://rg3.github.com/youtube-dl/
Description: downloader of videos from YouTube and other sites
 youtube-dl is a small command-line program to download videos from
 YouTube.com and other sites that don't provide direct links to the
 videos served.
 .
 youtube-dl allows the user, among other things, to choose a specific video
 quality to download (if available) or let the program automatically
 determine the best (or worst) quality video to grab. It supports
 downloading entire playlists and all videos from a given user.
 .
 Currently supported sites (or features of sites) are:
 .
 1up.com, 220.ro, 3sat, 4tube, 56.com, 8tracks, 9gag, AcademicEarth:Course,
 AddAnime, anitube.se, Aparat, AppleTrailers, archive.org videos, ARD,
 arte.tv, arte.tv:+7, arte.tv:creative, arte.tv:ddc, arte.tv:future,
 AUEngine, bambuser, bambuser:channel, Bandcamp, Bandcamp:album, BBC iPlayer,
 blinkx, blip.tv:user, BlipTV, Bloomberg, Break, Brightcove, canalc2.tv,
 canalplus.fr, CBS, Channel 9, Chilloutzone, Cinemassacre, clipfish,
 cliphunter, Clipsyndicate, cmt.com, CNN, CNNBlogs, CollegeHumor,
 ComedyCentral, The Daily Show / Colbert Report, Condé Nast media group: GQ,
 Glamour, Vanity Fair, Vogue, W Magazine, WIRED, Criterion, Crunchyroll,
 C-SPAN, culturebox.francetvinfo.fr, d8.tv, dailymotion,
 dailymotion:playlist, dailymotion:user, daum.net, defense.gouv.fr,
 DepositFiles, Discovery, Dotsub, Dropbox, EbaumsWorld, eHow, eitb.tv, El
 País, Escapist, EveryonesMixtape, ex.fm, ExtremeTube, facebook, faz.net,
 fernsehkritik.tv, fernsehkritik.tv:postecke, Firstpost.com, Видеоархив -
 Первый канал, Flickr, france2.fr:generation-quoi, FranceInter, France 2, 3,
 4, 5 and Ô, francetvinfo.fr, Freesound, freespeech.org, FunnyOrDie,
 Gamekings, GameSpot, Gametrailers, Generic downloader that works on some
 sites, Hark, helsinki.fi, HotNewHipHop, Howcast, Huffington Post, Hypem,
 ign.com, Internet Movie Database trailers, Internet Movie Database lists,
 Ina, InfoQ, Instagram, InternetVideoArchive, IPrima, ivi.ru, ivi.ru
 compilations, JadoreCettePub, JeuxVideo, jpopsuki.tv, Jukebox, justin.tv,
 Kankan, keek, KeezMovies, KhanAcademy, KickStarter, KontrTube.ru - Труба
 зовёт, la7.tv, LIFE | NEWS, LiveLeak, livestream, livestream:original,
 lynda.com videos, lynda.com online courses, m6, MacGameStore trailers,
 Malemotion, MDR, metacafe, Metacritic, mixcloud, Mofosex, Mooshare.biz,
 MPORA, MTV, mtviggy.com, muzu.tv, MySpace, MySpass, myvideo, Naver, NBA,
 NBCNews, NDR.de - Mediathek, NDTV, Newgrounds, National Film Board of
 Canada, nhl.com, NHL videocenter category, ニコニコ動画, Normalboots,
 Novamov, Nowness, NowVideo, Ooyala, ORF, PBS, photobucket, Google Plus,
 pluzz.francetv.fr, podomatic, PornHd, PornHub, Pornotube, Pyvideo,
 radiofrance, RBMARadio, RedTube, RingTV, RottenTomatoes, Roxwel, RTLnow,
 Rutube videos, Rutube channels, Rutube movies, Rutube person videos, Yahoo
 screen search (Example: "yvsearchall:purple fish" ), ServingSys, Sina,
 Slashdot, Slideshare, Smotri.com, Smotri.com broadcasts, Smotri.com
 community videos, Smotri.com user videos, Sohu, soundcloud, soundcloud:set,
 soundcloud:user, southpark.de, southparkstudios.com, Space, Spankwire,
 Spiegel, Spike, Stanford Open ClassRoom, Statigram, Steam, streamcloud.eu,
 StreamCZ, Syfy, SztvHu, Teamcoco, TechTalks, techtv.mit.edu, TED, TF1,
 ThePlatform, ThisAV, tinypic.com videos, tou.tv, Trilulilu, Tube8, Tudou,
 Tumblr, Tutv, tvp.pl, Unistra, ustream, ustream:channel, Vbox7, VeeHD, Veoh,
 Вести.Ru, Vevo, Vice, Viddler, Google Video search (Example:
 "gvsearch5:falling cat" ), video.mit.edu, VideoDetective, videofy.me,
 VideoPremium, viki, vimeo, vimeo:album, vimeo:channel, vimeo:group, Review
 pages on vimeo, vimeo:user, Vine, vk.com, Vube.com, wat.tv, Weibo, Wimp,
 Wistia, WorldStarHipHop, XHamster, XNXX, XTube, XVideos, Yahoo screen, Yahoo
 screen, YouJizz, Youku, YouPorn, YouTube.com, YouTube.com channels,
 YouTube.com favourite videos, "ytfav" keyword (requires authentication),
 Youtube watch history, "ythistory" keyword (requires authentication),
 YouTube.com playlists, YouTube.com recommended videos, "ytrec" keyword
 (requires authentication), YouTube.com searches (Example:
 "ytsearch10:running tortoise" ), YouTube.com searches, newest videos first
 (Example: "ytsearchdateall:falling cat" ), YouTube.com (multi-season) shows,
 YouTube.com subscriptions feed, "ytsubs" keyword(requires authentication),
 YouTube.com top lists, "yttoplist:{channel}:{list title}" (Example:
 "yttoplist:music:Top Tracks"), YouTube.com user videos (URL or "ytuser"
 keyword), Youtube watch later list, "ytwatchlater" keyword (requires
 authentication), ZDF.

path: Patches/Files/DebianPkg/control


code:
#!/bin/sh
set -e

# Automatically added by dh_python2:
if which pycompile >/dev/null 2>&1; then
	pycompile -p youtube-dl
fi

# End automatically added section

path: Patches/Files/DebianPkg/postinst


code:
#!/bin/sh
set -e

# Automatically added by dh_python2:
if which pyclean >/dev/null 2>&1; then
	pyclean -p youtube-dl
else
	dpkg -L youtube-dl | grep \.py$ | while read file
	do
		rm -f "${file}"[co] >/dev/null
  	done
fi

# End automatically added section

path: Patches/Files/DebianPkg/prerm


code:
This software builds on youtu`be-dl, and much gratitude is extended to them for their hard work released under a libre license.
This program as a whole is released under the GPLv3+ (a copy of the GPLv3 can be found in COPYING.txt).

For various reasons, youtube-dl removes some functionality from its parent. Any known such 'regressions' are documented here.
*In order to guarantee complete liberty, this software does not use its upstream's built-in JS and SWF interpreters. This prevents access to some videos on YouTube, as they use (unnecessary) obfuscated URLs.
*Support for Microsoft Windows and Mac OS X is no longer available, as there is no conceivable reason to provide it (the slight privacy/security concerns with our parent are irrelevant compared to the OS).
*Due to the use of a non-free client side authentication algorithm, login to iQiyi is not available.
*For reasons of consolidation with other package managers, the update mechanism has been removed.


code:
#!/bin/bash

cd `dirname $0`/..
#ReleaseSeries.MinorVersion
Version=`git rev-parse --abbrev-ref HEAD | tail -1`.$(( (`TZ=UTC date +%s`-`cat ../InitDate.txt`) / 16920 ))
if [ "x$1" != "x-r" ]; then
	sed -i "s/__version__ = '.*'/__version__ = '$Version'/" youtube_dl/version.py
	sed -i "s/<unreleased>/$Version/" ChangeLog
	sed -i "s/<unreleased>/$Version/" ../Patches/Files/ChangeLog
	#git tag -s -m "Release $version" "$version"
fi
echo -n ${Version}