Estava pensando em fazer um jogo (apenas testes) visão top-down (aka, tibia, ragnarok, etc..) , mas pelo proprio navegador usando long-polling.
Mas logo de inicio percebo alguns problemas (talvez até por causa da tecnologia: PHP)
Primeiro teste foi criar um long-polling simples.
No servidor, morria a hora com milesimos.
Tempo de resposta do ajax: 1ms ~ 3ms
Pela rede da empresa 100mbps 11~14ms
Isso fazendo outro ajax assim que o mesmo terminava (delay de 1ms)
Porem, não sei se alguem conhece o que é long-polling, num jogo online de verdade, eu acho, que seria em média uns 3 ajax por segundo. Isso da uns 0.333ms, que é um delay grande, ou seja, a maioria dos pings seriam menor que isso (eu espero)
Localhost, tambem testei e consegui abrir mais de 100 abas no chrome (ubuntu, core2duo 2.4 ou 2.8, nao lembro, 2gb de ram meia boca) e nao chegou no topo. Na verdade, acho que o q tava mais gastando processador era o chrome e nao o apache+php
Em jogos como tibia, ragnarok, temos um servidor. Esse, se eu fosse fazer, seria em c++.
Eu fiz um servidor em Delphi (para cliente parecido com tibia)
Em Windows (e outros SO tambem) os programas podem alocar memoria.
Portanto, o comportamento dos servidores ao iniciar é:
-- ler config
-- ler mapas
-- ler scripts, etc..
-- conectar com o banco
-- abrir conexao com a net
Ao jogador entrar:
-- carregam do banco e enviam as informacoes para o client
-- alocam um pedaço de memoria.
-- relaciona a conexao vs jogador vs memoria alocada
e ao jogador fechar/sair ou o servidor fechar (nao dando erro, é claro)
ele pega essas informações da RAM e jogam em banco de dados novamente
bom, pelo menos é assim que os OTServers funcionam.
Seria muito custoso, atualizar a cada movimento a tabela de jogadores. por isso, eh feita por exemplo a cada 2 horas, ou ao fechar, ou ao player sair.
Porem, em PHP o cenário é diferente. Geralmente os hosts limitam a memoria que pode ser utilizada por um script, um script nao pode rodar por mais de 90 segundos. etc..
Então alocar memoria compartilhada no PHP (
http://php.net/manual/en/book.shmop.php) em hosts com baixo custo é relamente dificil. Pois, alem da baixa quantidade (30mb) não é possivel ter scripts rodando sempre.
Então eu estava pensando em usar a sessao de cada jogador + banco de dados.
Porem, acho que teria que limitar os jogadores a 20 online (acho que mais que isso, alem de, na real, acabar com a banda do servidor, iria causar lentidao na maquina) que é bem menos que 700~900 que jogos com conexao Socket conseguem chegar (claro, com pCs com 8gb de ram, xeon, etc..)
O que acham da ideia? é viavel criar um jogo usando banco de dados fazendo updates a cada ação?
pois Ainda assim, pra ficar legal mesmo, precisamos fazer querys a cada ms para verifciar se há outro jogador por perto, e responder isso no ajax.
a estrutura do long polling é basicamente essa:
<?
joga os headers pra o cliente
while (nenhuma informacao nova|| timeout do javascript == false){
usleep(200);
}
captura todas as informacoes nova e envia para o cliente
fim
?>
E no cliente, ao receber a mensagem, faz todas as acoes e faz outro ajax.
Aqui tem um teste:
PHP:
Código:
<?php
ob_start();
header('Content-Type: text/javascript;charset=utf-8');
session_start();
$json = new StdClass;
$json->f = '';
$updatePos = false;
if (! empty( $_GET['name'] ) && empty($_SESSION['name'])) {
session_destroy();
session_start();
$_SESSION['name'] = $_GET['name'];
$_SESSION['state'] = 1;
$_SESSION['x'] = 20;
$_SESSION['y'] = 20;
$_SESSION['pid'] = uniqid();
$json->f = 'welcome';
$json->pid = $_SESSION['pid'];
$updatePos = true;
}
if( !empty( $_SESSION['pid']) ) {
$delay = 0;
while (true){
$_SESSION['a'] ++;
if ($_SESSION['a'] > 100){
$updatePos = true;
$_SESSION['a'] = 0;
}
if (!(empty($_GET['want_x'])) || $updatePos){
$_SESSION['x'] = $_SESSION['x'] + 5;
if($_SESSION['x'] > 500) $_SESSION['x'] = 0;
$json->x = $_SESSION['x'];
$json->f .= ',updatePos';
break;
}else{
if($delay >= 1000 * 1000 * 5){ // 5 segundos
$json->f .= ',idle';
break;
}
$delay += 5000;
usleep(5000);
}
}
}else{
$json->error = 1;
$json->message = 'Player not found.';
}
if($json->error === 0){
unset($json->error);
}
die( json_encode( $json ) );
?>
HTML:
Código:
<html>
<head>
<title>MMO teste</title>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.2.6/jquery.min.js" type="text/javascript" charset="utf-8"></script>
<style type="text/css" media="screen">
body{ background:#000;color:#fff;font-size:.9em; }
.msg{ background:#aaa;padding:.2em; border-bottom:1px #000 solid}
.old{ background-color:#246499;}
.new{ background-color:#3B9957;}
.error{ background-color:#992E36;}
#player { height: 100px; background-color: white; width: 60px; position: relative;color:black; }
</style>
<script type="text/javascript" charset="utf-8">
var game = {
playerName : '',
loading : 1,
state : 0,
};
function addmsg(type, msg){
$("#messages").append(
"<div class='msg "+ type +"'>"+ msg +"</div>"
);
}
var gameFunctions = {
idle : function (o){
console.log(o);
},
welcome: function (o){
game.state = 3;
game.pid = o.pid;
addmsg('new','Welcome to the world! Your PID is ' + game.pid);
$('#player_name').text(game.playerName);
},
updatePos: function (o){
$('#player').css('left', o.x + 'px');
}
};
function waitForMsg(){
if (game.loading) return;
var get = '?any=x';
if(game.state == 1){
game.state = 2;
get += '&name=' + game.playerName;
}
if(game.move_right){
get += '&name=' + game.playerName;
}
$.ajax({
type: "GET",
url: "./ajax_mmo.php" + get,
async: true,
cache: false,
timeout: 50000,
dataType: 'json',
success: function(o){ /* called when request to barge.php completes */
if(+(o.error)){
alert('Erro ' + o.message);
}else{
var af = o.f.split(',');
for(var i in af){
if(gameFunctions[af[i]])
gameFunctions[af[i]](o);
}
}
setTimeout(
'waitForMsg()', /* Request next message */
1
);
},
error: function(XMLHttpRequest, textStatus, errorThrown){
if (textStatus.toLowerCase() == 'timeout'){
alert();
setTimeout(
'waitForMsg()', /* Request next message */
1
);
}else{
addmsg("error", textStatus + " (" + errorThrown + ")");
}
},
});
};
function gameStart(e){
game.playerName = $('#nm_player').val();
$('#frmlogin').slideUp('slow');
addmsg('new', 'Loading...');
game.loading = 0;
game.state = 1;
waitForMsg();
}
$(document).ready(function(){
waitForMsg();
});
</script>
</head>
<body>
<form id="frmlogin">
<label>Name: <input style="-moz-border-radius: 4px;background:black none repeat scroll 0 0;border:1px solid blue;color:white;" id="nm_player"/></label>
<button type="button" onclick="gameStart(this)" style="-moz-border-radius: 4px;background:black none repeat scroll 0 0;border:1px solid blue;color:white;">Conectar</button>
</form>
<div id="messages">
</div>
<div id="player">
<span id="player_name"></span>
</div>
</body>
</html>