setMap
to write to the screen.
along the way I created a version of 2D life that performs better on the microcontroller JS impl than it did in the web browser.
(leo allocates an entire grid for every getTile
call, and then fills it up in an O(2n) way, so it's very slow, whereas my lookups are constant time (spatial hashmap))
the javascript driving my version is running on a non-JIT runtime optimized for memory, not speed. his is running on V8 in my web browser ...
my version can easily go at 20 fps in spade, his is sub-10 in my web browser ....getAll
to find all of the tiles on the map that are "living" or "dead."
however, Omay's map is 1028 tiles large.
So there's 2056 tiles between the arrays and their clones.
I'm going to rewrite it so that instead of using getAll
, it loops over the map using for loops.
I think this will use less memory because there won't be any arrays in JavaScript holding all of the tiles.audio_buffer->format->format.format
EDIT: now format
doesn't even look like a word. god help meputs("this little spriggy" " goes to the" " market!"); // > "this little spriggy goes to the market"this macro adds a
"
to the beginning of each line of a "block" of text (which in this case are discrete songs)),
me when I fix a bug:
wow, I can't believe I got 6 hours of free entertainment because I had a parameter on the wrong side of a ,
requestAnimationFrame(function frame(now) { with (async () => { and added one line to the end of my tile rendering loop: for (const oh of sightGrid) { const { x, y } = hex.axialToOffset(v2.add(oh, ph)); ctx.fillStyle = ['palegreen', 'green'][+(window.ohgod = !window.ohgod)]; drawHex(x, y, vec(hex.GRID_SIZE)); await new Promise(res => setTimeout(res, 100)); }and yeah now you can see it draw the cool pattern gradually so. yeah. that's a thing. > +(window.ohgod = !window.ohgod) i could have accomplished this by declaring a variable outside of my for loop but when this idea occurred to me I knew it would be useless and wanted to localize the code for this experiment onto a single line (that, and there is something horribly wrong with my brain and I think i am gradually descending into Terry Davis, please send elph)
setInterval(() => {
your players move forward, โก๏ธ
your enemies decide who to badger next, :threat:
some of your projectiles are just barely dodged, :flug:
and others smash satisfyingly into their targets :bonk:
}, 1000 / 60);
at each of these intervals, the server decides which of these occur, and which ... do not.
the difference is quite literally life and death for the denizens of your online realm:
if a player's input makes it to the server just after this update has occurred, they might just not move out of the way of the oncoming missile in time.
after one of these updates when the server has just finished making these vital decisions, it blasts relevant portions of them out to everyone who's connected to your server. (there's no point in updating you about something if it's happening on the other side of the map!)
meanwhile, in the player's web browser, these regular updates from the server are gradually applied, so the server's periodic contemplation is rendered as a seamless sequence of fluid movements...
unless those updates aren't really so regular at all ...
the contract that the server has with the client -- that the server will give the client evenly spaced out updates at regular-enough intervals so that the client can smoothly move between them -- isn't actually the same as the contract that setInterval
provides.
all setInterval(update, 1000)
means is that there will be ABOUT one thousand milliseconds between each of your updates, usually more. so if the actual interval ends up being 1009ms one frame, the next update won't be about 991ms later so that updates happen at regular one second intervals, it will be more like 1007ms later, so that now you're a total of 16 ms away from the contract you had with the client. this is known as drift.
at worst, this passes on all of the problems of using setInterval
for animation onto your game. but what if you're doing something fancier than just moving between the updates you get from the server as you get them? there's always going to be a random delay between what the server sends and what you get, anyway. it might make more sense to push the updates you get from the server onto a stack, and exactly as often as the server updates, pop the next set of positions off of that stack.
... except that if you're popping using one setInterval
on the client, and the server is pushing out those simulation ticks using a setInterval
of its own ... and each has a different drift ... they're quickly going to fall out of sync. if the server is drifting a lot, your stack might gradually accumulate thousands of positions that you just can't keep up with ... and if the server is drifting less than you are, you might run out of positions to show the player!
suffice it to say, it's super important to have a setInterval
that updates without drift. I threw together an implementation of one this morning. I call it a `tick`, since it works well for those simulation ticks I was talking about before. To test it, I went ahead and also made a drift visualizer. Here's a screenshot of it! The grey lines are spaced out at 0.5s intervals, and the first row of blue marks is made by setInterval
, while the other row of blue marks are made by my own tick
function. as you can see, they stick to the "wall clock time" a lot better than Interval
does, even when you leave it running for several minutes!
here's a screenshot! I'll post the source in the thread :)
fun fact: everything that moves is a CSS animation ๐
I didn't want to pollute the JS event loop, although now that I think about it, it might've been better to so that I can see how well my ticker keeps itself together in the presence of some event loop pressure.const tM = 0.5, vM = 0.75; if (t < tM) t = lerp(0, vM, invLerp(0, tM, t)); else t = lerp(vM, 1, invLerp(tM, 1, t));remap is such a useful idea
fread
those directly into a bunch of C structs. (I do have to handle endianness for integers, though)
in Rust, even if I used #[repr(C)]
to force a stable ABI for those structs, I'd still have to jump through a bunch more hoops (unsafe
) or pull in a dependency like bincode (which would in turn pull in serde ...).
anyway, so I quickly wrote a python metaprogram to generate a C header that can fread
(or just memcpy
) in all of the binary data into the fields in the game map object that the first python script pulls out of blender"^[A-Za-z0-9_]+[a-zA-Z0-9._%+-]+@[a-zA-Z0-9._-]+\.[a-zA-Z0-9]{2,3}$"
. notice that last part, which allows for 2 or 3 alphanumeric characters following the last .
. you can imagine how that might clash with someone's email.
now, you might be thinking, ced, i know it's tempting to just sidestep their clientside verification, but that's technically circumventing access protocols. that sounds really illegal! and you're totally right, which is why whoever did this probably has HORRIBLE impulse control ...
the second image demonstrates someone putting a break point on the line that actually sends the request and at that point, n
in the watch window should be the variable on the left, so that someone will be able to modify the new email to the unvalidated one ...
whoever did this probably discovered a couple useful things about chrome devtools in the process that they weren't already aware of ...