multiplayer canvas test, performance
Omdat ik nogal graag nieuwe dingen probeer, heb ik besloten om een canvas multiplayer test te maken.
Ajax om speler-input naar een database te sturen en meteen alle speler info op te halen om het vervolgens door Canvas allemaal weer te geven.
Een simpel voorbeeld hiervan was snel gemaakt
Voor de mensen die zich nu hetzelfde afvragen als ik voordat ik ermee begon,
Ja, de performance is nogal matig, lag is meer een regel dan een uitzondering. Nu is er een kleine kans dat dit aan mijn localhost + computer ligt maar om de een of andere reden betwijfel ik het.
Dus nu mijn vraag, wie denkt te weten hoe de performance te verbeteren valt?
Beveiliging en browser compatibiliteit zijn niet inbegrepen. Het is maar een test om te kijken hoe geschikt deze methode wel of niet is.
De database bestaat uit 1 table met 3 kolommen (id, x en y) dit is simpel gehouden omdat het een prototype is.
De php pagina:
Code (php)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php
require 'db_connect.php';
if($_SERVER['REQUEST_METHOD'] == "GET"){
$result = mysql_query("SELECT id FROM coords WHERE id=" . $_GET['id'] . " LIMIT 1");
if(mysql_num_rows($result) > 0)
mysql_query("UPDATE coords SET x=" . $_GET['x'] . ", y=" . $_GET['y'] . " WHERE id=" . $_GET['id'] . " LIMIT 1");
else
mysql_query("INSERT INTO coords (id, x, y) VALUES (" . $_GET['id'] . "," . $_GET['x'] . "," . $_GET['y'] . ");");
}
$result = mysql_query("SELECT id, x, y FROM coords");
while($row = mysql_fetch_row($result))
echo $row[0], "-", $row[1], "-", $row[2], "|";
?>
require 'db_connect.php';
if($_SERVER['REQUEST_METHOD'] == "GET"){
$result = mysql_query("SELECT id FROM coords WHERE id=" . $_GET['id'] . " LIMIT 1");
if(mysql_num_rows($result) > 0)
mysql_query("UPDATE coords SET x=" . $_GET['x'] . ", y=" . $_GET['y'] . " WHERE id=" . $_GET['id'] . " LIMIT 1");
else
mysql_query("INSERT INTO coords (id, x, y) VALUES (" . $_GET['id'] . "," . $_GET['x'] . "," . $_GET['y'] . ");");
}
$result = mysql_query("SELECT id, x, y FROM coords");
while($row = mysql_fetch_row($result))
echo $row[0], "-", $row[1], "-", $row[2], "|";
?>
Javascript:
Code (php)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
<script type="text/javascript">
// variabele klaarzetten
var ajax = new XMLHttpRequest();
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
var id = 1; // hardcoded, later aan localStorage, of cookie, of IP, of wat dan ook binden, voor testen lekker hardcoded
var x = 225; // hardcoded co-ordinaten
var y = 225;
// bewegen
document.onkeydown = function(event){
var key = event.keyCode;
switch(key){
case 37: // links
x --;
break;
case 38: // omhoog
y --;
break;
case 39: // rechts
x ++;
break;
case 40: // omlaag
y ++;
break;
}
}
// tekenen
function draw(){
if(ajax.readyState == 4 && ajax.status == 200){
// canvas wissen
ctx.clearRect(0,0,canvas.width,canvas.height);
// co-ordinaten ophalen.
var content = ajax.responseText;
var players = content.split("|");
for(i in players){
if(players[i] != ''){
var coords = players[i].split("-");
var p_id = coords[0];
var p_x = coords[1];
var p_y = coords[2];
ctx.fillRect(p_x,p_y,50,50);
}
}
}
}
function update(){
ajax.onreadystatechange = draw;
ajax.open('GET', 'test.php?id='+id+'&x='+x+'&y='+y, true);
ajax.send();
}
// zie dit als de 'refresh rate' dit komt neer op 10 fps, we moeten Ajax wel een beetje de tijd geven he,
setInterval("update()", 100);
</script>
// variabele klaarzetten
var ajax = new XMLHttpRequest();
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
var id = 1; // hardcoded, later aan localStorage, of cookie, of IP, of wat dan ook binden, voor testen lekker hardcoded
var x = 225; // hardcoded co-ordinaten
var y = 225;
// bewegen
document.onkeydown = function(event){
var key = event.keyCode;
switch(key){
case 37: // links
x --;
break;
case 38: // omhoog
y --;
break;
case 39: // rechts
x ++;
break;
case 40: // omlaag
y ++;
break;
}
}
// tekenen
function draw(){
if(ajax.readyState == 4 && ajax.status == 200){
// canvas wissen
ctx.clearRect(0,0,canvas.width,canvas.height);
// co-ordinaten ophalen.
var content = ajax.responseText;
var players = content.split("|");
for(i in players){
if(players[i] != ''){
var coords = players[i].split("-");
var p_id = coords[0];
var p_x = coords[1];
var p_y = coords[2];
ctx.fillRect(p_x,p_y,50,50);
}
}
}
}
function update(){
ajax.onreadystatechange = draw;
ajax.open('GET', 'test.php?id='+id+'&x='+x+'&y='+y, true);
ajax.send();
}
// zie dit als de 'refresh rate' dit komt neer op 10 fps, we moeten Ajax wel een beetje de tijd geven he,
setInterval("update()", 100);
</script>
Gewijzigd op 06/09/2010 10:16:36 door Johan Dam
Wat ook scheelt aan performance, is het lozen van MySQL :-)
MySQL is al zo klein mogelijk gemaakt, enige wat gebeurd is het updaten van de 'speler' coördinaten en het ophalen van alle andere coordinaten.
Enige wat hierbij verbeterd kan worden is dat de 'eigen speler' coordinaten niet via de db geupdate worden maar lokaal, dan ziet het er vloeiender uit maar daarmee maskeer je de performance ipv het op te lossen.
Ik denk dat Long-polling het makkelijkste is te implementeren. Je laat het PHP script gewoon wachten totdat er nieuwe data is alvorens hij een antwoord stuurt. Het PHP script moet dan luisteren naar nieuwe 'events', waarvoor je een soort dbus-achtige server nodig hebt. Iets heel simpels dat een bericht verspreidt naar alle luisteraars wanneer je er eentje naartoe stuurt .
Je laat alle clients verbinding maken met een PHP script, en dat script laat je wachten totdat er nieuwe berichten zijn van die server. Wanneer je dan van positie verplaatst, roep je een PHP script aan dat de nieuwe positie naar die server stuurt. Die verspreid het dan onder alle luisterende verbindingen, die dan de nieuwe positie echo'en en de verbinding verbreken. Zodra de luisterende verbinding verbroken wordt is er nieuwe data, en moet je opnieuw die luisterende verbinding starten. (Je moet alleen nog even nadenken over hoe je ervoor gaat zorgen dat je geen berichten mist, maar dat is vrij simpel)
Doordat je om de 0.1 seconde (single player) een mysql verbinding opent, 3 query's uitvoert en daarna weer sluit, heb je iedere seconde 30 query's. Dat betekend dus dat je in één minuut, 1800 query's uitvoert... Met twee spelers heb je er dus 3600, 3 spelers 5400 e.d.
Dus nee, MySQL is geen optie...
Dat klinkt inderdaad als een goede oplossing, of tenminste, als een verbetering :P of het goed is moeten we maar afwachten ;)
@Chris, tja, heb je een alternatief? De mysql code word zo effectief mogelijk gemaakt, het kan misschien met 2 queries als ik de speler registreer zodra de sessie word gestart bijvoorbeeld, al denk ik dat dit de beste oplossing is... Ik weet zo geen alternatief, andere vormen van SQL zullen weinig uitmaken, wegschrijven naar een bestandje lijkt me niet bepaald een vooruitgang...
Ben het met je eens wat JSON betreft, dit is nogal slordig. Zal het waarschijnlijk veranderen als die long-polling goed werkt.
Ja hoor, wat Jelmer net omschreef werkt perfect! Maar eigenlijk is PHP daar niet voor geschikt ;-)
Inderdaad. Een wachtende verbinding kost weer een paar mb met Apache en PHP geladen. Ik heb zelf een oplossing met haproxy en nodejs, waarbij ik een subdomein doorstuur naar nodejs wat dan de polling-verbindingen afhandelt. Op die manier haal ik PHP en Apache tenminste bij de wachtende luisterende verbindingen er al tussenuit. Voor het verzenden heb ik ze er nog wel tussen om te kunnen controleren of iemand wel iets mag sturen, en het te loggen naar de database etc.
Ik denk niet dat ik het te moeilijk wil gaan maken met de long polling, als hier al een serieus spel uit gaat komen dan word dat voor 2 - 4 spelers oid, en dan is het een grote ALS
Primitieve long polling werkt prima, alleen als je de pijl-toetsen te lang vast houd begint hij te laggen, groot gelijk sinds het dan gewoon een polling word (constante update)
Toevoeging op 06/09/2010 13:57:04:
Voorbeeldje online gezet, werkt best wel aardig :)
http://www.johandam.com/html5_test/game/