shithub: musw

Download patch

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);
+}