forked from oerkki/voxelands
api functionality pt1
This commit is contained in:
parent
bac883efe8
commit
61531780a4
|
@ -217,8 +217,6 @@ Client::Client(
|
|||
//m_env_mutex.Init();
|
||||
//m_con_mutex.Init();
|
||||
|
||||
m_httpclient = new HTTPClient(this);
|
||||
|
||||
m_mesh_update_thread.Start();
|
||||
|
||||
/*
|
||||
|
@ -244,8 +242,6 @@ Client::~Client()
|
|||
//JMutexAutoLock conlock(m_con_mutex); //bulk comment-out
|
||||
m_con.Disconnect();
|
||||
}
|
||||
if (g_settings->getBool("enable_http"))
|
||||
m_httpclient->stop();
|
||||
|
||||
m_mesh_update_thread.setRun(false);
|
||||
while(m_mesh_update_thread.IsRunning())
|
||||
|
@ -258,8 +254,6 @@ void Client::connect(Address address)
|
|||
//JMutexAutoLock lock(m_con_mutex); //bulk comment-out
|
||||
m_con.SetTimeoutMs(0);
|
||||
m_con.Connect(address);
|
||||
if (g_settings->getBool("enable_http"))
|
||||
m_httpclient->start(address);
|
||||
}
|
||||
|
||||
bool Client::connectedAndInitialized()
|
||||
|
|
|
@ -153,8 +153,6 @@ struct ClientEvent
|
|||
};
|
||||
};
|
||||
|
||||
class HTTPClient;
|
||||
|
||||
class Client : public con::PeerHandler, public InventoryManager
|
||||
{
|
||||
public:
|
||||
|
@ -374,7 +372,6 @@ private:
|
|||
bool m_server_hunger;
|
||||
|
||||
con::Connection m_con;
|
||||
HTTPClient *m_httpclient;
|
||||
ISoundManager *m_sound;
|
||||
|
||||
IrrlichtDevice *m_device;
|
||||
|
|
|
@ -118,8 +118,6 @@ void set_default_settings(Settings *settings)
|
|||
// Server stuff
|
||||
// "map-dir" doesn't exist by default.
|
||||
settings->setDefault("motd", "");
|
||||
settings->setDefault("server_name", "");
|
||||
settings->setDefault("server_address", "");
|
||||
settings->setDefault("max_users", "20");
|
||||
settings->setDefault("strict_protocol_version_checking", "false");
|
||||
settings->setDefault("disallow_empty_passwords","false");
|
||||
|
@ -131,8 +129,7 @@ void set_default_settings(Settings *settings)
|
|||
settings->setDefault("game_mode","adventure");
|
||||
set_adventure_defaults(settings);
|
||||
|
||||
// only enable http on the server for now
|
||||
// adventurous players can enable it on the client
|
||||
// only enable http on the server, singleplayer doesn't need it
|
||||
#ifndef SERVER
|
||||
settings->setDefault("enable_http","false");
|
||||
#else
|
||||
|
@ -158,6 +155,17 @@ void set_default_settings(Settings *settings)
|
|||
settings->setDefault("enable_experimental", "false");
|
||||
settings->setDefault("enable_lavabuckets", "true");
|
||||
settings->setDefault("enable_tnt", "true");
|
||||
|
||||
settings->setDefault("api_server", "api.voxelands.com");
|
||||
#ifdef SERVER
|
||||
settings->setDefault("api_announce","true");
|
||||
#else
|
||||
settings->setDefault("api_announce","false");
|
||||
#endif
|
||||
settings->setDefault("api_auth","true");
|
||||
settings->setDefault("server_name", "");
|
||||
settings->setDefault("server_address", "");
|
||||
|
||||
}
|
||||
|
||||
void set_creative_defaults(Settings *settings)
|
||||
|
@ -191,7 +199,7 @@ void set_survival_defaults(Settings *settings)
|
|||
settings->setDefault("enable_damage", "true");
|
||||
settings->setDefault("enable_suffocation", "true");
|
||||
settings->setDefault("enable_hunger", "true");
|
||||
settings->setDefault("max_mob_level", "aggressive");
|
||||
settings->setDefault("max_mob_level", "destructive");
|
||||
settings->setDefault("initial_inventory", "false");
|
||||
settings->setDefault("tool_wear","true");
|
||||
}
|
||||
|
|
374
src/http.cpp
374
src/http.cpp
|
@ -298,7 +298,7 @@ int HTTPRemoteClient::handleAPI()
|
|||
}else{
|
||||
txt += "private\n";
|
||||
}
|
||||
txt += "summary,motd,mode,name,players,public,version";
|
||||
txt += "summary,motd,mode,name,players,public,version,features";
|
||||
send((char*)txt.c_str());
|
||||
return 1;
|
||||
}else if (u1 == "motd") {
|
||||
|
@ -315,6 +315,10 @@ int HTTPRemoteClient::handleAPI()
|
|||
txt = g_settings->get("server_address");
|
||||
send((char*)txt.c_str());
|
||||
return 1;
|
||||
}else if (u1 == "features") {
|
||||
std::string txt = "summary,motd,mode,name,players,public,version,features";
|
||||
send((char*)txt.c_str());
|
||||
return 1;
|
||||
}else if (u1 == "version") {
|
||||
std::string txt = VERSION_STRING;
|
||||
send((char*)txt.c_str());
|
||||
|
@ -512,203 +516,102 @@ void HTTPRemoteClient::sendHeaders()
|
|||
m_socket->Send("\r\n",2);
|
||||
}
|
||||
|
||||
#ifndef SERVER
|
||||
#include "client.h"
|
||||
|
||||
/* main loop for the client http fetcher */
|
||||
void * HTTPClientThread::Thread()
|
||||
{
|
||||
ThreadStarted();
|
||||
|
||||
log_register_thread("HTTPClientThread");
|
||||
|
||||
DSTACK(__FUNCTION_NAME);
|
||||
|
||||
BEGIN_DEBUG_EXCEPTION_HANDLER
|
||||
|
||||
while (getRun()) {
|
||||
try{
|
||||
m_client->step();
|
||||
}catch (con::NoIncomingDataException &e) {
|
||||
}catch(con::PeerNotFoundException &e) {
|
||||
}
|
||||
}
|
||||
|
||||
END_DEBUG_EXCEPTION_HANDLER(errorstream)
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* HTTPClient
|
||||
* HTTP request
|
||||
*/
|
||||
|
||||
/* constructor */
|
||||
HTTPClient::HTTPClient(Client *client):
|
||||
m_thread(this)
|
||||
std::string http_request(char* host, char* url, char* post, int port)
|
||||
{
|
||||
m_client = client;
|
||||
m_req_mutex.Init();
|
||||
}
|
||||
|
||||
/* destructor */
|
||||
HTTPClient::~HTTPClient()
|
||||
{
|
||||
}
|
||||
|
||||
/* start the client http fetcher thread */
|
||||
void HTTPClient::start(const Address &address)
|
||||
{
|
||||
DSTACK(__FUNCTION_NAME);
|
||||
// Stop thread if already running
|
||||
m_thread.stop();
|
||||
|
||||
m_address = address;
|
||||
m_connection_failures = 0;
|
||||
|
||||
// Start thread
|
||||
m_thread.setRun(true);
|
||||
m_thread.Start();
|
||||
|
||||
infostream<<"HTTPClient: Started"<<std::endl;
|
||||
}
|
||||
|
||||
/* stop the client http fetcher thread */
|
||||
void HTTPClient::stop()
|
||||
{
|
||||
DSTACK(__FUNCTION_NAME);
|
||||
|
||||
m_thread.stop();
|
||||
|
||||
infostream<<"HTTPClient: Threads stopped"<<std::endl;
|
||||
}
|
||||
|
||||
/* the main function for the client loop */
|
||||
void HTTPClient::step()
|
||||
{
|
||||
if (m_requests.size() == 0) {
|
||||
#ifdef _WIN32
|
||||
Sleep(1000);
|
||||
#else
|
||||
sleep(1);
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<HTTPRequest> p;
|
||||
|
||||
m_req_mutex.Lock();
|
||||
p.swap(m_requests);
|
||||
m_req_mutex.Unlock();
|
||||
|
||||
for (std::vector<HTTPRequest>::iterator i = p.begin(); i != p.end(); ++i) {
|
||||
HTTPRequest q = *i;
|
||||
m_socket = new TCPSocket();
|
||||
if (m_socket == NULL) {
|
||||
m_req_mutex.Lock();
|
||||
m_requests.push_back(q);
|
||||
m_req_mutex.Unlock();
|
||||
continue;
|
||||
}
|
||||
if (m_socket->Connect(m_address) == false) {
|
||||
delete m_socket;
|
||||
m_req_mutex.Lock();
|
||||
m_requests.push_back(q);
|
||||
m_req_mutex.Unlock();
|
||||
m_connection_failures++;
|
||||
/* assume the server has no http */
|
||||
if (m_connection_failures > 4) {
|
||||
stop();
|
||||
p.clear();
|
||||
return;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
m_connection_failures = 0;
|
||||
|
||||
m_recv_headers.clear();
|
||||
m_send_headers.clear();
|
||||
|
||||
get(q.url);
|
||||
|
||||
int r = 0;
|
||||
int h = -1;
|
||||
m_recv_headers.setResponse(0);
|
||||
h = m_recv_headers.read(m_socket);
|
||||
if (h == -1 || m_recv_headers.getResponse() == 500) {
|
||||
delete m_socket;
|
||||
m_req_mutex.Lock();
|
||||
m_requests.push_back(q);
|
||||
m_req_mutex.Unlock();
|
||||
continue;
|
||||
}
|
||||
|
||||
r = m_recv_headers.getResponse();
|
||||
delete m_socket;
|
||||
}
|
||||
p.clear();
|
||||
}
|
||||
|
||||
/* add a request to the http client queue */
|
||||
void HTTPClient::pushRequest(HTTPRequestType type, std::string &data)
|
||||
{
|
||||
if (m_thread.getRun() == false)
|
||||
return;
|
||||
switch (type) {
|
||||
case HTTPREQUEST_NULL:
|
||||
break;
|
||||
default:;
|
||||
}
|
||||
}
|
||||
|
||||
/* add a request to the http client queue */
|
||||
void HTTPClient::pushRequest(std::string &url, std::string &data)
|
||||
{
|
||||
if (m_thread.getRun() == false)
|
||||
return;
|
||||
HTTPRequest r(url,data);
|
||||
m_req_mutex.Lock();
|
||||
m_requests.push_back(r);
|
||||
m_req_mutex.Unlock();
|
||||
}
|
||||
|
||||
/* send a http GET request to the server */
|
||||
bool HTTPClient::get(std::string &url)
|
||||
{
|
||||
m_send_headers.setLength(0);
|
||||
m_send_headers.setMethod("GET");
|
||||
m_send_headers.setUrl(url);
|
||||
|
||||
sendHeaders();
|
||||
return true;
|
||||
}
|
||||
|
||||
/* send http headers to the server */
|
||||
void HTTPClient::sendHeaders()
|
||||
{
|
||||
std::string v;
|
||||
Address addr;
|
||||
TCPSocket *sock;
|
||||
HTTPResponseHeaders headers;
|
||||
int s;
|
||||
char buff[1024];
|
||||
char buff[2048];
|
||||
|
||||
s = snprintf(buff,1024,"%s %s HTTP/1.0\r\n",m_send_headers.getMethod().c_str(),m_send_headers.getUrl().c_str());
|
||||
m_socket->Send(buff,s);
|
||||
|
||||
v = m_send_headers.getHeader("Content-Type");
|
||||
if (v != "") {
|
||||
s = snprintf(buff,1024,"Content-Type: %s\r\n",v.c_str());
|
||||
m_socket->Send(buff,s);
|
||||
addr.setPort(port);
|
||||
if (!host || !host[0]) {
|
||||
std::string h = g_settings->get("api_server");
|
||||
if (h == "")
|
||||
return "";
|
||||
host = (char*)h.c_str();
|
||||
}
|
||||
|
||||
s = m_send_headers.length();
|
||||
s = snprintf(buff,1024,"Content-Length: %d\r\n",s);
|
||||
m_socket->Send(buff,s);
|
||||
addr.Resolve(host);
|
||||
|
||||
s = snprintf(buff,1024,"User: %s\r\n",m_client->getLocalPlayer()->getName());
|
||||
m_socket->Send(buff,s);
|
||||
sock = new TCPSocket();
|
||||
if (!sock)
|
||||
return "";
|
||||
|
||||
m_socket->Send("\r\n",2);
|
||||
if (!sock->Connect(addr)) {
|
||||
delete sock;
|
||||
return "";
|
||||
}
|
||||
|
||||
if (post) {
|
||||
int l = strlen(post);
|
||||
s = snprintf(buff,2048,
|
||||
"POST %s HTTP/1.1\r\n"
|
||||
"Host: %s\r\n"
|
||||
"From: Voxelands HTTP Fetcher\r\n"
|
||||
"User-Agent: Voxelands/%s (Irrlicht; Voxelands) Voxelands/%s\r\n"
|
||||
"Accept: text/html,application/xhtml+xml,text/plain\r\n"
|
||||
"Accept-Language: en-us,en\r\n"
|
||||
"Accept-Charset: ISO-8859-1,utf-8\r\n"
|
||||
"Content-Type: application/x-www-form-urlencoded\r\n"
|
||||
"Content-Length: %d\r\n"
|
||||
"Connection: close\r\n\r\n",
|
||||
url,
|
||||
host,
|
||||
VERSION_STRING,
|
||||
VERSION_STRING,
|
||||
l
|
||||
);
|
||||
sock->Send(buff,s);
|
||||
sock->Send(post,l);
|
||||
}else{
|
||||
s = snprintf(buff,2048,
|
||||
"GET %s HTTP/1.1\r\n"
|
||||
"Host: %s\r\n"
|
||||
"From: Voxelands HTTP Fetcher\r\n"
|
||||
"User-Agent: Voxelands/%s (Irrlicht; Voxelands) Voxelands/%s\r\n"
|
||||
"Accept: text/html,application/xhtml+xml,text/plain\r\n"
|
||||
"Accept-Language: en-us,en\r\n"
|
||||
"Accept-Charset: ISO-8859-1,utf-8\r\n"
|
||||
"Connection: close\r\n\r\n",
|
||||
url,
|
||||
host,
|
||||
VERSION_STRING,
|
||||
VERSION_STRING
|
||||
);
|
||||
sock->Send(buff,s);
|
||||
}
|
||||
|
||||
if (!sock->WaitData(5000)) {
|
||||
delete sock;
|
||||
return "";
|
||||
}
|
||||
|
||||
if (headers.read(sock) < 0) {
|
||||
delete sock;
|
||||
return "";
|
||||
}
|
||||
|
||||
if (headers.getResponse() != 200) {
|
||||
delete sock;
|
||||
return "";
|
||||
}
|
||||
|
||||
std::string response("");
|
||||
|
||||
while (response.size() < headers.getLength() && (s = sock->Receive(buff,2047)) > 0) {
|
||||
buff[s] = 0;
|
||||
response += buff;
|
||||
}
|
||||
|
||||
delete sock;
|
||||
|
||||
return response;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* HTTPHeaders
|
||||
|
@ -825,3 +728,100 @@ int HTTPResponseHeaders::read(TCPSocket *sock)
|
|||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static char url_reserved[33] = {
|
||||
'!',
|
||||
'*',
|
||||
'\'',
|
||||
'(',
|
||||
')',
|
||||
';',
|
||||
':',
|
||||
'@',
|
||||
'&',
|
||||
'=',
|
||||
'+',
|
||||
'$',
|
||||
',',
|
||||
'/',
|
||||
'?',
|
||||
'#',
|
||||
'[',
|
||||
']',
|
||||
'"',
|
||||
'%',
|
||||
'-',
|
||||
'.',
|
||||
'<',
|
||||
'>',
|
||||
'\\',
|
||||
'^',
|
||||
'_',
|
||||
'`',
|
||||
'{',
|
||||
'|',
|
||||
'}',
|
||||
'~',
|
||||
0
|
||||
};
|
||||
|
||||
static int32_t is_reserved(char c)
|
||||
{
|
||||
int32_t i;
|
||||
if (c < 33)
|
||||
return 1;
|
||||
for (i=0; url_reserved[i]; i++) {
|
||||
if (c == url_reserved[i])
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* % encodes a string for http urls */
|
||||
std::string http_url_encode(std::string &str)
|
||||
{
|
||||
int32_t i;
|
||||
char buff[10];
|
||||
char* in = (char*)str.c_str();
|
||||
std::string out("");
|
||||
for (i=0; in[i] != 0; i++) {
|
||||
if (is_reserved(in[i])) {
|
||||
sprintf(buff,"%.2X",in[i]);
|
||||
out += "%";
|
||||
out += buff;
|
||||
continue;
|
||||
}
|
||||
out += in[i];
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
/* decodes a % encoded string from http urls */
|
||||
std::string http_url_decode(std::string &str)
|
||||
{
|
||||
int32_t i;
|
||||
int32_t o;
|
||||
int32_t k;
|
||||
char buff[10];
|
||||
char* in = (char*)str.c_str();
|
||||
std::string out("");
|
||||
for (i=0,o=0; in[i] != 0; i++) {
|
||||
if (in[i] == '%') {
|
||||
i++;
|
||||
if (in[i] == 0 || in[i+1] == 0)
|
||||
return 0;
|
||||
buff[0] = in[i++];
|
||||
buff[1] = in[i];
|
||||
buff[2] = 0;
|
||||
k = strtol(buff,NULL,16);
|
||||
if (!k)
|
||||
break;
|
||||
buff[0] = k;
|
||||
buff[1] = 0;
|
||||
out += buff;
|
||||
continue;
|
||||
}
|
||||
out += in[i];
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
|
83
src/http.h
83
src/http.h
|
@ -156,86 +156,7 @@ private:
|
|||
Server *m_server;
|
||||
};
|
||||
|
||||
enum HTTPRequestType {
|
||||
HTTPREQUEST_NULL
|
||||
};
|
||||
|
||||
struct HTTPRequest
|
||||
{
|
||||
HTTPRequest():
|
||||
url(""),
|
||||
post(""),
|
||||
data("")
|
||||
{
|
||||
}
|
||||
HTTPRequest(std::string &u, std::string &p, std::string &d)
|
||||
{
|
||||
url = u;
|
||||
post = p;
|
||||
data = d;
|
||||
}
|
||||
HTTPRequest(std::string &u, std::string &p)
|
||||
{
|
||||
url = u;
|
||||
post = p;
|
||||
data = "";
|
||||
}
|
||||
HTTPRequest(std::string &u)
|
||||
{
|
||||
url = u;
|
||||
post = "";
|
||||
data = "";
|
||||
}
|
||||
std::string url;
|
||||
std::string post;
|
||||
std::string data;
|
||||
};
|
||||
|
||||
#ifndef SERVER
|
||||
#include "client.h"
|
||||
class HTTPClient;
|
||||
|
||||
class HTTPClientThread : public SimpleThread
|
||||
{
|
||||
HTTPClient *m_client;
|
||||
|
||||
public:
|
||||
|
||||
HTTPClientThread(HTTPClient *client):
|
||||
SimpleThread(),
|
||||
m_client(client)
|
||||
{
|
||||
}
|
||||
|
||||
void * Thread();
|
||||
};
|
||||
|
||||
class HTTPClient
|
||||
{
|
||||
public:
|
||||
HTTPClient(Client *client);
|
||||
~HTTPClient();
|
||||
void start(const Address &address);
|
||||
void stop();
|
||||
void step();
|
||||
void pushRequest(HTTPRequestType type, std::string &data);
|
||||
void pushRequest(std::string &url, std::string &data);
|
||||
private:
|
||||
bool get(std::string &url);
|
||||
int read(char* buff, int size) {return m_socket->Receive(buff,size);}
|
||||
int readline(char* buff, int size) {return m_socket->ReceiveLine(buff,size);}
|
||||
void sendHeaders();
|
||||
|
||||
Address m_address;
|
||||
TCPSocket *m_socket;
|
||||
HTTPResponseHeaders m_recv_headers;
|
||||
HTTPRequestHeaders m_send_headers;
|
||||
std::vector<HTTPRequest> m_requests;
|
||||
JMutex m_req_mutex;
|
||||
HTTPClientThread m_thread;
|
||||
Client *m_client;
|
||||
int m_connection_failures;
|
||||
};
|
||||
#endif
|
||||
std::string http_request(char* host, char* url, char* post=NULL, int port=80);
|
||||
std::string http_url_encode(std::string &str);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -50,6 +50,7 @@
|
|||
#include "log.h"
|
||||
#include "base64.h"
|
||||
#include "sound.h"
|
||||
#include "http.h"
|
||||
|
||||
#define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
|
||||
|
||||
|
@ -988,8 +989,7 @@ Server::Server(
|
|||
m_env.getMap().addEventReceiver(this);
|
||||
|
||||
// If file exists, load environment metadata
|
||||
if(fs::PathExists(m_mapsavedir+DIR_DELIM+"env_meta.txt"))
|
||||
{
|
||||
if (fs::PathExists(m_mapsavedir+DIR_DELIM+"env_meta.txt")) {
|
||||
infostream<<"Server: Loading environment metadata"<<std::endl;
|
||||
m_env.loadMeta(m_mapsavedir);
|
||||
}
|
||||
|
@ -1093,6 +1093,45 @@ void Server::start(unsigned short port)
|
|||
m_thread.setRun(true);
|
||||
m_thread.Start();
|
||||
|
||||
// Announce game server to api server
|
||||
if (g_settings->getBool("api_announce")) {
|
||||
std::string url("/announce");
|
||||
std::string post("");
|
||||
std::string pre("");
|
||||
std::string sn = g_settings->get("server_name");
|
||||
if (sn != "") {
|
||||
post += pre+"server_name="+http_url_encode(sn);
|
||||
pre = "&";
|
||||
}
|
||||
std::string sa = g_settings->get("server_address");
|
||||
if (sa != "") {
|
||||
post += pre+"server_address="+http_url_encode(sa);
|
||||
pre = "&";
|
||||
}
|
||||
std::string sp = g_settings->get("port");
|
||||
if (sp != "") {
|
||||
post += pre+"server_port="+http_url_encode(sp);
|
||||
pre = "&";
|
||||
}
|
||||
|
||||
std::string response("");
|
||||
if (post == "") {
|
||||
response = http_request(NULL,(char*)url.c_str());
|
||||
}else{
|
||||
response = http_request(NULL,(char*)url.c_str(),(char*)post.c_str());
|
||||
}
|
||||
|
||||
if (response == "" || response == "Server Not Found") {
|
||||
errorstream<<"Server: Failed to announce to api server"<<std::endl;
|
||||
}else{
|
||||
if (sn == "")
|
||||
g_settings->set("server_name",response);
|
||||
if (sa == "")
|
||||
g_settings->set("server_address",response);
|
||||
infostream<<"Server: Announced to api server"<<std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
infostream<<"Server: Started on port "<<port<<std::endl;
|
||||
}
|
||||
|
||||
|
|
|
@ -500,8 +500,11 @@ int TCPSocket::Receive(void *data, int size)
|
|||
return r;
|
||||
}
|
||||
|
||||
if (FillBuffer() < size)
|
||||
int s = FillBuffer();
|
||||
if (s < 1)
|
||||
return 0;
|
||||
if (s < size)
|
||||
size = s;
|
||||
|
||||
memcpy(data,m_buff+m_bstart,size);
|
||||
m_bstart+=size;
|
||||
|
|
|
@ -0,0 +1,262 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
|
||||
CREATE DATABASE voxelands ;
|
||||
|
||||
USE voxelands ;
|
||||
|
||||
CREATE TABLE `servers` (
|
||||
`id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
`name` VARCHAR (255) NOT NULL,
|
||||
`addr` VARCHAR (255) NOT NULL,
|
||||
`port` INT(11) UNSIGNED,
|
||||
`mode` VARCHAR (20) NOT NULL,
|
||||
`motd` VARCHAR (255) NOT NULL,
|
||||
`players` INT(11) UNSIGNED NOT NULL DEFAULT '0',
|
||||
`public` VARCHAR (10) NOT NULL,
|
||||
`version` VARCHAR (50) NOT NULL,
|
||||
`features` VARCHAR (255) NOT NULL,
|
||||
`lastreply` INT(11) NOT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_bin ;
|
||||
|
||||
CREATE TABLE `players` (
|
||||
`id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
`name` VARCHAR(255) NOT NULL,
|
||||
`hash` VARCHAR(255) NOT NULL,
|
||||
`cookie` VARCHAR(255) NOT NULL,
|
||||
`server` INT(11) UNSIGNED NOT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_bin ;
|
||||
|
||||
CREATE USER 'apiuser'@'localhost' IDENTIFIED BY 'apipass';
|
||||
GRANT SELECT,INSERT,UPDATE ON `servers` TO 'apiuser'@'localhost';
|
||||
GRANT SELECT,INSERT,UPDATE ON `players` TO 'apiuser'@'localhost';
|
||||
|
||||
*/
|
||||
|
||||
$db = false;
|
||||
|
||||
function db_connect()
|
||||
{
|
||||
global $db;
|
||||
if ($db !== false)
|
||||
return;
|
||||
|
||||
$db = mysqli_connect("localhost","apiuser","apipass","voxelands");
|
||||
if (mysqli_connect_errno())
|
||||
$db = false;
|
||||
}
|
||||
|
||||
function db_close()
|
||||
{
|
||||
global $db;
|
||||
if ($db === false)
|
||||
return;
|
||||
$db->close();
|
||||
$db = false;
|
||||
}
|
||||
|
||||
function db_insert($server_name, $server_addr, $server_port)
|
||||
{
|
||||
global $db;
|
||||
db_connect();
|
||||
$name = db_escape($server_name);
|
||||
$addr = db_escape($server_addr);
|
||||
$port = db_escape($server_port);
|
||||
$time = time();
|
||||
$data = $db->query("SELECT `id` FROM `servers` WHERE `addr` = '$addr' AND `port` = '$port'");
|
||||
if ($data && $data->num_rows > 0) {
|
||||
$id = $data->fetch_array();
|
||||
$id = $id['id'];
|
||||
$data->close();
|
||||
$data = $db->query("UPDATE `servers` SET `lastreply` = '$time', `name` = '$name' WHERE `id` = '$id'");
|
||||
}else{
|
||||
if ($data)
|
||||
$data->close();
|
||||
$db->query("INSERT INTO `servers` (`name`,`addr`,`port`,`lastreply`) VALUES ('$name','$addr','$port','$time')");
|
||||
}
|
||||
}
|
||||
|
||||
function db_query_server($server_name=false,$server_mode=false,$version=false,$public=false)
|
||||
{
|
||||
global $db;
|
||||
db_connect();
|
||||
|
||||
$time = time()-1200;
|
||||
$q = "SELECT * FROM `servers` WHERE `lastreply` > $time";
|
||||
|
||||
if ($server_name !== false) {
|
||||
$name = db_escape($server_name);
|
||||
$q .= " AND `name` LIKE '%$name%'";
|
||||
}
|
||||
if ($server_mode !== false) {
|
||||
$mode = db_escape($server_mode);
|
||||
$q .= " AND `mode` = '$mode'";
|
||||
}
|
||||
if ($version !== false) {
|
||||
$v = db_escape($version);
|
||||
$q .= " AND `mode` LIKE '%$v%'";
|
||||
}
|
||||
if ($public == 'public' || $public == 'private') {
|
||||
$q .= " AND `public` = '$public'";
|
||||
}
|
||||
|
||||
$data = $db->query($q);
|
||||
if (!$data) {
|
||||
return array();
|
||||
}elseif ($data->num_rows < 1) {
|
||||
$data->close();
|
||||
return array();
|
||||
}
|
||||
|
||||
$result = array();
|
||||
while ($row = $data->fetch_array(MYSQLI_ASSOC)) {
|
||||
$result[] = $row;
|
||||
}
|
||||
$data->close();
|
||||
return $result;
|
||||
}
|
||||
|
||||
function db_query_player($player_name=false,$count=false,$start=false)
|
||||
{
|
||||
global $db;
|
||||
db_connect();
|
||||
|
||||
$q = "SELECT * FROM `players`";
|
||||
if ($player_name !== false) {
|
||||
$p = db_escape($player_name);
|
||||
$q .= " WHERE `name` LIKE '%$p%'";
|
||||
}
|
||||
if ($start !== false && $count !== false) {
|
||||
$start = intval($start);
|
||||
$count = intval($count);
|
||||
$q .= " LIMIT $start , $count";
|
||||
}elseif ($count !== false) {
|
||||
$count = intval($count);
|
||||
$q .= " LIMIT $count";
|
||||
}
|
||||
|
||||
$data = $db->query($q);
|
||||
if (!$data) {
|
||||
return array();
|
||||
}elseif ($data->num_rows < 1) {
|
||||
$data->close();
|
||||
return array();
|
||||
}
|
||||
|
||||
$result = array();
|
||||
while ($row = $data->fetch_array(MYSQLI_ASSOC)) {
|
||||
$result[] = $row;
|
||||
}
|
||||
$data->close();
|
||||
return $result;
|
||||
}
|
||||
|
||||
function db_escape($str)
|
||||
{
|
||||
global $db;
|
||||
db_connect();
|
||||
return $db->real_escape_string($str);
|
||||
}
|
||||
|
||||
function check_server($host, $port)
|
||||
{
|
||||
$socket = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
|
||||
$timeout = array("sec" => 1, "usec" => 0);
|
||||
socket_set_option($socket, SOL_SOCKET, SO_RCVTIMEO, $timeout);
|
||||
$buf = "\x4f\x45\x74\x03\x00\x00\x00\x03\xff\xdc\x01";
|
||||
socket_sendto($socket, $buf, strlen($buf), 0, $host, $port);
|
||||
$buf = socket_read($socket, 1000);
|
||||
if ($buf == "")
|
||||
return false;
|
||||
|
||||
/* we got a reply, read the peer id then send a disconnect */
|
||||
$peer_id = substr($buf, 9, 2);
|
||||
$buf = "\x4f\x45\x74\x03".$peer_id."\x00\x00\x03";
|
||||
socket_sendto($socket, $buf, strlen($buf), 0, $host, $port);
|
||||
socket_close($socket);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function error_send($msg)
|
||||
{
|
||||
header("Content-Type: text/plain\r\n");
|
||||
echo $msg;
|
||||
}
|
||||
|
||||
function txt_send($txt)
|
||||
{
|
||||
header("Content-Type: text/plain\r\n");
|
||||
echo $txt;
|
||||
}
|
||||
|
||||
function html_send($html)
|
||||
{
|
||||
readfile($_SERVER['DOCUMENT_ROOT']."/header.html");
|
||||
echo $html;
|
||||
readfile($_SERVER['DOCUMENT_ROOT']."/footer.html");
|
||||
}
|
||||
|
||||
function server_announce()
|
||||
{
|
||||
$server_addr = isset($_POST['server_address']) ? urldecode($_POST['server_address']) : $_SERVER['REMOTE_ADDR'];
|
||||
$server_name = isset($_POST['server_name']) ? urldecode($_POST['server_name']) : $server_addr;
|
||||
$server_port = isset($_POST['server_port']) ? urldecode($_POST['server_port']) : '30000';
|
||||
|
||||
if (!check_server($server_addr,$server_port))
|
||||
return error_send("Server Not Found");
|
||||
|
||||
db_connect();
|
||||
db_insert($server_name,$server_addr,$server_port);
|
||||
db_close();
|
||||
|
||||
txt_send($server_addr);
|
||||
}
|
||||
|
||||
function server_list()
|
||||
{
|
||||
$a = db_query_server();
|
||||
$txt = "servers: ".count($a)."\n\n";
|
||||
foreach ($a as $server) {
|
||||
if ($server['mode'] == '')
|
||||
$server['mode'] = 'adventure';
|
||||
$txt .= <<<EOT
|
||||
$server[name]
|
||||
$server[mode]
|
||||
$server[addr]:$server[port]
|
||||
|
||||
EOT;
|
||||
}
|
||||
txt_send($txt);
|
||||
}
|
||||
|
||||
function player_find()
|
||||
{
|
||||
txt_send("hello world");
|
||||
}
|
||||
|
||||
function home()
|
||||
{
|
||||
html_send("hello world");
|
||||
}
|
||||
|
||||
$u = explode("/",$_SERVER['REQUEST_URI']);
|
||||
if (count($u) < 2)
|
||||
$u = array("","home");
|
||||
|
||||
if ($u[1] == "announce") {
|
||||
server_announce();
|
||||
}elseif ($u[1] == "list") {
|
||||
server_list();
|
||||
}elseif ($u[1] == "player") {
|
||||
player_find();
|
||||
}else{
|
||||
home();
|
||||
}
|
||||
|
||||
db_close();
|
||||
|
||||
?>
|
Loading…
Reference in New Issue