diff options
Diffstat (limited to 'src/dedicated')
-rw-r--r-- | src/dedicated/Makefile.am | 7 | ||||
-rw-r--r-- | src/dedicated/console.cc | 485 | ||||
-rw-r--r-- | src/dedicated/console.h | 66 | ||||
-rw-r--r-- | src/dedicated/server.cc | 123 | ||||
-rw-r--r-- | src/dedicated/server.h | 19 |
5 files changed, 700 insertions, 0 deletions
diff --git a/src/dedicated/Makefile.am b/src/dedicated/Makefile.am new file mode 100644 index 0000000..bc2f741 --- /dev/null +++ b/src/dedicated/Makefile.am @@ -0,0 +1,7 @@ +METASOURCES = AUTO +libserver_la_SOURCES = console.cc server.cc +noinst_HEADERS = console.h server.h +noinst_LTLIBRARIES = libserver.la +INCLUDES = -I$(top_srcdir)/src +libserver_la_LDFLAGS = -avoid-version -no-undefined +libserver_la_LIBADD = $(top_builddir)/src/core/libcore.la diff --git a/src/dedicated/console.cc b/src/dedicated/console.cc new file mode 100644 index 0000000..9fdad4f --- /dev/null +++ b/src/dedicated/console.cc @@ -0,0 +1,485 @@ +/* + server/console.cc + This file is part of the Osirion project and is distributed under + the terms and conditions of the GNU General Public License version 2 +*/ + +#include <iostream> +#include <queue> +#include <string> +#include <sstream> +#include <iomanip> +#include <cmath> + +#include "server/console.h" +#include "core/core.h" +#include "sys/consoleinterface.h" +#include "auxiliary/functions.h" + +#ifdef HAVE_CURSES +#include <ncurses.h> +#endif + +namespace server { + +bool console_initialized = false; +bool console_updated = false; + +const size_t MAXHISTOLINES = 512; +float prev_time = 0; +Console server_console; + +#ifdef HAVE_CURSES +WINDOW *stdwin; +#endif + +Console *console() +{ + return (&server_console); +} + +void Console::init() +{ +#ifdef HAVE_CURSES + stdwin = initscr(); // initialize the ncurses window + cbreak(); // disable input line buffering + noecho(); // don't show typed characters + keypad(stdwin, TRUE); // enable special keys + nodelay(stdwin, TRUE); // non-blocking input + curs_set(1); // enable cursor + + if (has_colors() == TRUE) { + start_color(); + // this is ncurses-specific + use_default_colors(); + // COLOR_PAIR(0) is terminal default + init_pair(1, COLOR_RED, -1); + init_pair(2, COLOR_GREEN, -1); + init_pair(3, COLOR_YELLOW, -1); + init_pair(4, COLOR_BLUE, -1); + init_pair(5, COLOR_CYAN, -1); + init_pair(6, COLOR_MAGENTA, -1); + init_pair(7, COLOR_WHITE, -1); + init_pair(8, -1, -1); + } + console_initialized = true; + console_updated = true; + +#endif // HAVE_CURSES + + con_print << "^BInitializing console..." << std::endl; + +#ifdef HAVE_CURSES + server_console.history.clear(); + server_console.history.push_back(""); + server_console.history_pos = server_console.history.rbegin(); + server_console.input_pos = 0; + server_console.console_scroll = 0; + server_console.draw(); +#endif // HAVE_CURSES +} + +void Console::shutdown() +{ + con_print << "^BShutting down console..." << std::endl; + +#ifdef HAVE_CURSES + server_console.draw(); + endwin(); + console_initialized = false; + + server_console.dump(); +#endif +} + +Console::Console() +{ +} + +Console::~Console() +{ +} + +#ifdef HAVE_CURSES +void Console::dump() +{ + // dump console content + for (Queue::iterator it = log().begin(); it != log().end(); it++) { + sys::ConsoleInterface::print((*it)); + } +} + +void Console::resize() +{ + if (!console_initialized) + return; + + endwin(); + refresh(); + + draw(); +} + +void Console::print(const std::string & text) +{ + console_updated = true; +} + +void Console::set_color(const char *color_code) +{ + if (!has_colors()) + return; + + int color = 0; + bool bold = false; + + if (aux::is_base_color_code(color_code)) { + // base colors + // Default=0, Red=1, Green=2, Yellow=3, Blue=4, Cyan=5, Magenta=6, White=7 + + color = *(color_code+1) - '0'; + if (color == 3 || color == 7) + bold = true; + else + bold = false; + + } else if (aux::is_core_color_code(color_code)) { + + switch (*(color_code+1)) { + case 'N': // normal color + case 'D': // debug color + color = 0; + break; + case 'B': // bold color + color = 0; + bold = true; + break; + case 'W': // warning color + color = 3; + bold = true; + break; + case 'R': // error color + color = 1; + bold = true; + break; + case 'F': // fancy color + color = 2; + bold = true; + break; + } + } + + color_set(color, NULL); + if (bold) + attron(A_BOLD); + else + attroff(A_BOLD); + +} + +void Console::draw_background() +{ + color_set(0, NULL); + attroff(A_BOLD); + + bkgdset(' '); + clear(); + + // draw version string + color_set(2, NULL); + std::string versionstr("The Osirion Project "); + versionstr.append(core::version()); + int y = stdwin->_maxx - 1 - versionstr.size(); + if (y < 0) + y = 0; + mvaddnstr(0, y, versionstr.c_str(), stdwin->_maxx - 1); +} + +void Console::draw_status() +{ + if (core::game()) { + attroff(A_BOLD); + color_set(2, NULL); + int minutes = (int) floorf(core::game()->time() / 60.0f); + int seconds = (int) floorf( core::game()->time() - (float) minutes* 60.0f); + std::stringstream status; + status << "time " << std::setfill(' ') << std::setw(3) << minutes << ":" << std::setfill('0') << std::setw(2) << seconds; + mvaddnstr(0, 2, status.str().c_str(), status.str().size()); + } +} +void Console::draw_text() +{ + int w = stdwin->_maxx; + int h = stdwin->_maxy; + + if ((w < 3) || (h < 3)) + return; + + Queue lines; + + int height = stdwin->_maxy - 1; + int width = stdwin->_maxx - 1; + int bottom = (int) log().size() - console_scroll; + int current_line = 0; + + // parse console text, wrap long lines + for (Queue::iterator it = log().begin(); it != log().end() && current_line < bottom; it++) { + if (current_line >= bottom - height) { + std::string linedata(*it); + linedata += '\n'; + + std::string word; + int word_length = 0; + + std::string line; + int line_length = 0; + + const char *c = linedata.c_str(); + char pen = 'N'; + char wordpen = 'N'; + + while (*c) { + + // color code + if (aux::is_color_code(c)) { + c++; + pen = *c; + word += '^'; + word += pen; + } + + // new word, wrap if necessary + else if ((*c == '\n' ) || ( *c == ' ')) { + + if (line_length + word_length > width) { + if (line.size()) { + lines.push_back(line); + line.clear(); + line += '^'; + line += wordpen; + line_length = 0; + } + } + + line.append(word); + line_length += word_length; + + word.clear(); + word_length = 0; + wordpen = pen; + + // new line + if (*c == '\n' ) { + lines.push_back(line); + line.clear(); + line_length = 0; + // new word + } else if (*c == ' ' ) { + line += ' '; + line_length++; + } + } + + // new character + else { + word += *c; + word_length++; + + if (word_length == width) { + if (line.size()) { + lines.push_back(line); + line.clear(); + line += '^'; + line += wordpen; + line_length = 0; + } + + line.append(word); + line_length = word_length; + + word.clear(); + word_length = 0; + wordpen = pen; + } + } + + c++; + } + + } + current_line++; + } + + int y = stdwin->_maxy - 1; + color_set(0, NULL); + attroff(A_BOLD); + + for (Queue::reverse_iterator rit = lines.rbegin(); (y > 0) && (rit != lines.rend()); ++rit) { + + const char *c = (*rit).c_str(); + int x = 0; + + while (*c) { + if (aux::is_color_code(c)) { + set_color(c); + c++; + } else { + mvaddnstr(y, x, c, 1); + x++; + } + c++; + } + y--; + } +} + +void Console::draw_input() +{ + color_set(0, NULL); + attron(A_BOLD); + // draw input text + mvaddstr(stdwin->_maxy, 0, ">"); + mvaddstr(stdwin->_maxy, 1, (*history_pos).c_str()); + // fill the remainder with spaces + for (int i=1 + (*history_pos).size(); i < stdwin->_maxx; i++) + addch(' '); +} + +void Console::draw() +{ + if (!console_initialized) + return; + + draw_background(); + draw_status(); + draw_text(); + draw_input(); + move(stdwin->_maxy, 1 + input_pos); + wrefresh(stdwin); + + console_updated = false; +} + +void Console::frame() +{ + const size_t scroll_offset = 3; + History::reverse_iterator upit; + + if (!console_initialized) + return; + + bool input_updated = false; + + int key = wgetch(stdwin); + while (key != ERR) { + if (key == KEY_BACKSPACE || key == 8 || key == 127) { + if ((*history_pos).size() && input_pos) { + (*history_pos).erase(input_pos-1, 1); + input_pos--; + input_updated = true; + } + break; + } else if (key == KEY_STAB || key ==9) { + core::CommandBuffer::complete( (*history_pos), input_pos); + input_updated = true; + break; + } else if (key == KEY_LEFT) { + if (input_pos > 0) { + input_pos--; + } + input_updated = true; + break; + } else if (key == KEY_RIGHT) { + if (input_pos < (*history_pos).size()) { + input_pos++; + } + input_updated = true; + break; + } else if (key == KEY_HOME) { + input_pos = 0; + input_updated = true; + break; + } else if (key == KEY_END) { + input_pos = (*history_pos).size(); + input_updated = true; + break; + } else if (key == KEY_UP) { + upit = history_pos; + ++upit; + if (upit != history.rend()) { + history_pos = upit; + input_pos = (*history_pos).size(); + input_updated = true; + } + break; + } else if (key == KEY_DOWN) { + if (history_pos != history.rbegin()) { + --history_pos; + input_pos = (*history_pos).size(); + input_updated = true; + } + break; + } else if (key == KEY_ENTER || key == '\n') { + if ((*history_pos).size()) { + // store input into history + while (history.size() >= MAXHISTOLINES) { + history.pop_front(); + } + + core::cmd() << (*history_pos) << std::endl; + con_print << "^B>" << (*history_pos) << std::endl; + (*history.rbegin()) = (*history_pos); + + history.push_back(""); + history_pos = history.rbegin(); + input_pos = 0; + console_updated = true; + } + break; + } else if (key == KEY_PPAGE) { + console_scroll += scroll_offset; + if (console_scroll > log().size()) + console_scroll = log().size(); + console_updated = true; + break; + } else if (key == KEY_NPAGE) { + if (console_scroll > scroll_offset) + console_scroll -= scroll_offset; + else + console_scroll = 0; + console_updated = true; + break; + } else if ((key >= 32) && (key < 127) && ((*history_pos).size() < MAXCMDSIZE)) { + if (input_pos == (*history_pos).size()) { + (*history_pos) += (char)key; + } else { + (*history_pos).insert(input_pos, 1, (char)key); + } + input_pos++; + input_updated = true; + } + key = wgetch(stdwin); + } + + if (console_updated) { + draw(); + } else { + if (input_updated) { + draw_input(); + } + if (roundf(core::application()->time()) != prev_time) { + draw_status(); + prev_time = roundf(core::application()->time()); + input_updated = true; + } + if (input_updated) { + // move the cursor to input position + move(stdwin->_maxy, 1 + input_pos); + wrefresh(stdwin); + } + } +} +#endif // HAVE_CURSES + +} + diff --git a/src/dedicated/console.h b/src/dedicated/console.h new file mode 100644 index 0000000..13db3fc --- /dev/null +++ b/src/dedicated/console.h @@ -0,0 +1,66 @@ +/* + server/console.h + This file is part of the Osirion project and is distributed under + the terms of the GNU General Public License version 2 +*/ + +#ifndef __INCLUDED_SERVER_CONSOLE_H__ +#define __INCLUDED_SERVER_CONSOLE_H__ + +#include "sys/consoleinterface.h" + +namespace server { + +class Console : public sys::ConsoleInterface { +public: + Console(); + ~Console(); + + /// initialize the server console + static void init(); + /// shutdown the server console + static void shutdown(); + +#ifdef HAVE_CURSES + /// resize the console + virtual void resize(); + /// run one console frame + void frame(); + +protected: + /// draw the ncurses console + void draw(); + /// clear and draw background (ncurses) + void draw_background(); + /// draw status + void draw_status(); + /// draw the console text (ncurses) + void draw_text(); + /// draw the console input (ncurses) + void draw_input(); + /// dump console content to cout + void dump(); + /// print one line of text (do nothing) + virtual void print(const std::string & text); + +private: + typedef std::deque<std::string> History; + + /// set ncurses drawing color + void set_color(const char *color_code); + + // input history + History history; + History::reverse_iterator history_pos; + + size_t input_pos; + size_t console_scroll; +#endif +}; + +Console *console(); + +} + +#endif // __INCLUDED_SERVER_CONSOLE_H__ + diff --git a/src/dedicated/server.cc b/src/dedicated/server.cc new file mode 100644 index 0000000..a4424a0 --- /dev/null +++ b/src/dedicated/server.cc @@ -0,0 +1,123 @@ +/* + server/server.cc + This file is part of the Osirion project and is distributed under + the terms and conditions of the GNU General Public License version 2 +*/ + +#include <iostream> +#include <iomanip> + +#include "core/core.h" +#include "core/stats.h" +#include "core/timer.h" +#include "server/console.h" +#include "server/server.h" + +namespace server { + +//--- private definition ------------------------------------------ + +/// server Application implementation +class Server : public core::Application { +public: + /// initialize the server Application + virtual void init(int count, char **arguments); + + /// run the server Application + virtual void run(); + + /// shutdown the server Application + virtual void shutdown(); + + /// quit the server Application + virtual void quit(int status); +}; + + +Server app; + +//--- public ------------------------------------------------------ + +/// the server main loop +void main(int count, char **arguments) +{ + std::cout << core::name() << " " << core::version() << std::endl; + + for (int i =0; i < count; i++) + std::cout << arguments[i] << " "; + std::cout << std::endl; + + app.init(count, arguments); + app.run(); + app.shutdown(); +} + +//--- private ----------------------------------------------------- + +void Server::init(int count, char **arguments) +{ + con_print << "^BInitializing server..." << std::endl; + + core::Cvar::set("sv_dedicated", "1", core::Cvar::ReadOnly); + + core::Application::init(count, arguments); + + Console::init(); + + // the command line is in the buffer, execute it + core::CommandBuffer::exec(); + + std::string empty; + core::Application::connect(empty); +} + +void Server::run() +{ + float server_framerate = 1.0f / 25.0f; + + if (core::Cvar::sv_framerate->value()) + server_framerate = 1.0f / core::Cvar::sv_framerate->value(); + + core::Timer timer; + timer.mark(); + + while(connected()) { + frame(timer.timestamp()); +#ifdef HAVE_CURSES + console()->frame(); +#endif + } +} + +void Server::shutdown() +{ + con_print << "^BShutting down server..." << std::endl; + + float ratio = 0; + if (core::Stats::network_uncompressed_bytes_sent > 0) + ratio = 100.0f - floorf((float)core::Stats::network_bytes_sent / + (float) core::Stats::network_uncompressed_bytes_sent * 100.0f); + + int minutes = (int) floorf(time() / 60.0f); + int seconds = (int) floorf(time() - (float) minutes* 60.0f); + + con_debug << "Statistics:" << std::endl; + con_debug << " uptime " << std::setfill(' ') << std::setw(3) << minutes << ":" << std::setfill('0') << std::setw(2) << seconds << std::endl; + con_debug << " bytes sent " << std::setfill(' ') << std::setw(6) << core::Stats::network_bytes_sent / 1024 << " Kb" << std::endl; + con_debug << " bytes received " << std::setw(6) << core::Stats::network_bytes_received / 1024 << " Kb" << std::endl; + con_debug << " compression " << std::setw(6) << ratio << " %" << std::endl; + + core::Application::shutdown(); + + Console::shutdown(); + + quit(0); +} + +void Server::quit(int status) +{ + core::Application::quit(status); +} + +} // namespace server + diff --git a/src/dedicated/server.h b/src/dedicated/server.h new file mode 100644 index 0000000..4691e91 --- /dev/null +++ b/src/dedicated/server.h @@ -0,0 +1,19 @@ +/* + server/server.h + This file is part of the Osirion project and is distributed under + the terms and conditions of the GNU General Public License version 2 +*/ + +#ifndef __INCLUDED_SERVER_H__ +#define __INCLUDED_SERVER_H__ + +/// contains classes and functions to run a dedicated server +namespace server { + +/// the server main loop +void main(int count, char **arguments); + +} // namespace server + +#endif // __INCLUDED_SERVER_H__ + |