shithub: musw

Download patch

ref: 7ed124593ebda443fc4d0f1439d1fd8dc602ce72
parent: b57dba67153279a84a16bba40ae007c8ae710056
author: rodri <rgl@antares-labs.eu>
date: Sun Aug 1 07:56:12 EDT 2021

began building a universe for every party.
get rid of the notes file. i already keep them on paper.
adapted the client to use an rframe for drawing.
implemented some VModel methods for testing.
FPS2MS → HZ2MS. it makes more sense and is shorter.

--- a/assets/mdl/needle.vmdl
+++ b/assets/mdl/needle.vmdl
@@ -1,14 +1,13 @@
 # The Needle vector model
-
 v  10  0
 v   8 -4
+v   8 -4
 v -10 -6
+v -10 -6
 v -12  0
 v -10  6
+v -10  6
 v   8  4
-
-l 1 2
-l 2 3
-c 3 4 5
-l 5 6
-l 6 1
+v   8  4
+v  10  0
+llcll
--- a/dat.h
+++ b/dat.h
@@ -34,6 +34,7 @@
 typedef struct Derivative Derivative;
 typedef struct Conn Conn;
 typedef struct Player Player;
+typedef struct PInput PInput;
 typedef struct Lobby Lobby;
 typedef struct Party Party;
 
@@ -68,8 +69,8 @@
 struct Particle
 {
 	Point2 p, v;
-	double yaw;
-	double mass;
+	double θ, ω;
+	double mass; /* kg */
 };
 
 struct Bullet
@@ -83,6 +84,7 @@
 {
 	Particle;
 	Kind kind;
+	int fuel;
 	Bullet rounds[10];
 	VModel *mdl;
 	Matrix mdlxform;
@@ -98,8 +100,10 @@
 {
 	Ship ships[2];
 	Star star;
+	double t, timeacc;
 
-	int (*step)(Universe*);
+	void (*step)(Universe*, double);
+	void (*reset)(Universe*);
 };
 
 struct GameState
@@ -119,12 +123,19 @@
 	char dir[40];
 	int ctl;
 	int data;
+	int status;
 };
 
+struct PInput
+{
+	ulong kdown;
+};
+
 struct Player
 {
 	char *name;
 	Conn conn;
+	PInput oldinput, input;
 };
 
 struct Lobby
--- a/fns.h
+++ b/fns.h
@@ -1,4 +1,4 @@
-#define FPS2MS(fps)	(1000/(fps))
+#define HZ2MS(hz)	(1000/(hz))
 
 /*
  * alloc
@@ -37,3 +37,9 @@
 void delparty(Party*);
 void addparty(Party*);
 
+/*
+ * universe
+ */
+Universe *newuniverse(void);
+void deluniverse(Universe*);
+void inituniverse(Universe*);
--- a/mkfile
+++ b/mkfile
@@ -14,6 +14,7 @@
 	lobby.$O\
 	party.$O\
 	sprite.$O\
+	universe.$O\
 
 HFILES=\
 	dat.h\
--- a/musw.c
+++ b/musw.c
@@ -1,5 +1,6 @@
 #include <u.h>
 #include <libc.h>
+#include <bio.h>
 #include <thread.h>
 #include <draw.h>
 #include <mouse.h>
@@ -27,7 +28,7 @@
 	{.key = 'y',	.op = Ksay},
 	{.key = 'q',	.op = Kquit}
 };
-ulong kup, kdown;
+ulong kdown;
 
 typedef struct Ball Ball;
 struct Ball
@@ -35,12 +36,117 @@
 	Point2 p, v;
 };
 
+RFrame screenrf;
 Ball bouncer;
+Universe *universe;
+VModel *needlemdl;
 char winspec[32];
 int debug;
 
 
+Point
+toscreen(Point2 p)
+{
+	p = invrframexform(p, screenrf);
+	return Pt(p.x,p.y);
+}
+
+Point2
+fromscreen(Point p)
+{
+	return rframexform(Pt2(p.x,p.y,1), screenrf);
+}
+
+/*
+ * readvmodel and drawship are testing routines
+ * that will later be implemented as VModel methods.
+ */
+VModel *
+readvmodel(char *file)
+{
+	ulong lineno;
+	char *s, *args[2];
+	Biobuf *bin;
+	VModel *mdl;
+
+	bin = Bopen(file, OREAD);
+	if(bin == nil)
+		sysfatal("Bopen: %r");
+
+	mdl = emalloc(sizeof *mdl);
+	mdl->pts = nil;
+	mdl->npts = 0;
+	mdl->strokefmt = nil;
+
+	lineno = 0;
+	while(s = Brdline(bin, '\n')){
+		s[Blinelen(bin)-1] = 0;
+		lineno++;
+
+		switch(*s++){
+		case '#':
+			continue;
+		case 'v':
+			if(tokenize(s, args, nelem(args)) != nelem(args)){
+				werrstr("syntax error: %s:%lud 'v' expects %d args",
+					file, lineno, nelem(args));
+				free(mdl);
+				Bterm(bin);
+				return nil;
+			}
+			mdl->pts = erealloc(mdl->pts, ++mdl->npts*sizeof(Point2));
+			mdl->pts[mdl->npts-1].x = strtod(args[0], nil);
+			mdl->pts[mdl->npts-1].y = strtod(args[1], nil);
+			mdl->pts[mdl->npts-1].w = 1;
+			break;
+		case 'l':
+		case 'c':
+			mdl->strokefmt = strdup(s-1);
+			break;
+		}
+	}
+	Bterm(bin);
+
+	return mdl;
+}
+
 void
+drawship(Ship *ship, Image *dst)
+{
+	int i;
+	char *s;
+	Point pts[3];
+	VModel *mdl;
+	Point2 *p;
+	Matrix T = {
+		1, 0, ship->p.x,
+		0, 1, ship->p.y,
+		0, 0, 1
+	}, R = {
+		cos(ship->θ), -sin(ship->θ), 0,
+		sin(ship->θ), cos(ship->θ), 0,
+		0, 0, 1
+	};
+
+	mulm(T, R);
+	mdl = ship->mdl;
+	p = mdl->pts;
+	for(s = mdl->strokefmt; s != 0 && p-mdl->pts < mdl->npts; s++)
+		switch(*s){
+		case 'l':
+			line(dst, toscreen(xform(p[0], T)), toscreen(xform(p[1], T)), 0, 0, 0, display->white, ZP);
+			p += 2;
+			break;
+		case 'c':
+			for(i = 0; i < nelem(pts); i++)
+				pts[i] = toscreen(xform(p[i], T));
+			bezspline(dst, pts, nelem(pts), 0, 0, 0, display->white, ZP);
+			p += 3;
+			break;
+		}
+}
+
+void
 kbdproc(void *)
 {
 	Rune r;
@@ -84,11 +190,10 @@
 					break;
 				}
 		}
-		kup = ~kdown;
 
 		if(debug)
-			fprint(2, "kup   %.*lub\nkdown %.*lub\n",
-				sizeof(kup)*8, kup, sizeof(kdown)*8, kdown);
+			fprint(2, "kdown %.*lub\n",
+				sizeof(kdown)*8, kdown);
 	}
 }
 
@@ -95,7 +200,7 @@
 void
 threadnetrecv(void *arg)
 {
-	uchar buf[256];
+	uchar buf[1024];
 	int fd, n;
 	Ioproc *io;
 
@@ -103,10 +208,10 @@
 	io = ioproc();
 
 	while((n = ioread(io, fd, buf, sizeof buf)) > 0){
-		unpack(buf, n, "PP", &bouncer.p, &bouncer.v);
-
-		if(debug)
-			fprint(2, "bouncer %v %v\n", bouncer.p, bouncer.v);
+		unpack(buf, n, "PPdPdP", &bouncer.p,
+			&universe->ships[0].p, &universe->ships[0].θ,
+			&universe->ships[1].p, &universe->ships[1].θ,
+			&universe->star.p);
 	}
 	closeioproc(io);
 }
@@ -135,8 +240,12 @@
 	lockdisplay(display);
 
 	draw(screen, screen->r, display->black, nil, ZP);
-	fillellipse(screen, addpt(screen->r.min,Pt(Dx(screen->r)/2,Dy(screen->r)/2+bouncer.p.y)), 2, 2, display->white, ZP);
+	fillellipse(screen, toscreen(bouncer.p), 2, 2, display->white, ZP);
 
+	drawship(&universe->ships[0], screen);
+	drawship(&universe->ships[1], screen);
+	fillellipse(screen, toscreen(universe->star.p), 4, 4, display->white, ZP);
+
 	flushimage(display, 1);
 	unlockdisplay(display);
 }
@@ -154,6 +263,8 @@
 		sysfatal("resize failed");
 	unlockdisplay(display);
 
+	screenrf.p = Pt2(screen->r.min.x+Dx(screen->r)/2,screen->r.max.y-Dy(screen->r)/2,1);
+
 	/* ignore move events */
 	if(Dx(screen->r) != SCRW || Dy(screen->r) != SCRH){
 		fd = open("/dev/wctl", OWRITE);
@@ -203,6 +314,10 @@
 	display->locking = 1;
 	unlockdisplay(display);
 
+	screenrf.p = Pt2(screen->r.min.x+Dx(screen->r)/2,screen->r.max.y-Dy(screen->r)/2,1);
+	screenrf.bx = Vec2(1, 0);
+	screenrf.by = Vec2(0,-1);
+
 	proccreate(kbdproc, nil, 4096);
 
 	fd = dial(server, nil, nil, nil);
@@ -209,6 +324,13 @@
 	if(fd < 0)
 		sysfatal("dial: %r");
 
+	universe = newuniverse();
+	needlemdl = readvmodel("assets/mdl/needle.vmdl");
+	if(needlemdl == nil)
+		sysfatal("readvmodel: %r");
+	universe->ships[0].mdl = needlemdl;
+	universe->ships[1].mdl = needlemdl;
+
 	threadcreate(threadnetrecv, &fd, 4096);
 	threadcreate(threadresize, mc, 4096);
 
@@ -215,6 +337,6 @@
 	io = ioproc();
 	for(;;){
 		redraw();
-		iosleep(io, FPS2MS(30));
+		iosleep(io, HZ2MS(30));
 	}
 }
--- a/muswd.c
+++ b/muswd.c
@@ -66,20 +66,24 @@
 broadcaststate(void)
 {
 	int i, n;
-	uchar buf[256];
+	uchar buf[1024];
 	Player *player;
-	Party *p, *np;
+	Party *p;
 
 	for(p = theparty.next; p != &theparty; p = p->next){
-		n = pack(buf, sizeof buf, "PP", p->state.p, p->state.v);
+		n = pack(buf, sizeof buf, "PPdPdP",
+			p->state.p,
+			p->u->ships[0].p, p->u->ships[0].θ,
+			p->u->ships[1].p, p->u->ships[1].θ,
+			p->u->star.p);
 
 		for(i = 0; i < nelem(p->players); i++){
 			if(write(p->players[i].conn.data, buf, n) != n){
 				player = &p->players[i^1];
 				lobby->takeseat(lobby, player->conn.dir, player->conn.ctl, player->conn.data);
-				np = p->prev;
-				delparty(p);
-				p = np;
+				/* step back and delete the spoiled party */
+				p = p->prev;
+				delparty(p->next);
 				break;
 			}
 		}
@@ -112,7 +116,8 @@
 
 		if(lobby->getcouple(lobby, couple) != -1){
 			newparty(couple);
-			resetsim(theparty.prev); /* reset the new party */
+			resetsim(theparty.prev);
+			theparty.prev->u->reset(theparty.prev->u);
 		}
 
 		now = nanosec();
@@ -120,8 +125,15 @@
 		then = now;
 
 		for(p = theparty.next; p != &theparty; p = p->next){
+			p->u->timeacc += frametime/1e9;
 			p->state.timeacc += frametime/1e9;
 
+			while(p->u->timeacc >= Δt){
+				p->u->step(p->u, Δt);
+				p->u->timeacc -= Δt;
+				p->u->t += Δt;
+			}
+
 			while(p->state.timeacc >= Δt){
 				integrate(&p->state, p->state.t, Δt);
 				p->state.timeacc -= Δt;
@@ -131,7 +143,7 @@
 
 		broadcaststate();
 
-		iosleep(io, FPS2MS(70));
+		iosleep(io, HZ2MS(70));
 	}
 }
 
@@ -158,10 +170,17 @@
 {
 	ulong i = 0;
 	Party *p;
+	Ship *s;
 
-	for(p = theparty.next; p != &theparty; p = p->next, i++)
-		fprint(fd, "%lud [p %v	v %v]\n",
+	for(p = theparty.next; p != &theparty; p = p->next, i++){
+		fprint(fd, "%lud p %v	v %v\n",
 			i, p->state.p, p->state.v);
+		for(s = &p->u->ships[0]; s-p->u->ships < nelem(p->u->ships); s++){
+			fprint(fd, "%lud s%lld k%d p %v v %v θ %g ω %g m %g f %d\n",
+				i, s-p->u->ships, s->kind, s->p, s->v, s->θ, s->ω, s->mass, s->fuel);
+		}
+		fprint(fd, "%lud S p %v m %g\n", i, p->u->star.p, p->u->star.mass);
+	}
 }
 
 
--- a/notes
+++ /dev/null
@@ -1,17 +1,0 @@
-• there's, at most, two players waiting in the lobby at a given time.
-it makes no sense to allocate more than two seats since the threadsim
-will consume them whenever they are ready to join the party.  i'm
-thinking of using channels to synchronize the two threads, so
-threadsim doesn't loop doing nothing (but sleeping) until at least a
-couple of players join.
-
-• the integrator has to operate with vectors and the different objects
-in the universe, some of which may require their own governing laws.
-
-• think of a way to pack the bullets efficiently.  will they be part
-of the global state broadcast?  what does the client need to know to
-render and manage them?
-
-• it could be beneficial to do dynamics in the client as well, which
-means sending more data, and probably require a tighter sync, but a
-smoother user experience.
--- a/party.c
+++ b/party.c
@@ -23,6 +23,8 @@
 	p->players[0] = players[0];
 	p->players[1] = players[1];
 
+	p->u = newuniverse();
+
 	addparty(p);
 
 	return p;
@@ -33,6 +35,7 @@
 {
 	p->next->prev = p->prev;
 	p->prev->next = p->next;
+	deluniverse(p->u);
 	free(p);
 }
 
--- a/physics.c
+++ b/physics.c
@@ -18,6 +18,25 @@
 	return Vec2(0, -k*s->p.y - b*s->v.y);
 }
 
+/*
+ * XXX: remember to take thrust into account, based on user input.
+ */
+static Point2
+accelship(Universe *u, Ship *s, double)
+{
+	double g, d;
+
+	d = vec2len(subpt2(u->star.p, s->p));
+	g = G*u->star.mass/(d*d);
+	return mulpt2(normvec2(subpt2(u->star.p, s->p)), g);
+}
+
+static Point2
+accelbullet(Universe *, Bullet *, double)
+{
+	return Vec2(0,0);
+}
+
 static Derivative
 eval(GameState *s0, double t, double Δt, Derivative *d)
 {
--- /dev/null
+++ b/universe.c
@@ -1,0 +1,63 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include "libgeometry/geometry.h"
+#include "dat.h"
+#include "fns.h"
+
+static void
+universe_step(Universe *u, double Δt)
+{
+	//integrate(u, u->t, Δt);
+}
+
+static void
+universe_reset(Universe *u)
+{
+	int i, j;
+
+	for(i = 0; i < nelem(u->ships); i++){
+		for(j = 0; j < nelem(u->ships[i].rounds); j++)
+			memset(&u->ships[i].rounds[j], 0, sizeof(Bullet));
+		memset(&u->ships[i].Particle, 0, sizeof(Particle));
+	}
+	memset(&u->star.Particle, 0, sizeof(Particle));
+	inituniverse(u);
+}
+
+void
+inituniverse(Universe *u)
+{
+	u->ships[0].p = Pt2(SCRW/2-50,SCRH/2-50,1);
+	u->ships[0].θ = (180+45)*DEG;
+	u->ships[0].mass = 10e3; /* 10 tons */
+	u->ships[0].kind = NEEDLE;
+	u->ships[0].fuel = 100;
+
+	u->ships[1].p = Pt2(-SCRW/2+50,-SCRH/2+50,1);
+	u->ships[1].θ = 45*DEG;
+	u->ships[1].mass = 40e3; /* 40 tons */
+	u->ships[1].kind = WEDGE;
+	u->ships[1].fuel = 200;
+
+	u->star.p = Pt2(0,0,1);
+	u->star.mass = 5.97e24; /* earth's mass */
+}
+
+Universe *
+newuniverse(void)
+{
+	Universe *u;
+
+	u = emalloc(sizeof(Universe));
+	memset(u, 0, sizeof *u);
+	u->step = universe_step;
+	u->reset = universe_reset;
+	return u;
+}
+
+void
+deluniverse(Universe *u)
+{
+	free(u);
+}