From d276cd9961e05c184d4fd653bb9e92a288a09ec3 Mon Sep 17 00:00:00 2001 From: rodri Date: Wed, 21 Jul 2021 05:05:07 +0000 Subject: initial commit. implemented basic server loop, with separate threads to handle connections and run the simulations. --- dat.h | 25 +++++++++++++ fns.h | 1 + mkfile | 14 ++++++++ musw.c | 0 muswd.c | 118 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ nanosec.c | 109 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ physics.c | 100 ++++++++++++++++++++++++++++++++++++++++++++++++++++ readme.md | 0 stats.c | 18 ++++++++++ 9 files changed, 385 insertions(+) create mode 100644 dat.h create mode 100644 fns.h create mode 100644 mkfile create mode 100644 musw.c create mode 100644 muswd.c create mode 100644 nanosec.c create mode 100644 physics.c create mode 100644 readme.md create mode 100644 stats.c diff --git a/dat.h b/dat.h new file mode 100644 index 0000000..c93b24e --- /dev/null +++ b/dat.h @@ -0,0 +1,25 @@ +typedef struct GameState GameState; +typedef struct Derivative Derivative; +typedef struct Stats Stats; +typedef struct Sprite Sprite; + +struct Stats +{ + double cur; + double total; + double min, avg, max; + uvlong nupdates; + + void (*update)(Stats*, double); +}; + +struct GameState +{ + double x, v; + Stats stats; +}; + +struct Derivative +{ + double dx, dv; +}; diff --git a/fns.h b/fns.h new file mode 100644 index 0000000..f46c8bd --- /dev/null +++ b/fns.h @@ -0,0 +1 @@ +uvlong nanosec(void); diff --git a/mkfile b/mkfile new file mode 100644 index 0000000..6579d68 --- /dev/null +++ b/mkfile @@ -0,0 +1,14 @@ + +#include +#include +#include "dat.h" +#include "fns.h" + +int debug; + +double t, Δt; + +static long +_iolisten(va_list *arg) +{ + char *adir, *ldir; + + adir = va_arg(*arg, char*); + ldir = va_arg(*arg, char*); + + return listen(adir, ldir); +} + +long +iolisten(Ioproc *io, char *adir, char *ldir) +{ + return iocall(io, _iolisten, adir, ldir); +} + +void +threadlisten(void *arg) +{ + int lcfd; + char *adir, ldir[40]; + Ioproc *io; + + adir = arg; + io = ioproc(); + + for(;;){ + lcfd = iolisten(io, adir, ldir); + if(lcfd < 0){ + fprint(2, "iolisten: %r\n"); + continue; + } + /* + * handle connection and allocate user on a seat, ready + * to play + */ + } +} + +void +resetsim(void) +{ + memset(&state, 0, sizeof(GameState)); + state.x = 100; + state.stats.update = statsupdate; + t = 0; +} + +void +threadsim(void *) +{ + uvlong then, now; + double frametime, timeacc; + + Δt = 0.01; + then = nanosec(); + timeacc = 0; + + resetsim(); + + for(;;){ + now = nanosec(); + frametime = now - then; + then = now; + timeacc += frametime/1e9; + + while(timeacc >= Δt){ + integrate(&state, t, Δt); + timeacc -= Δt; + t += Δt; + } + + sleep(66); + } +} + +void +usage(void) +{ + fprint(2, "usage: %s [-d]\n", argv0); + threadexitsall("usage"); +} + +void +threadmain(int argc, char *argv[]) +{ + int acfd; + char adir[40]; + + ARGBEGIN{ + case 'd': + debug++; + break; + default: + usage(); + }ARGEND; + if(argc != 0) + usage(); + + acfd = announce("tcp!*!112", adir); + if(acfd < 0) + sysfatal("announce: %r"); + + threadcreate(threadlisten, adir, 1024); + threadcreate(threadsim, nil, 8192); + threadexits(nil); +} diff --git a/nanosec.c b/nanosec.c new file mode 100644 index 0000000..f82d47a --- /dev/null +++ b/nanosec.c @@ -0,0 +1,109 @@ +#include +#include +#include + +/* + * This code is a mixture of cpuid(1) and the nanosec() found in vmx, + * in order to force the use of nsec(2) in case we are running in a + * virtualized environment where the clock is mis-bhyve-ing. + */ + +typedef struct Res { + ulong ax, bx, cx, dx; +} Res; + +static uchar _cpuid[] = { + 0x5E, /* POP SI (PC) */ + 0x5D, /* POP BP (Res&) */ + 0x58, /* POP AX */ + 0x59, /* POP CX */ + + 0x51, /* PUSH CX */ + 0x50, /* PUSH AX */ + 0x55, /* PUSH BP */ + 0x56, /* PUSH SI */ + + 0x31, 0xDB, /* XOR BX, BX */ + 0x31, 0xD2, /* XOR DX, DX */ + + 0x0F, 0xA2, /* CPUID */ + + 0x89, 0x45, 0x00, /* MOV AX, 0(BP) */ + 0x89, 0x5d, 0x04, /* MOV BX, 4(BP) */ + 0x89, 0x4d, 0x08, /* MOV CX, 8(BP) */ + 0x89, 0x55, 0x0C, /* MOV DX, 12(BP) */ + 0xC3, /* RET */ +}; + +static Res (*cpuid)(ulong ax, ulong cx) = (Res(*)(ulong, ulong)) _cpuid; + +/* + * nsec() is wallclock and can be adjusted by timesync + * so need to use cycles() instead, but fall back to + * nsec() in case we can't + */ +uvlong +nanosec(void) +{ + static uvlong fasthz, xstart; + char buf[13], path[128]; + ulong w; + uvlong x, div; + int fd; + Res r; + + if(fasthz == ~0ULL) + return nsec() - xstart; + + if(fasthz == 0){ + /* first long in a.out header */ + snprint(path, sizeof path, "/proc/%d/text", getpid()); + fd = open(path, OREAD); + if(fd < 0) + goto Wallclock; + if(read(fd, buf, 4) != 4){ + close(fd); + goto Wallclock; + } + close(fd); + + w = ((ulong *) buf)[0]; + + switch(w){ + default: + goto Wallclock; + case 0x978a0000: /* amd64 */ + /* patch out POP BP -> POP AX */ + _cpuid[1] = 0x58; + case 0xeb010000: /* 386 */ + break; + } + segflush(_cpuid, sizeof(_cpuid)); + + r = cpuid(0x40000000, 0); + ((ulong *) buf)[0] = r.bx; + ((ulong *) buf)[1] = r.cx; + ((ulong *) buf)[2] = r.dx; + buf[12] = 0; + + if(strstr(buf, "bhyve") != nil) + goto Wallclock; + + if(_tos->cyclefreq){ + fasthz = _tos->cyclefreq; + cycles(&xstart); + } else { +Wallclock: + fasthz = ~0ULL; + xstart = nsec(); + } + return 0; + } + cycles(&x); + x -= xstart; + + /* this is ugly */ + for(div = 1000000000ULL; x < 0x1999999999999999ULL && div > 1 ; div /= 10ULL, x *= 10ULL); + + return x / (fasthz / div); +} diff --git a/physics.c b/physics.c new file mode 100644 index 0000000..5f45fd9 --- /dev/null +++ b/physics.c @@ -0,0 +1,100 @@ +#include +#include +#include +#include "dat.h" +#include "fns.h" + +//enum { DYNTIME, RENTIME, NSTATS }; +//Stats simstats[NSTATS]; + + +/* + * Dynamics stepper + * + * Currently set to a basic spring-damper system. + */ +static double +accel(GameState *s, double t) +{ + static double k = 15, b = 0.1; + + USED(t); + return -k*s->x - b*s->v; +} + +static Derivative +eval(GameState *s0, double t, double Δt, Derivative *d) +{ + GameState s; + Derivative res; + + s.x = s0->x + d->dx*Δt; + s.v = s0->v + d->dv*Δt; + + res.dx = s.v; + res.dv = accel(&s, t+Δt); + return res; +} + +/* + * Explicit Euler Integrator + */ +static void +euler0(GameState *s, double t, double Δt) +{ + static Derivative ZD = {0,0}; + Derivative d; + + d = eval(s, t, Δt, &ZD); + + s->x += d.dx*Δt; + s->v += d.dv*Δt; +} + +/* + * Semi-implicit Euler Integrator + */ +static void +euler1(GameState *s, double t, double Δt) +{ + static Derivative ZD = {0,0}; + Derivative d; + + d = eval(s, t, Δt, &ZD); + + s->v += d.dv*Δt; + s->x += s->v*Δt; +} + +/* + * RK4 Integrator + */ +static void +rk4(GameState *s, double t, double Δt) +{ + static Derivative ZD = {0,0}; + Derivative a, b, c, d; + double dxdt, dvdt; + + a = eval(s, t, 0, &ZD); + b = eval(s, t, Δt/2, &a); + c = eval(s, t, Δt/2, &b); + d = eval(s, t, Δt, &c); + + dxdt = 1.0/6 * (a.dx + 2*(b.dx + c.dx) + d.dx); + dvdt = 1.0/6 * (a.dv + 2*(b.dv + c.dv) + d.dv); + + s->x += dxdt*Δt; + s->v += dvdt*Δt; +} + +/* + * The Integrator + */ +void +integrate(GameState *s, double t, double Δt) +{ + //euler0(s, t, Δt); + //euler1(s, t, Δt); + rk4(s, t, Δt); +} diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..e69de29 diff --git a/stats.c b/stats.c new file mode 100644 index 0000000..2232937 --- /dev/null +++ b/stats.c @@ -0,0 +1,18 @@ +#include +#include +#include +#include "dat.h" +#include "fns.h" + +static double min(double a, double b) { return a < b? a: b; } +static double max(double a, double b) { return a > b? a: b; } + +void +statsupdate(Stats *s, double n) +{ + s->cur = n; + s->total += s->cur; + s->avg = s->total/++s->nupdates; + s->min = min(s->cur, s->min); + s->max = max(s->cur, s->max); +} -- cgit v1.2.3