new auto masterserver
parent
ef6b8bee07
commit
ee07c3f7cf
|
@ -40,6 +40,8 @@ src/cguittfont/CMakeFiles/
|
|||
src/cguittfont/libcguittfont.a
|
||||
src/cguittfont/cmake_install.cmake
|
||||
src/cguittfont/Makefile
|
||||
src/json/CMakeFiles/
|
||||
src/json/libjson.a
|
||||
CMakeCache.txt
|
||||
CPackConfig.cmake
|
||||
CPackSourceConfig.cmake
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
# Look for json, use our own if not found
|
||||
|
||||
#FIND_PATH(JSON_INCLUDE_DIR json.h)
|
||||
|
||||
#FIND_LIBRARY(JSON_LIBRARY NAMES json)
|
||||
|
||||
#IF(JSON_LIBRARY AND JSON_INCLUDE_DIR)
|
||||
# SET( JSON_FOUND TRUE )
|
||||
#ENDIF(JSON_LIBRARY AND JSON_INCLUDE_DIR)
|
||||
|
||||
#IF(JSON_FOUND)
|
||||
# MESSAGE(STATUS "Found system json header file in ${JSON_INCLUDE_DIR}")
|
||||
# MESSAGE(STATUS "Found system json library ${JSON_LIBRARY}")
|
||||
#ELSE(JSON_FOUND)
|
||||
SET(JSON_INCLUDE_DIR ${PROJECT_SOURCE_DIR}/json)
|
||||
SET(JSON_LIBRARY json)
|
||||
MESSAGE(STATUS "Using project json library")
|
||||
#ENDIF(JSON_FOUND)
|
|
@ -159,7 +159,7 @@
|
|||
#media_fetch_threads = 8
|
||||
|
||||
# Url to the server list displayed in the Multiplayer Tab
|
||||
#serverlist_url = servers.minetest.ru/server.list
|
||||
#serverlist_url = servers.minetest.net
|
||||
# File in client/serverlist/ that contains your favorite servers displayed in the Multiplayer Tab
|
||||
#serverlist_file = favoriteservers.txt
|
||||
|
||||
|
@ -172,6 +172,16 @@
|
|||
# Server stuff
|
||||
#
|
||||
|
||||
# Name of server
|
||||
#server_name = Minetest server
|
||||
# Description of server
|
||||
#server_description = mine here
|
||||
# Domain name of server
|
||||
#server_address = game.minetest.net
|
||||
# Homepage of server
|
||||
#server_url = http://minetest.net
|
||||
# Automaticaly report to masterserver
|
||||
#server_announce = 0
|
||||
# Default game (default when creating a new world)
|
||||
#default_game = minetest
|
||||
# World directory (everything in the world is stored here)
|
||||
|
|
|
@ -5,6 +5,7 @@ cmake_minimum_required( VERSION 2.6 )
|
|||
mark_as_advanced(EXECUTABLE_OUTPUT_PATH LIBRARY_OUTPUT_PATH)
|
||||
mark_as_advanced(JTHREAD_INCLUDE_DIR JTHREAD_LIBRARY)
|
||||
mark_as_advanced(SQLITE3_INCLUDE_DIR SQLITE3_LIBRARY)
|
||||
mark_as_advanced(JSON_INCLUDE_DIR JSON_LIBRARY)
|
||||
|
||||
option(ENABLE_CURL "Enable cURL support for fetching media" 1)
|
||||
|
||||
|
@ -170,6 +171,7 @@ endif()
|
|||
|
||||
find_package(Jthread REQUIRED)
|
||||
find_package(Sqlite3 REQUIRED)
|
||||
find_package(Json REQUIRED)
|
||||
|
||||
if(USE_FREETYPE)
|
||||
find_package(Freetype REQUIRED)
|
||||
|
@ -242,6 +244,7 @@ set(common_SRCS
|
|||
biome.cpp
|
||||
clientserver.cpp
|
||||
staticobject.cpp
|
||||
serverlist.cpp
|
||||
util/serialize.cpp
|
||||
util/directiontables.cpp
|
||||
util/numeric.cpp
|
||||
|
@ -303,7 +306,6 @@ set(minetest_SRCS
|
|||
filecache.cpp
|
||||
tile.cpp
|
||||
shader.cpp
|
||||
serverlist.cpp
|
||||
game.cpp
|
||||
main.cpp
|
||||
)
|
||||
|
@ -332,6 +334,7 @@ include_directories(
|
|||
${JTHREAD_INCLUDE_DIR}
|
||||
${SQLITE3_INCLUDE_DIR}
|
||||
${LUA_INCLUDE_DIR}
|
||||
${JSON_INCLUDE_DIR}
|
||||
)
|
||||
|
||||
if(USE_FREETYPE)
|
||||
|
@ -341,6 +344,12 @@ if(USE_FREETYPE)
|
|||
)
|
||||
endif(USE_FREETYPE)
|
||||
|
||||
if(USE_CURL)
|
||||
include_directories(
|
||||
${CURL_INCLUDE_DIR}
|
||||
)
|
||||
endif(USE_CURL)
|
||||
|
||||
set(EXECUTABLE_OUTPUT_PATH "${CMAKE_SOURCE_DIR}/bin")
|
||||
|
||||
if(BUILD_CLIENT)
|
||||
|
@ -359,18 +368,15 @@ if(BUILD_CLIENT)
|
|||
${JTHREAD_LIBRARY}
|
||||
${SQLITE3_LIBRARY}
|
||||
${LUA_LIBRARY}
|
||||
${JSON_LIBRARY}
|
||||
${PLATFORM_LIBS}
|
||||
${CLIENT_PLATFORM_LIBS}
|
||||
)
|
||||
|
||||
if(USE_CURL)
|
||||
target_link_libraries(
|
||||
${PROJECT_NAME}
|
||||
${CURL_LIBRARY}
|
||||
)
|
||||
include_directories(
|
||||
${CURL_INCLUDE_DIR}
|
||||
)
|
||||
endif(USE_CURL)
|
||||
if(USE_FREETYPE)
|
||||
target_link_libraries(
|
||||
|
@ -388,12 +394,20 @@ if(BUILD_SERVER)
|
|||
${ZLIB_LIBRARIES}
|
||||
${JTHREAD_LIBRARY}
|
||||
${SQLITE3_LIBRARY}
|
||||
${JSON_LIBRARY}
|
||||
${GETTEXT_LIBRARY}
|
||||
${LUA_LIBRARY}
|
||||
${PLATFORM_LIBS}
|
||||
)
|
||||
if(USE_CURL)
|
||||
target_link_libraries(
|
||||
${PROJECT_NAME}server
|
||||
${CURL_LIBRARY}
|
||||
)
|
||||
endif(USE_CURL)
|
||||
endif(BUILD_SERVER)
|
||||
|
||||
|
||||
#
|
||||
# Set some optimizations and tweaks
|
||||
#
|
||||
|
@ -569,4 +583,9 @@ else (LUA_FOUND)
|
|||
add_subdirectory(lua)
|
||||
endif (LUA_FOUND)
|
||||
|
||||
if (JSON_FOUND)
|
||||
else (JSON_FOUND)
|
||||
add_subdirectory(json)
|
||||
endif (JSON_FOUND)
|
||||
|
||||
#end
|
||||
|
|
|
@ -130,8 +130,13 @@ void set_default_settings(Settings *settings)
|
|||
|
||||
settings->setDefault("media_fetch_threads", "8");
|
||||
|
||||
settings->setDefault("serverlist_url", "servers.minetest.ru/server.list");
|
||||
settings->setDefault("serverlist_url", "servers.minetest.net");
|
||||
settings->setDefault("serverlist_file", "favoriteservers.txt");
|
||||
settings->setDefault("server_announce", "false");
|
||||
settings->setDefault("server_url", "");
|
||||
settings->setDefault("server_address", "");
|
||||
settings->setDefault("server_name", "");
|
||||
settings->setDefault("server_description", "");
|
||||
|
||||
settings->setDefault("font_path", porting::getDataPath("fonts" DIR_DELIM "liberationsans.ttf"));
|
||||
settings->setDefault("font_size", "13");
|
||||
|
|
|
@ -108,6 +108,7 @@ enum
|
|||
GUI_ID_ENABLE_PARTICLES_CB,
|
||||
GUI_ID_DAMAGE_CB,
|
||||
GUI_ID_CREATIVE_CB,
|
||||
GUI_ID_PUBLIC_CB,
|
||||
GUI_ID_JOIN_GAME_BUTTON,
|
||||
GUI_ID_CHANGE_KEYS_BUTTON,
|
||||
GUI_ID_DELETE_WORLD_BUTTON,
|
||||
|
@ -562,6 +563,14 @@ void GUIMainMenu::regenerateGui(v2u32 screensize)
|
|||
Environment->addCheckBox(m_data->enable_damage, rect, this, GUI_ID_DAMAGE_CB,
|
||||
wgettext("Enable Damage"));
|
||||
}
|
||||
#if USE_CURL
|
||||
{
|
||||
core::rect<s32> rect(0, 0, 250, 30);
|
||||
rect += m_topleft_server + v2s32(30+20+250+20, 60);
|
||||
Environment->addCheckBox(m_data->enable_public, rect, this, GUI_ID_PUBLIC_CB,
|
||||
wgettext("Public"));
|
||||
}
|
||||
#endif
|
||||
// Delete world button
|
||||
{
|
||||
core::rect<s32> rect(0, 0, 130, 30);
|
||||
|
@ -841,6 +850,11 @@ void GUIMainMenu::readInput(MainMenuData *dst)
|
|||
if(e != NULL && e->getType() == gui::EGUIET_CHECK_BOX)
|
||||
dst->enable_damage = ((gui::IGUICheckBox*)e)->isChecked();
|
||||
}
|
||||
{
|
||||
gui::IGUIElement *e = getElementFromId(GUI_ID_PUBLIC_CB);
|
||||
if(e != NULL && e->getType() == gui::EGUIET_CHECK_BOX)
|
||||
dst->enable_public = ((gui::IGUICheckBox*)e)->isChecked();
|
||||
}
|
||||
{
|
||||
gui::IGUIElement *e = getElementFromId(GUI_ID_FANCYTREE_CB);
|
||||
if(e != NULL && e->getType() == gui::EGUIET_CHECK_BOX)
|
||||
|
@ -912,8 +926,8 @@ void GUIMainMenu::readInput(MainMenuData *dst)
|
|||
{
|
||||
ServerListSpec server =
|
||||
getServerListSpec(wide_to_narrow(dst->address), wide_to_narrow(dst->port));
|
||||
dst->servername = server.name;
|
||||
dst->serverdescription = server.description;
|
||||
dst->servername = server["name"].asString();
|
||||
dst->serverdescription = server["description"].asString();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1174,13 +1188,31 @@ void GUIMainMenu::updateGuiServerList()
|
|||
i != m_data->servers.end(); i++)
|
||||
{
|
||||
std::string text;
|
||||
if (i->name != "" && i->description != "")
|
||||
text = i->name + " (" + i->description + ")";
|
||||
else if (i->name !="")
|
||||
text = i->name;
|
||||
else
|
||||
text = i->address + ":" + i->port;
|
||||
|
||||
if ((*i)["clients"].asString().size())
|
||||
text += (*i)["clients"].asString();
|
||||
if ((*i)["clients_max"].asString().size())
|
||||
text += "/" + (*i)["clients_max"].asString();
|
||||
text += " ";
|
||||
if ((*i)["version"].asString().size())
|
||||
text += (*i)["version"].asString() + " ";
|
||||
if ((*i)["password"].asString().size())
|
||||
text += "*";
|
||||
if ((*i)["creative"].asString().size())
|
||||
text += "C";
|
||||
if ((*i)["damage"].asString().size())
|
||||
text += "D";
|
||||
if ((*i)["pvp"].asString().size())
|
||||
text += "P";
|
||||
text += " ";
|
||||
|
||||
if ((*i)["name"] != "" && (*i)["description"] != "")
|
||||
text += (*i)["name"].asString() + " (" + (*i)["description"].asString() + ")";
|
||||
else if ((*i)["name"] !="")
|
||||
text += (*i)["name"].asString();
|
||||
else
|
||||
text += (*i)["address"].asString() + ":" + (*i)["port"].asString();
|
||||
|
||||
serverlist->addItem(narrow_to_wide(text).c_str());
|
||||
}
|
||||
}
|
||||
|
@ -1191,26 +1223,26 @@ void GUIMainMenu::serverListOnSelected()
|
|||
{
|
||||
gui::IGUIListBox *serverlist = (gui::IGUIListBox*)getElementFromId(GUI_ID_SERVERLIST);
|
||||
u16 id = serverlist->getSelected();
|
||||
if (id < 0) return;
|
||||
//if (id < 0) return; // u16>0!
|
||||
((gui::IGUIEditBox*)getElementFromId(GUI_ID_ADDRESS_INPUT))
|
||||
->setText(narrow_to_wide(m_data->servers[id].address).c_str());
|
||||
->setText(narrow_to_wide(m_data->servers[id]["address"].asString()).c_str());
|
||||
((gui::IGUIEditBox*)getElementFromId(GUI_ID_PORT_INPUT))
|
||||
->setText(narrow_to_wide(m_data->servers[id].port).c_str());
|
||||
->setText(narrow_to_wide(m_data->servers[id]["port"].asString()).c_str());
|
||||
}
|
||||
}
|
||||
|
||||
ServerListSpec GUIMainMenu::getServerListSpec(std::string address, std::string port)
|
||||
{
|
||||
ServerListSpec server;
|
||||
server.address = address;
|
||||
server.port = port;
|
||||
server["address"] = address;
|
||||
server["port"] = port;
|
||||
for(std::vector<ServerListSpec>::iterator i = m_data->servers.begin();
|
||||
i != m_data->servers.end(); i++)
|
||||
{
|
||||
if (i->address == address && i->port == port)
|
||||
if ((*i)["address"] == address && (*i)["port"] == port)
|
||||
{
|
||||
server.description = i->description;
|
||||
server.name = i->name;
|
||||
server["description"] = (*i)["description"];
|
||||
server["name"] = (*i)["name"];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -55,6 +55,7 @@ struct MainMenuData
|
|||
// Server options
|
||||
bool creative_mode;
|
||||
bool enable_damage;
|
||||
bool enable_public;
|
||||
int selected_world;
|
||||
bool simple_singleplayer_mode;
|
||||
// Actions
|
||||
|
@ -77,6 +78,7 @@ struct MainMenuData
|
|||
// Server opts
|
||||
creative_mode(false),
|
||||
enable_damage(false),
|
||||
enable_public(false),
|
||||
selected_world(0),
|
||||
simple_singleplayer_mode(false),
|
||||
// Actions
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
if( UNIX )
|
||||
set(json_SRCS jsoncpp.cpp)
|
||||
set(json_platform_LIBS "")
|
||||
else( UNIX )
|
||||
set(json_SRCS jsoncpp.cpp)
|
||||
set(json_platform_LIBS "")
|
||||
endif( UNIX )
|
||||
|
||||
add_library(json ${json_SRCS})
|
||||
|
||||
target_link_libraries(
|
||||
json
|
||||
${json_platform_LIBS}
|
||||
)
|
|
@ -0,0 +1,16 @@
|
|||
#!/bin/sh
|
||||
cd ..
|
||||
svn co https://jsoncpp.svn.sourceforge.net/svnroot/jsoncpp/trunk/jsoncpp jsoncpp
|
||||
svn up jsoncpp
|
||||
cd jsoncpp
|
||||
python amalgamate.py
|
||||
cp -R dist/json ..
|
||||
cp dist/jsoncpp.cpp ../json
|
||||
|
||||
# maybe you need to patch:
|
||||
# src/json/jsoncpp.cpp:
|
||||
# -#include <json/json.h>
|
||||
# +#include "json/json.h"
|
||||
|
||||
#svn export --force https://jsoncpp.svn.sourceforge.net/svnroot/jsoncpp/trunk/jsoncpp/src/lib_json json
|
||||
#svn export --force https://jsoncpp.svn.sourceforge.net/svnroot/jsoncpp/trunk/jsoncpp/include/json json
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
10
src/main.cpp
10
src/main.cpp
|
@ -1095,6 +1095,7 @@ int main(int argc, char *argv[])
|
|||
#else
|
||||
bool run_dedicated_server = cmd_args.getFlag("server");
|
||||
#endif
|
||||
g_settings->set("server_dedicated", run_dedicated_server ? "true" : "false");
|
||||
if(run_dedicated_server)
|
||||
{
|
||||
DSTACK("Dedicated server branch");
|
||||
|
@ -1593,6 +1594,7 @@ int main(int argc, char *argv[])
|
|||
|
||||
g_settings->set("creative_mode", itos(menudata.creative_mode));
|
||||
g_settings->set("enable_damage", itos(menudata.enable_damage));
|
||||
g_settings->set("server_announce", itos(menudata.enable_public));
|
||||
g_settings->set("name", playername);
|
||||
g_settings->set("address", address);
|
||||
g_settings->set("port", itos(port));
|
||||
|
@ -1619,10 +1621,10 @@ int main(int argc, char *argv[])
|
|||
else if (address != "")
|
||||
{
|
||||
ServerListSpec server;
|
||||
server.name = menudata.servername;
|
||||
server.address = wide_to_narrow(menudata.address);
|
||||
server.port = wide_to_narrow(menudata.port);
|
||||
server.description = menudata.serverdescription;
|
||||
server["name"] = menudata.servername;
|
||||
server["address"] = wide_to_narrow(menudata.address);
|
||||
server["port"] = wide_to_narrow(menudata.port);
|
||||
server["description"] = menudata.serverdescription;
|
||||
ServerList::insert(server);
|
||||
}
|
||||
|
||||
|
|
|
@ -51,6 +51,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
#include "sound.h" // dummySoundManager
|
||||
#include "event_manager.h"
|
||||
#include "hex.h"
|
||||
#include "serverlist.h"
|
||||
#include "util/string.h"
|
||||
#include "util/pointedthing.h"
|
||||
#include "util/mathconstants.h"
|
||||
|
@ -961,9 +962,11 @@ Server::Server(
|
|||
{
|
||||
m_liquid_transform_timer = 0.0;
|
||||
m_print_info_timer = 0.0;
|
||||
m_masterserver_timer = 0.0;
|
||||
m_objectdata_timer = 0.0;
|
||||
m_emergethread_trigger_timer = 0.0;
|
||||
m_savemap_timer = 0.0;
|
||||
m_clients_number = 0;
|
||||
|
||||
m_env_mutex.Init();
|
||||
m_con_mutex.Init();
|
||||
|
@ -1505,7 +1508,7 @@ void Server::AsyncRunStep()
|
|||
counter = 0.0;
|
||||
|
||||
JMutexAutoLock lock2(m_con_mutex);
|
||||
|
||||
m_clients_number = 0;
|
||||
if(m_clients.size() != 0)
|
||||
infostream<<"Players:"<<std::endl;
|
||||
for(core::map<u16, RemoteClient*>::Iterator
|
||||
|
@ -1519,10 +1522,25 @@ void Server::AsyncRunStep()
|
|||
continue;
|
||||
infostream<<"* "<<player->getName()<<"\t";
|
||||
client->PrintInfo(infostream);
|
||||
++m_clients_number;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#if USE_CURL
|
||||
// send masterserver announce
|
||||
{
|
||||
float &counter = m_masterserver_timer;
|
||||
if((!counter || counter >= 300.0) && g_settings->getBool("server_announce") == true)
|
||||
{
|
||||
ServerList::sendAnnounce(!counter ? "start" : "update", m_clients_number);
|
||||
counter = 0.01;
|
||||
}
|
||||
counter += dtime;
|
||||
}
|
||||
#endif
|
||||
|
||||
//if(g_settings->getBool("enable_experimental"))
|
||||
{
|
||||
|
||||
|
@ -5186,6 +5204,10 @@ void dedicated_server_loop(Server &server, bool &kill)
|
|||
if(server.getShutdownRequested() || kill)
|
||||
{
|
||||
infostream<<"Dedicated server quitting"<<std::endl;
|
||||
#if USE_CURL
|
||||
if(g_settings->getBool("server_announce") == true)
|
||||
ServerList::sendAnnounce("delete");
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
|
@ -720,6 +720,7 @@ private:
|
|||
// Some timers
|
||||
float m_liquid_transform_timer;
|
||||
float m_print_info_timer;
|
||||
float m_masterserver_timer;
|
||||
float m_objectdata_timer;
|
||||
float m_emergethread_trigger_timer;
|
||||
float m_savemap_timer;
|
||||
|
@ -737,6 +738,7 @@ private:
|
|||
JMutex m_con_mutex;
|
||||
// Connected clients (behind the con mutex)
|
||||
core::map<u16, RemoteClient*> m_clients;
|
||||
u16 m_clients_number; //for announcing masterserver
|
||||
|
||||
// Bann checking
|
||||
BanManager m_banmanager;
|
||||
|
|
|
@ -27,6 +27,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
#include "filesys.h"
|
||||
#include "porting.h"
|
||||
#include "log.h"
|
||||
#include "json/json.h"
|
||||
#if USE_CURL
|
||||
#include <curl/curl.h>
|
||||
#endif
|
||||
|
@ -83,7 +84,7 @@ std::vector<ServerListSpec> getOnline()
|
|||
{
|
||||
CURLcode res;
|
||||
|
||||
curl_easy_setopt(curl, CURLOPT_URL, g_settings->get("serverlist_url").c_str());
|
||||
curl_easy_setopt(curl, CURLOPT_URL, (g_settings->get("serverlist_url")+"/list").c_str());
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, ServerList::WriteCallback);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &liststring);
|
||||
|
||||
|
@ -92,8 +93,7 @@ std::vector<ServerListSpec> getOnline()
|
|||
errorstream<<"Serverlist at url "<<g_settings->get("serverlist_url")<<" not found (internet connection?)"<<std::endl;
|
||||
curl_easy_cleanup(curl);
|
||||
}
|
||||
|
||||
return ServerList::deSerialize(liststring);
|
||||
return ServerList::deSerializeJson(liststring);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -106,8 +106,8 @@ bool deleteEntry (ServerListSpec server)
|
|||
std::vector<ServerListSpec> serverlist = ServerList::getLocal();
|
||||
for(unsigned i = 0; i < serverlist.size(); i++)
|
||||
{
|
||||
if (serverlist[i].address == server.address
|
||||
&& serverlist[i].port == server.port)
|
||||
if (serverlist[i]["address"] == server["address"]
|
||||
&& serverlist[i]["port"] == server["port"])
|
||||
{
|
||||
serverlist.erase(serverlist.begin() + i);
|
||||
}
|
||||
|
@ -150,17 +150,21 @@ std::vector<ServerListSpec> deSerialize(std::string liststring)
|
|||
{
|
||||
std::vector<ServerListSpec> serverlist;
|
||||
std::istringstream stream(liststring);
|
||||
std::string line;
|
||||
std::string line, tmp;
|
||||
while (std::getline(stream, line))
|
||||
{
|
||||
std::transform(line.begin(), line.end(),line.begin(), ::toupper);
|
||||
if (line == "[SERVER]")
|
||||
{
|
||||
ServerListSpec thisserver;
|
||||
std::getline(stream, thisserver.name);
|
||||
std::getline(stream, thisserver.address);
|
||||
std::getline(stream, thisserver.port);
|
||||
std::getline(stream, thisserver.description);
|
||||
std::getline(stream, tmp);
|
||||
thisserver["name"] = tmp;
|
||||
std::getline(stream, tmp);
|
||||
thisserver["address"] = tmp;
|
||||
std::getline(stream, tmp);
|
||||
thisserver["port"] = tmp;
|
||||
std::getline(stream, tmp);
|
||||
thisserver["description"] = tmp;
|
||||
serverlist.push_back(thisserver);
|
||||
}
|
||||
}
|
||||
|
@ -173,13 +177,100 @@ std::string serialize(std::vector<ServerListSpec> serverlist)
|
|||
for(std::vector<ServerListSpec>::iterator i = serverlist.begin(); i != serverlist.end(); i++)
|
||||
{
|
||||
liststring += "[server]\n";
|
||||
liststring += i->name + "\n";
|
||||
liststring += i->address + "\n";
|
||||
liststring += i->port + "\n";
|
||||
liststring += i->description + "\n";
|
||||
liststring += (*i)["name"].asString() + "\n";
|
||||
liststring += (*i)["address"].asString() + "\n";
|
||||
liststring += (*i)["port"].asString() + "\n";
|
||||
liststring += (*i)["description"].asString() + "\n";
|
||||
liststring += "\n";
|
||||
}
|
||||
return liststring;
|
||||
}
|
||||
|
||||
std::vector<ServerListSpec> deSerializeJson(std::string liststring)
|
||||
{
|
||||
std::vector<ServerListSpec> serverlist;
|
||||
Json::Value root;
|
||||
Json::Reader reader;
|
||||
std::istringstream stream(liststring);
|
||||
if (!liststring.size()) {
|
||||
return serverlist;
|
||||
}
|
||||
if (!reader.parse( stream, root ) )
|
||||
{
|
||||
errorstream << "Failed to parse server list " << reader.getFormattedErrorMessages();
|
||||
return serverlist;
|
||||
}
|
||||
if (root["list"].isArray())
|
||||
for (unsigned int i = 0; i < root["list"].size(); i++)
|
||||
{
|
||||
if (root["list"][i].isObject()) {
|
||||
serverlist.push_back(root["list"][i]);
|
||||
}
|
||||
}
|
||||
return serverlist;
|
||||
}
|
||||
|
||||
std::string serializeJson(std::vector<ServerListSpec> serverlist)
|
||||
{
|
||||
Json::Value root;
|
||||
Json::Value list(Json::arrayValue);
|
||||
for(std::vector<ServerListSpec>::iterator i = serverlist.begin(); i != serverlist.end(); i++)
|
||||
{
|
||||
list.append(*i);
|
||||
}
|
||||
root["list"] = list;
|
||||
Json::StyledWriter writer;
|
||||
return writer.write( root );
|
||||
}
|
||||
|
||||
|
||||
#if USE_CURL
|
||||
static size_t ServerAnnounceCallback(void *contents, size_t size, size_t nmemb, void *userp)
|
||||
{
|
||||
return 0;
|
||||
//((std::string*)userp)->append((char*)contents, size * nmemb);
|
||||
//return size * nmemb;
|
||||
}
|
||||
void sendAnnounce(std::string action, u16 clients) {
|
||||
Json::Value server;
|
||||
if (action.size())
|
||||
server["action"] = action;
|
||||
server["port"] = g_settings->get("port");
|
||||
if (action != "del") {
|
||||
server["name"] = g_settings->get("server_name");
|
||||
server["description"] = g_settings->get("server_description");
|
||||
server["address"] = g_settings->get("server_address");
|
||||
server["version"] = VERSION_STRING;
|
||||
server["url"] = g_settings->get("server_url");
|
||||
server["creative"] = g_settings->get("creative_mode");
|
||||
server["damage"] = g_settings->get("enable_damage");
|
||||
server["dedicated"] = g_settings->get("server_dedicated");
|
||||
server["password"] = g_settings->getBool("disallow_empty_password");
|
||||
server["pvp"] = g_settings->getBool("enable_pvp");
|
||||
server["clients"] = clients;
|
||||
server["clients_max"] = g_settings->get("max_users");
|
||||
}
|
||||
if(server["action"] == "start")
|
||||
actionstream << "announcing to " << g_settings->get("serverlist_url") << std::endl;
|
||||
Json::StyledWriter writer;
|
||||
CURL *curl;
|
||||
curl = curl_easy_init();
|
||||
if (curl)
|
||||
{
|
||||
CURLcode res;
|
||||
curl_easy_setopt(curl, CURLOPT_URL, (g_settings->get("serverlist_url")+std::string("/announce?json=")+curl_easy_escape(curl, writer.write( server ).c_str(), 0)).c_str());
|
||||
//curl_easy_setopt(curl, CURLOPT_USERAGENT, "minetest");
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, ServerList::ServerAnnounceCallback);
|
||||
//curl_easy_setopt(curl, CURLOPT_WRITEDATA, &liststring);
|
||||
curl_easy_setopt(curl, CURLOPT_TIMEOUT, 1);
|
||||
curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 1);
|
||||
res = curl_easy_perform(curl);
|
||||
//if (res != CURLE_OK)
|
||||
// errorstream<<"Serverlist at url "<<g_settings->get("serverlist_url")<<" not found (internet connection?)"<<std::endl;
|
||||
curl_easy_cleanup(curl);
|
||||
}
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
} //namespace ServerList
|
||||
|
|
|
@ -19,17 +19,12 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
|
||||
#include <iostream>
|
||||
#include "config.h"
|
||||
#include "json/json.h"
|
||||
|
||||
#ifndef SERVERLIST_HEADER
|
||||
#define SERVERLIST_HEADER
|
||||
|
||||
struct ServerListSpec
|
||||
{
|
||||
std::string name;
|
||||
std::string address;
|
||||
std::string port;
|
||||
std::string description;
|
||||
};
|
||||
typedef Json::Value ServerListSpec;
|
||||
|
||||
namespace ServerList
|
||||
{
|
||||
|
@ -41,6 +36,11 @@ namespace ServerList
|
|||
bool insert(ServerListSpec server);
|
||||
std::vector<ServerListSpec> deSerialize(std::string liststring);
|
||||
std::string serialize(std::vector<ServerListSpec>);
|
||||
std::vector<ServerListSpec> deSerializeJson(std::string liststring);
|
||||
std::string serializeJson(std::vector<ServerListSpec>);
|
||||
#if USE_CURL
|
||||
void sendAnnounce(std::string action = "", u16 clients = 0);
|
||||
#endif
|
||||
} //ServerList namespace
|
||||
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Minetest server list</title>
|
||||
<link rel="stylesheet" href="style.css"/>
|
||||
</head>
|
||||
<body><div id="table"></div></body>
|
||||
</html>
|
||||
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
|
||||
<script src="list.js"></script>
|
|
@ -0,0 +1,72 @@
|
|||
function e(s) {
|
||||
if (typeof s === "undefined") s = '';
|
||||
return s.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"'); //mc"
|
||||
}
|
||||
function human_time(t) {
|
||||
var n = 's';
|
||||
if (!t || t < 0) t = 0;
|
||||
var f = 0;
|
||||
var s = parseInt((new Date().getTime() / 1000 - (t || 0)));
|
||||
if (!s || s <= 0) s = 0;
|
||||
if (s == 0) return 'now';
|
||||
if (s >= 60) {
|
||||
s /= 60;
|
||||
n = 'm';
|
||||
if (s >= 60) {
|
||||
s /= 60;
|
||||
n = 'h';
|
||||
f = 1;
|
||||
if (s >= 24) {
|
||||
s /= 24;
|
||||
n = 'd';
|
||||
f = 1;
|
||||
if (s >= 30) {
|
||||
s /= 30;
|
||||
n = 'M';
|
||||
f = 1;
|
||||
if (s >= 12) {
|
||||
s /= 12;
|
||||
n = 'y';
|
||||
f = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return ((f ? parseFloat(s).toFixed(1) : parseInt(s)) + n);
|
||||
}
|
||||
function success(r) {
|
||||
if (!r || !r.list) return;
|
||||
var h = '<table><tr><th>ip:port</th><th>clients, max</th><th>version</th><th>name</th><th>desc</th><th>flags</th><th>updated/started</th><th>ping</th></tr>';
|
||||
for (var i = 0; i < r.list.length; ++i) {
|
||||
var s = r.list[i];
|
||||
if (!s) continue;
|
||||
h += '<tr>';
|
||||
h += '<td>' + e(s.address) + ':' + e(s.port) + '</td>';
|
||||
h += '<td>' + e(s.clients) + (s.clients_max ? '/' + e(s.clients_max) : '') + (s.clients_top ? ', ' + s.clients_top : '') + '</td>';
|
||||
h += '<td>' + e(s.version) + '</td>';
|
||||
h += '<td>';
|
||||
if (s.url) h += '<a href="' + e(s.url) + '">';
|
||||
h += e(s.name || s.url);
|
||||
if (s.url) h += '</a>';
|
||||
h += '</td>';
|
||||
h += '<td>' + e(s.description) + '</td>';
|
||||
h += '<td>' + e(s.password ? 'Pwd ' : '') + (s.creative ? 'Cre ' : '') + (s.damage ? 'Dmg ' : '') + (s.pvp ? 'Pvp ' : '') + (s.dedicated ? 'Ded ' : '') + '</td>';
|
||||
if (!s.time || s.time < 0) s.time = 0;
|
||||
if (!s.start || s.start < 0) s.start = 0;
|
||||
h += '<td>' + human_time(s.time) + (s.start ? '/' + human_time(s.start) : '') + '</td>';
|
||||
h += '<td>' + (s.ping ? parseFloat(s.ping).toFixed(3)*1000 : '') + '</td>';
|
||||
h += '</tr>';
|
||||
}
|
||||
h += '</table>'
|
||||
jQuery('#table').html(h);
|
||||
}
|
||||
function get() {
|
||||
jQuery.ajax({
|
||||
url: 'list',
|
||||
dataType: 'json',
|
||||
success: success
|
||||
});
|
||||
setTimeout(get, 60000);
|
||||
}
|
||||
get();
|
|
@ -0,0 +1,257 @@
|
|||
#!/usr/bin/perl
|
||||
|
||||
=info
|
||||
install:
|
||||
cpan JSON JSON::XS
|
||||
touch list_full list
|
||||
chmod a+rw list_full list
|
||||
|
||||
freebsd:
|
||||
www/fcgiwrap www/nginx
|
||||
|
||||
rc.conf.local:
|
||||
nginx_enable="YES"
|
||||
fcgiwrap_enable="YES"
|
||||
fcgiwrap_user="www"
|
||||
|
||||
nginx:
|
||||
|
||||
location / {
|
||||
index index.html;
|
||||
}
|
||||
location /announce {
|
||||
fastcgi_pass unix:/var/run/fcgiwrap/fcgiwrap.sock;
|
||||
fastcgi_param SCRIPT_FILENAME $document_root/master.cgi;
|
||||
include fastcgi_params;
|
||||
}
|
||||
|
||||
|
||||
apache .htaccess:
|
||||
AddHandler cgi-script .cgi
|
||||
DirectoryIndex index.html
|
||||
Options +ExecCGI +FollowSymLinks
|
||||
Order allow,deny
|
||||
<FilesMatch (\.(html?|cgi|fcgi|css|js|gif|png|jpe?g|ico)|(^)|\w+)$>
|
||||
Allow from all
|
||||
</FilesMatch>
|
||||
Deny from all
|
||||
|
||||
|
||||
=cut
|
||||
|
||||
use strict;
|
||||
no strict qw(refs);
|
||||
use warnings "NONFATAL" => "all";
|
||||
no warnings qw(uninitialized);
|
||||
use utf8;
|
||||
use Socket;
|
||||
use Time::HiRes qw(time sleep);
|
||||
use IO::Socket::INET;
|
||||
use JSON;
|
||||
use Net::Ping;
|
||||
our $root_path;
|
||||
($ENV{'SCRIPT_FILENAME'} || $0) =~ m|^(.+)[/\\].+?$|; #v0w
|
||||
$root_path = $1 . '/' if $1;
|
||||
$root_path =~ s|\\|/|g;
|
||||
|
||||
our %config = (
|
||||
#debug => 1,
|
||||
list_full => $root_path . 'list_full',
|
||||
list_pub => $root_path . 'list',
|
||||
time_purge => 86400 * 30,
|
||||
time_alive => 650,
|
||||
source_check => 1,
|
||||
ping_timeout => 3,
|
||||
ping => 1,
|
||||
mineping => 1,
|
||||
pingable => 1,
|
||||
trusted => [qw( 176.9.122.10 )], #masterserver self ip - if server on same ip with masterserver doesnt announced
|
||||
#blacklist => [], # [qw(2.3.4.5 4.5.6.7 8.9.0.1), '1.2.3.4', qr/^10\.20\.30\./, ], # list, or quoted, ips, or regex
|
||||
);
|
||||
do($root_path . 'config.pl');
|
||||
our $ping = Net::Ping->new("udp", $config{ping_timeout});
|
||||
$ping->hires();
|
||||
|
||||
sub get_params_one(@) {
|
||||
local %_ = %{ref $_[0] eq 'HASH' ? shift : {}};
|
||||
for (@_) {
|
||||
tr/+/ /, s/%([a-f\d]{2})/pack 'H*', $1/gei for my ($k, $v) = /^([^=]+=?)=(.+)$/ ? ($1, $2) : (/^([^=]*)=?$/, /^-/);
|
||||
$_{$k} = $v;
|
||||
}
|
||||
wantarray ? %_ : \%_;
|
||||
}
|
||||
|
||||
sub get_params(;$$) { #v7
|
||||
my ($string, $delim) = @_;
|
||||
$delim ||= '&';
|
||||
read(STDIN, local $_ = '', $ENV{'CONTENT_LENGTH'}) if !$string and $ENV{'CONTENT_LENGTH'};
|
||||
local %_ =
|
||||
$string
|
||||
? get_params_one split $delim, $string
|
||||
: (get_params_one(@ARGV), map { get_params_one split $delim, $_ } split(/;\s*/, $ENV{'HTTP_COOKIE'}), $ENV{'QUERY_STRING'}, $_);
|
||||
wantarray ? %_ : \%_;
|
||||
}
|
||||
|
||||
sub get_params_utf8(;$$) {
|
||||
local $_ = &get_params;
|
||||
utf8::decode $_ for %$_;
|
||||
wantarray ? %$_ : $_;
|
||||
}
|
||||
|
||||
sub file_rewrite(;$@) {
|
||||
local $_ = shift;
|
||||
return unless open my $fh, '>', $_;
|
||||
print $fh @_;
|
||||
}
|
||||
|
||||
sub file_read ($) {
|
||||
open my $f, '<', $_[0] or return;
|
||||
local $/ = undef;
|
||||
my $ret = <$f>;
|
||||
close $f;
|
||||
return \$ret;
|
||||
}
|
||||
|
||||
sub read_json {
|
||||
my $ret = {};
|
||||
eval { $ret = JSON->new->utf8->relaxed(1)->decode(${ref $_[0] ? $_[0] : file_read($_[0]) or \''} || '{}'); }; #'mc
|
||||
warn "json error [$@] on [", ${ref $_[0] ? $_[0] : \$_[0]}, "]" if $@;
|
||||
$ret;
|
||||
}
|
||||
|
||||
sub printu (@) {
|
||||
for (@_) {
|
||||
print($_), next unless utf8::is_utf8($_);
|
||||
my $s = $_;
|
||||
utf8::encode($s);
|
||||
print($s);
|
||||
}
|
||||
}
|
||||
|
||||
sub name_to_ip_noc($) {
|
||||
my ($name) = @_;
|
||||
unless ($name =~ /^\d+\.\d+\.\d+\.\d+$/) {
|
||||
local $_ = (gethostbyname($name))[4];
|
||||
return ($name, 1) unless length($_) == 4;
|
||||
$name = inet_ntoa($_);
|
||||
}
|
||||
return $name;
|
||||
}
|
||||
|
||||
sub float {
|
||||
return ($_[0] < 8 and $_[0] - int($_[0]))
|
||||
? sprintf('%.' . ($_[0] < 1 ? 3 : ($_[0] < 3 ? 2 : 1)) . 'f', $_[0])
|
||||
: int($_[0]);
|
||||
|
||||
}
|
||||
|
||||
sub mineping ($$) {
|
||||
my ($addr, $port) = @_;
|
||||
warn "mineping($addr, $port)" if $config{debug};
|
||||
my $data;
|
||||
my $time = time;
|
||||
eval {
|
||||
my $socket = IO::Socket::INET->new(
|
||||
'PeerAddr' => $addr,
|
||||
'PeerPort' => $port,
|
||||
'Proto' => 'udp',
|
||||
'Timeout' => $config{ping_timeout},
|
||||
);
|
||||
$socket->send("\x4f\x45\x74\x03\x00\x00\x00\x01");
|
||||
local $SIG{ALRM} = sub { die "alarm time out"; };
|
||||
alarm $config{ping_timeout};
|
||||
$socket->recv($data, POSIX::BUFSIZ) or die "recv: $!";
|
||||
alarm 0;
|
||||
1; # return value from eval on normalcy
|
||||
} or return 0;
|
||||
return 0 unless length $data;
|
||||
$time = float(time - $time);
|
||||
warn "recvd: ", length $data, " [$time]" if $config{debug};
|
||||
return $time;
|
||||
}
|
||||
|
||||
sub request (;$) {
|
||||
my ($r) = @_;
|
||||
$r ||= \%ENV;
|
||||
my $param = get_params_utf8;
|
||||
my $after = sub {
|
||||
if ($param->{json}) {
|
||||
my $j = {};
|
||||
eval { $j = JSON->new->decode($param->{json}) || {} };
|
||||
$param->{$_} = $j->{$_} for keys %$j;
|
||||
delete $param->{json};
|
||||
}
|
||||
if (%$param) {
|
||||
s/^false$// for values %$param;
|
||||
$param->{ip} = $r->{REMOTE_ADDR};
|
||||
for (@{$config{blacklist}}) {
|
||||
return if $param->{ip} ~~ $_;
|
||||
}
|
||||
$param->{address} ||= $param->{ip};
|
||||
if ($config{source_check} and name_to_ip_noc($param->{address}) ne $param->{ip} and !($param->{ip} ~~ $config{trusted})) {
|
||||
warn("bad address [$param->{address}] ne [$param->{ip}]") if $config{debug};
|
||||
return;
|
||||
}
|
||||
$param->{port} ||= 30000;
|
||||
$param->{key} = "$param->{ip}:$param->{port}";
|
||||
$param->{off} = time if $param->{action} ~~ 'delete';
|
||||
|
||||
if ($config{ping} and $param->{action} ne 'delete') {
|
||||
if ($config{mineping}) {
|
||||
$param->{ping} = mineping($param->{ip}, $param->{port});
|
||||
} else {
|
||||
$ping->port_number($param->{port});
|
||||
$ping->service_check(0);
|
||||
my ($pingret, $duration, $ip) = $ping->ping($param->{address});
|
||||
if ($ip ne $param->{ip} and !($param->{ip} ~~ $config{trusted})) {
|
||||
warn "strange ping ip [$ip] != [$param->{ip}]" if $config{debug};
|
||||
return if $config{source_check} and !($param->{ip} ~~ $config{trusted});
|
||||
}
|
||||
$param->{ping} = $duration if $pingret;
|
||||
warn " PING t=$config{ping_timeout}, $param->{address}:$param->{port} = ( $pingret, $duration, $ip )" if $config{debug};
|
||||
}
|
||||
}
|
||||
my $list = read_json($config{list_full}) || {};
|
||||
warn "readed[$config{list_full}] list size=", scalar @{$list->{list}};
|
||||
my $listk = {map { $_->{key} => $_ } @{$list->{list}}};
|
||||
my $old = $listk->{$param->{key}};
|
||||
$param->{time} = $old->{time} if $param->{off};
|
||||
$param->{time} ||= int time;
|
||||
$param->{start} = $param->{action} ~~ 'start' ? $param->{time} : $old->{start} || $param->{time};
|
||||
delete $param->{start} if $param->{off};
|
||||
$param->{first} ||= $old->{first} || $old->{time} || $param->{time};
|
||||
$param->{clients_top} = $old->{clients_top} if $old->{clients_top} > $param->{clients};
|
||||
$param->{clients_top} ||= $param->{clients} || 0;
|
||||
delete $param->{action};
|
||||
$listk->{$param->{key}} = $param;
|
||||
$list->{list} = [grep { $_->{time} > time - $config{time_purge} } values %$listk];
|
||||
file_rewrite($config{list_full}, JSON->new->encode($list));
|
||||
warn "writed[$config{list_full}] list size=", scalar @{$list->{list}};
|
||||
$list->{list} = [
|
||||
sort { $b->{clients} <=> $a->{clients} || $a->{start} <=> $b->{start} }
|
||||
grep { $_->{time} > time - $config{time_alive} and !$_->{off} and (!$config{ping} or !$config{pingable} or $_->{ping}) }
|
||||
@{$list->{list}}
|
||||
];
|
||||
file_rewrite($config{list_pub}, JSON->new->encode($list));
|
||||
warn "writed[$config{list_pub}] list size=", scalar @{$list->{list}};
|
||||
}
|
||||
};
|
||||
return [200, ["Content-type", "application/json"], [JSON->new->encode({})]], $after;
|
||||
}
|
||||
|
||||
sub request_cgi {
|
||||
my ($p, $after) = request(@_);
|
||||
shift @$p;
|
||||
printu join "\n", map { join ': ', @$_ } shift @$p;
|
||||
printu "\n\n";
|
||||
printu join '', map { join '', @$_ } @$p;
|
||||
if (fork) {
|
||||
unless ($config{debug}) {
|
||||
close STDOUT;
|
||||
close STDERR;
|
||||
}
|
||||
} else {
|
||||
$after->() if ref $after ~~ 'CODE';
|
||||
}
|
||||
}
|
||||
request_cgi() unless caller;
|
|
@ -0,0 +1,14 @@
|
|||
table {
|
||||
max-width: 100%;
|
||||
background-color: transparent;
|
||||
border-collapse: collapse;
|
||||
border-spacing: 0;
|
||||
}
|
||||
|
||||
td, th {
|
||||
border: 1px solid gray;
|
||||
}
|
||||
|
||||
div#table table {
|
||||
width: 100%;
|
||||
}
|
Loading…
Reference in New Issue