Switch campaignd to use asio by reusing wesnothd code via server_base class

This commit is contained in:
loonycyborg 2016-06-15 22:14:29 +03:00
parent 1aac722378
commit 358bf3f9f8
11 changed files with 351 additions and 307 deletions

View file

@ -399,11 +399,6 @@ if env["prereqs"]:
env = conf.Finish()
campaignd_env = env.Clone()
conf = campaignd_env.Configure(**configure_args)
have_sdl_net()
campaignd_env = conf.Finish()
client_env = env.Clone()
conf = client_env.Configure(**configure_args)
have_client_prereqs = have_server_prereqs & have_sdl_other() & \
@ -457,7 +452,7 @@ if env["prereqs"]:
test_env = client_env.Clone()
conf = test_env.Configure(**configure_args)
have_test_prereqs = have_client_prereqs and conf.CheckBoost('unit_test_framework') and conf.CheckSDL("SDL2_net", header_file = "SDL_net") \
have_test_prereqs = have_client_prereqs and conf.CheckBoost('unit_test_framework') \
or Warning("WARN: Unit tests are disabled because their prerequisites are not met")
test_env = conf.Finish()
if not have_test_prereqs and "test" in env["default_targets"]:
@ -494,7 +489,7 @@ if not env['nls']:
#
print "---[applying configuration]---"
for env in [test_env, campaignd_env, client_env, env]:
for env in [test_env, client_env, env]:
build_root="#/"
if os.path.isabs(env["build_dir"]):
build_root = ""
@ -553,7 +548,7 @@ try:
except:
pass
Export(Split("env campaignd_env client_env test_env have_client_prereqs have_server_prereqs have_test_prereqs"))
Export(Split("env client_env test_env have_client_prereqs have_server_prereqs have_test_prereqs"))
SConscript(dirs = Split("po doc packaging/windows packaging/systemd"))
binaries = Split("wesnoth wesnothd cutter exploder campaignd test")
@ -567,7 +562,7 @@ builds = {
builds["glibcxx_debug"].update(builds["debug"])
build = env["build"]
for env in [test_env, campaignd_env, client_env, env]:
for env in [test_env, client_env, env]:
env["extra_flags_glibcxx_debug"] = env["extra_flags_debug"]
env.AppendUnique(**builds[build])
env.Append(CXXFLAGS = Split(os.environ.get('CXXFLAGS', [])), LINKFLAGS = Split(os.environ.get('LDFLAGS', [])))

View file

@ -6,7 +6,7 @@ from glob import glob
Import("*")
for env in [test_env, campaignd_env, client_env, env]:
for env in [test_env, client_env, env]:
env.Append(CPPDEFINES = "$EXTRA_DEFINE")
#color_range.cpp should be removed, but game_config depends on it.
@ -22,7 +22,6 @@ libwesnoth_core_sources = Split("""
map/map.cpp
md5.cpp
mt_rng.cpp
network.cpp
seed_rng.cpp
serialization/binary_or_text.cpp
serialization/parser.cpp
@ -33,17 +32,14 @@ libwesnoth_core_sources = Split("""
serialization/unicode.cpp
serialization/validator.cpp
terrain/type_data.cpp
thread.cpp
tools/schema/tag.cpp
tstring.cpp
util.cpp
version.cpp
""")
libwesnoth_core_sources.extend(campaignd_env.Object("network_worker.cpp", EXTRA_DEFINE = env['raw_sockets'] and "NETWORK_USE_RAW_SOCKETS" or None))
game_config_env = campaignd_env.Clone()
filesystem_env = campaignd_env.Clone()
game_config_env = env.Clone()
filesystem_env = env.Clone()
if env["PLATFORM"] != "win32":
game_config_env.Append(CPPDEFINES = "WESNOTH_PATH='\"$datadir\"'")
if env['localedirname']:
@ -83,7 +79,7 @@ else:
filesystem_env.Object("gettext_boost.cpp")
])
libwesnoth_core = [campaignd_env.Library("wesnoth_core", libwesnoth_core_sources)]
libwesnoth_core = [env.Library("wesnoth_core", libwesnoth_core_sources)]
libwesnoth_sources = Split("""
arrow.cpp
@ -154,7 +150,7 @@ libwesnoth = client_env.Library("wesnoth", libwesnoth_sources)
dummy_video_sources = Split("""
tools/dummy_video.cpp
""")
libdummy_video = campaignd_env.Library("dummy_video", dummy_video_sources)
libdummy_video = test_env.Library("dummy_video", dummy_video_sources)
libcampaignd_sources = Split("""
addon/validation.cpp
@ -618,7 +614,7 @@ def WesnothProgram(env, target, source, can_build, **kw):
locals()[target] = bin
Export(target)
for env in [test_env, campaignd_env, client_env, env]:
for env in [test_env, client_env, env]:
env.AddMethod(WesnothProgram)
wesnoth_objects = ["wesnoth.cpp", libwesnoth_extras, libwesnoth_core, libwesnoth,
@ -630,13 +626,14 @@ client_env.WesnothProgram("wesnoth", wesnoth_objects, have_client_prereqs)
campaignd_sources = Split("""
campaign_server/addon_utils.cpp
campaign_server/blacklist.cpp
server/input_stream.cpp
server/server_base.cpp
server/simple_wml.cpp
""")
if env["PLATFORM"] == "win32": env["fifodir"] = ""
campaignd_sources.extend(campaignd_env.Object("campaign_server/campaign_server.cpp", EXTRA_DEFINE = env['fifodir'] and "FIFODIR='\"$fifodir\"'" or None))
campaignd_sources.extend(env.Object("campaign_server/campaign_server.cpp", EXTRA_DEFINE = env['fifodir'] and "FIFODIR='\"$fifodir\"'" or None))
campaignd_env.WesnothProgram("campaignd", campaignd_sources + [libwesnoth_core, libdummy_video, libcampaignd], have_server_prereqs)
env.WesnothProgram("campaignd", campaignd_sources + [libwesnoth_core, libcampaignd], have_server_prereqs)
wesnothd_sources = Split("""
server/ban.cpp
@ -717,7 +714,6 @@ test_sources = Split("""
tests/test_make_enum.cpp
tests/test_map_location.cpp
tests/test_mp_connect.cpp
tests/test_network_worker.cpp
tests/test_recall_list.cpp
tests/test_rng.cpp
tests/test_sdl_utils.cpp

View file

@ -23,7 +23,6 @@
#include "filesystem.hpp"
#include "log.hpp"
#include "network_worker.hpp"
#include "serialization/binary_or_text.hpp"
#include "serialization/parser.hpp"
#include "serialization/string_utils.hpp"
@ -57,53 +56,17 @@ static lg::log_domain log_campaignd("campaignd");
#define WRN_CS LOG_STREAM(warn, log_campaignd)
#define ERR_CS LOG_STREAM(err, log_campaignd)
//compatibility code for MS compilers
#ifndef SIGHUP
#define SIGHUP 20
#endif
/** @todo FIXME: should define SIGINT here too, but to what? */
static lg::log_domain log_config("config");
#define ERR_CONFIG LOG_STREAM(err, log_config)
#define WRN_CONFIG LOG_STREAM(warn, log_config)
static lg::log_domain log_server("server");
#define ERR_SERVER LOG_STREAM(err, log_server)
#include "server/send_receive_wml_helpers.ipp"
namespace {
/**
* Whether to reload the server configuration as soon as possible
* (e.g. after SIGHUP).
*/
sig_atomic_t need_reload = 0;
void flag_sighup(int signal)
{
assert(signal == SIGHUP);
LOG_CS << "SIGHUP caught, scheduling config reload.\n";
need_reload = 1;
}
void exit_sigint(int signal)
{
assert(signal == SIGINT);
LOG_CS << "SIGINT caught, exiting without cleanup immediately.\n";
exit(0);
}
void exit_sigterm(int signal)
{
assert(signal == SIGTERM);
LOG_CS << "SIGTERM caught, exiting without cleanup immediately.\n";
exit(128 + SIGTERM);
}
time_t monotonic_clock()
{
#if defined(_POSIX_MONOTONIC_CLOCK) && !defined(_WIN32)
timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
return ts.tv_sec;
#else
#warning monotonic_clock() is not truly monotonic!
return time(nullptr);
#endif
}
/* Secure password storage functions */
bool authenticate(config& campaign, const config::attribute_value& passphrase)
{
@ -136,29 +99,19 @@ void set_passphrase(config& campaign, std::string passphrase)
namespace campaignd {
server::server(const std::string& cfg_file, size_t min_threads, size_t max_threads)
: cfg_()
server::server(const std::string& cfg_file)
: server_base(load_config(), true)
, cfg_()
, cfg_file_(cfg_file)
, read_only_(false)
, compress_level_(0)
, input_()
, hooks_()
, handlers_()
, feedback_url_format_()
, feedback_url_format_()
, blacklist_()
, blacklist_file_()
, port_(load_config())
, net_manager_(min_threads, max_threads)
, server_manager_(port_)
{
#ifndef _MSC_VER
signal(SIGHUP, flag_sighup);
#endif
signal(SIGINT, exit_sigint);
signal(SIGTERM, exit_sigterm);
LOG_CS << "Port: " << port_ << " Worker threads min/max: " << min_threads
<< '/' << max_threads << '\n';
LOG_CS << "Port: " << port_ << " ";
// Ensure all campaigns to use secure hash passphrase storage
if(!read_only_) {
@ -196,9 +149,6 @@ int server::load_config()
LOG_CS << "READ-ONLY MODE ACTIVE\n";
}
const bool use_system_sendfile = cfg_["network_use_system_sendfile"].to_bool();
network_worker_pool::set_use_system_sendfile(use_system_sendfile);
// Seems like compression level above 6 is a waste of CPU cycles.
compress_level_ = cfg_["compress_level"].to_int(6);
@ -218,9 +168,17 @@ int server::load_config()
if(!cfg_["control_socket"].empty()) {
const std::string& path = cfg_["control_socket"].str();
if(!input_.get() || input_->path() != path) {
input_.reset(new input_stream(cfg_["control_socket"]));
#ifndef _WIN32
const int res = mkfifo(path.c_str(),0660);
if(res != 0 && errno != EEXIST) {
ERR_CS << "could not make fifo at '" << path << "' (" << strerror(errno) << ")\n";
} else {
int fifo = open(path.c_str(), O_RDWR|O_NONBLOCK);
input_.assign(fifo);
LOG_CS << "opened fifo at '" << path << "'. Server commands may be written to this file.\n";
read_from_fifo();
}
#endif
}
// Ensure the campaigns list WML exists even if empty, other functions
@ -230,13 +188,120 @@ int server::load_config()
// Certain config values are saved to WML again so that a given server
// instance's parameters remain constant even if the code defaults change
// at some later point.
cfg_["network_use_system_sendfile"] = use_system_sendfile;
cfg_["compress_level"] = compress_level_;
// But not the listening port number.
return cfg_["port"].to_int(default_campaignd_port);
}
void server::handle_new_client(socket_ptr socket)
{
async_receive_doc(socket,
boost::bind(&server::handle_request, this, _1, _2)
);
}
void server::handle_request(socket_ptr socket, boost::shared_ptr<simple_wml::document> doc)
{
config data;
read(data, doc->output());
config::all_children_iterator i = data.ordered_begin();
if(i != data.ordered_end()) {
// We only handle the first child.
const config::any_child& c = *i;
request_handlers_table::const_iterator j
= handlers_.find(c.key);
if(j != handlers_.end()) {
// Call the handler.
j->second(this, request(c.key, c.cfg, socket));
} else {
send_error("Unrecognized [" + c.key + "] request.",socket);
}
}
}
#ifndef _WIN32
void server::handle_read_from_fifo(const boost::system::error_code& error, std::size_t)
{
if(error) {
std::cout << error.message() << std::endl;
return;
}
std::istream is(&admin_cmd_);
std::string cmd;
std::getline(is, cmd);
const control_line ctl = cmd;
if(ctl == "shut_down") {
LOG_CS << "Shut down requested by admin, shutting down...\n";
} else if(ctl == "readonly") {
if(ctl.args_count()) {
cfg_["read_only"] = read_only_ = utils::string_bool(ctl[1], true);
}
LOG_CS << "Read only mode: " << (read_only_ ? "enabled" : "disabled") << '\n';
} else if(ctl == "flush") {
LOG_CS << "Flushing config to disk...\n";
write_config();
} else if(ctl == "reload") {
if(ctl.args_count()) {
if(ctl[1] == "blacklist") {
LOG_CS << "Reloading blacklist...\n";
load_blacklist();
} else {
ERR_CS << "Unrecognized admin reload argument: " << ctl[1] << '\n';
}
} else {
LOG_CS << "Reloading all configuration...\n";
load_config();
LOG_CS << "Reloaded configuration\n";
}
} else if(ctl == "setpass") {
if(ctl.args_count() != 2) {
ERR_CS << "Incorrect number of arguments for 'setpass'\n";
} else {
const std::string& addon_id = ctl[1];
const std::string& newpass = ctl[2];
config& campaign = get_campaign(addon_id);
if(!campaign) {
ERR_CS << "Add-on '" << addon_id << "' not found, cannot set passphrase\n";
} else if(newpass.empty()) {
// Shouldn't happen!
ERR_CS << "Add-on passphrases may not be empty!\n";
} else {
set_passphrase(campaign, newpass);
write_config();
LOG_CS << "New passphrase set for '" << addon_id << "'\n";
}
}
} else {
ERR_CS << "Unrecognized admin command: " << ctl.full() << '\n';
}
read_from_fifo();
}
#endif
void server::handle_sighup(const boost::system::error_code&, int)
{
LOG_CS << "SIGHUP caught, reloading config.\n";
load_config(); // TODO: handle port number config changes
LOG_CS << "Reloaded configuration\n";
sighup_.async_wait(boost::bind(&server::handle_sighup, this, _1, _2));
}
void server::load_blacklist()
{
// We *always* want to clear the blacklist first, especially if we are
@ -309,21 +374,18 @@ void server::fire(const std::string& hook, const std::string& addon)
#endif
}
void server::send_message(const std::string& msg, network::connection sock)
void server::send_message(const std::string& msg, socket_ptr sock)
{
config cfg;
cfg.add_child("message")["message"] = msg;
network::send_data(cfg, sock);
async_send_message(sock, msg);
}
void server::send_error(const std::string& msg, network::connection sock)
void server::send_error(const std::string& msg, socket_ptr sock)
{
config cfg;
cfg.add_child("error")["message"] = msg;
ERR_CS << "[" << network::ip_address(sock) << "]: " << msg << '\n';
network::send_data(cfg, sock);
ERR_CS << "[" << client_address(sock) << "]: " << msg << '\n';
async_send_error(sock, msg);
}
/*
void server::run()
{
network::connection sock = 0;
@ -332,72 +394,7 @@ void server::run()
for(;;)
{
if(need_reload) {
load_config(); // TODO: handle port number config changes
need_reload = 0;
last_ts = 0;
LOG_CS << "Reloaded configuration\n";
}
try {
bool force_flush = false;
std::string admin_cmd;
if(input_ && input_->read_line(admin_cmd)) {
control_line ctl = admin_cmd;
if(ctl == "shut_down") {
LOG_CS << "Shut down requested by admin, shutting down...\n";
break;
} else if(ctl == "readonly") {
if(ctl.args_count()) {
cfg_["read_only"] = read_only_ = utils::string_bool(ctl[1], true);
}
LOG_CS << "Read only mode: " << (read_only_ ? "enabled" : "disabled") << '\n';
} else if(ctl == "flush") {
force_flush = true;
LOG_CS << "Flushing config to disk...\n";
} else if(ctl == "reload") {
if(ctl.args_count()) {
if(ctl[1] == "blacklist") {
LOG_CS << "Reloading blacklist...\n";
load_blacklist();
} else {
ERR_CS << "Unrecognized admin reload argument: " << ctl[1] << '\n';
}
} else {
LOG_CS << "Reloading all configuration...\n";
need_reload = 1;
// Avoid flush timer ellapsing
continue;
}
} else if(ctl == "setpass") {
if(ctl.args_count() != 2) {
ERR_CS << "Incorrect number of arguments for 'setpass'\n";
} else {
const std::string& addon_id = ctl[1];
const std::string& newpass = ctl[2];
config& campaign = get_campaign(addon_id);
if(!campaign) {
ERR_CS << "Add-on '" << addon_id << "' not found, cannot set passphrase\n";
} else if(newpass.empty()) {
// Shouldn't happen!
ERR_CS << "Add-on passphrases may not be empty!\n";
} else {
set_passphrase(campaign, newpass);
write_config();
LOG_CS << "New passphrase set for '" << addon_id << "'\n";
}
}
} else {
ERR_CS << "Unrecognized admin command: " << ctl.full() << '\n';
}
}
const time_t cur_ts = monotonic_clock();
// Write config to disk every ten minutes.
if(force_flush || labs(cur_ts - last_ts) >= 10*60) {
@ -405,35 +402,6 @@ void server::run()
last_ts = cur_ts;
}
network::process_send_queue();
sock = network::accept_connection();
if(sock) {
LOG_CS << "received connection from " << network::ip_address(sock) << "\n";
}
config data;
while((sock = network::receive_data(data, 0)) != network::null_connection)
{
config::all_children_iterator i = data.ordered_begin();
if(i != data.ordered_end()) {
// We only handle the first child.
const config::any_child& c = *i;
request_handlers_table::const_iterator j
= handlers_.find(c.key);
if(j != handlers_.end()) {
// Call the handler.
j->second(this, request(c.key, c.cfg, sock));
} else {
send_error("Unrecognized [" + c.key + "] request.",
sock);
}
}
}
} catch(network::error& e) {
if(!e.socket) {
ERR_CS << "fatal network error: " << e.message << "\n";
@ -465,6 +433,7 @@ void server::run()
SDL_Delay(20);
}
}
*/
void server::register_handler(const std::string& cmd, const request_handler& func)
{
@ -570,7 +539,13 @@ void server::handle_request_campaign_list(const server::request& req)
config response;
response.add_child("campaigns", campaign_list);
std::cerr << " size: " << (network::send_data(response, req.sock)/1024) << "KiB\n";
std::string wml;
std::ostringstream ostr(wml);
write(ostr, response);
simple_wml::document doc(wml.c_str(), simple_wml::INIT_STATIC);
doc.compress();
async_send_doc(req.sock, doc, boost::bind(&server::handle_new_client, this, _1));
}
void server::handle_request_campaign(const server::request& req)
@ -592,7 +567,8 @@ void server::handle_request_campaign(const server::request& req)
}
std::cerr << " size: " << size/1024 << "KiB\n";
network::send_file(campaign["filename"], req.sock);
async_send_file(req.sock, campaign["filename"],
boost::bind(&server::handle_new_client, this, _1), null_handler);
// Clients doing upgrades or some other specific thing shouldn't bump
// the downloads count. Default to true for compatibility with old
// clients that won't tell us what they are trying to do.
@ -870,7 +846,7 @@ void server::handle_change_passphrase(const server::request& req)
} // end namespace campaignd
int main(int argc, char**argv)
int main()
{
game_config::path = filesystem::get_cwd();
@ -880,22 +856,15 @@ int main(int argc, char**argv)
try {
std::cerr << "Wesnoth campaignd v" << game_config::revision << " starting...\n";
const std::string& cfg_path = filesystem::normalize_path("server.cfg");
const std::string cfg_path = filesystem::normalize_path("server.cfg");
if(argc >= 2 && atoi(argv[1])){
campaignd::server(cfg_path, atoi(argv[1])).run();
} else {
campaignd::server(cfg_path).run();
}
campaignd::server(cfg_path).run();
} catch(config::error& /*e*/) {
std::cerr << "Could not parse config file\n";
return 1;
} catch(filesystem::io_exception& /*e*/) {
std::cerr << "File I/O error\n";
return 2;
} catch(network::error& e) {
std::cerr << "Aborted with network error: " << e.message << '\n';
return 3;
} catch(std::bad_function_call& /*e*/) {
std::cerr << "Bad request handler function call\n";
return 4;

View file

@ -16,8 +16,8 @@
#define CAMPAIGN_SERVER_HPP_INCLUDED
#include "campaign_server/blacklist.hpp"
#include "network.hpp"
#include "server/input_stream.hpp"
#include "server/server_base.hpp"
#include "server/simple_wml.hpp"
#include "utils/functional.hpp"
#include <boost/scoped_ptr.hpp>
@ -28,19 +28,12 @@ namespace campaignd {
/**
* Legacy add-ons server.
*/
class server : private boost::noncopyable
class server : private boost::noncopyable, public server_base
{
public:
explicit server(const std::string& cfg_file,
size_t min_threads = 10,
size_t max_threads = 0);
explicit server(const std::string& cfg_file);
~server();
/**
* Runs the server request processing loop.
*/
void run();
private:
/**
* Client request information object.
@ -54,7 +47,7 @@ private:
const std::string& cmd;
const config& cfg;
const network::connection sock;
const socket_ptr sock;
const std::string addr;
/**
@ -70,11 +63,11 @@ private:
*/
request(const std::string& reqcmd,
const config& reqcfg,
network::connection reqsock)
socket_ptr reqsock)
: cmd(reqcmd)
, cfg(reqcfg)
, sock(reqsock)
, addr(network::ip_address(sock))
, addr(client_address(sock))
{}
};
@ -87,8 +80,6 @@ private:
bool read_only_;
int compress_level_; /**< Used for add-on archives. */
boost::scoped_ptr<input_stream> input_; /**< Server control socket. */
std::map<std::string, std::string> hooks_;
request_handlers_table handlers_;
@ -99,8 +90,12 @@ private:
int port_;
const network::manager net_manager_;
const network::server_manager server_manager_;
void handle_new_client(socket_ptr socket);
void handle_request(socket_ptr socket, boost::shared_ptr<simple_wml::document> doc);
void handle_read_from_fifo(const boost::system::error_code& error, std::size_t bytes_transferred);
void handle_sighup(const boost::system::error_code& error, int signal_number);
/**
* Reads the server configuration from WML.
@ -173,17 +168,13 @@ private:
void handle_delete(const request&);
void handle_change_passphrase(const request&);
//
// Generic responses.
//
/**
* Send a client an informational message.
*
* The WML sent consists of a document containing a single @p [message]
* child with a @a message attribute holding the value of @a msg.
*/
void send_message(const std::string& msg, network::connection sock);
void send_message(const std::string& msg, socket_ptr sock);
/**
* Send a client an error message.
@ -193,7 +184,7 @@ private:
* sending the error to the client, a line with the client IP and message
* is recorded to the server log.
*/
void send_error(const std::string& msg, network::connection sock);
void send_error(const std::string& msg, socket_ptr sock);
};
} // end namespace campaignd

View file

@ -27,49 +27,49 @@
#include <iomanip>
std::map<Uint32, Uint32> recolor_range(const color_range& new_range, const std::vector<Uint32>& old_rgb){
std::map<Uint32, Uint32> map_rgb;
std::map<uint32_t, uint32_t> recolor_range(const color_range& new_range, const std::vector<uint32_t>& old_rgb){
std::map<uint32_t, uint32_t> map_rgb;
Uint16 new_red = (new_range.mid() & 0x00FF0000)>>16;
Uint16 new_green= (new_range.mid() & 0x0000FF00)>>8;
Uint16 new_blue = (new_range.mid() & 0x000000FF);
Uint16 max_red = (new_range.max() & 0x00FF0000)>>16;
Uint16 max_green= (new_range.max() & 0x0000FF00)>>8 ;
Uint16 max_blue = (new_range.max() & 0x000000FF) ;
Uint16 min_red = (new_range.min() & 0x00FF0000)>>16;
Uint16 min_green= (new_range.min() & 0x0000FF00)>>8 ;
Uint16 min_blue = (new_range.min() & 0x000000FF) ;
uint16_t new_red = (new_range.mid() & 0x00FF0000)>>16;
uint16_t new_green= (new_range.mid() & 0x0000FF00)>>8;
uint16_t new_blue = (new_range.mid() & 0x000000FF);
uint16_t max_red = (new_range.max() & 0x00FF0000)>>16;
uint16_t max_green= (new_range.max() & 0x0000FF00)>>8 ;
uint16_t max_blue = (new_range.max() & 0x000000FF) ;
uint16_t min_red = (new_range.min() & 0x00FF0000)>>16;
uint16_t min_green= (new_range.min() & 0x0000FF00)>>8 ;
uint16_t min_blue = (new_range.min() & 0x000000FF) ;
// Map first color in vector to exact new color
Uint32 temp_rgb= old_rgb.empty() ? 0 : old_rgb[0];
Uint16 old_r=(temp_rgb & 0X00FF0000)>>16;
Uint16 old_g=(temp_rgb & 0X0000FF00)>>8;
Uint16 old_b=(temp_rgb & 0X000000FF);
Uint16 reference_avg = (( old_r + old_g + old_b) / 3);
uint32_t temp_rgb= old_rgb.empty() ? 0 : old_rgb[0];
uint16_t old_r=(temp_rgb & 0X00FF0000)>>16;
uint16_t old_g=(temp_rgb & 0X0000FF00)>>8;
uint16_t old_b=(temp_rgb & 0X000000FF);
uint16_t reference_avg = (( old_r + old_g + old_b) / 3);
for(std::vector< Uint32 >::const_iterator temp_rgb2 = old_rgb.begin();
for(std::vector< uint32_t >::const_iterator temp_rgb2 = old_rgb.begin();
temp_rgb2 != old_rgb.end(); ++temp_rgb2)
{
Uint16 old_r=((*temp_rgb2) & 0X00FF0000)>>16;
Uint16 old_g=((*temp_rgb2) & 0X0000FF00)>>8;
Uint16 old_b=((*temp_rgb2) & 0X000000FF);
uint16_t old_r=((*temp_rgb2) & 0X00FF0000)>>16;
uint16_t old_g=((*temp_rgb2) & 0X0000FF00)>>8;
uint16_t old_b=((*temp_rgb2) & 0X000000FF);
const Uint16 old_avg = (( old_r + old_g + old_b) / 3);
const uint16_t old_avg = (( old_r + old_g + old_b) / 3);
// Calculate new color
Uint32 new_r, new_g, new_b;
uint32_t new_r, new_g, new_b;
if(reference_avg && old_avg <= reference_avg){
float old_rat = static_cast<float>(old_avg)/reference_avg;
new_r=Uint32( old_rat * new_red + (1 - old_rat) * min_red);
new_g=Uint32( old_rat * new_green + (1 - old_rat) * min_green);
new_b=Uint32( old_rat * new_blue + (1 - old_rat) * min_blue);
new_r=uint32_t( old_rat * new_red + (1 - old_rat) * min_red);
new_g=uint32_t( old_rat * new_green + (1 - old_rat) * min_green);
new_b=uint32_t( old_rat * new_blue + (1 - old_rat) * min_blue);
}else if(255 - reference_avg){
float old_rat = (255.0f - static_cast<float>(old_avg)) /
(255.0f - reference_avg);
new_r=static_cast<Uint32>( old_rat * new_red + (1 - old_rat) * max_red);
new_g=static_cast<Uint32>( old_rat * new_green + (1 - old_rat) * max_green);
new_b=static_cast<Uint32>( old_rat * new_blue + (1 - old_rat) * max_blue);
new_r=static_cast<uint32_t>( old_rat * new_red + (1 - old_rat) * max_red);
new_g=static_cast<uint32_t>( old_rat * new_green + (1 - old_rat) * max_green);
new_b=static_cast<uint32_t>( old_rat * new_blue + (1 - old_rat) * max_blue);
}else{
new_r=0; new_g=0; new_b=0; // Suppress warning
assert(false);
@ -81,21 +81,21 @@ std::map<Uint32, Uint32> recolor_range(const color_range& new_range, const std::
if(new_g>255) new_g=255;
if(new_b>255) new_b=255;
Uint32 newrgb = (new_r << 16) + (new_g << 8) + (new_b );
uint32_t newrgb = (new_r << 16) + (new_g << 8) + (new_b );
map_rgb[*temp_rgb2]=newrgb;
}
return map_rgb;
}
bool string2rgb(const std::string& s, std::vector<Uint32>& result) {
result = std::vector<Uint32>();
std::vector<Uint32> out;
bool string2rgb(const std::string& s, std::vector<uint32_t>& result) {
result = std::vector<uint32_t>();
std::vector<uint32_t> out;
std::vector<std::string> rgb_vec = utils::split(s);
std::vector<std::string>::iterator c=rgb_vec.begin();
while(c!=rgb_vec.end())
{
Uint32 rgb_hex;
uint32_t rgb_hex;
if(c->length() != 6)
{
try {
@ -127,14 +127,14 @@ bool string2rgb(const std::string& s, std::vector<Uint32>& result) {
return true;
}
std::vector<Uint32> palette(color_range cr){
std::vector<uint32_t> palette(color_range cr){
// generate a color palette from a color range
std::vector<Uint32> temp,res;
std::set<Uint32> clist;
std::vector<uint32_t> temp,res;
std::set<uint32_t> clist;
// use blue to make master set of possible colors
for(int i=255;i!=0;i--){
int j=255-i;
Uint32 rgb = i;
uint32_t rgb = i;
temp.push_back(rgb);
rgb = (j << 16) + (j << 8) + 255;
temp.push_back(rgb);
@ -143,19 +143,19 @@ std::vector<Uint32> palette(color_range cr){
// Use recolor function to generate list of possible colors.
// Could use a special function, would be more efficient,
// but harder to maintain.
std::map<Uint32,Uint32> cmap = recolor_range(cr,temp);
for(std::map<Uint32,Uint32>::const_iterator k=cmap.begin(); k!=cmap.end();++k){
std::map<uint32_t,uint32_t> cmap = recolor_range(cr,temp);
for(std::map<uint32_t,uint32_t>::const_iterator k=cmap.begin(); k!=cmap.end();++k){
clist.insert(k->second);
}
res.push_back(cmap[255]);
for(std::set<Uint32>::const_iterator c=clist.begin();c!=clist.end();++c){
for(std::set<uint32_t>::const_iterator c=clist.begin();c!=clist.end();++c){
if(*c != res[0] && *c!=0 && *c != 0x00FFFFFF){
res.push_back(*c);}
}
return(res);
}
std::string rgb2highlight(Uint32 rgb)
std::string rgb2highlight(uint32_t rgb)
{
std::ostringstream h;
// Must match what the escape interpreter for marked-up-text expects
@ -165,7 +165,7 @@ std::string rgb2highlight(Uint32 rgb)
return h.str();
}
std::string rgb2highlight_pango(Uint32 rgb)
std::string rgb2highlight_pango(uint32_t rgb)
{
std::ostringstream h;
// Must match what the pango expects
@ -189,7 +189,7 @@ std::string color_range::debug() const
{
std::ostringstream o;
static const Uint32 mask = 0x00FFFFFF;
static const uint32_t mask = 0x00FFFFFF;
o << std::hex << std::setfill('0')
<< '{' << std::setw(6) << (mid_ & mask)

View file

@ -27,13 +27,12 @@
#include <map>
#include <string>
#include <vector>
#include <SDL_types.h>
#include <stdint.h>
/* Convert comma separated string into rgb values.
* Return false and empty result on error.
*/
bool string2rgb(const std::string& s, std::vector<Uint32>& result);
bool string2rgb(const std::string& s, std::vector<uint32_t>& result);
/**
* A color range definition is made of four reference RGB colors, used
@ -61,13 +60,13 @@ public:
* @param min Minimum color shade
* @param rep High-contrast reference color
*/
color_range(Uint32 mid , Uint32 max = 0x00FFFFFF , Uint32 min = 0x00000000 , Uint32 rep = 0x00808080):mid_(mid),max_(max),min_(min),rep_(rep){}
color_range(uint32_t mid , uint32_t max = 0x00FFFFFF , uint32_t min = 0x00000000 , uint32_t rep = 0x00808080):mid_(mid),max_(max),min_(min),rep_(rep){}
/**
* Constructor, which expects four reference RGB colors.
* @param v STL vector with the four reference colors in order.
*/
color_range(const std::vector<Uint32>& v)
color_range(const std::vector<uint32_t>& v)
: mid_(v.size() ? v[0] : 0x00808080),
max_(v.size() > 1 ? v[1] : 0x00FFFFFF),
min_(v.size() > 2 ? v[2] : 0x00000000),
@ -79,13 +78,13 @@ public:
color_range() : mid_(0x00808080), max_(0x00FFFFFF), min_(0x00000000), rep_(0x00808080) {}
/** Average color shade. */
Uint32 mid() const{return(mid_);}
uint32_t mid() const{return(mid_);}
/** Maximum color shade. */
Uint32 max() const{return(max_);}
uint32_t max() const{return(max_);}
/** Minimum color shade. */
Uint32 min() const{return(min_);}
uint32_t min() const{return(min_);}
/** High-contrast shade, intended for the minimap markers. */
Uint32 rep() const{return(rep_);}
uint32_t rep() const{return(rep_);}
bool operator<(const color_range& b) const
{
@ -106,13 +105,13 @@ public:
std::string debug() const;
private:
Uint32 mid_ , max_ , min_ , rep_;
uint32_t mid_ , max_ , min_ , rep_;
};
/**
* Creates a reference color palette from a color range.
*/
std::vector<Uint32> palette(color_range cr);
std::vector<uint32_t> palette(color_range cr);
/**
* Converts a source palette using the specified color_range object.
@ -123,17 +122,17 @@ std::vector<Uint32> palette(color_range cr);
* @return A STL map of colors, with the keys being source palette elements, and the values
* are the result of applying the color range conversion on it.
*/
std::map<Uint32, Uint32> recolor_range(const color_range& new_rgb, const std::vector<Uint32>& old_rgb);
std::map<uint32_t, uint32_t> recolor_range(const color_range& new_rgb, const std::vector<uint32_t>& old_rgb);
/**
* Converts a color value to WML text markup syntax for highlighting.
* For example, 0x00CC00CC becomes "<204,0,204>".
*/
std::string rgb2highlight(Uint32 rgb);
std::string rgb2highlight(uint32_t rgb);
/**
* Converts a color value to WML text markup syntax for highlighting.
* For example, 0x00CC00CC becomes "#CC00CC".
*/
std::string rgb2highlight_pango(Uint32 rgb);
std::string rgb2highlight_pango(uint32_t rgb);
#endif

View file

@ -118,11 +118,11 @@ namespace game_config
std::string shroud_prefix, fog_prefix;
std::string flag_rgb;
std::vector<Uint32> red_green_scale;
std::vector<Uint32> red_green_scale_text;
std::vector<uint32_t> red_green_scale;
std::vector<uint32_t> red_green_scale_text;
static std::vector<Uint32> blue_white_scale;
static std::vector<Uint32> blue_white_scale_text;
static std::vector<uint32_t> blue_white_scale;
static std::vector<uint32_t> blue_white_scale_text;
double hp_bar_scaling = 0.666;
double xp_bar_scaling = 0.5;
@ -136,7 +136,7 @@ namespace game_config
std::map<std::string, color_range > team_rgb_range;
std::map<std::string, t_string > team_rgb_name;
std::map<std::string, std::vector<Uint32> > team_rgb_colors;
std::map<std::string, std::vector<uint32_t> > team_rgb_colors;
const version_info wesnoth_version(VERSION);
const version_info min_savegame_version(MIN_SAVEGAME_VERSION);
@ -341,7 +341,7 @@ namespace game_config
continue;
}
std::string id = *a1;
std::vector<Uint32> temp;
std::vector<uint32_t> temp;
if(!string2rgb(*a2, temp)) {
std::stringstream ss;
ss << "can't parse color string:\n" << teamC.debug() << "\n";
@ -353,7 +353,7 @@ namespace game_config
LOG_NG << "registered color range '" << id << "': " << team_rgb_range[id].debug() << '\n';
//generate palette of same name;
std::vector<Uint32> tp = palette(team_rgb_range[id]);
std::vector<uint32_t> tp = palette(team_rgb_range[id]);
if (tp.empty()) {
continue;
}
@ -364,7 +364,7 @@ namespace game_config
{
for (const config::attribute &rgb : cp.attribute_range())
{
std::vector<Uint32> temp;
std::vector<uint32_t> temp;
if(!string2rgb(rgb.second, temp)) {
ERR_NG << "Invalid color palette: " << rgb.second << std::endl;
}
@ -378,7 +378,7 @@ namespace game_config
{
std::map<std::string, color_range>::const_iterator i = team_rgb_range.find(name);
if(i == team_rgb_range.end()) {
std::vector<Uint32> temp;
std::vector<uint32_t> temp;
if(!string2rgb(name, temp)) {
throw config::error(_("Invalid color range: ") + name);
}
@ -388,13 +388,13 @@ namespace game_config
return i->second;
}
const std::vector<Uint32>& tc_info(const std::string& name)
const std::vector<uint32_t>& tc_info(const std::string& name)
{
std::map<std::string, std::vector<Uint32> >::const_iterator i = team_rgb_colors.find(name);
std::map<std::string, std::vector<uint32_t> >::const_iterator i = team_rgb_colors.find(name);
if(i == team_rgb_colors.end()) {
std::vector<Uint32> temp;
std::vector<uint32_t> temp;
if(!string2rgb(name, temp)) {
static std::vector<Uint32> stv;
static std::vector<uint32_t> stv;
ERR_NG << "Invalid color palette: " << name << std::endl;
return stv;
}
@ -404,16 +404,16 @@ namespace game_config
return i->second;
}
Uint32 red_to_green(int val, bool for_text){
const std::vector<Uint32>& color_scale =
uint32_t red_to_green(int val, bool for_text){
const std::vector<uint32_t>& color_scale =
for_text ? red_green_scale_text : red_green_scale;
val = std::max<int>(0, std::min<int>(val, 100));
int lvl = (color_scale.size()-1) * val / 100;
return color_scale[lvl];
}
Uint32 blue_to_white(int val, bool for_text){
const std::vector<Uint32>& color_scale =
uint32_t blue_to_white(int val, bool for_text){
const std::vector<uint32_t>& color_scale =
for_text ? blue_white_scale_text : blue_white_scale;
val = std::max<int>(0, std::min<int>(val, 100));
int lvl = (color_scale.size()-1) * val / 100;

View file

@ -15,8 +15,12 @@
#ifndef SEND_RECEIVE_WML_HELPERS_HPP
#define SEND_RECEIVE_WML_HELPERS_HPP
#include <sys/sendfile.h>
#include "config.h"
#include "server_base.hpp"
#include "simple_wml.hpp"
#include "filesystem.hpp"
template<typename Handler, typename ErrorHandler>
struct handle_doc
@ -72,13 +76,95 @@ static void null_handler(socket_ptr)
{
}
#ifdef HAVE_SENDFILE
template <typename Handler, typename ErrorHandler>
struct sendfile_op
{
socket_ptr sock_;
int fd_;
Handler handler_;
ErrorHandler error_handler_;
off_t offset_;
std::size_t total_bytes_transferred_;
// Function call operator meeting WriteHandler requirements.
// Used as the handler for the async_write_some operation.
void operator()(boost::system::error_code ec, std::size_t)
{
// Put the underlying socket into non-blocking mode.
if (!ec)
if (!sock_->native_non_blocking())
sock_->native_non_blocking(true, ec);
if (!ec)
{
for (;;)
{
// Try the system call.
errno = 0;
int n = ::sendfile(sock_->native_handle(), fd_, &offset_, 65536);
ec = boost::system::error_code(n < 0 ? errno : 0,
boost::asio::error::get_system_category());
total_bytes_transferred_ += ec ? 0 : n;
// Retry operation immediately if interrupted by signal.
if (ec == boost::asio::error::interrupted)
continue;
// Check if we need to run the operation again.
if (ec == boost::asio::error::would_block
|| ec == boost::asio::error::try_again)
{
// We have to wait for the socket to become ready again.
sock_->async_write_some(boost::asio::null_buffers(), *this);
return;
}
if (ec || n == 0)
{
// An error occurred, or we have reached the end of the file.
// Either way we must exit the loop so we can call the handler.
break;
}
// Loop around to try calling sendfile again.
}
}
close(fd_);
if(ec)
error_handler_(sock_);
else
handler_(sock_);
}
};
template<typename Handler, typename ErrorHandler>
void async_send_file(socket_ptr socket, const std::string& filename, Handler handler, ErrorHandler error_handler)
{
std::vector<boost::asio::const_buffer> buffers;
size_t filesize = filesystem::file_size(filename);
int in_file(open(filename.c_str(), O_RDONLY));
sendfile_op<Handler, ErrorHandler> op = { socket, in_file, handler, error_handler, 0, 0 };
handle_doc<Handler, ErrorHandler> handle_send_doc(socket, handler, error_handler, filesize, nullptr);
buffers.push_back(boost::asio::buffer(handle_send_doc.data_size->buf, 4));
async_write(*socket, buffers, op);
}
#endif
template<typename Handler>
void async_send_doc(socket_ptr socket, simple_wml::document& doc, Handler handler)
inline void async_send_doc(socket_ptr socket, simple_wml::document& doc, Handler handler)
{
async_send_doc(socket, doc, handler, null_handler);
}
static void async_send_doc(socket_ptr socket, simple_wml::document& doc)
inline void async_send_doc(socket_ptr socket, simple_wml::document& doc)
{
async_send_doc(socket, doc, null_handler, null_handler);
}
@ -120,14 +206,14 @@ struct handle_receive_doc : public handle_doc<Handler, ErrorHandler>
};
template<typename Handler, typename ErrorHandler>
void async_receive_doc(socket_ptr socket, Handler handler, ErrorHandler error_handler)
inline void async_receive_doc(socket_ptr socket, Handler handler, ErrorHandler error_handler)
{
handle_receive_doc<Handler, ErrorHandler> handle_receive_doc(socket, handler, error_handler);
async_read(*socket, boost::asio::buffer(handle_receive_doc.data_size->buf, 4), handle_receive_doc);
}
template<typename Handler>
void async_receive_doc(socket_ptr socket, Handler handler)
inline void async_receive_doc(socket_ptr socket, Handler handler)
{
async_receive_doc(socket, handler, null_handler);
}

View file

@ -323,7 +323,6 @@ void server::handle_read_from_fifo(const boost::system::error_code& error, std::
}
read_from_fifo();
}
#endif

View file

@ -186,3 +186,11 @@ void async_send_warning(socket_ptr socket, const std::string& msg, const char* w
async_send_doc(socket, doc);
}
void async_send_message(socket_ptr socket, const std::string& msg)
{
simple_wml::document doc;
doc.root().add_child("message").set_attr_dup("message", msg.c_str());
async_send_doc(socket, doc);
}

View file

@ -76,5 +76,6 @@ bool check_error(const boost::system::error_code& error, socket_ptr socket);
void async_send_error(socket_ptr socket, const std::string& msg, const char* error_code = "");
void async_send_warning(socket_ptr socket, const std::string& msg, const char* warning_code = "");
void async_send_message(socket_ptr socket, const std::string& msg);
#endif // SERVER_BASE_HPP