Closed cs6413110 closed 6 months ago
Figuring out what I want to do for determining if the tank has changed cell position(chunk loading).
Timeline as of now is something like this:
1.) Chunk loading detection on Tank and Engine for multiplayer 2.) Chunk loading formula for Multiplayer 3.) Implement new chunk loading over old 4.) Implement updating events for each entity (Tank, Damage, AI, Shot, Block) 5.) Code update streaming for Multiplayer 6.) Bugfixes
Beta chunkloading coords
chunkload(t, x, y) {
return;
const w = ?, h = ?;
const ocx = Math.floor(t.x/100), ocy = Math.floor(t.y/100), ncx = Math.floor(x/100), ncy = Math.floor(y/100);
const xd = ocx-ncx, yd = ocy-ncy, yda = yd < 0 ? -1 : 1, xda = xd < 0 ? -1 : 1;
// New Loaded Rect Coords
const nhrx = ncx-w/2*xda, nhry = ncy-h/2*yda, nhrw = w*xda, nhrh = Math.min(h, Math.abs(yd))*yda;
const nvrx = nhrx, nvry = nhry+nhrh, nvrw = Math.min(w, Math.abs(xd))*xda, nvrh = (h-Math.min(h, Math.abs(yd)))*yda;
const ohrx = ocx+w/2*xda, ohry = ncy-h/2*yda, ohrw = -nhrw, ohrh = -nhrh;
const ovrx = ohrx, ovry = ohry+ohrw, ovrw = -nvrw, ovrh = -nvrh;
}
Chunk loading simulation
<canvas id='canvas' width='500', height='500'></canvas>
<script>
const canvas = document.getElementById('canvas'), draw = canvas.getContext('2d');
canvas.style = 'border: 1px solid black';
let t = {x: 0, y: 0}, x = 0, y = 0;
(a = (t, x, y) => {
const s = 20;
t.x *= 100;
t.y *= 100;
x *= 100;
y *= 100;
const w = 7, h = 5; // must be odd ig?
const ocx = Math.floor(t.x/100)+.5, ocy = Math.floor(t.y/100)+.5, ncx = Math.floor(x/100)+.5, ncy = Math.floor(y/100)+.5;
const xd = ocx-ncx, yd = ocy-ncy, yda = yd < 0 ? -1 : 1, xda = xd < 0 ? -1 : 1;
// New Loaded Rect Coords
const nhrx = ncx-w/2*xda, nhry = ncy-h/2*yda, nhrw = w*xda, nhrh = Math.min(h, Math.abs(yd))*yda;
const nvrx = nhrx, nvry = nhry+nhrh, nvrw = Math.min(w, Math.abs(xd))*xda, nvrh = (h-Math.min(h, Math.abs(yd)))*yda;
const ohrx = ocx+w/2*xda, ohry = ocy+h/2*yda, ohrw = -nhrw, ohrh = -nhrh;
const ovrx = ohrx, ovry = ohry+ohrh, ovrw = -nvrw, ovrh = -nvrh;
t.x /= 100;
t.y /= 100;
x /= 100;
y /= 100;
// GUI
draw.clearRect(0, 0, 500, 500);
draw.lineWidth = 1;
draw.strokeStyle = '#000000';
for (let i = 1; i < Math.floor(500/s); i++) {
draw.moveTo(i*s, 0);
draw.lineTo(i*s, 500);
draw.moveTo(0, i*s);
draw.lineTo(500, i*s);
}
draw.stroke();
draw.fillStyle = '#ff0000';
draw.fillRect(ocx*s-2, ocy*s-2, 4, 4);
draw.fillStyle = '#00ff00';
draw.fillRect(ncx*s-2, ncy*s-2, 4, 4);
draw.lineWidth = 5;
draw.strokeStyle = '#ff0000';
draw.strokeRect((ocx-w/2)*s, (ocy-h/2)*s, w*s, h*s);
draw.strokeStyle = '#00ff00';
draw.strokeRect((ncx-w/2)*s, (ncy-h/2)*s, w*s, h*s);
draw.lineWidth = 3;
draw.strokeStyle = '#00ffff';
draw.strokeRect(nvrx*s, nvry*s, nvrw*s, nvrh*s);
draw.strokeRect(nhrx*s, nhry*s, nhrw*s, nhrh*s);
draw.strokeStyle = '#ffff00';
draw.strokeRect(ohrx*s, ohry*s, ohrw*s, ohrh*s);
draw.strokeRect(ovrx*s, ovry*s, ovrw*s, ovrh*s);
})(t, x, y);
document.addEventListener('keydown', e => {
e.preventDefault();
if (e.keyCode == 39) x++;
if (e.keyCode == 37) x--;
if (e.keyCode == 38) y--;
if (e.keyCode == 40) y++;
if (e.keyCode == 68) t.x++;
if (e.keyCode == 65) t.x--;
if (e.keyCode == 87) t.y--;
if (e.keyCode == 83) t.y++;
a(t, x, y);
});
</script>
The math works :D WASD to move old viewport, Arrow keys to move new viewport.
Implementing as a for loop
Converted to for loop :) Now time to optimize/simplify
<canvas id='canvas' width='500', height='500'></canvas>
<script>
const canvas = document.getElementById('canvas'), draw = canvas.getContext('2d');
canvas.style = 'border: 1px solid black';
let t = {x: 0, y: 0}, x = 0, y = 0;
(a = (t, x, y) => {
const s = 20;
t.x *= 100;
t.y *= 100;
x *= 100;
y *= 100;
const w = 7, h = 5; // must be odd ig?
let ocx = Math.floor(t.x/100)+.5, ocy = Math.floor(t.y/100)+.5, ncx = Math.floor(x/100)+.5, ncy = Math.floor(y/100)+.5;
let xd = ocx-ncx, yd = ocy-ncy, yda = yd < 0 ? -1 : 1, xda = xd < 0 ? -1 : 1;
// New Loaded Rect Coords
let nhrx = ncx-w/2*xda, nhry = ncy-h/2*yda, nhrw = w*xda, nhrh = Math.min(h, Math.abs(yd))*yda;
let nvrx = nhrx, nvry = nhry+nhrh, nvrw = Math.min(w, Math.abs(xd))*xda, nvrh = (h-Math.min(h, Math.abs(yd)))*yda;
let ohrx = ocx+w/2*xda, ohry = ocy+h/2*yda, ohrw = -nhrw, ohrh = -nhrh;
let ovrx = ohrx, ovry = ohry+ohrh, ovrw = -nvrw, ovrh = -nvrh;
t.x /= 100;
t.y /= 100;
x /= 100;
y /= 100;
// GUI
draw.clearRect(0, 0, 500, 500);
draw.fillStyle = '#0000ff';
let nys = yda > 0 ? nhry : nhry-1;
let nxs = xda > 0 ? nhrx : nhrx-1;
for (let yl = false, y = nys; y != nys+h*yda; y += yda) {
if (y === nys+nhrh) yl = true;
for (let x = nxs; x != nxs+(yl ? nvrw : w*xda); x += xda) {
draw.fillRect(x*s+1, y*s+1, s-2, s-2);
}
}
draw.fillStyle = '#ff0000';
let oys = yda > 0 ? ohry-1 : ohry;
let oxs = xda > 0 ? ohrx-1 : ohrx;
let iterations = 0;
for (let yl = false, y = oys; y != oys-h*yda; y -= yda) {
if (y === oys+ohrh) yl = true;
for (let x = oxs; x != oxs-(yl ? Math.abs(ovrw) : w)*xda; x -= xda) {
iterations++;
if (iterations > 300) {
alert(x+' '+(-xda)+' '+(oxs-(yl ? ovrw : w)*xda));
break;
}
draw.fillRect(x*s+1, y*s+1, s-2, s-2);
}
}
draw.lineWidth = 1;
draw.strokeStyle = '#000000';
for (let i = 1; i < Math.floor(500/s); i++) {
draw.moveTo(i*s, 0);
draw.lineTo(i*s, 500);
draw.moveTo(0, i*s);
draw.lineTo(500, i*s);
}
draw.stroke();
draw.fillStyle = '#ff0000';
draw.fillRect(ocx*s-2, ocy*s-2, 4, 4);
draw.fillStyle = '#00ff00';
draw.fillRect(ncx*s-2, ncy*s-2, 4, 4);
draw.lineWidth = 5;
draw.strokeStyle = '#ff0000';
draw.strokeRect((ocx-w/2)*s, (ocy-h/2)*s, w*s, h*s);
draw.strokeStyle = '#00ff00';
draw.strokeRect((ncx-w/2)*s, (ncy-h/2)*s, w*s, h*s);
draw.lineWidth = 3;
draw.strokeStyle = '#00ffff';
draw.strokeRect(nvrx*s, nvry*s, nvrw*s, nvrh*s);
draw.strokeRect(nhrx*s, nhry*s, nhrw*s, nhrh*s);
draw.strokeStyle = '#ffff00';
draw.strokeRect(ohrx*s, ohry*s, ohrw*s, ohrh*s);
draw.strokeRect(ovrx*s, ovry*s, ovrw*s, ovrh*s);
})(t, x, y);
document.addEventListener('keydown', e => {
e.preventDefault();
if (e.keyCode == 39) x++;
if (e.keyCode == 37) x--;
if (e.keyCode == 38) y--;
if (e.keyCode == 40) y++;
if (e.keyCode == 68) t.x++;
if (e.keyCode == 65) t.x--;
if (e.keyCode == 87) t.y--;
if (e.keyCode == 83) t.y++;
a(t, x, y);
});
</script>
Ok, done with testing. Final code ready for actual beta cases.
chunkload(t, x, y) {
const w = 21, h = 15;
const ocx = Math.floor(t.x/100)+.5, ocy = Math.floor(t.y/100)+.5, ncx = Math.floor(x/100)+.5, ncy = Math.floor(y/100)+.5;
let xd = ocx-ncx, yd = ocy-ncy, yda = yd < 0 ? -1 : 1, xda = xd < 0 ? -1 : 1;
for (let yl = false, nys = (yda > 0 ? 0 : -1)+ncy-h/2*yda, y = nys; y != nys+h*yda; y += yda) {
if (y === nys+Math.min(h, Math.abs(yd))*yda) yl = true;
for (let nxs = (xda > 0 ? 0 : -1)+ncx-w/2*xda, x = nxs; x != nxs+(yl ? Math.min(w, Math.abs(xd))*xda : w*xda); x += xda) {
draw.fillRect(x*s+1, y*s+1, s-2, s-2);
}
}
for (let yl = false, oys = (yda > 0 ? -1 : 0)+ocy+h/2*yda, y = oys; y != oys-h*yda; y -= yda) {
if (y === oys-Math.min(h, Math.abs(yd))*yda) yl = true;
for (let oxs = (xda > 0 ? -1 : 0)+ocx+w/2*xda, x = oxs; x != oxs-(yl ? Math.abs(Math.min(w, Math.abs(xd))*xda) : w)*xda; x -= xda) {
draw.fillRect(x*s+1, y*s+1, s-2, s-2);
}
}
}
Did some brainstorming about how to actually link up the chunk loading, event sending, etc.
Also did you know the game technically waits up to 15ms to send updates to clients instead of instantly. I'm going to change this so that it does it instantly, but if updates are too fast it will rate limit to 60/s.
I don't like this so upgrading....
A.createTemplate('message', class {b = []; pt = []; ai = []; s = []; d = []; event = 'hostupdate'; delete = {b: [], pt: [], ai: [], s: [], d: []}}, m => {
for (const property of ['b', 'pt', 'ai', 's', 'd']) {
m[property].length = 0;
m.delete[property].length = 0;
}
Thinking somthing more like:
{
event: 'hostupdate',
b: [],
pt: [],
ai: [],
s: [],
d: [],
delete: [],
}
Because ids don't need entity data
all 3 of my remaining brain cells are too small to understand this
Beta Server for testing is on port 443 instead of port 8080
Good?
Replace the :8080 at the end of the multiplayer join link to :443 to join.
Recoding on a seperate branch so its more organized. Recoding AI and turrets to use memory efficent + update based code. Working on figuring out optimizing entity creation/cached updating. Maybe an update multiple thing.
Nope. I actually haven't implemented anything yet. All I've been doing is preparation. So the recent lag spikes aren't caused by this at all.
I have coded stuff, but its a complete upheaval of entire multiplayer system so its slow
Lag Update self-reminder: Chunkload only triggers on client position updating as of now. Trigger it also on grapple pos change and initial chunkload of Engine.add()
Making progress on debugging chunk loading bug. I know why and am just working on solving infinite loop bug.
demo i used for debugging for future reference:
<canvas id='canvas' width='300' height='300' style='border: 1px solid green'></canvas>
<canvas id='canvas2' width='300' height='300' style='border: 1px solid red'></canvas>
WASD to move red rect, Arrows for green rect. Green bordered is the correct algorithm, red is incorrect. The current working solution is redundant. For the most part they are the same but certain chunks won't load when both reds and partially out of the rendering border limit.
<div id='output'></div>
<button onclick='test()'>adf</button>
<script>
window.onerror = alert;
const canvas = document.getElementById('canvas'), draw = canvas.getContext('2d');
const canvas2 = document.getElementById('canvas2'), draw2 = canvas2.getContext('2d');
draw.setTransform(.05, 0, 0, .05, 75, 75);
draw2.setTransform(.05, 0, 0, .05, 75, 75);
let ox = 1500, oy = 1500, nx = 1500, ny = 1500;
document.addEventListener('keydown', e => {
e.preventDefault();
let k = e.keyCode;
if (k == 87) oy -= 100;
if (k == 83) oy += 100;
if (k == 65) ox -= 100;
if (k == 68) ox += 100;
if (k == 38) ny -= 100;
if (k == 40) ny += 100;
if (k == 37) nx -= 100;
if (k == 39) nx += 100;
draw.clearRect(-1500, -1500, 6000, 6000);
draw2.clearRect(-1500, -1500, 6000, 6000);
chunkloadERRORED({x: ox, y: oy}, nx, ny);
chunkloadFORCED({x: ox, y: oy}, nx, ny);
});
function test() {
for (let x1 = 0; x1 < 3000; x1 += 100) {
for (let y1 = 0; y1 < 3000; y1 += 100) {
for (let x2 = 0; x2 < 3000; x2 += 100) {
for (let y2 = 0; y2 < 3000; y2 += 100) {
draw.clearRect(-1500, -1500, 6000, 6000);
draw2.clearRect(-1500, -1500, 6000, 6000);
chunkloadERRORED({x: x1, y: y1}, x2, y2);
chunkloadFORCED({x: x1, y: y1}, x2, y2);
if (canvas.toDataURL() !== canvas2.toDataURL()) document.getElementById('output').innerHTML += 'fail';
}
}
}
}
}
const m = o => Math.max(0, Math.min(29, o));
const m2 = o => Math.max(-1, Math.min(30, o));
function chunkloadERRORED(t, x, y) {
const w = 21, h = 15;
const ocx = Math.floor(t.x/100)+.5, ocy = Math.floor(t.y/100)+.5, ncx = Math.floor(x/100)+.5, ncy = Math.floor(y/100)+.5;
const xd = ocx-ncx, yd = ocy-ncy, yda = yd < 0 ? -1 : 1, xda = xd < 0 ? -1 : 1, yl = Math.min(h, Math.abs(yd))*yda;
for (let nys = (yda > 0 ? 0 : -1)+ncy-h/2*yda, y = m(nys), l = false; (yda > 0 ? (y < m2(nys+h*yda)) : (y > m2(nys+h*yda))); y += yda) {
if (yda < 0 ? y <= nys+yl : y >= nys+yl) l = true;
for (let nxs = (xda > 0 ? 0 : -1)+ncx-w/2*xda, x = m(nxs); (xda > 0 ? (x < m2(nxs+(l ? Math.min(w, Math.abs(xd)) : w)*xda)) : (x > m2(nxs+(l ? Math.min(w, Math.abs(xd)) : w)*xda))); x += xda) {
draw2.fillStyle='#00ff00';
draw2.fillRect(x*100, y*100, 100, 100);
}
}
for (let oys = (yda > 0 ? -1 : 0)+ocy+h/2*yda, y = m(oys), l = false; (yda < 0 ? (y < m2(oys-h*yda)) : (y > m2(oys-h*yda))); y -= yda) {
if (yda > 0 ? y <= oys-yl : y >= oys-yl) l = true;
for (let oxs = (xda > 0 ? -1 : 0)+ocx+w/2*xda, x = m(oxs); (xda < 0 ? (x < m2(oxs-(l ? Math.min(w, Math.abs(xd)) : w)*xda)) : (x > m2(oxs-(l ? Math.min(w, Math.abs(xd)) : w)*xda))); x -= xda) {
draw2.fillStyle = '#ff0000';
draw2.fillRect(x*100, y*100, 100, 100);
}
}
}
function chunkloadFORCED(t, x, y) {
const w = 21, h = 15;
const ocx = Math.floor(t.x/100)+.5, ocy = Math.floor(t.y/100)+.5, ncx = Math.floor(x/100)+.5, ncy = Math.floor(y/100)+.5;
const xd = ocx-ncx, yd = ocy-ncy, yda = yd < 0 ? -1 : 1, xda = xd < 0 ? -1 : 1, yl = Math.min(h, Math.abs(yd))*yda;
for (let nys = (yda > 0 ? 0 : -1)+ncy-h/2*yda, y = nys, l = false; y != nys+h*yda; y += yda) {
if (y === nys+yl) l = true;
for (let nxs = (xda > 0 ? 0 : -1)+ncx-w/2*xda, x = nxs; x != nxs+(l ? Math.min(w, Math.abs(xd)) : w)*xda; x += xda) {
if (x >= 0 && x <= 29 && y >= 0 && y <= 29) {
draw.fillStyle='#00ff00';
draw.fillRect(x*100, y*100, 100, 100);
}
}
}
for (let oys = (yda > 0 ? -1 : 0)+ocy+h/2*yda, y = oys, l = false; y != oys-h*yda; y -= yda) {
if (y === oys-yl) l = true;
for (let oxs = (xda > 0 ? -1 : 0)+ocx+w/2*xda, x = oxs; x != oxs-(l ? Math.min(w, Math.abs(xd)) : w)*xda; x -= xda) {
if (x >= 0 && x <= 29 && y >= 0 && y <= 29) {
draw.fillStyle = '#ff0000';
draw.fillRect(x*100, y*100, 100, 100);
}
}
}
}
</script>
Notes for future self:
Progress Update:
when lag ded
or in a week
Ok so it partially works
Didn't account for entities that can move out of your render distance so I have to add a thing to track that.
Also chunkloaded exploded for no reason
Imma test with low render distance to see what's wrong
Just debugging now
Progress on the lag update.