Compare commits

...

22 Commits

Author SHA1 Message Date
mid
b32671567e Switch to strncasecmp
All checks were successful
k4 Build Test / do_the_build (push) Successful in 52s
2025-07-13 10:15:02 +03:00
mid
f4cf597a95 _DEFAULT_SOURCE and _GNU_SOURCE
Some checks failed
k4 Build Test / do_the_build (push) Failing after 30s
2025-07-13 09:57:52 +03:00
mid
5f2e84d8cf Support LOOPPOINT metadata field for ogg files
Some checks failed
k4 Build Test / do_the_build (push) Failing after 30s
2025-07-12 23:27:33 +03:00
mid
7754e410fd EVEN MORE STABLIER
All checks were successful
k4 Build Test / do_the_build (push) Successful in 56s
2025-07-05 14:06:07 +03:00
mid
5a3a524cb3 add libgomp
All checks were successful
k4 Build Test / do_the_build (push) Successful in 50s
2025-07-05 11:26:59 +03:00
mid
55b8e827c2 Strip
All checks were successful
k4 Build Test / do_the_build (push) Successful in 50s
2025-07-05 10:39:43 +03:00
mid
368664d246 Bug fixes
All checks were successful
k4 Build Test / do_the_build (push) Successful in 56s
2025-06-29 22:22:13 +03:00
mid
90f0e2dd42 Physics stability 2025-06-29 20:47:24 +03:00
mid
355df2ee44 Extend network traversal to local addresses 2025-06-29 20:47:14 +03:00
mid
e126db573a Change top-level dir name
All checks were successful
k4 Build Test / do_the_build (push) Successful in 58s
2025-06-28 14:01:32 +03:00
mid
ceb8ccb7d4 README.md 2025-06-28 13:20:38 +03:00
mid
e81750300d Abstract shader parameters (to make them modifiable later) 2025-06-28 13:18:58 +03:00
mid
446ff24fcf Make time per-scene 2025-06-28 13:18:24 +03:00
mid
309af8f59e stopsmooth 2025-06-28 13:17:58 +03:00
mid
7b6ce73fa5 Improve ray-based character controller 2025-06-28 13:17:20 +03:00
mid
ce465ef449 Fix ray origin 2025-06-12 12:00:16 +03:00
mid
9478bca2ad Raycasting character controller 2025-06-11 10:14:56 +03:00
mid
da59158fd1 fix 2?
All checks were successful
k4 Build Test / do_the_build (push) Successful in 53s
2025-05-11 16:22:45 +03:00
mid
ff2924a0b7 fix?
Some checks failed
k4 Build Test / do_the_build (push) Failing after 4s
2025-05-11 16:21:05 +03:00
mid
24ddb047aa Updated runner to Debian 12
All checks were successful
k4 Build Test / do_the_build (push) Successful in 1m3s
2025-05-11 15:32:58 +03:00
mid
0ba2cdee1c Remove __in6_u usage
Some checks failed
k4 Build Test / do_the_build (push) Failing after 59s
2025-05-11 15:26:25 +03:00
mid
ae4a62f584 fix k3 submodule
Some checks failed
k4 Build Test / do_the_build (push) Failing after 26s
2025-05-11 11:41:17 +03:00
18 changed files with 585 additions and 258 deletions

View File

@@ -15,10 +15,11 @@ jobs:
submodules: 'true'
- run: mkdir build build/k3 build/k3/compr bin bin/assets
- run: CC="i686-w64-mingw32-gcc" CFLAGS="-I/usr/i686-w64-mingw32/include -I/usr/i686-w64-mingw32/include/lua5.3 -L/usr/i686-w64-mingw32/lib -Wno-error" make -B
- run: CC="i686-linux-gnu-gcc" CFLAGS="-I/usr/i686-linux-gnu/include -I/usr/i686-linux-gnu/include/lua5.3 -L/usr/i686-linux-gnu/lib -Wno-error" make -B
- run: cp /usr/lib/gcc/i686-w64-mingw32/10-win32/libgcc_s_dw2-1.dll /usr/lib/gcc/i686-w64-mingw32/10-win32/libstdc++-6.dll /usr/i686-w64-mingw32/lib/libportaudio-2.dll /usr/i686-w64-mingw32/lib/libwinpthread-1.dll bin/
- run: CC="i686-linux-gnu-gcc" CFLAGS="-I/usr/i686-linux-gnu/include -I/usr/i686-linux-gnu/include/lua5.3 -L/usr/i686-linux-gnu/lib -Wno-error -include /home/git/force_link_glibc_2.20.h" make -B
- run: cp /usr/lib/gcc/i686-w64-mingw32/12-win32/libgcc_s_dw2-1.dll /usr/lib/gcc/i686-w64-mingw32/12-win32/libstdc++-6.dll /usr/i686-w64-mingw32/lib/libportaudio-2.dll /usr/i686-w64-mingw32/lib/libwinpthread-1.dll /usr/lib/gcc/i686-w64-mingw32/12-win32/libgomp-1.dll bin/
- run: cp -r /home/git/k4templateassets/* bin/assets/
- run: zip -9r "k4${{ github.ref_name }}.zip" bin/
- run: mv bin k4
- run: zip -9r "k4${{ github.ref_name }}.zip" k4/
- name: Create package
uses: akkuman/gitea-release-action@v1
with:

View File

@@ -10,11 +10,11 @@ k3_HDRS := $(call rwildcard,k3/src,*.h)
k3_OBJS := $(patsubst k3/src/%.c, build/k3/%.o, $(k3_SRCS))
k3_DEPS := $(patsubst k3/src/%.c, build/k3/%.d, $(k3_SRCS))
CFLAGS := $(CFLAGS) -Ik3/src -O2 -fopenmp
CFLAGS := $(CFLAGS) -D_GNU_SOURCE -D_DEFAULT_SOURCE -Ik3/src -O2 -fopenmp -flto -s
ifneq (,$(findstring mingw,$(CC)))
CFLAGS := -static-libgcc -static-libstdc++ -std=gnu99 -march=pentium4 -D_WIN32_WINNT=0x600 -DENET_FEATURE_ADDRESS_MAPPING -fno-pic -no-pie -fms-extensions -fno-pie -Isrc $(CFLAGS)
LIBS := -l:libglfw3.a -lopengl32 -pthread -lm -l:libode.a -l:libvorbisfile.a -l:libvorbis.a -l:libogg.a -lportaudio -lgdi32 -lws2_32 -lwinmm -lstdc++ -lole32 -lsetupapi -lhid -l:liblua5.3.a $(LIBS)
LIBS := -l:libglfw3.a -lopengl32 -pthread -lm -l:libode.a -l:libvorbisfile.a -l:libvorbis.a -l:libogg.a -lportaudio -lgdi32 -lws2_32 -lwinmm -lstdc++ -lole32 -lsetupapi -lhid -l:liblua5.3.a -liphlpapi $(LIBS)
else
CFLAGS := -march=opteron $(SAN) -std=gnu99 -DENET_FEATURE_ADDRESS_MAPPING -fms-extensions -fno-pic -no-pie -fno-pie -Isrc $(CFLAGS)
LIBS := -lglfw3 -pthread -ldl -lm -lode -lstdc++ -llua5.3 -lvorbis -lvorbisfile -lportaudio $(LIBS)

1
README.md Normal file
View File

@@ -0,0 +1 @@
Homepage: https://mid.net.ua/k4.html

1
k3 Submodule

Submodule k3 added at 4d74b5e3e9

View File

@@ -5,6 +5,8 @@
#include"resman.h"
#include<cglm/vec3.h>
#define SUBFEET(cp) (cp->capsule.radius)
struct Game Game;
void game_init() {
@@ -29,8 +31,8 @@ void game_init() {
}
struct CollisionPair {
dGeomID g1; // greater
dGeomID g2; // lesser
uint16_t e1; // greater
uint16_t e2; // lesser
uint8_t x;
} __attribute__((packed));
static size_t activeCollisionCount, activeCollisionCapacity;
@@ -38,22 +40,22 @@ static struct CollisionPair *activeCollisions;
int pair_comparator(const void *a_, const void *b_) {
const struct CollisionPair *a = a_;
const struct CollisionPair *b = b_;
if(a->g1 == b->g1) {
return (uintptr_t) a->g2 - (uintptr_t) b->g2;
if(a->e1 == b->e1) {
return (intmax_t) a->e2 - (intmax_t) b->e2;
} else {
return (uintptr_t) a->g1 - (uintptr_t) b->g1;
return (intmax_t) a->e1 - (intmax_t) b->e1;
}
}
static int activate_pair(dGeomID g1, dGeomID g2) {
static int activate_pair(uint16_t e1, uint16_t e2) {
struct CollisionPair p = {
.g1 = g1 > g2 ? g1 : g2,
.g2 = g1 > g2 ? g2 : g1,
.e1 = e1 > e2 ? e1 : e2,
.e2 = e1 > e2 ? e2 : e1,
};
struct CollisionPair *peepee = bsearch(&p, activeCollisions, activeCollisionCount, sizeof(struct CollisionPair), pair_comparator);
if(peepee) {
peepee->x++;
peepee->x = 2;
return TRIGGER_EV_CONTINUOUS;
}
@@ -72,8 +74,8 @@ static int activate_pair(dGeomID g1, dGeomID g2) {
static void tick_pairs() {
for(size_t i = 0; i < activeCollisionCount;) {
if(--activeCollisions[i].x == 0) {
uint16_t e1 = (uintptr_t) dGeomGetData(activeCollisions[i].g1);
uint16_t e2 = (uintptr_t) dGeomGetData(activeCollisions[i].g2);
uint16_t e1 = activeCollisions[i].e1;
uint16_t e2 = activeCollisions[i].e2;
if(e1 != ENT_ID_INVALID) {
struct CPhysics *p1 = game_getcomponent(e1, physics);
@@ -97,10 +99,21 @@ static void tick_pairs() {
}
}
static void vec3_project_to_plane(vec3 v, vec3 n, vec3 ret) {
float scale = glm_vec3_dot(v, n) / glm_vec3_dot(n, n);
vec3 n2;
glm_vec3_scale(n, scale, n2);
glm_vec3_sub(v, n2, ret);
}
static void contact_callback(void *data, dGeomID g1, dGeomID g2) {
uint16_t e1 = (uintptr_t) dGeomGetData(g1);
uint16_t e2 = (uintptr_t) dGeomGetData(g2);
if(e1 != ENT_ID_INVALID && e1 == e2) return;
dBodyID b1 = dGeomGetBody(g1);
dBodyID b2 = dGeomGetBody(g2);
@@ -124,8 +137,6 @@ static void contact_callback(void *data, dGeomID g1, dGeomID g2) {
if(movingPlatform) {
const dReal *platvel = dBodyGetLinearVel(movingPlatform == 1 ? b1 : b2);
//if(i == 0)printf("get %f %f %f\n", platvel[0], platvel[1], platvel[2]);
contact[i].surface.mode |= dContactMotion1 | dContactMotion2 | dContactMotionN | dContactFDir1;
contact[i].surface.mode |= dContactSoftERP;
contact[i].surface.soft_erp = 0.9;
@@ -146,31 +157,44 @@ static void contact_callback(void *data, dGeomID g1, dGeomID g2) {
int ghost = 0;
if(dGeomGetCategoryBits(g1) & CATEGORY_GHOST) ghost = 1;
if(dGeomGetCategoryBits(g2) & CATEGORY_GHOST) ghost = 1;
if(numc) {
int triggerType = activate_pair(g1, g2);
int triggerType = activate_pair(e1, e2);
if(e1 != ENT_ID_INVALID) {
struct CPhysics *cp = game_getcomponent(e1, physics);
if(!cp) {
// Entity being killed
// Entity being killed maybe
return;
}
if(cp->dynamics & CPHYSICS_GHOST) {
ghost = 1;
}
struct CMovement *cm = game_getcomponent(e1, movement);
if(cm && cm->holding != ENT_ID_INVALID && cm->holding == e2) {
ghost = 1;
}
}
if(e2 != ENT_ID_INVALID) {
struct CPhysics *cp = game_getcomponent(e2, physics);
if(!cp) {
// Entity being killed
// Entity being killed maybe
return;
}
if(cp->dynamics & CPHYSICS_GHOST) {
ghost = 1;
}
struct CMovement *cm = game_getcomponent(e2, movement);
if(cm && cm->holding != ENT_ID_INVALID && cm->holding == e1) {
ghost = 1;
}
}
float friction = 1;
@@ -180,37 +204,6 @@ static void contact_callback(void *data, dGeomID g1, dGeomID g2) {
friction *= c->friction;
vec4 q;
if(b1) {
memcpy(q, dBodyGetQuaternion(b1), sizeof(q));
} else {
dGeomGetQuaternion(c->geom, q);
}
{
float temp = q[0];
q[0] = q[1];
q[1] = q[2];
q[2] = q[3];
q[3] = temp;
}
vec3 n = {0, 1, 0};
glm_quat_rotatev(q, n, n);
if(!ghost) for(int i = 0; i < numc; i++) {
if(glm_vec3_dot(contact[i].geom.normal, n) >= 0.7) {
struct CMovement *m = game_getcomponent(e1, movement);
if(m) {
if(m->holding != ENT_ID_INVALID && m->holding == e2) {
ghost = 1;
}
m->canJump = 1;
break;
}
}
}
if(c->trigger != TRIGGER_INVALID) {
Game.triggers[c->trigger - 1](c->trigger, e1, e2, triggerType);
}
@@ -221,37 +214,6 @@ static void contact_callback(void *data, dGeomID g1, dGeomID g2) {
friction *= c->friction;
vec4 q;
if(b2) {
memcpy(q, dBodyGetQuaternion(b2), sizeof(q));
} else {
dGeomGetQuaternion(c->geom, q);
}
{
float temp = q[0];
q[0] = q[1];
q[1] = q[2];
q[2] = q[3];
q[3] = temp;
}
vec3 n = {0, -1, 0};
glm_quat_rotatev(q, n, n);
if(!ghost) for(int i = 0; i < numc; i++) {
if(glm_vec3_dot(contact[i].geom.normal, n) >= 0.7) {
struct CMovement *m = game_getcomponent(e2, movement);
if(m) {
if(m->holding != ENT_ID_INVALID && m->holding == e1) {
ghost = 1;
}
m->canJump = 1;
break;
}
}
}
if(c->trigger != TRIGGER_INVALID) {
Game.triggers[c->trigger - 1](c->trigger, e2, e1, triggerType);
}
@@ -349,7 +311,102 @@ void game_raycast(struct LocalRay *rays, size_t count) {
}
}
static void game_character_controller_raycast_handler(void *data, dGeomID g1, dGeomID g2) {
if(dGeomGetClass(g1) != dRayClass && dGeomGetClass(g2) != dRayClass) {
return;
}
uint16_t e1 = (uintptr_t) dGeomGetData(g1);
uint16_t e2 = (uintptr_t) dGeomGetData(g2);
if(e1 == e2) {
// This means the ray is hitting the entity casting it.
return;
}
dContact contact[1];
memset(contact, 0, sizeof(contact));
int numc = dCollide(g1, g2, 1, &contact[0].geom, sizeof(dContact));
if(numc) {
vec3 n = {0, -1, 0};
struct CPhysics *cp1 = game_getcomponent(e1, physics);
struct CPhysics *cp2 = game_getcomponent(e2, physics);
if((cp2 && (cp2->dynamics & CPHYSICS_GHOST)) || (cp1 && (cp1->dynamics & CPHYSICS_GHOST))) {
return;
}
if(dGeomGetClass(g1) == dRayClass) {
dBodyID bid = dGeomGetBody(cp1->geom);
struct CMovement *cm = game_getcomponent(e1, movement);
cm->groundDepth = dGeomRayGetLength(g1) - contact[0].geom.depth;
if(glm_vec3_dot(contact[0].geom.normal, n) <= -0.7 && cm->groundDepth > SUBFEET(cp1)) {
cm->canJump = 1;
glm_vec3_scale(contact[0].geom.normal, -1, cm->groundNormal);
}
} else if(dGeomGetClass(g2) == dRayClass) {
dBodyID bid = dGeomGetBody(cp2->geom);
struct CMovement *cm = game_getcomponent(e2, movement);
cm->groundDepth = dGeomRayGetLength(g2) - contact[0].geom.depth;
if(glm_vec3_dot(contact[0].geom.normal, n) >= +0.7 && cm->groundDepth > SUBFEET(cp2)) {
cm->canJump = 1;
glm_vec3_scale(contact[0].geom.normal, +1, cm->groundNormal);
}
}
}
}
void game_character_controller_raycast() {
dGeomID rayGeoms[Game.entities.movementCount];
for(size_t i = 0; i < Game.entities.movementCount; i++) {
Game.entities.movement[i].canJump = 0;
}
for(int i = 0; i < Game.entities.movementCount; i++) {
struct CPhysics *cp = game_getcomponent(Game.entities.movement[i].entity, physics);
if(!cp || !cp->geom || cp->type != CPHYSICS_CAPSULE) {
rayGeoms[i] = NULL;
continue;
}
rayGeoms[i] = dCreateRay(Game.space, cp->capsule.length / 2 + cp->capsule.radius + SUBFEET(cp));
dGeomSetData(rayGeoms[i], (void*) (uintptr_t) Game.entities.movement[i].entity);
dGeomSetCategoryBits(rayGeoms[i], CATEGORY_RAY);
dGeomSetCollideBits(rayGeoms[i], CATEGORY_STATIC | CATEGORY_ENTITY);
dBodyID bid = dGeomGetBody(cp->geom);
const float *position = dBodyGetPosition(bid);
dGeomRaySet(rayGeoms[i], position[0], position[1], position[2], 0, -1, 0);
}
dSpaceCollide(Game.space, NULL, game_character_controller_raycast_handler);
for(int i = 0; i < Game.entities.movementCount; i++) {
if(rayGeoms[i]) {
dGeomDestroy(rayGeoms[i]);
}
}
}
void game_update() {
game_character_controller_raycast();
for(size_t i = 0; i < Game.entities.playerctrlCount; i++) {
struct CPlayerCtrl *cc = &Game.entities.playerctrl[i];
@@ -395,9 +452,18 @@ void game_update() {
if(cp && cp->geom) {
dBodyID bid = dGeomGetBody(cp->geom);
if(Game.entities.movement[i].canJump) {
if(dBodyGetLinearVel(bid)[1] <= 0) {
Game.entities.movement[i].isJumping = false;
}
if(!Game.entities.movement[i].isJumping) {
const float *pos = dBodyGetPosition(bid);
dBodySetPosition(bid, pos[0], pos[1] + Game.entities.movement[i].groundDepth - cp->capsule.radius / 2 - SUBFEET(cp), pos[2]);
dBodySetLinearVel(bid, 0, 0, 0);
}
dBodySetGravityMode(bid, !Game.entities.movement[i].canJump);
}
dBodySetGravityMode(bid, Game.entities.movement[i].canJump && !Game.entities.movement[i].isJumping ? 0 : 1);
}
}
@@ -433,14 +499,21 @@ void game_update() {
Game.entities.physics[i].box.l);
break;
case CPHYSICS_CAPSULE:
Game.entities.physics[i].geom = dCreateCapsule(Game.space,
case CPHYSICS_CAPSULE: {
dGeomID top = dCreateCapsule(Game.space,
Game.entities.physics[i].capsule.radius,
Game.entities.physics[i].capsule.length);
Game.entities.physics[i].capsule.length - Game.entities.physics[i].capsule.radius);
dGeomID feet = dCreateSphere(Game.space,
Game.entities.physics[i].capsule.radius);
Game.entities.physics[i].geom = top;
Game.entities.physics[i].geom2 = feet;
dMassSetCapsuleTotal(&mass, Game.entities.physics[i].mass, 2, Game.entities.physics[i].capsule.radius, Game.entities.physics[i].capsule.length);
break;
}
case CPHYSICS_SPHERE:
Game.entities.physics[i].geom = dCreateSphere(Game.space,
Game.entities.physics[i].sphere.radius);
@@ -454,6 +527,12 @@ void game_update() {
dGeomSetCollideBits(Game.entities.physics[i].geom, Game.entities.physics[i].collide);
dGeomSetData(Game.entities.physics[i].geom, (void*) Game.entities.physics[i].entity);
if(Game.entities.physics[i].type == CPHYSICS_CAPSULE) {
dGeomSetCategoryBits(Game.entities.physics[i].geom2, CATEGORY_ENTITY | CATEGORY_GHOST);
dGeomSetCollideBits(Game.entities.physics[i].geom2, Game.entities.physics[i].collide);
dGeomSetData(Game.entities.physics[i].geom2, (void*) Game.entities.physics[i].entity);
}
if((Game.entities.physics[i].dynamics & ~CPHYSICS_GHOST) != CPHYSICS_STATIC) {
dBodyID body = dBodyCreate(Game.phys);
@@ -464,10 +543,16 @@ void game_update() {
}
dGeomSetBody(Game.entities.physics[i].geom, body);
if(Game.entities.physics[i].geom2) {
dGeomSetBody(Game.entities.physics[i].geom2, body);
}
if(Game.entities.physics[i].type == CPHYSICS_CAPSULE) {
dBodySetMaxAngularSpeed(body, 0);
dGeomSetOffsetPosition(Game.entities.physics[i].geom, 0, Game.entities.physics[i].capsule.radius / 2, 0);
dGeomSetOffsetPosition(Game.entities.physics[i].geom2, 0, -Game.entities.physics[i].capsule.length / 2, 0);
// Rotate to Y-up
dQuaternion q;
dQFromAxisAndAngle(q, 1, 0, 0, M_PI * 0.5);
@@ -528,6 +613,10 @@ void game_update() {
if(glm_vec3_norm(wishvel) > 3) {
glm_vec3_scale_as(wishvel, 3, wishvel);
}
} else {
// We project the movement vector onto the ground plane (set by game_character_controller_raycast_handler)
// to "slide" along the ground with minimal bouncing
vec3_project_to_plane(wishvel, Game.entities.movement[mi].groundNormal, wishvel);
}
float currentspeed = glm_vec3_dot(wishvel, dBodyGetLinearVel(bid));
@@ -550,21 +639,17 @@ void game_update() {
dQFromAxisAndAngle(q, 0, 1, 0, Game.entities.movement[mi].pointing + M_PI);
dBodySetQuaternion(bid, q);
if(Game.entities.movement[mi].jump && (0||Game.entities.movement[mi].canJump)) {
Game.entities.movement[mi].canJump = 0;
dVector3 force;
dWorldImpulseToForce(Game.phys, 1.f / GAME_TPS, 0, 5, 0, force);
if(Game.entities.movement[mi].jump && Game.entities.movement[mi].canJump) {
dVector3 force = {};
dWorldImpulseToForce(Game.phys, 1.f / GAME_TPS, 0, 6.5, 0, force);
if(bid) {
dBodyAddForce(bid, force[0], force[1], force[2]);
}
}
}
}
for(size_t i = 0; i < Game.entities.movementCount; i++) {
Game.entities.movement[i].canJump = 0;
Game.entities.movement[mi].isJumping = true;
}
}
}
dSpaceCollide(Game.space, 0, &contact_callback);
@@ -901,8 +986,8 @@ void game_cleanup() {
Game.phys = dWorldCreate();
dWorldSetGravity(Game.phys, 0, -15, 0);
dWorldSetCFM(Game.phys, 0.0001);
dWorldSetERP(Game.phys, 0.5);
dWorldSetCFM(Game.phys, 0.00002);
dWorldSetERP(Game.phys, 0.1);
Game.space = dHashSpaceCreate(NULL);
@@ -916,10 +1001,14 @@ void game_killentity(uint16_t eid) {
if(cp) {
dBodyID bid = dGeomGetBody(cp->geom);
dGeomDestroy(cp->geom);
if(bid) {
for(dGeomID gid = dBodyGetFirstGeom(bid); gid; gid = dBodyGetNextGeom(gid)) {
dGeomDestroy(gid);
}
dBodyDestroy(bid);
} else {
dGeomDestroy(cp->geom);
}
game_killcomponent_ptr(cp, physics);

View File

@@ -13,6 +13,7 @@
#define CATEGORY_STATIC 1
#define CATEGORY_RAY 2
#define CATEGORY_ENTITY 4
#define CATEGORY_GHOST 8
#define TRIGGER_INVALID 0
@@ -47,7 +48,7 @@ struct TrimeshData {
#define CPHYSICS_GHOST 128
struct CPhysics {
uint16_t entity;
dGeomID geom;
dGeomID geom, geom2;
uint16_t trigger;
uint8_t type;
@@ -87,7 +88,9 @@ struct CMovement {
uint16_t entity;
vec3 dir;
float pointing;
char jump, canJump;
char jump, canJump, isJumping;
vec3 groundNormal;
float groundDepth;
uint16_t holding;
};

View File

@@ -6,6 +6,11 @@
#include<math.h>
#include<pthread.h>
#include<assert.h>
#include"k3.h"
#ifdef _WIN32
#define strncasecmp _strnicmp
#endif
static uint32_t FinalSampleRate;
static uint8_t FinalChannels;
@@ -19,12 +24,22 @@ struct k3MixSource {
char *filepath;
OggVorbis_File vf;
int bitstream;
size_t looppoint;
};
struct k3MixSource *k3MixSourceFile(const char *path) {
struct k3MixSource *ret = calloc(1, sizeof(*ret));
ret->filepath = strdup(path);
ov_fopen(path, &ret->vf);
ret->bitstream = 0;
ret->looppoint = 0;
for(size_t ci = 0; ci < ret->vf.vc->comments; ci++) {
if(strncasecmp(ret->vf.vc->user_comments[ci], "looppoint=", 10) == 0) {
ret->looppoint = strtol(ret->vf.vc->user_comments[ci] + 10, NULL, 0);
k3Log(k3_DEBUG, "%s has loop point %lu", path, ret->looppoint);
}
}
return ret;
}
void k3MixSourceClose(struct k3MixSource *src) {
@@ -42,7 +57,7 @@ __attribute__((optimize("Ofast"))) static intmax_t ogg_read(struct k3MixWave *th
long lastRead = ov_read_float(&od->vf, &ni, sampleCount, &od->bitstream);
if(this->loop && lastRead == 0) {
ov_pcm_seek(&od->vf, 0);
ov_pcm_seek(&od->vf, od->looppoint);
continue;
} else if(lastRead <= 0) {
this->end = true;
@@ -161,12 +176,13 @@ static void queue_clone(struct k3MixWave *this, struct k3MixWave *new) {
new->clone = queue_clone;
new->close = queue_close;
new->end = this->end;
new->loop = this->loop;
new->dam = this->dam;
new->volume = this->volume;
}
struct k3MixWave *k3MixQueue() {
struct k3MixWave *ret = malloc(sizeof(*ret));
struct k3MixWave *ret = calloc(1, sizeof(*ret));
ret->refs = 1;
ret->sampleRate = FinalSampleRate;
ret->channels = FinalChannels;
@@ -182,6 +198,7 @@ struct k3MixWave *k3MixQueue() {
ret->loop = 0;
ret->dam = 0;
ret->volume = 1;
ret->end = false;
return ret;
}
@@ -260,6 +277,7 @@ static void power_measurement_clone(struct k3MixWave *this, struct k3MixWave *ne
new->clone = power_measurement_clone;
new->close = power_measurement_close;
new->end = this->end;
new->loop = this->loop;
new->dam = this->dam;
new->volume = this->volume;
@@ -308,6 +326,10 @@ struct k3MixWave *k3MixPowerMeasurement(struct k3MixWave *child) {
static size_t playingCount, playingCapacity;
static struct k3MixWave **playings;
void k3MixStopSmooth(struct k3MixWave *wav) {
wav->fade = -0.01;
}
void k3MixStop(struct k3MixWave *wav) {
for(size_t i = 0; i < playingCount; i++) {
if(playings[i] == wav) {
@@ -346,6 +368,16 @@ __attribute__((optimize("Ofast"))) static void k3MixDoYourThang(size_t sampleCou
for(size_t i = 0; i < playingCount;) {
intmax_t read = playings[i]->read(playings[i], sampleCount, FinalData);
if(playings[i]->fade) {
playings[i]->volume += playings[i]->fade;
if(playings[i]->volume > 1) {
playings[i]->volume = 1;
playings[i]->fade = 0;
} else if(playings[i]->volume < 0) {
playings[i]->end = true;
}
}
if(playings[i]->end) {
k3MixStop(playings[i]);
} else i++;

View File

@@ -29,6 +29,7 @@ struct k3MixWave {
bool end;
uint16_t dam;
float volume;
float fade;
};
void k3MixInit(uint32_t sampleRate, uint8_t channels);
@@ -44,6 +45,7 @@ struct k3MixWave *k3MixPowerMeasurement(struct k3MixWave *child);
float k3MixPowerMeasurementGetRMS(struct k3MixWave*);
void k3MixStop(struct k3MixWave*);
void k3MixStopSmooth(struct k3MixWave*);
struct k3MixWave *k3MixPlay(struct k3MixWave*);
void k3MixPlayDirect(struct k3MixWave*);

View File

@@ -17,6 +17,8 @@
#include"net_hi.h"
#include"k3particles.h"
#include<GLFW/glfw3.h>
#include"ssort.h"
#include<ctype.h>
/*
* This is by far the least clean or well-maintained source in the
@@ -722,6 +724,17 @@ static int dagame_mixstop(lua_State *L) {
return 1;
}
static int dagame_mixstopsmooth(lua_State *L) {
struct mixitem *i = lua_touserdata(L, 1);
assert(i->type != MIXITEM_SOURCE);
k3MixStopSmooth(i->thing);
lua_pushvalue(L, 1);
return 1;
}
static int dagame_mixpower(lua_State *L) {
struct mixitem *i = lua_touserdata(L, 1);
@@ -2211,9 +2224,9 @@ static int dagame_set_texture_reduction(lua_State *L) {
}
int PeercodeHandler = LUA_NOREF;
static void get_peercode_callback(void *ud, const char *peercode_bin) {
char peercode[STOON_CONN_INFO_SIZE * 2 + 1] = {};
for(int i = 0; i < STOON_CONN_INFO_SIZE; i++) {
static void get_peercode_callback(void *ud, struct StoonPeercode *peercode_bin) {
char peercode[sizeof(*peercode_bin) * 2 + 1] = {};
for(int i = 0; i < sizeof(*peercode_bin); i++) {
snprintf(peercode + i * 2, 3, "%02x", ((uint8_t*) peercode_bin)[i]);
}
@@ -2283,11 +2296,11 @@ static int get_peercode_bin(const char *peercode, char *peercode_bin) {
if(!peercode) return 0;
if(!peercode_bin) return 0;
if(strlen(peercode) < 2 * STOON_CONN_INFO_SIZE) {
if(strlen(peercode) < 2 * sizeof(struct StoonPeercode)) {
return 0;
}
for(int i = 0; i < STOON_CONN_INFO_SIZE; i++) {
for(int i = 0; i < sizeof(struct StoonPeercode); i++) {
int b = 0;
if(peercode[i * 2 + 0] >= '0' && peercode[i * 2 + 0] <= '9') {
@@ -2315,8 +2328,8 @@ static int dagame_net_host(lua_State *L) {
}
static int dagame_net_join(lua_State *L) {
char peercode[STOON_CONN_INFO_SIZE];
if(!get_peercode_bin(lua_tostring(L, 1), peercode)) {
struct StoonPeercode peercode;
if(!get_peercode_bin(lua_tostring(L, 1), &peercode)) {
lua_pushliteral(L, "Invalid peercode");
lua_error(L);
return 0;
@@ -2324,20 +2337,20 @@ static int dagame_net_join(lua_State *L) {
net_hi_setup(false);
lua_pushboolean(L, net_hi_connect(peercode));
lua_pushboolean(L, net_hi_connect(&peercode));
return 1;
}
static int dagame_net_punch(lua_State *L) {
char peercode[STOON_CONN_INFO_SIZE];
if(!get_peercode_bin(lua_tostring(L, 1), peercode)) {
struct StoonPeercode peercode;
if(!get_peercode_bin(lua_tostring(L, 1), &peercode)) {
lua_pushliteral(L, "Invalid peercode");
lua_error(L);
return 0;
}
net_hi_add_punch(peercode);
net_hi_add_punch(&peercode);
return 0;
}
@@ -2404,7 +2417,7 @@ static int dagame_mdl_desc_append(lua_State *L) {
}
static int os_time(lua_State *L) {
lua_pushnumber(L, glfwGetTime() - LuaapiStartTime);
lua_pushnumber(L, isnan(LuaapiStartTime) ? 0 : glfwGetTime() - LuaapiStartTime);
return 1;
}
@@ -2755,6 +2768,9 @@ void luaapi_init() {
lua_pushcfunction(L, dagame_mixstop);
lua_setfield(L, -2, "stop");
lua_pushcfunction(L, dagame_mixstopsmooth);
lua_setfield(L, -2, "stopsmooth");
lua_pushcfunction(L, game_mixqueue);
lua_setfield(L, -2, "queue");
@@ -3113,6 +3129,8 @@ void luaapi_init() {
}
void luaapi_load(const char *name) {
LuaapiStartTime = NAN;
lua_getglobal(L, "require");
lua_pushstring(L, name);
if(lua_pcall(L, 1, 1, 0) != LUA_OK) {
@@ -3123,7 +3141,7 @@ void luaapi_load(const char *name) {
lua_getfield(L, -1, "load");
if(lua_pcall(L, 0, 0, 0) != LUA_OK) {
puts(lua_tostring(L, -1));
k3Log(k3_ERR, "%s", lua_tostring(L, -1));
lua_pop(L, 1);
return;
}
@@ -3131,6 +3149,8 @@ void luaapi_load(const char *name) {
if(Game.isAuthority) {
luaapi_join(NULL, 0);
}
LuaapiStartTime = glfwGetTime();
}
void luaapi_render(double dt, double alpha) {
@@ -3265,26 +3285,99 @@ static const char *k3glslloader(const char *fn) {
return data;
}
#define KV_STORE_SIZE 128
struct KVPair {
char *key;
char *value;
};
typedef struct KVPair KVStore[KV_STORE_SIZE];
static intmax_t kvpair_compare(const void *a_, const void *b_) {
const struct KVPair *a = a_;
const struct KVPair *b = b_;
if(!a->key) {
return 1;
} else if(!b->key) {
return -1;
}
return strcmp(a->key, b->key);
}
static char *kvstore_tostring(KVStore store) {
ssort(store, KV_STORE_SIZE, sizeof(struct KVPair), kvpair_compare);
const size_t sz = 2048;
char *ret = calloc(1, sz);
for(size_t i = 0; i < KV_STORE_SIZE; i++) {
if(store[i].key == NULL) {
break;
}
char def[256];
snprintf(def, sizeof(def), "%s\x1F%s\x1E", store[i].key, store[i].value);
strncat(ret, def, sz - strlen(ret) - 1);
}
return ret;
}
static bool kvstore_add(KVStore store, char *key, char *value) {
for(size_t i = 0; i < KV_STORE_SIZE; i++) {
if(store[i].key == NULL) {
store[i].key = key;
store[i].value = value;
return true;
}
}
return false;
}
static bool kvstore_add_fmt(KVStore store, char *key, const char *format, ...) {
va_list vl;
va_start(vl, format);
va_list vl2;
va_copy(vl2, vl);
size_t len = vsnprintf(NULL, 0, format, vl2);
char *def = malloc(len);
vsnprintf(def, len, format, vl);
va_end(vl);
va_end(vl2);
return kvstore_add(store, key, def);
}
static void kvstore_free(KVStore store) {
for(size_t i = 0; i < KV_STORE_SIZE; i++) {
free(store[i].key);
free(store[i].value);
}
}
// The glsl field of a material table is cached and stored
// in the resource manager. This function generates its name.
static char *glsl_table_fullname() {
KVStore store = {};
lua_getfield(L, -1, "vs");
lua_geti(L, -1, 1); // Version
size_t vsVer = lua_tointeger(L, -1);
lua_geti(L, -2, 2); // Filename
char *vsFile = strdup(lua_tostring(L, -1));
kvstore_add(store, strdup("vs"), strdup(lua_tostring(L, -1)));
kvstore_add_fmt(store, strdup("vsv"), "%lu", vsVer);
lua_pop(L, 3);
lua_getfield(L, -1, "fs");
lua_geti(L, -1, 1); // Version
size_t fsVer = lua_tointeger(L, -1);
lua_geti(L, -2, 2); // Filename
char *fsFile = strdup(lua_tostring(L, -1));
kvstore_add(store, strdup("fs"), strdup(lua_tostring(L, -1)));
kvstore_add_fmt(store, strdup("fsv"), "%lu", fsVer);
lua_pop(L, 3);
char ret[1024];
snprintf(ret, sizeof(ret), "\x1E%u\x1E%s\x1E%u\x1E%s\x1E", vsVer, vsFile, fsVer, fsFile);
lua_getfield(L, -1, "defs");
lua_pushnil(L);
while(lua_next(L, -2)) {
@@ -3294,19 +3387,17 @@ static char *glsl_table_fullname() {
continue;
}
char def[128];
snprintf(def, sizeof(def), "%s\x1F%s\x1E", lua_tostring(L, -2), lua_tostring(L, -1));
strncat(ret, def, sizeof(ret) - strlen(ret) - 1);
kvstore_add(store, strdup(lua_tostring(L, -2)), strdup(lua_tostring(L, -1)));
lua_pop(L, 1);
}
lua_pop(L, 1);
free(vsFile);
free(fsFile);
char *fullname = kvstore_tostring(store);
return strdup(ret);
kvstore_free(store);
return fullname;
}
static int parse_glsl_table(struct k3Mat *mat) {

View File

@@ -227,6 +227,7 @@ static int eng_init() {
GameWnd = glfwCreateWindow(START_WIDTH, START_HEIGHT, START_TITLE, NULL, NULL);
}
// Attempt compatibility
if(!GameWnd) {
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_ANY_PROFILE);
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_FALSE);
@@ -299,7 +300,7 @@ static void fix_resol() {
}
static void eng_ui_init() {
set_ui_mode(1);
set_ui_mode(!!UiActive);
}
static void fps_counter_step(double newDT) {
@@ -338,6 +339,7 @@ static int gr_shadowmap_init() {
"!!ARBfp1.0\n"
"TEMP col;\n"
"TEX col, fragment.texcoord, texture[0], 2D;\n"
"MOV result.color, col;\n"
"MAD col.xyz, -col, 29.9, 30.1;\n"
"RCP col.x, col.x;\n"
"RCP col.y, col.y;\n"
@@ -393,7 +395,6 @@ int main(int argc_, char **argv_) {
argv = argv_;
eng_init();
eng_ui_init();
gr_shadowmap_init();
gr_bloom_init();
@@ -413,7 +414,7 @@ int main(int argc_, char **argv_) {
luaapi_load(k4_get_arg("script") ? k4_get_arg("script") : "init");
//
LuaapiStartTime = glfwGetTime();
eng_ui_init();
LastTime = glfwGetTime();
@@ -520,11 +521,16 @@ int main(int argc_, char **argv_) {
InstantCamShift--;
}
if(LuaapiFirstPerson) {
struct CRender *c = game_getcomponent(Game.spectated, render);
struct CRender *cr = game_getcomponent(Game.spectated, render);
struct CPhysics *cp = game_getcomponent(Game.spectated, physics);
if(c) {
if(cr) {
vec3 p;
glm_vec3_lerp(c->posLast, c->pos, alpha, p);
glm_vec3_lerp(cr->posLast, cr->pos, alpha, p);
if(cp && cp->type == CPHYSICS_CAPSULE) {
p[1] += cp->capsule.length / 2;
}
mat4 view;
glm_look(p, cameraForwardDir, (vec3) {0, 1, 0}, view);

View File

@@ -26,11 +26,7 @@ void net_client_init() {
Game.isAuthority = 0;
}
bool net_client_connect(const char *addr, uint16_t port) {
ENetAddress eaddr;
enet_address_set_host(&eaddr, addr);
eaddr.port = port;
bool net_client_connect(ENetAddress eaddr) {
peer = enet_host_connect(host, &eaddr, 1, 0);
ENetEvent ev;

View File

@@ -1,7 +1,9 @@
#pragma once
#include"enet.h"
void net_client_init();
bool net_client_connect(const char *addr, uint16_t port);
bool net_client_connect(ENetAddress addr);
void net_client_receive();
void net_client_update();
void net_client_dejitter();

View File

@@ -7,9 +7,15 @@
#include"net_server.h"
#include"net_client.h"
#include<math.h>
#include"k3.h"
/*
* Handles networking on a higher-level, combining hole punching with
* basic client & server interface
* */
static void *ReqPeercodeUD;
static void(*ReqPeercodeCB)(void*, const char *peercode);
static void(*ReqPeercodeCB)(void*, struct StoonPeercode *peercode);
static struct Stoon stoon;
@@ -17,11 +23,11 @@ static bool inited = false;
static bool isHost;
typedef struct {
#define MASK_IPv4 1
#define MASK_IPv6 2
int mask;
ENetAddress v4;
ENetAddress v6;
ENetAddress pv4;
ENetAddress pv6;
ENetAddress lv4;
ENetAddress lv6;
} ToPunch;
static ToPunch *topunch = NULL;
static size_t topunchCount = 0;
@@ -30,13 +36,22 @@ static double Timeout = 0;
static bool connected = false;
bool net_hi_request_peercode(void *ud, void(*callback)(void*, const char *peercode)) {
static uint16_t servport = 0;
bool net_hi_request_peercode(void *ud, void(*callback)(void*, struct StoonPeercode *peercode)) {
if(inited) return false;
ReqPeercodeUD = ud;
ReqPeercodeCB = callback;
stoon = stoon_init("stun.easybell.de", 3478, 26656);
for(size_t port = 25000; port <= 26656; port++) {
stoon = stoon_init("stun.easybell.de", 3478, port);
if(stoon.fd4 >= 0 || stoon.fd6 >= 0) {
servport = port;
break;
}
}
Timeout = 0;
@@ -52,7 +67,7 @@ bool net_hi_setup(bool host) {
isHost = host;
if(host) {
net_server_init();
net_server_init(servport);
} else {
net_client_init();
}
@@ -67,16 +82,17 @@ void net_hi_update(double now) {
if(stoon_req(&stoon)) {
if(stoon_listen(&stoon)) {
uint8_t peercode[STOON_CONN_INFO_SIZE] = {};
stoon_serialize(&stoon, peercode);
ReqPeercodeCB(ReqPeercodeUD, peercode);
ReqPeercodeCB(ReqPeercodeUD, &stoon.peercode);
ReqPeercodeCB = NULL;
} else {
k3Log(k3_INFO, "stoon_listen failure");
Timeout = now + 1;
}
} else {
k3Log(k3_INFO, "stoon_req failure");
Timeout = now + 1;
}
@@ -97,11 +113,11 @@ void net_hi_update(double now) {
for(size_t tpi = 0; tpi < topunchCount; tpi++) {
ToPunch *tp = &topunch[tpi];
if(tp->mask & MASK_IPv4) {
enet_socket_send(h->socket, &tp->v4, &buf, 1);
} else if(tp->mask & MASK_IPv6) {
enet_socket_send(h->socket, &tp->v6, &buf, 1);
}
if(tp->lv6.port) enet_socket_send(h->socket, &tp->lv6, &buf, 1);
if(tp->lv4.port) enet_socket_send(h->socket, &tp->lv4, &buf, 1);
if(tp->pv6.port) enet_socket_send(h->socket, &tp->pv6, &buf, 1);
if(tp->pv4.port) enet_socket_send(h->socket, &tp->pv4, &buf, 1);
}
}
@@ -111,55 +127,85 @@ void net_hi_update(double now) {
}
}
static void stoonpeer_to_enets(const char *peercode, ENetAddress *v4, ENetAddress *v6, int *mask) {
memset(v4->host.__in6_u.__u6_addr8, 0, 16);
v4->host.__in6_u.__u6_addr8[10] = 0xFF;
v4->host.__in6_u.__u6_addr8[11] = 0xFF;
v4->host.__in6_u.__u6_addr8[12] = peercode[0];
v4->host.__in6_u.__u6_addr8[13] = peercode[1];
v4->host.__in6_u.__u6_addr8[14] = peercode[2];
v4->host.__in6_u.__u6_addr8[15] = peercode[3];
v4->port = ntohs(*(uint16_t*) &peercode[4]);
static bool stoonpeer_to_enet4(uint8_t *publicV4, uint8_t *publicP4, ENetAddress *enet) {
memset(enet, 0, sizeof(*enet));
memcpy(v6->host.__in6_u.__u6_addr8, peercode + 6, 16);
v6->port = ntohs(*(uint16_t*) &peercode[22]);
*mask = 0;
if(v4->port) {
*mask |= MASK_IPv4;
}
if(v6->port) {
*mask |= MASK_IPv6;
}
if(*(uint32_t*) publicV4 == 0 || *(uint16_t*) publicP4 == 0) {
return false;
}
void net_hi_add_punch(const char *peercode) {
int mask;
ENetAddress v4 = {}, v6 = {};
stoonpeer_to_enets(peercode, &v4, &v6, &mask);
uint8_t *addr8 = (uint8_t*) &enet->host;
memset(addr8, 0, 16);
addr8[10] = 0xFF;
addr8[11] = 0xFF;
addr8[12] = publicV4[0];
addr8[13] = publicV4[1];
addr8[14] = publicV4[2];
addr8[15] = publicV4[3];
enet->port = *(uint16_t*) publicP4;
return true;
}
static bool stoonpeer_to_enet6(uint8_t *publicV6, uint8_t *publicP6, ENetAddress *enet) {
memset(enet, 0, sizeof(*enet));
if(*(uint32_t*) publicV6 == 0 || *(uint16_t*) publicP6 == 0) {
return false;
}
uint8_t *addr8 = (uint8_t*) &enet->host;
memcpy(addr8, publicV6, 16);
enet->port = *(uint16_t*) publicP6;
return true;
}
void net_hi_add_punch(struct StoonPeercode *peercode) {
topunch = realloc(topunch, sizeof(*topunch) * (++topunchCount));
topunch[topunchCount - 1] = (ToPunch) {
.v4 = v4,
.v6 = v6,
.mask = mask,
};
ToPunch *tp = &topunch[topunchCount - 1];
stoonpeer_to_enet4(peercode->publicV4, peercode->publicP4, &tp->pv4);
stoonpeer_to_enet6(peercode->publicV6, peercode->publicP6, &tp->pv6);
stoonpeer_to_enet4(peercode->localV4, peercode->localP4, &tp->lv4);
stoonpeer_to_enet6(peercode->localV6, peercode->localP6, &tp->lv6);
}
bool net_hi_connect(const char *peercode) {
bool net_hi_connect(struct StoonPeercode *peercode) {
if(!inited) return false;
if(connected) return false;
char ip[64];
int port;
if(*(uint16_t*) (peercode + 22) == 0) {
inet_ntop(AF_INET, peercode, ip, sizeof(ip));
port = ntohs(*(uint16_t*) (peercode + 4));
} else {
inet_ntop(AF_INET6, peercode + 6, ip, sizeof(ip));
port = ntohs(*(uint16_t*) (peercode + 22));
ENetAddress pv4, pv6;
stoonpeer_to_enet4(peercode->publicV4, peercode->publicP4, &pv4);
stoonpeer_to_enet6(peercode->publicV6, peercode->publicP6, &pv6);
ENetAddress lv4, lv6;
stoonpeer_to_enet4(peercode->localV4, peercode->localP4, &lv4);
stoonpeer_to_enet6(peercode->localV6, peercode->localP6, &lv6);
if(lv6.port && net_client_connect(lv6)) {
k3Log(k3_INFO, "Found local IPv6");
return true;
}
if(lv4.port && net_client_connect(lv4)) {
k3Log(k3_INFO, "Found local IPv4");
return true;
}
return net_client_connect(ip, port);
if(pv6.port && net_client_connect(pv6)) {
k3Log(k3_INFO, "Found remote IPv6");
return true;
}
if(pv4.port && net_client_connect(pv4)) {
k3Log(k3_INFO, "Found remote IPv4");
return true;
}
k3Log(k3_INFO, "Failed to establish connection");
return false;
}

View File

@@ -2,10 +2,12 @@
#include<stdbool.h>
bool net_hi_request_peercode(void *ud, void(*callback)(void*, const char *peercode));
struct StoonPeercode;
bool net_hi_request_peercode(void *ud, void(*callback)(void*, struct StoonPeercode *peercode));
bool net_hi_setup(bool host);
void net_hi_update(double now);
void net_hi_add_punch(const char *peercode);
void net_hi_add_punch(struct StoonPeercode *peercode);
bool net_hi_connect(const char *peercode);
bool net_hi_connect(struct StoonPeercode *peercode);

View File

@@ -18,8 +18,8 @@ struct Conn {
#define MAX_PLAYERS 64
static struct Conn conns[MAX_PLAYERS];
void net_server_init() {
host = enet_host_create(&(ENetAddress) {.host = ENET_HOST_ANY, .port = 26656}, 16, 1, 0, 0);
void net_server_init(uint16_t port) {
host = enet_host_create(&(ENetAddress) {.host = ENET_HOST_ANY, .port = port}, 16, 1, 0, 0);
Game.isMultiplayer = 1;
Game.isAuthority = 1;

View File

@@ -5,7 +5,7 @@
struct _ENetPeer;
struct bstr;
void net_server_init();
void net_server_init(uint16_t port);
void net_server_receive();
void net_server_update();

View File

@@ -4,11 +4,13 @@
#include<winsock2.h>
#include<ws2tcpip.h>
#include<ntsecapi.h>
#include<iphlpapi.h>
#else
#include<netdb.h>
#include<sys/socket.h>
#include<sys/random.h>
#include<arpa/inet.h>
#include<ifaddrs.h>
#endif
#include<unistd.h>
#include<sys/types.h>
@@ -43,7 +45,7 @@ struct StunMsg {
uint8_t id[12];
};
static int stoon_init_mini(struct addrinfo *serv, uint16_t myport) {
static int stoon_init_mini(struct Stoon *this, struct addrinfo *serv, uint16_t myport) {
#ifdef _WIN32
errno = 0;
int fd = socket(serv->ai_family, serv->ai_socktype, serv->ai_protocol);
@@ -59,16 +61,79 @@ static int stoon_init_mini(struct addrinfo *serv, uint16_t myport) {
char p[6];
sprintf(p, "%u", myport);
struct addrinfo *myaddr;
if(getaddrinfo(NULL, p, &(struct addrinfo) {.ai_family = serv->ai_family, .ai_socktype = SOCK_DGRAM, .ai_protocol = IPPROTO_UDP, .ai_flags = AI_PASSIVE}, &myaddr)) {
struct addrinfo *myaddrinfo;
if(getaddrinfo(NULL, p, &(struct addrinfo) {.ai_family = serv->ai_family, .ai_socktype = SOCK_DGRAM, .ai_protocol = IPPROTO_UDP, .ai_flags = AI_PASSIVE}, &myaddrinfo)) {
close(fd);
return -1;
}
if(bind(fd, myaddr->ai_addr, myaddr->ai_addrlen)) {
if(bind(fd, myaddrinfo->ai_addr, myaddrinfo->ai_addrlen)) {
close(fd);
return -2;
}
freeaddrinfo(myaddr);
freeaddrinfo(myaddrinfo);
#ifdef _WIN32
size_t sz = 1024 * 64;
IP_ADAPTER_ADDRESSES *addrs = malloc(sz);
if(GetAdaptersAddresses(serv->ai_family, GAA_FLAG_SKIP_MULTICAST, NULL, addrs, &sz) == NO_ERROR) {
for(IP_ADAPTER_ADDRESSES *ifa = addrs; ifa; ifa = ifa->Next) {
if(!ifa->FirstUnicastAddress) {
continue;
}
if(serv->ai_family == AF_INET6) {
uint8_t *addr = (void*) &((struct sockaddr_in6*) ifa->FirstUnicastAddress->Address.lpSockaddr)->sin6_addr;
if(addr[0] == 0 && addr[1] == 0 && addr[2] == 0 && addr[3] == 0 && addr[4] == 0 && addr[5] == 0 && addr[6] == 0 && addr[7] == 0 && addr[8] == 0 && addr[9] == 0 && addr[10] == 0 && addr[11] == 0 && addr[12] == 0 && addr[13] == 0 && addr[14] == 0 && (addr[15] == 0 || addr[15] == 1)) {
continue;
}
memcpy(this->peercode.localV6, addr, 16);
memcpy(this->peercode.localP6, &myport, 2);
} else {
uint8_t *addr = (void*) &((struct sockaddr_in*) ifa->FirstUnicastAddress->Address.lpSockaddr)->sin_addr;
if(addr[0] == 0 && addr[1] == 0 && addr[2] == 0 && addr[3] == 0 || addr[0] == 127 && addr[1] == 0 && addr[2] == 0 && addr[3] == 1) {
continue;
}
memcpy(this->peercode.localV4, addr, 4);
memcpy(this->peercode.localP4, &myport, 2);
}
}
}
free(addrs);
#else
struct ifaddrs *ifaddr;
if(getifaddrs(&ifaddr) >= 0) {
for(struct ifaddrs *ifa = ifaddr; ifa; ifa = ifa->ifa_next) {
if(!ifa->ifa_addr || ifa->ifa_addr->sa_family != serv->ai_family) {
continue;
}
if(serv->ai_family == AF_INET6) {
uint8_t *addr = (void*) &((struct sockaddr_in6*) ifa->ifa_addr)->sin6_addr;
if(addr[0] == 0 && addr[1] == 0 && addr[2] == 0 && addr[3] == 0 && addr[4] == 0 && addr[5] == 0 && addr[6] == 0 && addr[7] == 0 && addr[8] == 0 && addr[9] == 0 && addr[10] == 0 && addr[11] == 0 && addr[12] == 0 && addr[13] == 0 && addr[14] == 0 && (addr[15] == 0 || addr[15] == 1)) {
continue;
}
memcpy(this->peercode.localV6, addr, 16);
memcpy(this->peercode.localP6, &myport, 2);
} else {
uint8_t *addr = (void*) &((struct sockaddr_in*) ifa->ifa_addr)->sin_addr;
if(addr[0] == 0 && addr[1] == 0 && addr[2] == 0 && addr[3] == 0 || addr[0] == 127 && addr[1] == 0 && addr[2] == 0 && addr[3] == 1) {
continue;
}
memcpy(this->peercode.localV4, addr, 4);
memcpy(this->peercode.localP4, &myport, 2);
}
break;
}
freeifaddrs(ifaddr);
}
#endif
return fd;
}
@@ -92,18 +157,16 @@ struct Stoon stoon_init(const char *stunhost, uint16_t stunport, uint16_t myport
}
}
struct Stoon ret;
struct Stoon ret = {};
memset(&ret.my4, 0, sizeof(ret.my4));
if(v4) {
memcpy(&ret.stu4, v4->ai_addr, v4->ai_addrlen);
ret.fd4 = stoon_init_mini(v4, myport);
ret.fd4 = stoon_init_mini(&ret, v4, myport);
} else ret.fd4 = -1;
memset(&ret.my6, 0, sizeof(ret.my6));
if(v6) {
memcpy(&ret.stu6, v6->ai_addr, v6->ai_addrlen);
ret.fd6 = stoon_init_mini(v6, myport);
ret.fd6 = stoon_init_mini(&ret, v6, myport);
} else ret.fd6 = -1;
ret.poonchstage = 0;
@@ -121,12 +184,21 @@ static int stoon_req_mini(struct Stoon *this, int *fd, struct sockaddr *serv, in
if(errno == ENETUNREACH) {
close(*fd);
*fd = -1;
return false;
}
}
return true;
}
int stoon_req(struct Stoon *this) {
if(this->fd4 >= 0) stoon_req_mini(this, &this->fd4, (struct sockaddr*) &this->stu4, sizeof(struct sockaddr_in));
if(this->fd6 >= 0) stoon_req_mini(this, &this->fd6, (struct sockaddr*) &this->stu6, sizeof(struct sockaddr_in6));
if(this->fd4 < 0 && this->fd6 < 0) {
return false;
}
return true;
}
static int stoon_listen_mini(struct Stoon *this, int fd, struct sockaddr *serv, int servlen) {
@@ -161,11 +233,10 @@ static int stoon_listen_mini(struct Stoon *this, int fd, struct sockaddr *serv,
uint16_t netfam = ntohs(((uint16_t*) d)[2]);
uint16_t publicPort = ntohs(((uint16_t*) d)[3]) ^ (STUN_MAGIC >> 16);
if(netfam == STUN_NETFAM_IPV4) {
uint32_t publicIp = ntohl(((uint32_t*) d)[2]) ^ STUN_MAGIC;
uint32_t publicIp = htonl(ntohl(((uint32_t*) d)[2]) ^ STUN_MAGIC);
this->my4.sin_family = AF_INET;
memcpy(&this->my4.sin_addr, &publicIp, 4);
this->my4.sin_port = htons(publicPort);
memcpy(this->peercode.publicV4, &publicIp, 4);
memcpy(this->peercode.publicP4, &publicPort, 2);
return 1;
} else if(netfam == STUN_NETFAM_IPV6) {
@@ -175,9 +246,8 @@ static int stoon_listen_mini(struct Stoon *this, int fd, struct sockaddr *serv,
publicIp[2] = ((uint32_t*) d)[4] ^ ((uint32_t*) res.base.id)[1];
publicIp[3] = ((uint32_t*) d)[5] ^ ((uint32_t*) res.base.id)[2];
this->my6.sin6_family = AF_INET6;
memcpy(&this->my6.sin6_addr, &publicIp, 16);
this->my6.sin6_port = htons(publicPort);
memcpy(this->peercode.publicV6, &publicIp, 16);
memcpy(this->peercode.publicP6, &publicPort, 2);
return 1;
} else {
@@ -188,12 +258,12 @@ static int stoon_listen_mini(struct Stoon *this, int fd, struct sockaddr *serv,
d += 4 + attribLen;
}
return 0;
return false;
}
int stoon_listen(struct Stoon *this) {
if(this->fd4 >= 0) if(stoon_listen_mini(this, this->fd4, (struct sockaddr*) &this->stu4, sizeof(struct sockaddr_in)) < 0) return 0;
if(this->fd6 >= 0) if(stoon_listen_mini(this, this->fd6, (struct sockaddr*) &this->stu6, sizeof(struct sockaddr_in6)) < 0) return 0;
return 1;
return true;
}
void stoon_keepalive(struct Stoon *this) {
@@ -217,31 +287,7 @@ void stoon_kill(struct Stoon *this) {
this->fd6 = -1;
}
int stoon_v4_available(struct Stoon *this) {
return this->fd4 >= 0;
}
int stoon_v6_available(struct Stoon *this) {
return this->fd6 >= 0;
}
void stoon_serialize(struct Stoon *this, uint8_t ret[static STOON_CONN_INFO_SIZE]) {
if(this->fd4 >= 0) {
memcpy(ret + 0, &this->my4.sin_addr, 4);
memcpy(ret + 4, &this->my4.sin_port, 2);
} else {
memset(ret, 0, 6);
}
if(this->fd6 >= 0) {
memcpy(ret + 6, &this->my6.sin6_addr, 16);
memcpy(ret + 22, &this->my6.sin6_port, 2);
} else {
memset(ret + 6, 0, 18);
}
}
int stoon_poonch(struct Stoon *this, const uint8_t peerdata[static STOON_CONN_INFO_SIZE]) {
/*int stoon_poonch(struct Stoon *this, const uint8_t peerdata[static STOON_CONN_INFO_SIZE]) {
struct {
uint32_t addr4;
uint16_t port4;
@@ -303,7 +349,7 @@ int stoon_poonch(struct Stoon *this, const uint8_t peerdata[static STOON_CONN_IN
}
return STOON_POONCH_NO_KNOCK;
}
}*/
#ifdef STOON_STANDALONE
int main() {

View File

@@ -6,6 +6,7 @@
#include<stddef.h>
#include<stdint.h>
#include<stdbool.h>
#ifdef _WIN32
#include<winsock2.h>
@@ -16,8 +17,6 @@
/* Standalone hole punching libary (IPv4 & IPv6 compatible) */
#define STOON_CONN_INFO_SIZE (4 + 2 + 16 + 2)
#define POONCH_MAGIC 0x504F4348
#define POONCH_STAGE_KNOCKING 0
#define POONCH_STAGE_ACK 1
@@ -26,16 +25,30 @@ struct Poonch {
uint32_t stage;
};
struct StoonPeercode {
uint8_t publicV4[4];
uint8_t publicP4[2];
uint8_t publicV6[16];
uint8_t publicP6[2];
uint8_t localV4[4];
uint8_t localP4[2];
uint8_t localV6[16];
uint8_t localP6[2];
};
struct Stoon {
struct sockaddr_in stu4;
struct sockaddr_in my4;
int fd4;
struct sockaddr_in6 stu6;
struct sockaddr_in6 my6;
int fd6;
uint8_t poonchstage;
struct StoonPeercode peercode;
};
// stunhost: Self-explanatory
@@ -53,12 +66,8 @@ void stoon_keepalive(struct Stoon*);
void stoon_kill(struct Stoon*);
int stoon_v4_available(struct Stoon*);
int stoon_v6_available(struct Stoon*);
void stoon_serialize(struct Stoon*, uint8_t ret[static STOON_CONN_INFO_SIZE]);
#define STOON_POONCH_INCOMPATIBLE_NETFAMS (-1)
/*#define STOON_POONCH_INCOMPATIBLE_NETFAMS (-1)
#define STOON_POONCH_NO_KNOCK 0
#define STOON_POONCH_ACKED 1
int stoon_poonch(struct Stoon*, const uint8_t peerdata[static STOON_CONN_INFO_SIZE]);
*/