<commit_before>/*
   Copyright (c) 2011, The Mineserver Project
   All rights reserved.

  Redistribution and use in source and binary forms, with or without
  modification, are permitted provided that the following conditions are met:
  * Redistributions of source code must retain the above copyright
    notice, this list of conditions and the following disclaimer.
  * Redistributions in binary form must reproduce the above copyright
    notice, this list of conditions and the following disclaimer in the
    documentation and/or other materials provided with the distribution.
  * Neither the name of the The Mineserver Project nor the
    names of its contributors may be used to endorse or promote products
    derived from this software without specific prior written permission.

  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
  ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
  DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

#include <cstdlib>

#include <sys/stat.h>
#ifdef WIN32
#include <conio.h>
#include <winsock2.h>
#include <process.h>
#include <direct.h>
#else
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <cstring>
#include <netdb.h>
#include <unistd.h>
#include <sys/times.h>
#endif
#include <sys/types.h>
#include <fcntl.h>
#include <cassert>
#include <deque>
#include <map>
#include <iostream>
#include <sstream>
#include <fstream>
#include <algorithm>
#include <event.h>
#include <ctime>
#include <vector>
#include <zlib.h>
#include <signal.h>
#include <errno.h>
#include <sys/stat.h>

#include "constants.h"
#include "mineserver.h"
#include "logger.h"
#include "sockets.h"
#include "tools.h"
#include "map.h"
#include "user.h"
#include "chat.h"
#include "worldgen/mapgen.h"
#include "worldgen/nethergen.h"
#include "worldgen/heavengen.h"
#include "worldgen/biomegen.h"
#include "worldgen/eximgen.h"
#include "config.h"
#include "config/node.h"
#include "nbt.h"
#include "packets.h"
#include "physics.h"
#include "plugin.h"
#include "furnaceManager.h"
#include "cliScreen.h"
#include "hook.h"
#include "mob.h"
//#include "minecart.h"
#ifdef WIN32
static bool quit = false;
#endif

int setnonblock(int fd)
{
#ifdef WIN32
  u_long iMode = 1;
  ioctlsocket(fd, FIONBIO, &iMode);
#else
  int flags;

  flags  = fcntl(fd, F_GETFL);
  flags |= O_NONBLOCK;
  fcntl(fd, F_SETFL, flags);
#endif

  return 1;
}

// Handle signals
void sighandler(int sig_num)
{
  Mineserver::get()->stop();
}

#ifndef WIN32
void pipehandler(int sig_num)
{
  //Do nothing
}
#endif

std::string removeChar(std::string str, const char* c)
{
  const size_t loc = str.find(c);
  if (loc != std::string::npos)
  {
    return str.replace(loc, 1, "");
  }
  return str;
}

// What is the purpose of "code"? -- louisdx
int printHelp(int code)
{
  std::cout
      << "Mineserver " << VERSION << "\n"
      << "Usage: mineserver [CONFIG_FILE] [OVERRIDE]...\n"
      << "   or: mineserver -h|--help\n"
      << "\n"
      << "Syntax for overrides is: +VARIABLE=VALUE\n"
      << "\n"
      << "Examples:\n"
      << "  mineserver /etc/mineserver/config.cfg +system.path.home=\"/var/lib/mineserver\" +net.port=25565\n";
  return code;
}


int main(int argc, char* argv[])
{
  signal(SIGTERM, sighandler);
  signal(SIGINT, sighandler);

#ifndef WIN32
  signal(SIGPIPE, pipehandler);
#else
  signal(SIGBREAK, sighandler);
#endif

  std::srand((uint32_t)std::time(NULL));

  std::string cfg;
  std::vector<std::string> overrides;

  for (int i = 1; i < argc; i++)
  {
    const std::string arg(argv[i]);

    switch (arg[0])
    {
    case '-':   // option
      // we have only '-h' and '--help' now, so just return with help
      return printHelp(EXIT_SUCCESS);

    case '+':   // override
      overrides.push_back(arg.substr(1));
      break;

    default:    // otherwise, it is config file
      if (!cfg.empty())
      {
        LOG2(ERROR, "Only single CONFIG_FILE argument is allowed!");
        return EXIT_FAILURE;
      }
      cfg = arg;
      break;
    }
  }

  const std::string DEFAULT_HOME = pathExpandUser(std::string("~/.mineserver"));

  // create home and copy files if necessary
  // TODO: winex: this one needs cfg and overrides to be taken into account!
  Mineserver::get()->homePrepare(DEFAULT_HOME);

#ifdef DEBUG
  // using CONFIG_FILE in current directory is only for development purposes
  const std::string cfgcwd = std::string("./") + CONFIG_FILE;
  struct stat st;
  if ((stat(cfgcwd.c_str(), &st) == 0)) // && S_ISREG(st.st_mode) )
  {
    cfg = cfgcwd;
  }
#endif

  if (cfg.empty())
  {
    cfg = DEFAULT_HOME + '/' + CONFIG_FILE;
  }

  // load config
  Config* config = Mineserver::get()->config();
  if (!config->load(cfg))
  {
    return EXIT_FAILURE;
  }

  LOG2(INFO, "Using config: " + cfg);

  if (overrides.size())
  {
    std::stringstream override_config;
    for (size_t i = 0; i < overrides.size(); i++)
    {
      LOG2(INFO, "Overriden: " + overrides[i]);
      override_config << overrides[i] << ';' << std::endl;
    }
    // override config
    if (!config->load(override_config))
    {
      LOG2(ERROR, "Error when parsing overrides: maybe you forgot to doublequote string values?");
      return EXIT_FAILURE;
    }
  }

  bool ret = Mineserver::get()->init();

  if (!ret)
  {
    LOG2(ERROR, "Failed to start Mineserver!");
  }
  else
  {
    ret = Mineserver::get()->run();
  }

  Mineserver::get()->free();

  return ret ? EXIT_SUCCESS : EXIT_FAILURE;
}


Mineserver::Mineserver()
  :  m_socketlisten  (0),
     m_saveInterval  (0),
     m_lastSave      (std::time(NULL)),
     m_pvp_enabled   (false),
     m_damage_enabled(false),
     m_only_helmets  (false),
     m_running       (false),
     m_eventBase     (NULL),

     // core modules
     m_config        (new Config),
     m_screen        (new CliScreen),
     m_logger        (new Logger),

     m_plugin        (NULL),
     m_chat          (NULL),
     m_furnaceManager(NULL),
     m_packetHandler (NULL),
     m_inventory     (NULL),
     m_mobs          (NULL)
{
  memset(&m_listenEvent, 0, sizeof(event));
  initConstants();
}

Mineserver::~Mineserver()
{
  freeConstants();

  delete m_logger;
  delete m_screen;
  delete m_config;
}


bool Mineserver::init()
{
  // expand '~', '~user' in next vars
  bool error = false;
  const char* vars[] =
  {
    "system.path.data",
    "system.path.plugins",
    "system.path.home",
    "system.pid_file",
  };
  for (size_t i = 0; i < sizeof(vars) / sizeof(vars[0]); i++)
  {
    ConfigNode* node = config()->mData(vars[i]);
    if (!node)
    {
      LOG2(ERROR, std::string("Variable is missing: ") + vars[i]);
      error = true;
      continue;
    }
    if (node->type() != CONFIG_NODE_STRING)
    {
      LOG2(ERROR, std::string("Variable is not string: ") + vars[i]);
      error = true;
      continue;
    }

    const std::string newvalue = pathExpandUser(node->sData());
    node->setData(newvalue);
    LOG2(INFO, std::string(vars[i]) + " = \"" + newvalue + "\"");
  }
  if (error)
  {
    return false;
  }

  const std::string str = config()->sData("system.path.home");
#ifdef WIN32
  if (_chdir(str.c_str()) != 0)
#else
  if (chdir(str.c_str()) != 0)
#endif
  {
    LOG2(ERROR, "Failed to change working directory to: " + str);
    return false;
  }

  // Write PID to file
  std::ofstream pid_out((config()->sData("system.pid_file")).c_str());
  if (!pid_out.fail())
  {
#ifdef WIN32
    pid_out << _getpid();
#else
    pid_out << getpid();
#endif
  }
  pid_out.close();


  // screen::init() needs m_plugin
  m_plugin = new Plugin;

  init_plugin_api();

  if (config()->bData("system.interface.use_cli"))
  {
    // Init our Screen
    screen()->init(VERSION);
  }


  LOG2(INFO, "Welcome to Mineserver v" + VERSION);

  MapGen* mapgen = new MapGen;
  MapGen* nethergen = new NetherGen;
  MapGen* heavengen = new HeavenGen;
  MapGen* biomegen = new BiomeGen;
  MapGen* eximgen = new EximGen;
  m_mapGenNames.push_back(mapgen);
  m_mapGenNames.push_back(nethergen);
  m_mapGenNames.push_back(heavengen);
  m_mapGenNames.push_back(biomegen);
  m_mapGenNames.push_back(eximgen);

  m_saveInterval = m_config->iData("map.save_interval");

  m_only_helmets = m_config->bData("system.armour.helmet_strict");
  m_pvp_enabled = m_config->bData("system.pvp.enabled");
  m_damage_enabled = m_config->bData("system.damage.enabled");

  const char* key = "map.storage.nbt.directories"; // Prefix for worlds config
  if (m_config->has(key) && (m_config->type(key) == CONFIG_NODE_LIST))
  {
    std::list<std::string>* tmp = m_config->mData(key)->keys();
    int n = 0;
    for (std::list<std::string>::const_iterator it = tmp->begin(); it != tmp->end(); ++it)
    {
      m_map.push_back(new Map());
      Physics* phy = new Physics;
      phy->map = n;
      m_physics.push_back(phy);
      int k = m_config->iData((std::string(key) + ".") + (*it));
      if ((uint32_t)k >= m_mapGenNames.size())
      {
        std::ostringstream s;
        s << "Error! Mapgen number " << k << " in config. " << m_mapGenNames.size() << " Mapgens known";
        LOG2(INFO, s.str());
      }
      // WARNING: if k is too big this will be an access error! -- louisdx
      MapGen* m = m_mapGenNames[k];
      m_mapGen.push_back(m);
      n++;

    }
    delete tmp;
  }
  else
  {
    LOG2(WARNING, "Cannot find map.storage.nbt.directories.*");
  }

  if (m_map.size() == 0)
  {
    LOG2(ERROR, "No worlds in Config!");
    return false;
  }

  m_chat           = new Chat;
  m_furnaceManager = new FurnaceManager;
  m_packetHandler  = new PacketHandler;
  m_inventory      = new Inventory(m_config->sData("system.path.data") + '/' + "recipes", ".recipe", "ENABLED_RECIPES.cfg");
  m_mobs           = new Mobs;

  return true;
}

bool Mineserver::free()
{
  // Let the user know we're shutting the server down cleanly
  LOG2(INFO, "Shutting down...");

  // Close the cli session if its in use
  if (config() && config()->bData("system.interface.use_cli"))
  {
    screen()->end();
  }

  // Free memory
  for (std::vector<Map*>::size_type i = 0; i < m_map.size(); i++)
  {
    delete m_map[i];
    delete m_physics[i];
    delete m_mapGen[i];
  }

  delete m_chat;
  delete m_furnaceManager;
  delete m_packetHandler;
  delete m_inventory;
  delete m_mobs;

  if (m_plugin)
  {
    delete m_plugin;
    m_plugin = NULL;
  }

  // Remove the PID file
#ifdef WIN32
  _unlink((config()->sData("system.pid_file")).c_str());
#else
  unlink((config()->sData("system.pid_file")).c_str());
#endif

  return true;
}


event_base* Mineserver::getEventBase()
{
  return m_eventBase;
}

void Mineserver::saveAll()
{
  for (std::vector<Map*>::size_type i = 0; i < m_map.size(); i++)
  {
    m_map[i]->saveWholeMap();
  }
  saveAllPlayers();
}

void Mineserver::saveAllPlayers()
{
  if (users().size() == 0)
  {
    return;
  }
  for (int32_t i = users().size() - 1; i >= 0; i--)
  {
    if (users()[i]->logged)
    {
      users()[i]->saveData();
    }
  }
}


bool Mineserver::run()
{
  uint32_t starttime = (uint32_t)time(0);
  uint32_t tick      = (uint32_t)time(0);


  // load plugins
  if (config()->has("system.plugins") && (config()->type("system.plugins") == CONFIG_NODE_LIST))
  {
    std::list<std::string>* tmp = config()->mData("system.plugins")->keys();
    for (std::list<std::string>::const_iterator it = tmp->begin(); it != tmp->end(); ++it)
    {
      std::string path  = config()->sData("system.path.plugins");
      std::string name  = config()->sData("system.plugins." + (*it));
      std::string alias = *it;
      if (name[0] == '_')
      {
        path = "";
        alias = name;
        name = name.substr(1);
      }

      plugin()->loadPlugin(name, path, alias);
    }
    delete tmp;
  }

  // Initialize map
  for (int i = 0; i < (int)m_map.size(); i++)
  {
    physics(i)->enabled = (config()->bData("system.physics.enabled"));

    m_map[i]->init(i);
    if (config()->bData("map.generate_spawn.enabled"))
    {
      LOG2(INFO, "Generating spawn area...");
      int size = config()->iData("map.generate_spawn.size");
      bool show_progress = config()->bData("map.generate_spawn.show_progress");
#ifdef __FreeBSD__
      show_progress = false;
#endif

#ifdef WIN32
      DWORD t_begin = 0, t_end = 0;
#else
      clock_t t_begin = 0, t_end = 0;
#endif

      for (int x = -size; x <= size; x++)
      {
        if (show_progress)
        {
#ifdef WIN32
          t_begin = timeGetTime();
#else
          t_begin = clock();
#endif
        }
        for (int z = -size; z <= size; z++)
        {
          m_map[i]->loadMap(x, z);
        }

        if (show_progress)
        {
#ifdef WIN32
          t_end = timeGetTime();
          LOG2(INFO, dtos((x + size + 1) *(size * 2 + 1)) + "/" + dtos((size * 2 + 1) *(size * 2 + 1)) + " done. " + dtos((t_end - t_begin) / (size * 2 + 1)) + "ms per chunk");
#else
          t_end = clock();
          LOG2(INFO, dtos((x + size + 1) *(size * 2 + 1)) + "/" + dtos((size * 2 + 1) *(size * 2 + 1)) + " done. " + dtos(((t_end - t_begin) / (CLOCKS_PER_SEC / 1000)) / (size * 2 + 1)) + "ms per chunk");
#endif
        }
      }
    }
#ifdef DEBUG
    LOG(DEBUG, "Map", "Spawn area ready!");
#endif
  }

  // Initialize packethandler
  packetHandler()->init();

  // Load ip from config
  const std::string ip = config()->sData("net.ip");

  // Load port from config
  const int port = config()->iData("net.port");

#ifdef WIN32
  WSADATA wsaData;
  int iResult;
  // Initialize Winsock
  iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
  if (iResult != 0)
  {
    LOG2(ERROR, std::string("WSAStartup failed with error: " + iResult));
    return false;
  }
#endif

  struct sockaddr_in addresslisten;
  int reuse = 1;

  m_eventBase = reinterpret_cast<event_base*>(event_init());
#ifdef WIN32
  m_socketlisten = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
#else
  m_socketlisten = socket(AF_INET, SOCK_STREAM, 0);
#endif

  if (m_socketlisten < 0)
  {
    LOG2(ERROR, "Failed to create listen socket");
    return false;
  }

  memset(&addresslisten, 0, sizeof(addresslisten));

  addresslisten.sin_family      = AF_INET;
  addresslisten.sin_addr.s_addr = inet_addr(ip.c_str());
  addresslisten.sin_port        = htons(port);

  setsockopt(m_socketlisten, SOL_SOCKET, SO_REUSEADDR, (char*)&reuse, sizeof(reuse));

  // Bind to port
  if (bind(m_socketlisten, (struct sockaddr*)&addresslisten, sizeof(addresslisten)) < 0)
  {
    LOG2(ERROR, "Failed to bind to " + ip + ":" + dtos(port));
    return false;
  }

  if (listen(m_socketlisten, 5) < 0)
  {
    LOG2(ERROR, "Failed to listen to socket");
    return false;
  }

  setnonblock(m_socketlisten);
  event_set(&m_listenEvent, m_socketlisten, EV_WRITE | EV_READ | EV_PERSIST, accept_callback, NULL);
  event_add(&m_listenEvent, NULL);

  if (ip == "0.0.0.0")
  {
    // Print all local IPs
    char name[255];
    gethostname(name, sizeof(name));
    struct hostent* hostinfo = gethostbyname(name);
    LOG2(INFO, "Listening on: ");
    int ipIndex = 0;
    while (hostinfo && hostinfo->h_addr_list[ipIndex])
    {
      const std::string ip(inet_ntoa(*(struct in_addr*)hostinfo->h_addr_list[ipIndex++]));
      LOG2(INFO, ip + ":" + dtos(port));<commit_msg>Moved "Listening to:" log message<commit_after>/*
   Copyright (c) 2011, The Mineserver Project
   All rights reserved.

  Redistribution and use in source and binary forms, with or without
  modification, are permitted provided that the following conditions are met:
  * Redistributions of source code must retain the above copyright
    notice, this list of conditions and the following disclaimer.
  * Redistributions in binary form must reproduce the above copyright
    notice, this list of conditions and the following disclaimer in the
    documentation and/or other materials provided with the distribution.
  * Neither the name of the The Mineserver Project nor the
    names of its contributors may be used to endorse or promote products
    derived from this software without specific prior written permission.

  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
  ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
  DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

#include <cstdlib>

#include <sys/stat.h>
#ifdef WIN32
#include <conio.h>
#include <winsock2.h>
#include <process.h>
#include <direct.h>
#else
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <cstring>
#include <netdb.h>
#include <unistd.h>
#include <sys/times.h>
#endif
#include <sys/types.h>
#include <fcntl.h>
#include <cassert>
#include <deque>
#include <map>
#include <iostream>
#include <sstream>
#include <fstream>
#include <algorithm>
#include <event.h>
#include <ctime>
#include <vector>
#include <zlib.h>
#include <signal.h>
#include <errno.h>
#include <sys/stat.h>

#include "constants.h"
#include "mineserver.h"
#include "logger.h"
#include "sockets.h"
#include "tools.h"
#include "map.h"
#include "user.h"
#include "chat.h"
#include "worldgen/mapgen.h"
#include "worldgen/nethergen.h"
#include "worldgen/heavengen.h"
#include "worldgen/biomegen.h"
#include "worldgen/eximgen.h"
#include "config.h"
#include "config/node.h"
#include "nbt.h"
#include "packets.h"
#include "physics.h"
#include "plugin.h"
#include "furnaceManager.h"
#include "cliScreen.h"
#include "hook.h"
#include "mob.h"
//#include "minecart.h"
#ifdef WIN32
static bool quit = false;
#endif

int setnonblock(int fd)
{
#ifdef WIN32
  u_long iMode = 1;
  ioctlsocket(fd, FIONBIO, &iMode);
#else
  int flags;

  flags  = fcntl(fd, F_GETFL);
  flags |= O_NONBLOCK;
  fcntl(fd, F_SETFL, flags);
#endif

  return 1;
}

// Handle signals
void sighandler(int sig_num)
{
  Mineserver::get()->stop();
}

#ifndef WIN32
void pipehandler(int sig_num)
{
  //Do nothing
}
#endif

std::string removeChar(std::string str, const char* c)
{
  const size_t loc = str.find(c);
  if (loc != std::string::npos)
  {
    return str.replace(loc, 1, "");
  }
  return str;
}

// What is the purpose of "code"? -- louisdx
int printHelp(int code)
{
  std::cout
      << "Mineserver " << VERSION << "\n"
      << "Usage: mineserver [CONFIG_FILE] [OVERRIDE]...\n"
      << "   or: mineserver -h|--help\n"
      << "\n"
      << "Syntax for overrides is: +VARIABLE=VALUE\n"
      << "\n"
      << "Examples:\n"
      << "  mineserver /etc/mineserver/config.cfg +system.path.home=\"/var/lib/mineserver\" +net.port=25565\n";
  return code;
}


int main(int argc, char* argv[])
{
  signal(SIGTERM, sighandler);
  signal(SIGINT, sighandler);

#ifndef WIN32
  signal(SIGPIPE, pipehandler);
#else
  signal(SIGBREAK, sighandler);
#endif

  std::srand((uint32_t)std::time(NULL));

  std::string cfg;
  std::vector<std::string> overrides;

  for (int i = 1; i < argc; i++)
  {
    const std::string arg(argv[i]);

    switch (arg[0])
    {
    case '-':   // option
      // we have only '-h' and '--help' now, so just return with help
      return printHelp(EXIT_SUCCESS);

    case '+':   // override
      overrides.push_back(arg.substr(1));
      break;

    default:    // otherwise, it is config file
      if (!cfg.empty())
      {
        LOG2(ERROR, "Only single CONFIG_FILE argument is allowed!");
        return EXIT_FAILURE;
      }
      cfg = arg;
      break;
    }
  }

  const std::string DEFAULT_HOME = pathExpandUser(std::string("~/.mineserver"));

  // create home and copy files if necessary
  // TODO: winex: this one needs cfg and overrides to be taken into account!
  Mineserver::get()->homePrepare(DEFAULT_HOME);

#ifdef DEBUG
  // using CONFIG_FILE in current directory is only for development purposes
  const std::string cfgcwd = std::string("./") + CONFIG_FILE;
  struct stat st;
  if ((stat(cfgcwd.c_str(), &st) == 0)) // && S_ISREG(st.st_mode) )
  {
    cfg = cfgcwd;
  }
#endif

  if (cfg.empty())
  {
    cfg = DEFAULT_HOME + '/' + CONFIG_FILE;
  }

  // load config
  Config* config = Mineserver::get()->config();
  if (!config->load(cfg))
  {
    return EXIT_FAILURE;
  }

  LOG2(INFO, "Using config: " + cfg);

  if (overrides.size())
  {
    std::stringstream override_config;
    for (size_t i = 0; i < overrides.size(); i++)
    {
      LOG2(INFO, "Overriden: " + overrides[i]);
      override_config << overrides[i] << ';' << std::endl;
    }
    // override config
    if (!config->load(override_config))
    {
      LOG2(ERROR, "Error when parsing overrides: maybe you forgot to doublequote string values?");
      return EXIT_FAILURE;
    }
  }

  bool ret = Mineserver::get()->init();

  if (!ret)
  {
    LOG2(ERROR, "Failed to start Mineserver!");
  }
  else
  {
    ret = Mineserver::get()->run();
  }

  Mineserver::get()->free();

  return ret ? EXIT_SUCCESS : EXIT_FAILURE;
}


Mineserver::Mineserver()
  :  m_socketlisten  (0),
     m_saveInterval  (0),
     m_lastSave      (std::time(NULL)),
     m_pvp_enabled   (false),
     m_damage_enabled(false),
     m_only_helmets  (false),
     m_running       (false),
     m_eventBase     (NULL),

     // core modules
     m_config        (new Config),
     m_screen        (new CliScreen),
     m_logger        (new Logger),

     m_plugin        (NULL),
     m_chat          (NULL),
     m_furnaceManager(NULL),
     m_packetHandler (NULL),
     m_inventory     (NULL),
     m_mobs          (NULL)
{
  memset(&m_listenEvent, 0, sizeof(event));
  initConstants();
}

Mineserver::~Mineserver()
{
  freeConstants();

  delete m_logger;
  delete m_screen;
  delete m_config;
}


bool Mineserver::init()
{
  // expand '~', '~user' in next vars
  bool error = false;
  const char* vars[] =
  {
    "system.path.data",
    "system.path.plugins",
    "system.path.home",
    "system.pid_file",
  };
  for (size_t i = 0; i < sizeof(vars) / sizeof(vars[0]); i++)
  {
    ConfigNode* node = config()->mData(vars[i]);
    if (!node)
    {
      LOG2(ERROR, std::string("Variable is missing: ") + vars[i]);
      error = true;
      continue;
    }
    if (node->type() != CONFIG_NODE_STRING)
    {
      LOG2(ERROR, std::string("Variable is not string: ") + vars[i]);
      error = true;
      continue;
    }

    const std::string newvalue = pathExpandUser(node->sData());
    node->setData(newvalue);
    LOG2(INFO, std::string(vars[i]) + " = \"" + newvalue + "\"");
  }
  if (error)
  {
    return false;
  }

  const std::string str = config()->sData("system.path.home");
#ifdef WIN32
  if (_chdir(str.c_str()) != 0)
#else
  if (chdir(str.c_str()) != 0)
#endif
  {
    LOG2(ERROR, "Failed to change working directory to: " + str);
    return false;
  }

  // Write PID to file
  std::ofstream pid_out((config()->sData("system.pid_file")).c_str());
  if (!pid_out.fail())
  {
#ifdef WIN32
    pid_out << _getpid();
#else
    pid_out << getpid();
#endif
  }
  pid_out.close();


  // screen::init() needs m_plugin
  m_plugin = new Plugin;

  init_plugin_api();

  if (config()->bData("system.interface.use_cli"))
  {
    // Init our Screen
    screen()->init(VERSION);
  }


  LOG2(INFO, "Welcome to Mineserver v" + VERSION);

  MapGen* mapgen = new MapGen;
  MapGen* nethergen = new NetherGen;
  MapGen* heavengen = new HeavenGen;
  MapGen* biomegen = new BiomeGen;
  MapGen* eximgen = new EximGen;
  m_mapGenNames.push_back(mapgen);
  m_mapGenNames.push_back(nethergen);
  m_mapGenNames.push_back(heavengen);
  m_mapGenNames.push_back(biomegen);
  m_mapGenNames.push_back(eximgen);

  m_saveInterval = m_config->iData("map.save_interval");

  m_only_helmets = m_config->bData("system.armour.helmet_strict");
  m_pvp_enabled = m_config->bData("system.pvp.enabled");
  m_damage_enabled = m_config->bData("system.damage.enabled");

  const char* key = "map.storage.nbt.directories"; // Prefix for worlds config
  if (m_config->has(key) && (m_config->type(key) == CONFIG_NODE_LIST))
  {
    std::list<std::string>* tmp = m_config->mData(key)->keys();
    int n = 0;
    for (std::list<std::string>::const_iterator it = tmp->begin(); it != tmp->end(); ++it)
    {
      m_map.push_back(new Map());
      Physics* phy = new Physics;
      phy->map = n;
      m_physics.push_back(phy);
      int k = m_config->iData((std::string(key) + ".") + (*it));
      if ((uint32_t)k >= m_mapGenNames.size())
      {
        std::ostringstream s;
        s << "Error! Mapgen number " << k << " in config. " << m_mapGenNames.size() << " Mapgens known";
        LOG2(INFO, s.str());
      }
      // WARNING: if k is too big this will be an access error! -- louisdx
      MapGen* m = m_mapGenNames[k];
      m_mapGen.push_back(m);
      n++;

    }
    delete tmp;
  }
  else
  {
    LOG2(WARNING, "Cannot find map.storage.nbt.directories.*");
  }

  if (m_map.size() == 0)
  {
    LOG2(ERROR, "No worlds in Config!");
    return false;
  }

  m_chat           = new Chat;
  m_furnaceManager = new FurnaceManager;
  m_packetHandler  = new PacketHandler;
  m_inventory      = new Inventory(m_config->sData("system.path.data") + '/' + "recipes", ".recipe", "ENABLED_RECIPES.cfg");
  m_mobs           = new Mobs;

  return true;
}

bool Mineserver::free()
{
  // Let the user know we're shutting the server down cleanly
  LOG2(INFO, "Shutting down...");

  // Close the cli session if its in use
  if (config() && config()->bData("system.interface.use_cli"))
  {
    screen()->end();
  }

  // Free memory
  for (std::vector<Map*>::size_type i = 0; i < m_map.size(); i++)
  {
    delete m_map[i];
    delete m_physics[i];
    delete m_mapGen[i];
  }

  delete m_chat;
  delete m_furnaceManager;
  delete m_packetHandler;
  delete m_inventory;
  delete m_mobs;

  if (m_plugin)
  {
    delete m_plugin;
    m_plugin = NULL;
  }

  // Remove the PID file
#ifdef WIN32
  _unlink((config()->sData("system.pid_file")).c_str());
#else
  unlink((config()->sData("system.pid_file")).c_str());
#endif

  return true;
}


event_base* Mineserver::getEventBase()
{
  return m_eventBase;
}

void Mineserver::saveAll()
{
  for (std::vector<Map*>::size_type i = 0; i < m_map.size(); i++)
  {
    m_map[i]->saveWholeMap();
  }
  saveAllPlayers();
}

void Mineserver::saveAllPlayers()
{
  if (users().size() == 0)
  {
    return;
  }
  for (int32_t i = users().size() - 1; i >= 0; i--)
  {
    if (users()[i]->logged)
    {
      users()[i]->saveData();
    }
  }
}


bool Mineserver::run()
{
  uint32_t starttime = (uint32_t)time(0);
  uint32_t tick      = (uint32_t)time(0);


  // load plugins
  if (config()->has("system.plugins") && (config()->type("system.plugins") == CONFIG_NODE_LIST))
  {
    std::list<std::string>* tmp = config()->mData("system.plugins")->keys();
    for (std::list<std::string>::const_iterator it = tmp->begin(); it != tmp->end(); ++it)
    {
      std::string path  = config()->sData("system.path.plugins");
      std::string name  = config()->sData("system.plugins." + (*it));
      std::string alias = *it;
      if (name[0] == '_')
      {
        path = "";
        alias = name;
        name = name.substr(1);
      }

      plugin()->loadPlugin(name, path, alias);
    }
    delete tmp;
  }

  // Initialize map
  for (int i = 0; i < (int)m_map.size(); i++)
  {
    physics(i)->enabled = (config()->bData("system.physics.enabled"));

    m_map[i]->init(i);
    if (config()->bData("map.generate_spawn.enabled"))
    {
      LOG2(INFO, "Generating spawn area...");
      int size = config()->iData("map.generate_spawn.size");
      bool show_progress = config()->bData("map.generate_spawn.show_progress");
#ifdef __FreeBSD__
      show_progress = false;
#endif

#ifdef WIN32
      DWORD t_begin = 0, t_end = 0;
#else
      clock_t t_begin = 0, t_end = 0;
#endif

      for (int x = -size; x <= size; x++)
      {
        if (show_progress)
        {
#ifdef WIN32
          t_begin = timeGetTime();
#else
          t_begin = clock();
#endif
        }
        for (int z = -size; z <= size; z++)
        {
          m_map[i]->loadMap(x, z);
        }

        if (show_progress)
        {
#ifdef WIN32
          t_end = timeGetTime();
          LOG2(INFO, dtos((x + size + 1) *(size * 2 + 1)) + "/" + dtos((size * 2 + 1) *(size * 2 + 1)) + " done. " + dtos((t_end - t_begin) / (size * 2 + 1)) + "ms per chunk");
#else
          t_end = clock();
          LOG2(INFO, dtos((x + size + 1) *(size * 2 + 1)) + "/" + dtos((size * 2 + 1) *(size * 2 + 1)) + " done. " + dtos(((t_end - t_begin) / (CLOCKS_PER_SEC / 1000)) / (size * 2 + 1)) + "ms per chunk");
#endif
        }
      }
    }
#ifdef DEBUG
    LOG(DEBUG, "Map", "Spawn area ready!");
#endif
  }

  // Initialize packethandler
  packetHandler()->init();

  // Load ip from config
  const std::string ip = config()->sData("net.ip");

  // Load port from config
  const int port = config()->iData("net.port");

#ifdef WIN32
  WSADATA wsaData;
  int iResult;
  // Initialize Winsock
  iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
  if (iResult != 0)
  {
    LOG2(ERROR, std::string("WSAStartup failed with error: " + iResult));
    return false;
  }
#endif

  struct sockaddr_in addresslisten;
  int reuse = 1;

  m_eventBase = reinterpret_cast<event_base*>(event_init());
#ifdef WIN32
  m_socketlisten = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
#else
  m_socketlisten = socket(AF_INET, SOCK_STREAM, 0);
#endif

  if (m_socketlisten < 0)
  {
    LOG2(ERROR, "Failed to create listen socket");
    return false;
  }

  memset(&addresslisten, 0, sizeof(addresslisten));

  addresslisten.sin_family      = AF_INET;
  addresslisten.sin_addr.s_addr = inet_addr(ip.c_str());
  addresslisten.sin_port        = htons(port);

  setsockopt(m_socketlisten, SOL_SOCKET, SO_REUSEADDR, (char*)&reuse, sizeof(reuse));

  // Bind to port
  if (bind(m_socketlisten, (struct sockaddr*)&addresslisten, sizeof(addresslisten)) < 0)
  {
    LOG2(ERROR, "Failed to bind to " + ip + ":" + dtos(port));
    return false;
  }

  if (listen(m_socketlisten, 5) < 0)
  {
    LOG2(ERROR, "Failed to listen to socket");
    return false;
  }

  setnonblock(m_socketlisten);
  event_set(&m_listenEvent, m_socketlisten, EV_WRITE | EV_READ | EV_PERSIST, accept_callback, NULL);
  event_add(&m_listenEvent, NULL);

  LOG2(INFO, "Listening on: ");
  if (ip == "0.0.0.0")
  {
    // Print all local IPs
    char name[255];
    gethostname(name, sizeof(name));
    struct hostent* hostinfo = gethostbyname(name);
    int ipIndex = 0;
    while (hostinfo && hostinfo->h_addr_list[ipIndex])
    {
      const std::string ip(inet_ntoa(*(struct in_addr*)hostinfo->h_addr_list[ipIndex++]));
      LOG2(INFO, ip + ":" + dtos(port));
{'fraction_non_alphanumeric': 0.1563985548583381, 'fraction_numerical': 0.00912721049629207, 'mean_word_length': 3.142707102533806, 'pattern_counts': {'":': 4, '<': 179, '<?xml version=': 0, '>': 195, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 2, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2052344', 'n_tokens_mistral': 11639, 'n_tokens_neox': 11176, 'n_words': 2773}