ref: d276cd9961e05c184d4fd653bb9e92a288a09ec3
author: rodri <rgl@antares-labs.eu>
date: Wed Jul 21 01:05:07 EDT 2021
initial commit. implemented basic server loop, with separate threads to handle connections and run the simulations.
--- /dev/null
+++ b/dat.h
@@ -1,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;
+};
--- /dev/null
+++ b/fns.h
@@ -1,0 +1,1 @@
+uvlong nanosec(void);
--- /dev/null
+++ b/mkfile
@@ -1,0 +1,14 @@
+</$objtype/mkfile
+
+BIN=/$objtype/bin/games
+TARG=\
+ musw\
+ muswd\
+
+OFILES=
+
+HFILES=\
+ dat.h\
+ fns.h\
+
+</sys/src/cmd/mkmany
--- /dev/null
+++ b/muswd.c
@@ -1,0 +1,118 @@
+#include <u.h>
+#include <libc.h>
+#include <thread.h>
+#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);
+}
--- /dev/null
+++ b/nanosec.c
@@ -1,0 +1,109 @@
+#include <u.h>
+#include <libc.h>
+#include <tos.h>
+
+/*
+ * 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);
+}
--- /dev/null
+++ b/physics.c
@@ -1,0 +1,100 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#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);
+}
--- /dev/null
+++ b/stats.c
@@ -1,0 +1,18 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#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);
+}