<commit_before>#include "CurlClient.h"

#include "BasicAuth.h"

#include <cstdio>
#include <cstring>
#include <cassert>
#include <cctype>
#include <iostream>
#include <pthread.h>

using namespace std;

#include <curl/curl.h>

static bool is_initialized = false;

class CurlClient : public HTTPClient {
 public:
  CurlClient(const std::string & _interface, const std::string & _user_agent, bool _enable_cookies = true, bool _enable_keepalive = true)
    : HTTPClient(_user_agent, _enable_cookies, _enable_keepalive),
      interface_name(_interface)
  {
    assert(is_initialized);
  }

  ~CurlClient() {
    if (curl) curl_easy_cleanup(curl);
  }

  void request(const HTTPRequest & req, const Authorization & auth, HTTPClientInterface & callback) override {
#if 0
    if (req.getURI().empty()) {
      return HTTPResponse(0, "empty URI");
    }
#endif
    if (!initialize()) {
      // return HTTPResponse(0, "Unable to initialize client");
      return;
    }

    // string ascii_uri = encodeIDN(uri);
    // if (ascii_uri.empty()) {
    //   cerr << "failed to convert url " << uri << endl;
    //   assert(0);
    // }

    struct curl_slist * headers = 0;
    if (req.getType() == HTTPRequest::POST) {
      if (!req.getContentType().empty()) {
	string h = "Content-type: ";
	h += req.getContentType();
	headers = curl_slist_append(headers, h.c_str());
      }
    }

    const BasicAuth * basic = dynamic_cast<const BasicAuth *>(&auth);
    if (basic) {
      string s = basic->getUsername();
      s += ':';
      s += basic->getPassword();
      curl_easy_setopt(curl, CURLOPT_USERPWD, s.c_str());
    } else {
      string auth_header = auth.createHeader();
      if (!auth_header.empty()) {
	string s = auth.getHeaderName();
	s += ": ";
	s += auth_header;
	headers = curl_slist_append(headers, s.c_str());
      }
    }

    map<string, string> combined_headers;
    for (auto & hd : default_headers) {
      combined_headers[hd.first] = hd.second;
    }
    for (auto & hd : req.getHeaders()) {
      combined_headers[hd.first] = hd.second;
    }
    for (auto & hd : combined_headers) {
      string s = hd.first;
      s += ": ";
      s += hd.second;
      headers = curl_slist_append(headers, s.c_str());
    }

    if (req.getType() == HTTPRequest::POST) {
      curl_easy_setopt(curl, CURLOPT_HTTPGET, 0);
      curl_easy_setopt(curl, CURLOPT_POST, 1);
      curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, req.getContent().size());
      curl_easy_setopt(curl, CURLOPT_POSTFIELDS, req.getContent().data());
    } else {
      curl_easy_setopt(curl, CURLOPT_POST, 0);
      curl_easy_setopt(curl, CURLOPT_HTTPGET, 1);
    }
    curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, req.getFollowLocation());
    curl_easy_setopt(curl, CURLOPT_URL, req.getURI().c_str());
    curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);

    curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 30);
    curl_easy_setopt(curl, CURLOPT_TIMEOUT, req.getTimeout());

    curl_easy_setopt(curl, CURLOPT_HEADERDATA, &callback);
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, &callback);
    curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, &callback);

    CURLcode res = curl_easy_perform(curl);
    // if (res != 0) {
    //   response.setResultCode(500);
    //   response.setErrorText(curl_easy_strerror(res));
    // }

    callback.handleDisconnect();

    if (headers) {
      curl_easy_setopt(curl, CURLOPT_HTTPHEADER, 0);
      curl_slist_free_all(headers);
    }

    curl_easy_setopt(curl, CURLOPT_HEADERDATA, 0);
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, 0);
    curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, 0);
    curl_easy_setopt(curl, CURLOPT_USERPWD, 0);
    curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, 0);
    curl_easy_setopt(curl, CURLOPT_POSTFIELDS, 0);
  }

  void clearCookies() override {
    if (curl) {
      curl_easy_setopt(curl, CURLOPT_URL, "ALL"); // what does this do?
    }
  }

 protected:
  bool initialize() {
    if (!curl) {
      curl = curl_easy_init();
      assert(curl);
      if (curl) {
#ifndef WIN32
	if (!interface_name.empty()) {
	  int r = curl_easy_setopt(curl, CURLOPT_INTERFACE, interface_name.c_str());
	  assert(r != CURLE_INTERFACE_FAILED);
	}
#endif
	curl_easy_setopt(curl, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
	curl_easy_setopt(curl, CURLOPT_USERAGENT, user_agent.c_str());
	curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
	curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data_func);
	curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, progress_func);
	curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, headers_func);
	curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0);
	curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0);
	curl_easy_setopt(curl, CURLOPT_DNS_CACHE_TIMEOUT, 600);
	curl_easy_setopt(curl, CURLOPT_VERBOSE, 0);
	curl_easy_setopt(curl, CURLOPT_ACCEPT_ENCODING, "gzip, deflate");

	if (!cookie_jar.empty()) {
	  curl_easy_setopt(curl, CURLOPT_COOKIEFILE, cookie_jar.c_str());
	  curl_easy_setopt(curl, CURLOPT_COOKIEJAR, cookie_jar.c_str());
	} else if (enable_cookies) {
	  curl_easy_setopt(curl, CURLOPT_COOKIEFILE, "");
	}
	if (!enable_keepalive) {
	  curl_easy_setopt(curl, CURLOPT_FRESH_CONNECT, 1);
	  curl_easy_setopt(curl, CURLOPT_FORBID_REUSE, 1);
	  // curl_easy_setopt(curl, CURLOPT_MAXCONNECTS, 10);
	}
      }
    }

    return curl != 0;
  }

 private:
  static size_t write_data_func(void * buffer, size_t size, size_t nmemb, void *userp);
  static size_t headers_func(void * buffer, size_t size, size_t nmemb, void *userp);
  static int progress_func(void *  clientp, double dltotal, double dlnow, double ultotal, double ulnow);

  std::string interface_name;
  CURL * curl = 0;
};

size_t
CurlClient::write_data_func(void * buffer, size_t size, size_t nmemb, void * userp) {
  size_t s = size * nmemb;
  // Content-Type: multipart/x-mixed-replace; boundary=myboundary

  HTTPClientInterface * callback = (HTTPClientInterface *)(userp);
  assert(callback);
  return callback->handleChunk(s, (const char *)buffer) ? s : 0;
}

size_t
CurlClient::headers_func(void * buffer, size_t size, size_t nmemb, void *userp) {
  HTTPClientInterface * callback = (HTTPClientInterface *)(userp);

  size_t s = size * nmemb;
  bool keep_running = true;
  if (buffer) {
    string input((const char*)buffer, s);

    if (input.compare(0, 5, "HTTP/") == 0) {
      auto pos1 = input.find_first_of(' ');
      if (pos1 != string::npos) {
	auto pos2 = input.find_first_of(' ', pos1 + 1);
	if (pos2 != string::npos) {
	  auto s2 = input.substr(pos1, pos2 - pos1);
	  int code = atoi(s2.c_str());
	  callback->handleResultCode(code);
	}
      }
    } else {
      int pos1 = 0;
      for ( ; pos1 < input.size() && input[pos1] != ':'; pos1++) { }
      int pos2 = input[pos1] == ':' ? pos1 + 1 : pos1;
      for (; pos2 < input.size() && isspace(input[pos2]); pos2++) { }
      int pos3 = input.size();
      for (; pos3 > 0 && isspace(input[pos3 - 1]); pos3--) { }
      std::string key = input.substr(0, pos1);
      std::string value = input.substr(pos2, pos3 - pos2);

      if (key == "Location" && !callback->handleRedirectUrl(value)) {
	keep_running = false;
      }
      callback->handleHeader(key, value);
    }
  }

  return keep_running ? s : 0;
}

int
CurlClient::progress_func(void * clientp, double dltotal, double dlnow, double ultotal, double ulnow) {
  HTTPClientInterface * callback = (HTTPClientInterface *)(clientp);
  assert(callback);
  return callback->onIdle(true) ? 0 : 1;
}

#ifndef __APPLE__
static pthread_mutex_t *lockarray;

#include <openssl/crypto.h>

static void lock_callback(int mode, int type, const char *file, int line) {
  (void)file;
  (void)line;
  if (mode & CRYPTO_LOCK) {
    pthread_mutex_lock(&(lockarray[type]));
  } else {
    pthread_mutex_unlock(&(lockarray[type]));
  }
}

static unsigned long thread_id(void) {
  unsigned long ret;

#ifdef WIN32
  ret = (unsigned long)(pthread_self().p);
#else
  ret = (unsigned long)pthread_self();
#endif
  return(ret);
}

static void init_locks(void) {
  int i;

  lockarray=(pthread_mutex_t *)OPENSSL_malloc(CRYPTO_num_locks() *
					      sizeof(pthread_mutex_t));
  for (i=0; i<CRYPTO_num_locks(); i++) {
    pthread_mutex_init(&(lockarray[i]),NULL);
  }

  CRYPTO_set_id_callback((unsigned long (*)())thread_id);
  CRYPTO_set_locking_callback(lock_callback); // (void (*)())lock_callback);
}

static void kill_locks(void)
{
  int i;

  CRYPTO_set_locking_callback(NULL);
  for (i=0; i<CRYPTO_num_locks(); i++)
    pthread_mutex_destroy(&(lockarray[i]));

  OPENSSL_free(lockarray);
}
#endif

#if 0
#include <gcrypt.h>
#include <errno.h>
#include <gnutls/gnutls.h>

GCRY_THREAD_OPTION_PTHREAD_IMPL;
#endif

std::unique_ptr<HTTPClient>
CurlClientFactory::createClient2(const std::string & _user_agent, bool _enable_cookies, bool _enable_keepalive) {
  return std::unique_ptr<CurlClient>(new CurlClient("", _user_agent, _enable_cookies, _enable_keepalive));
}

void
CurlClientFactory::globalInit() {
  is_initialized = true;

#ifndef __APPLE__
#if 0
  gcry_control(GCRYCTL_SET_THREAD_CBS, &gcry_threads_pthread);
  gnutls_global_init();
#else
  init_locks();
#endif
#endif

  curl_global_init(CURL_GLOBAL_DEFAULT);

#if 0
  thread_setup();
#endif
#if 0
  gnutls_global_init();
#endif
}

void
CurlClientFactory::globalCleanup() {
  is_initialized = false;

  curl_global_cleanup();
#ifndef __APPLE__
  kill_locks();
#endif
}
<commit_msg>disable timeout, set connect timeout<commit_after>#include "CurlClient.h"

#include "BasicAuth.h"

#include <cstdio>
#include <cstring>
#include <cassert>
#include <cctype>
#include <iostream>
#include <pthread.h>

using namespace std;

#include <curl/curl.h>

static bool is_initialized = false;

class CurlClient : public HTTPClient {
 public:
  CurlClient(const std::string & _interface, const std::string & _user_agent, bool _enable_cookies = true, bool _enable_keepalive = true)
    : HTTPClient(_user_agent, _enable_cookies, _enable_keepalive),
      interface_name(_interface)
  {
    assert(is_initialized);
  }

  ~CurlClient() {
    if (curl) curl_easy_cleanup(curl);
  }

  void request(const HTTPRequest & req, const Authorization & auth, HTTPClientInterface & callback) override {
#if 0
    if (req.getURI().empty()) {
      return HTTPResponse(0, "empty URI");
    }
#endif
    if (!initialize()) {
      // return HTTPResponse(0, "Unable to initialize client");
      return;
    }

    // string ascii_uri = encodeIDN(uri);
    // if (ascii_uri.empty()) {
    //   cerr << "failed to convert url " << uri << endl;
    //   assert(0);
    // }

    struct curl_slist * headers = 0;
    if (req.getType() == HTTPRequest::POST) {
      if (!req.getContentType().empty()) {
	string h = "Content-type: ";
	h += req.getContentType();
	headers = curl_slist_append(headers, h.c_str());
      }
    }

    const BasicAuth * basic = dynamic_cast<const BasicAuth *>(&auth);
    if (basic) {
      string s = basic->getUsername();
      s += ':';
      s += basic->getPassword();
      curl_easy_setopt(curl, CURLOPT_USERPWD, s.c_str());
    } else {
      string auth_header = auth.createHeader();
      if (!auth_header.empty()) {
	string s = auth.getHeaderName();
	s += ": ";
	s += auth_header;
	headers = curl_slist_append(headers, s.c_str());
      }
    }

    map<string, string> combined_headers;
    for (auto & hd : default_headers) {
      combined_headers[hd.first] = hd.second;
    }
    for (auto & hd : req.getHeaders()) {
      combined_headers[hd.first] = hd.second;
    }
    for (auto & hd : combined_headers) {
      string s = hd.first;
      s += ": ";
      s += hd.second;
      headers = curl_slist_append(headers, s.c_str());
    }

    if (req.getType() == HTTPRequest::POST) {
      curl_easy_setopt(curl, CURLOPT_HTTPGET, 0);
      curl_easy_setopt(curl, CURLOPT_POST, 1);
      curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, req.getContent().size());
      curl_easy_setopt(curl, CURLOPT_POSTFIELDS, req.getContent().data());
    } else {
      curl_easy_setopt(curl, CURLOPT_POST, 0);
      curl_easy_setopt(curl, CURLOPT_HTTPGET, 1);
    }
    curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, req.getFollowLocation());
    curl_easy_setopt(curl, CURLOPT_URL, req.getURI().c_str());
    curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);

    curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, req.getConnectTimeout());
    // curl_easy_setopt(curl, CURLOPT_TIMEOUT, req.getTimeout());

    curl_easy_setopt(curl, CURLOPT_HEADERDATA, &callback);
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, &callback);
    curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, &callback);

    CURLcode res = curl_easy_perform(curl);
    // if (res != 0) {
    //   response.setResultCode(500);
    //   response.setErrorText(curl_easy_strerror(res));
    // }

    callback.handleDisconnect();

    if (headers) {
      curl_easy_setopt(curl, CURLOPT_HTTPHEADER, 0);
      curl_slist_free_all(headers);
    }

    curl_easy_setopt(curl, CURLOPT_HEADERDATA, 0);
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, 0);
    curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, 0);
    curl_easy_setopt(curl, CURLOPT_USERPWD, 0);
    curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, 0);
    curl_easy_setopt(curl, CURLOPT_POSTFIELDS, 0);
  }

  void clearCookies() override {
    if (curl) {
      curl_easy_setopt(curl, CURLOPT_URL, "ALL"); // what does this do?
    }
  }

 protected:
  bool initialize() {
    if (!curl) {
      curl = curl_easy_init();
      assert(curl);
      if (curl) {
#ifndef WIN32
	if (!interface_name.empty()) {
	  int r = curl_easy_setopt(curl, CURLOPT_INTERFACE, interface_name.c_str());
	  assert(r != CURLE_INTERFACE_FAILED);
	}
#endif
	curl_easy_setopt(curl, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
	curl_easy_setopt(curl, CURLOPT_USERAGENT, user_agent.c_str());
	curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
	curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data_func);
	curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, progress_func);
	curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, headers_func);
	curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0);
	curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0);
	curl_easy_setopt(curl, CURLOPT_DNS_CACHE_TIMEOUT, 600);
	curl_easy_setopt(curl, CURLOPT_VERBOSE, 0);
	curl_easy_setopt(curl, CURLOPT_ACCEPT_ENCODING, "gzip, deflate");

	if (!cookie_jar.empty()) {
	  curl_easy_setopt(curl, CURLOPT_COOKIEFILE, cookie_jar.c_str());
	  curl_easy_setopt(curl, CURLOPT_COOKIEJAR, cookie_jar.c_str());
	} else if (enable_cookies) {
	  curl_easy_setopt(curl, CURLOPT_COOKIEFILE, "");
	}
	if (!enable_keepalive) {
	  curl_easy_setopt(curl, CURLOPT_FRESH_CONNECT, 1);
	  curl_easy_setopt(curl, CURLOPT_FORBID_REUSE, 1);
	  // curl_easy_setopt(curl, CURLOPT_MAXCONNECTS, 10);
	}
      }
    }

    return curl != 0;
  }

 private:
  static size_t write_data_func(void * buffer, size_t size, size_t nmemb, void *userp);
  static size_t headers_func(void * buffer, size_t size, size_t nmemb, void *userp);
  static int progress_func(void *  clientp, double dltotal, double dlnow, double ultotal, double ulnow);

  std::string interface_name;
  CURL * curl = 0;
};

size_t
CurlClient::write_data_func(void * buffer, size_t size, size_t nmemb, void * userp) {
  size_t s = size * nmemb;
  // Content-Type: multipart/x-mixed-replace; boundary=myboundary

  HTTPClientInterface * callback = (HTTPClientInterface *)(userp);
  assert(callback);
  return callback->handleChunk(s, (const char *)buffer) ? s : 0;
}

size_t
CurlClient::headers_func(void * buffer, size_t size, size_t nmemb, void *userp) {
  HTTPClientInterface * callback = (HTTPClientInterface *)(userp);

  size_t s = size * nmemb;
  bool keep_running = true;
  if (buffer) {
    string input((const char*)buffer, s);

    if (input.compare(0, 5, "HTTP/") == 0) {
      auto pos1 = input.find_first_of(' ');
      if (pos1 != string::npos) {
	auto pos2 = input.find_first_of(' ', pos1 + 1);
	if (pos2 != string::npos) {
	  auto s2 = input.substr(pos1, pos2 - pos1);
	  int code = atoi(s2.c_str());
	  callback->handleResultCode(code);
	}
      }
    } else {
      int pos1 = 0;
      for ( ; pos1 < input.size() && input[pos1] != ':'; pos1++) { }
      int pos2 = input[pos1] == ':' ? pos1 + 1 : pos1;
      for (; pos2 < input.size() && isspace(input[pos2]); pos2++) { }
      int pos3 = input.size();
      for (; pos3 > 0 && isspace(input[pos3 - 1]); pos3--) { }
      std::string key = input.substr(0, pos1);
      std::string value = input.substr(pos2, pos3 - pos2);

      if (key == "Location" && !callback->handleRedirectUrl(value)) {
	keep_running = false;
      }
      callback->handleHeader(key, value);
    }
  }

  return keep_running ? s : 0;
}

int
CurlClient::progress_func(void * clientp, double dltotal, double dlnow, double ultotal, double ulnow) {
  HTTPClientInterface * callback = (HTTPClientInterface *)(clientp);
  assert(callback);
  return callback->onIdle(true) ? 0 : 1;
}

#ifndef __APPLE__
static pthread_mutex_t *lockarray;

#include <openssl/crypto.h>

static void lock_callback(int mode, int type, const char *file, int line) {
  (void)file;
  (void)line;
  if (mode & CRYPTO_LOCK) {
    pthread_mutex_lock(&(lockarray[type]));
  } else {
    pthread_mutex_unlock(&(lockarray[type]));
  }
}

static unsigned long thread_id(void) {
  unsigned long ret;

#ifdef WIN32
  ret = (unsigned long)(pthread_self().p);
#else
  ret = (unsigned long)pthread_self();
#endif
  return(ret);
}

static void init_locks(void) {
  int i;

  lockarray=(pthread_mutex_t *)OPENSSL_malloc(CRYPTO_num_locks() *
					      sizeof(pthread_mutex_t));
  for (i=0; i<CRYPTO_num_locks(); i++) {
    pthread_mutex_init(&(lockarray[i]),NULL);
  }

  CRYPTO_set_id_callback((unsigned long (*)())thread_id);
  CRYPTO_set_locking_callback(lock_callback); // (void (*)())lock_callback);
}

static void kill_locks(void)
{
  int i;

  CRYPTO_set_locking_callback(NULL);
  for (i=0; i<CRYPTO_num_locks(); i++)
    pthread_mutex_destroy(&(lockarray[i]));

  OPENSSL_free(lockarray);
}
#endif

#if 0
#include <gcrypt.h>
#include <errno.h>
#include <gnutls/gnutls.h>

GCRY_THREAD_OPTION_PTHREAD_IMPL;
#endif

std::unique_ptr<HTTPClient>
CurlClientFactory::createClient2(const std::string & _user_agent, bool _enable_cookies, bool _enable_keepalive) {
  return std::unique_ptr<CurlClient>(new CurlClient("", _user_agent, _enable_cookies, _enable_keepalive));
}

void
CurlClientFactory::globalInit() {
  is_initialized = true;

#ifndef __APPLE__
#if 0
  gcry_control(GCRYCTL_SET_THREAD_CBS, &gcry_threads_pthread);
  gnutls_global_init();
#else
  init_locks();
#endif
#endif

  curl_global_init(CURL_GLOBAL_DEFAULT);

#if 0
  thread_setup();
#endif
#if 0
  gnutls_global_init();
#endif
}

void
CurlClientFactory::globalCleanup() {
  is_initialized = false;

  curl_global_cleanup();
#ifndef __APPLE__
  kill_locks();
#endif
}
<|endoftext|>


<commit_before>/*----------------------------------------------------------------------------*/
/* Copyright (c) FIRST 2015. All Rights Reserved.                             */
/* Open Source Software - may be modified and shared by FRC teams. The code   */
/* must be accompanied by the FIRST BSD license file in the root directory of */
/* the project.                                                               */
/*----------------------------------------------------------------------------*/

#include "Dispatcher.h"

#include "tcpsockets/TCPAcceptor.h"
#include "tcpsockets/TCPConnector.h"

using namespace nt;

#define DEBUG(str) puts(str)

std::unique_ptr<Dispatcher> Dispatcher::m_instance;

static NT_Type GetEntryType(unsigned int id) {
  // TODO
  return NT_UNASSIGNED;
}

Dispatcher::Dispatcher()
    : m_server(false),
      m_active(false),
      m_update_rate(100),
      m_do_flush(false),
      m_do_reconnect(false) {}

Dispatcher::~Dispatcher() { Stop(); }

void Dispatcher::StartServer(const char* listen_address, unsigned int port) {
  {
    std::lock_guard<std::mutex> lock(m_user_mutex);
    if (m_active) return;
    m_active = true;
  }
  m_server = true;
  m_dispatch_thread = std::thread(&Dispatcher::DispatchThreadMain, this);
  m_clientserver_thread =
      std::thread(&Dispatcher::ServerThreadMain, this, listen_address, port);
}

void Dispatcher::StartClient(const char* server_name, unsigned int port) {
  {
    std::lock_guard<std::mutex> lock(m_user_mutex);
    if (m_active) return;
    m_active = true;
  }
  m_server = false;
  m_dispatch_thread = std::thread(&Dispatcher::DispatchThreadMain, this);
  m_clientserver_thread =
      std::thread(&Dispatcher::ClientThreadMain, this, server_name, port);
}

void Dispatcher::Stop() {
  {
    std::lock_guard<std::mutex> lock(m_user_mutex);
    m_active = false;

    // close all connections
    for (auto& conn : m_connections) conn.net->Stop();
  }

  // wake up dispatch thread with a flush
  m_flush_cv.notify_one();

  // wake up client thread with a reconnect
  ClientReconnect();

  // wake up server thread by shutting down the socket
  if (m_server_acceptor) m_server_acceptor->shutdown();

  // join threads
  if (m_dispatch_thread.joinable()) m_dispatch_thread.join();
  if (m_clientserver_thread.joinable()) m_clientserver_thread.join();
}

void Dispatcher::SetUpdateRate(double interval) {
  // don't allow update rates faster than 100 ms
  if (interval < 0.1)
    interval = 0.1;
  m_update_rate = interval * 1000;
}

void Dispatcher::SetIdentity(llvm::StringRef name) {
  std::lock_guard<std::mutex> lock(m_user_mutex);
  m_identity = name;
}

void Dispatcher::Flush() {
  auto now = std::chrono::steady_clock::now();
  {
    std::lock_guard<std::mutex> lock(m_flush_mutex);
    // don't allow flushes more often than every 100 ms
    if ((now - m_last_flush) < std::chrono::milliseconds(100))
      return;
    m_last_flush = now;
    m_do_flush = true;
  }
  m_flush_cv.notify_one();
}

void Dispatcher::DispatchThreadMain() {
  auto timeout_time = std::chrono::steady_clock::now();
  int count = 0;
  while (m_active) {
    // handle loop taking too long
    auto start = std::chrono::steady_clock::now();
    if (start > timeout_time)
      timeout_time = start;

    // wait for periodic or when flushed
    timeout_time += std::chrono::milliseconds(m_update_rate);
    std::unique_lock<std::mutex> lock(m_flush_mutex);
    m_reconnect_cv.wait_until(lock, timeout_time,
                              [&] { return !m_active || m_do_flush; });
    m_do_flush = false;
    lock.unlock();
    if (!m_active) break;  // in case we were woken up to terminate

    if (++count > 10) {
      DEBUG("dispatch running");
      count = 0;
    }
  }
}

void Dispatcher::ServerThreadMain(const char* listen_address,
                                  unsigned int port) {
  m_server_acceptor.reset(
      new TCPAcceptor(static_cast<int>(port), listen_address));
  if (m_server_acceptor->start() != 0) {
    m_active = false;
    return;
  }
  while (m_active) {
    auto stream = m_server_acceptor->accept();
    if (!stream) {
      m_active = false;
      break;
    }
    DEBUG("server got a connection");

    // add to connections list
    Connection conn;
    conn.net.reset(new NetworkConnection(std::move(stream), GetEntryType));
    conn.net->Start();
    AddConnection(std::move(conn));
  }
}

void Dispatcher::ClientThreadMain(const char* server_name, unsigned int port) {
  unsigned int proto_rev = 0x0300;
  while (m_active) {
    // get identity
    std::string self_id;
    {
      std::lock_guard<std::mutex> lock(m_user_mutex);
      self_id = m_identity;
    }

    // sleep between retries
    std::this_thread::sleep_for(std::chrono::milliseconds(500));

    // try to connect (with timeout)
    DEBUG("client trying to connect");
    auto stream = TCPConnector::connect(server_name, static_cast<int>(port), 1);
    if (!stream) continue;  // keep retrying
    DEBUG("client connected");

    Connection conn;
    conn.net.reset(new NetworkConnection(std::move(stream), GetEntryType));
    conn.net->set_proto_rev(proto_rev);
    conn.net->Start();

    // send client hello
    DEBUG("client sending hello");
    conn.net->outgoing().push(
        NetworkConnection::Outgoing{Message::ClientHello(self_id)});

    // wait for response
    auto msg = conn.net->incoming().pop();
    if (!msg) {
      // disconnected, retry
      DEBUG("client disconnected waiting for first response");
      proto_rev = 0x0300;
      continue;
    }

    if (msg->Is(Message::kProtoUnsup)) {
      // reconnect with lower protocol (if possible)
      if (proto_rev <= 0x0200) {
        // no more options, abort (but keep trying to connect)
        proto_rev = 0x0300;
        continue;
      }
      proto_rev = 0x0200;
      continue;
    }

    if (proto_rev == 0x0300) {
      // should be server hello; if not, disconnect, but keep trying to connect
      if (!msg->Is(Message::kServerHello)) continue;
      conn.remote_id = msg->str();
      // get the next message (blocks)
      msg = conn.net->incoming().pop();
    }

    while (true) {
      if (!msg) {
        // disconnected, retry
        DEBUG("client disconnected waiting for initial entries");
        proto_rev = 0x0300;
        continue;
      }
      if (msg->Is(Message::kServerHelloDone)) break;
    }

    // add to connections list (the dispatcher thread will handle from here)
    AddConnection(std::move(conn));

    // block until told to reconnect
    std::unique_lock<std::mutex> lock(m_reconnect_mutex);
    m_reconnect_cv.wait(lock, [&] { return m_do_reconnect; });
    m_do_reconnect = false;
    lock.unlock();
  }
}

void Dispatcher::ClientReconnect() {
  if (m_server) return;
  {
    std::lock_guard<std::mutex> lock(m_reconnect_mutex);
    m_do_reconnect = true;
  }
  m_reconnect_cv.notify_one();
}

void Dispatcher::AddConnection(Connection&& conn) {
  std::lock_guard<std::mutex> lock(m_user_mutex);
  m_connections.push_back(std::move(conn));
}
<commit_msg>Continue implementing client.<commit_after>/*----------------------------------------------------------------------------*/
/* Copyright (c) FIRST 2015. All Rights Reserved.                             */
/* Open Source Software - may be modified and shared by FRC teams. The code   */
/* must be accompanied by the FIRST BSD license file in the root directory of */
/* the project.                                                               */
/*----------------------------------------------------------------------------*/

#include "Dispatcher.h"

#include "tcpsockets/TCPAcceptor.h"
#include "tcpsockets/TCPConnector.h"

using namespace nt;

#define DEBUG(str) puts(str)

std::unique_ptr<Dispatcher> Dispatcher::m_instance;

static NT_Type GetEntryType(unsigned int id) {
  // TODO
  return NT_UNASSIGNED;
}

Dispatcher::Dispatcher()
    : m_server(false),
      m_active(false),
      m_update_rate(100),
      m_do_flush(false),
      m_do_reconnect(false) {}

Dispatcher::~Dispatcher() { Stop(); }

void Dispatcher::StartServer(const char* listen_address, unsigned int port) {
  {
    std::lock_guard<std::mutex> lock(m_user_mutex);
    if (m_active) return;
    m_active = true;
  }
  m_server = true;
  m_dispatch_thread = std::thread(&Dispatcher::DispatchThreadMain, this);
  m_clientserver_thread =
      std::thread(&Dispatcher::ServerThreadMain, this, listen_address, port);
}

void Dispatcher::StartClient(const char* server_name, unsigned int port) {
  {
    std::lock_guard<std::mutex> lock(m_user_mutex);
    if (m_active) return;
    m_active = true;
  }
  m_server = false;
  m_dispatch_thread = std::thread(&Dispatcher::DispatchThreadMain, this);
  m_clientserver_thread =
      std::thread(&Dispatcher::ClientThreadMain, this, server_name, port);
}

void Dispatcher::Stop() {
  {
    std::lock_guard<std::mutex> lock(m_user_mutex);
    m_active = false;

    // close all connections
    for (auto& conn : m_connections) conn.net->Stop();
  }

  // wake up dispatch thread with a flush
  m_flush_cv.notify_one();

  // wake up client thread with a reconnect
  ClientReconnect();

  // wake up server thread by shutting down the socket
  if (m_server_acceptor) m_server_acceptor->shutdown();

  // join threads
  if (m_dispatch_thread.joinable()) m_dispatch_thread.join();
  if (m_clientserver_thread.joinable()) m_clientserver_thread.join();
}

void Dispatcher::SetUpdateRate(double interval) {
  // don't allow update rates faster than 100 ms
  if (interval < 0.1)
    interval = 0.1;
  m_update_rate = interval * 1000;
}

void Dispatcher::SetIdentity(llvm::StringRef name) {
  std::lock_guard<std::mutex> lock(m_user_mutex);
  m_identity = name;
}

void Dispatcher::Flush() {
  auto now = std::chrono::steady_clock::now();
  {
    std::lock_guard<std::mutex> lock(m_flush_mutex);
    // don't allow flushes more often than every 100 ms
    if ((now - m_last_flush) < std::chrono::milliseconds(100))
      return;
    m_last_flush = now;
    m_do_flush = true;
  }
  m_flush_cv.notify_one();
}

void Dispatcher::DispatchThreadMain() {
  auto timeout_time = std::chrono::steady_clock::now();
  int count = 0;
  while (m_active) {
    // handle loop taking too long
    auto start = std::chrono::steady_clock::now();
    if (start > timeout_time)
      timeout_time = start;

    // wait for periodic or when flushed
    timeout_time += std::chrono::milliseconds(m_update_rate);
    std::unique_lock<std::mutex> lock(m_flush_mutex);
    m_reconnect_cv.wait_until(lock, timeout_time,
                              [&] { return !m_active || m_do_flush; });
    m_do_flush = false;
    lock.unlock();
    if (!m_active) break;  // in case we were woken up to terminate

    if (++count > 10) {
      DEBUG("dispatch running");
      count = 0;
    }
  }
}

void Dispatcher::ServerThreadMain(const char* listen_address,
                                  unsigned int port) {
  m_server_acceptor.reset(
      new TCPAcceptor(static_cast<int>(port), listen_address));
  if (m_server_acceptor->start() != 0) {
    m_active = false;
    return;
  }
  while (m_active) {
    auto stream = m_server_acceptor->accept();
    if (!stream) {
      m_active = false;
      break;
    }
    DEBUG("server got a connection");

    // add to connections list
    Connection conn;
    conn.net.reset(new NetworkConnection(std::move(stream), GetEntryType));
    conn.net->Start();
    AddConnection(std::move(conn));
  }
}

void Dispatcher::ClientThreadMain(const char* server_name, unsigned int port) {
  unsigned int proto_rev = 0x0300;
  while (m_active) {
    // get identity
    std::string self_id;
    {
      std::lock_guard<std::mutex> lock(m_user_mutex);
      self_id = m_identity;
    }

    // sleep between retries
    std::this_thread::sleep_for(std::chrono::milliseconds(500));

    // try to connect (with timeout)
    DEBUG("client trying to connect");
    auto stream = TCPConnector::connect(server_name, static_cast<int>(port), 1);
    if (!stream) continue;  // keep retrying
    DEBUG("client connected");

    Connection conn;
    conn.net.reset(new NetworkConnection(std::move(stream), GetEntryType));
    conn.net->set_proto_rev(proto_rev);
    conn.net->Start();

    // send client hello
    DEBUG("client sending hello");
    conn.net->outgoing().push(
        NetworkConnection::Outgoing{Message::ClientHello(self_id)});

    // wait for response
    auto msg = conn.net->incoming().pop();
    if (!msg) {
      // disconnected, retry
      DEBUG("client disconnected waiting for first response");
      proto_rev = 0x0300;
      continue;
    }

    if (msg->Is(Message::kProtoUnsup)) {
      // reconnect with lower protocol (if possible)
      if (proto_rev <= 0x0200) {
        // no more options, abort (but keep trying to connect)
        proto_rev = 0x0300;
        continue;
      }
      proto_rev = 0x0200;
      continue;
    }

    if (proto_rev >= 0x0300) {
      // should be server hello; if not, disconnect, but keep trying to connect
      if (!msg->Is(Message::kServerHello)) continue;
      conn.remote_id = msg->str();
      // get the next message (blocks)
      msg = conn.net->incoming().pop();
    }

    // receive initial assignments
    std::vector<std::shared_ptr<Message>> incoming;
    while (true) {
      if (!msg) {
        // disconnected, retry
        DEBUG("client disconnected waiting for initial entries");
        proto_rev = 0x0300;
        continue;
      }
      if (msg->Is(Message::kServerHelloDone)) break;
      if (!msg->Is(Message::kEntryAssign)) {
        // unexpected message
        DEBUG("received message other than entry assignment during initial handshake");
        proto_rev = 0x0300;
        continue;
      }
      incoming.push_back(msg);
      // get the next message (blocks)
      msg = conn.net->incoming().pop();
    }

    // generate outgoing assignments
    NetworkConnection::Outgoing outgoing;

    if (proto_rev >= 0x0300)
      outgoing.push_back(Message::ClientHelloDone());

    if (!outgoing.empty())
      conn.net->outgoing().push(std::move(outgoing));

    // add to connections list (the dispatcher thread will handle from here)
    AddConnection(std::move(conn));

    // block until told to reconnect
    std::unique_lock<std::mutex> lock(m_reconnect_mutex);
    m_reconnect_cv.wait(lock, [&] { return m_do_reconnect; });
    m_do_reconnect = false;
    lock.unlock();
  }
}

void Dispatcher::ClientReconnect() {
  if (m_server) return;
  {
    std::lock_guard<std::mutex> lock(m_reconnect_mutex);
    m_do_reconnect = true;
  }
  m_reconnect_cv.notify_one();
}

void Dispatcher::AddConnection(Connection&& conn) {
  std::lock_guard<std::mutex> lock(m_user_mutex);
  m_connections.push_back(std::move(conn));
}
<|endoftext|>


<commit_before>#include "Emscripten.h"
#include "SDL.h"
#include "Context.h"
#include <cstdlib>

#ifdef __EMSCRIPTEN__

extern Context* _context;

#include <emscripten.h>

void emDeinit();

extern "C" void emFilesSyncedShutdown()
{
	SDL_Event event;
	event.type = EVERYTHING_DONE;
	SDL_PushEvent(&event);
}

extern "C" void emFilesSyncedStartup()
{
	SDL_Event event;
	event.type = EVERYTHING_READY;
	SDL_PushEvent(&event);
}

void emSyncFsAndStartup()
{
	EM_ASM(
		console.log("Mounting filesystem");

		FS.mkdir('/persistent');
		FS.mkdir('/imported');
		FS.mount(IDBFS, {}, '/persistent');

		FS.syncfs(true, function(err) {
            assert(!err);
			console.log("Filesystem is loaded, starting up");
            Module.ccall('emFilesSyncedStartup', 'v');
		});
	);
}

void emSyncFs()
{
	EM_ASM(
		console.log("Syncing filesystem");

		FS.syncfs(false, function(err) {
            assert(!err);
			console.log("Filesystem synced");
		});
	);
}

void emSyncFsAndShutdown()
{
	EM_ASM(
		console.log("Saving filesystem");
		FS.syncfs(function(err) {
            assert(!err);
			console.log("Filesystem is saved, shutting down");
            Module.ccall('emFilesSyncedShutdown', 'v');
		});
	);
}

const char * emOnBeforeUnload(int eventType, const void *reserved, void *userData)
{
	return "";
}

extern "C" void emOnFileImported()
{
	SDL_Event event;
	event.type = SONG_IMPORTED;
	SDL_PushEvent(&event);
}

extern "C" void emExportFile()
{
	SDL_Event event;
	event.type = EXPORT_SONG;
	SDL_PushEvent(&event);
}

extern "C" void emPlaySong()
{
	SDL_Event event;
	event.type = PLAY_SONG;
	SDL_PushEvent(&event);
}

extern "C" void emNewSong()
{
	SDL_Event event;
	event.type = NEW_SONG;
	SDL_PushEvent(&event);
}

extern "C" const char * emRequestSong()
{
	return _context->mainEditor.getSongBase64().c_str();
}

extern "C" void emAppReady()
{
	emscripten_run_script("appReady();");
}

#else

void emSyncFsAndShutdown() {}
void emSyncFsAndStartup() {}

#endif

<commit_msg>HTML5 version should run appReady() only if it is defined<commit_after>#include "Emscripten.h"
#include "SDL.h"
#include "Context.h"
#include <cstdlib>

#ifdef __EMSCRIPTEN__

extern Context* _context;

#include <emscripten.h>

void emDeinit();

extern "C" void emFilesSyncedShutdown()
{
	SDL_Event event;
	event.type = EVERYTHING_DONE;
	SDL_PushEvent(&event);
}

extern "C" void emFilesSyncedStartup()
{
	SDL_Event event;
	event.type = EVERYTHING_READY;
	SDL_PushEvent(&event);
}

void emSyncFsAndStartup()
{
	EM_ASM(
		console.log("Mounting filesystem");

		FS.mkdir('/persistent');
		FS.mkdir('/imported');
		FS.mount(IDBFS, {}, '/persistent');

		FS.syncfs(true, function(err) {
            assert(!err);
			console.log("Filesystem is loaded, starting up");
            Module.ccall('emFilesSyncedStartup', 'v');
		});
	);
}

void emSyncFs()
{
	EM_ASM(
		console.log("Syncing filesystem");

		FS.syncfs(false, function(err) {
            assert(!err);
			console.log("Filesystem synced");
		});
	);
}

void emSyncFsAndShutdown()
{
	EM_ASM(
		console.log("Saving filesystem");
		FS.syncfs(function(err) {
            assert(!err);
			console.log("Filesystem is saved, shutting down");
            Module.ccall('emFilesSyncedShutdown', 'v');
		});
	);
}

const char * emOnBeforeUnload(int eventType, const void *reserved, void *userData)
{
	return "";
}

extern "C" void emOnFileImported()
{
	SDL_Event event;
	event.type = SONG_IMPORTED;
	SDL_PushEvent(&event);
}

extern "C" void emExportFile()
{
	SDL_Event event;
	event.type = EXPORT_SONG;
	SDL_PushEvent(&event);
}

extern "C" void emPlaySong()
{
	SDL_Event event;
	event.type = PLAY_SONG;
	SDL_PushEvent(&event);
}

extern "C" void emNewSong()
{
	SDL_Event event;
	event.type = NEW_SONG;
	SDL_PushEvent(&event);
}

extern "C" const char * emRequestSong()
{
	return _context->mainEditor.getSongBase64().c_str();
}

extern "C" void emAppReady()
{
	emscripten_run_script("if (typeof appReady === \"function\") appReady();");
}

#else

void emSyncFsAndShutdown() {}
void emSyncFsAndStartup() {}

#endif

<|endoftext|>


<commit_before>#include "Game/Clock.h"

#include <array>
#include <chrono>
using namespace std::chrono_literals;
#include <cassert>
#include <limits>

#include "Game/Game_Result.h"

Clock::Clock(double duration_seconds,
             size_t moves_to_reset,
             double increment_seconds,
             Color starting_turn,
             std::chrono::system_clock::time_point previous_start_time) noexcept :
    timers({fractional_seconds(duration_seconds), fractional_seconds(duration_seconds)}),
    initial_start_time(Clock::fractional_seconds(duration_seconds)),
    increment_time({Clock::fractional_seconds(increment_seconds), Clock::fractional_seconds(increment_seconds)}),
    move_count_reset(moves_to_reset),
    whose_turn(starting_turn),
    game_start_date_time(previous_start_time)
{
    assert(whose_turn != NONE);
}

Game_Result Clock::punch() noexcept
{
    assert(clocks_running);

    auto time_this_punch = std::chrono::steady_clock::now();
    whose_turn = opposite(whose_turn);

    if( ! is_in_use())
    {
        return {};
    }

    timers[whose_turn] -= (time_this_punch - time_previous_punch);

    if(++moves_to_reset_clocks[whose_turn] == move_count_reset)
    {
        timers[whose_turn] += initial_start_time;
        moves_to_reset_clocks[whose_turn] = 0;
    }

    time_previous_punch = time_this_punch;
    timers[whose_turn] += increment_time[whose_turn];

    if(timers[whose_turn] < 0s)
    {
        return Game_Result(opposite(whose_turn), Game_Result_Type::TIME_FORFEIT);
    }
    else
    {
        return {};
    }
}

void Clock::unpunch() noexcept
{
    moves_to_reset_clocks[whose_turn] -= 1;
    moves_to_reset_clocks[opposite(whose_turn)] -= 1;
    punch();
}

void Clock::stop() noexcept
{
    auto time_stop = std::chrono::steady_clock::now();
    timers[whose_turn] -= (time_stop - time_previous_punch);
    clocks_running = false;
}

void Clock::start() noexcept
{
    static const auto default_game_start_date_time = std::chrono::system_clock::time_point{};
    time_previous_punch = std::chrono::steady_clock::now();
    if(game_start_date_time == default_game_start_date_time)
    {
        game_start_date_time = std::chrono::system_clock::now();
    }
    clocks_running = true;
}

double Clock::time_left(Color color) const noexcept
{
    if( ! is_in_use())
    {
        return 0.0;
    }

    if(whose_turn != color || ! clocks_running)
    {
        return timers[color].count();
    }
    else
    {
        auto now = std::chrono::steady_clock::now();
        return fractional_seconds(timers[color] - (now - time_previous_punch)).count();
    }
}

size_t Clock::moves_until_reset(Color color) const noexcept
{
    if(move_count_reset > 0)
    {
        return move_count_reset - moves_to_reset_clocks[color];
    }
    else
    {
        return std::numeric_limits<int>::max();
    }
}

Color Clock::running_for() const noexcept
{
    return whose_turn;
}

void Clock::set_time(Color player, double new_time_seconds) noexcept
{
    timers[player] = fractional_seconds(new_time_seconds);
    initial_start_time = fractional_seconds(std::max(new_time_seconds, initial_start_time.count()));
    time_previous_punch = std::chrono::steady_clock::now();
}

void Clock::set_increment(Color player, double new_increment_time_seconds) noexcept
{
    increment_time[player] = fractional_seconds(new_increment_time_seconds);
}

void Clock::set_next_time_reset(size_t moves_to_reset) noexcept
{
    move_count_reset = moves_to_reset_clocks[running_for()] + moves_to_reset;
}

double Clock::running_time_left() const noexcept
{
    return time_left(running_for());
}

bool Clock::is_running() const noexcept
{
    return clocks_running;
}

std::chrono::system_clock::time_point Clock::game_start_date_and_time() const noexcept
{
    return game_start_date_time;
}

size_t Clock::moves_per_time_period() const noexcept
{
    return move_count_reset;
}

double Clock::initial_time() const noexcept
{
    return initial_start_time.count();
}

double Clock::increment(Color color) const noexcept
{
    return increment_time[color].count();
}

bool Clock::is_in_use() const noexcept
{
    return initial_start_time > 0s;
}
<commit_msg>Put whose_turn switch in punch() back where it was<commit_after>#include "Game/Clock.h"

#include <array>
#include <chrono>
using namespace std::chrono_literals;
#include <cassert>
#include <limits>

#include "Game/Game_Result.h"

Clock::Clock(double duration_seconds,
             size_t moves_to_reset,
             double increment_seconds,
             Color starting_turn,
             std::chrono::system_clock::time_point previous_start_time) noexcept :
    timers({fractional_seconds(duration_seconds), fractional_seconds(duration_seconds)}),
    initial_start_time(Clock::fractional_seconds(duration_seconds)),
    increment_time({Clock::fractional_seconds(increment_seconds), Clock::fractional_seconds(increment_seconds)}),
    move_count_reset(moves_to_reset),
    whose_turn(starting_turn),
    game_start_date_time(previous_start_time)
{
    assert(whose_turn != NONE);
}

Game_Result Clock::punch() noexcept
{
    assert(clocks_running);

    auto time_this_punch = std::chrono::steady_clock::now();

    timers[whose_turn] -= (time_this_punch - time_previous_punch);

    if(++moves_to_reset_clocks[whose_turn] == move_count_reset)
    {
        timers[whose_turn] += initial_start_time;
        moves_to_reset_clocks[whose_turn] = 0;
    }

    time_previous_punch = time_this_punch;
    timers[whose_turn] += increment_time[whose_turn];

    whose_turn = opposite(whose_turn);

    if(timers[opposite(whose_turn)] < 0s)
    {
        return Game_Result(whose_turn, Game_Result_Type::TIME_FORFEIT);
    }
    else
    {
        return {};
    }
}

void Clock::unpunch() noexcept
{
    moves_to_reset_clocks[whose_turn] -= 1;
    moves_to_reset_clocks[opposite(whose_turn)] -= 1;
    punch();
}

void Clock::stop() noexcept
{
    auto time_stop = std::chrono::steady_clock::now();
    timers[whose_turn] -= (time_stop - time_previous_punch);
    clocks_running = false;
}

void Clock::start() noexcept
{
    static const auto default_game_start_date_time = std::chrono::system_clock::time_point{};
    time_previous_punch = std::chrono::steady_clock::now();
    if(game_start_date_time == default_game_start_date_time)
    {
        game_start_date_time = std::chrono::system_clock::now();
    }
    clocks_running = true;
}

double Clock::time_left(Color color) const noexcept
{
    if( ! is_in_use())
    {
        return 0.0;
    }

    if(whose_turn != color || ! clocks_running)
    {
        return timers[color].count();
    }
    else
    {
        auto now = std::chrono::steady_clock::now();
        return fractional_seconds(timers[color] - (now - time_previous_punch)).count();
    }
}

size_t Clock::moves_until_reset(Color color) const noexcept
{
    if(move_count_reset > 0)
    {
        return move_count_reset - moves_to_reset_clocks[color];
    }
    else
    {
        return std::numeric_limits<int>::max();
    }
}

Color Clock::running_for() const noexcept
{
    return whose_turn;
}

void Clock::set_time(Color player, double new_time_seconds) noexcept
{
    timers[player] = fractional_seconds(new_time_seconds);
    initial_start_time = fractional_seconds(std::max(new_time_seconds, initial_start_time.count()));
    time_previous_punch = std::chrono::steady_clock::now();
}

void Clock::set_increment(Color player, double new_increment_time_seconds) noexcept
{
    increment_time[player] = fractional_seconds(new_increment_time_seconds);
}

void Clock::set_next_time_reset(size_t moves_to_reset) noexcept
{
    move_count_reset = moves_to_reset_clocks[running_for()] + moves_to_reset;
}

double Clock::running_time_left() const noexcept
{
    return time_left(running_for());
}

bool Clock::is_running() const noexcept
{
    return clocks_running;
}

std::chrono::system_clock::time_point Clock::game_start_date_and_time() const noexcept
{
    return game_start_date_time;
}

size_t Clock::moves_per_time_period() const noexcept
{
    return move_count_reset;
}

double Clock::initial_time() const noexcept
{
    return initial_start_time.count();
}

double Clock::increment(Color color) const noexcept
{
    return increment_time[color].count();
}

bool Clock::is_in_use() const noexcept
{
    return initial_start_time > 0s;
}
<|endoftext|>


<commit_before>#include "Game/Piece.h"

#include <cctype>
#include <vector>
#include <algorithm>
#include <cassert>
#include <array>

#include "Game/Board.h"
#include "Game/Piece_Types.h"

#include "Moves/Move.h"
#include "Moves/Direction.h"
#include "Moves/Pawn_Move.h"
#include "Moves/Pawn_Capture.h"
#include "Moves/Pawn_Double_Move.h"
#include "Moves/Pawn_Promotion.h"
#include "Moves/Pawn_Promotion_by_Capture.h"
#include "Moves/En_Passant.h"
#include "Moves/Castle.h"

#include "Utility/String.h"

int Piece::maximum_piece_height = 0;

//! Create a piece.

//! \param color_in The color of the piece.
//! \param type_in The type of piece.
Piece::Piece(Color color_in, Piece_Type type_in) :
    my_color(color_in),
    my_type(type_in)
{
    switch(type())
    {
        case PAWN:
            add_pawn_moves();
            add_pawn_art();
            break;
        case ROOK:
            add_rook_moves();
            add_rook_art();
            break;
        case KNIGHT:
            add_knight_moves();
            add_knight_art();
            break;
        case BISHOP:
            add_bishop_moves();
            add_bishop_art();
            break;
        case QUEEN:
            add_bishop_moves();
            add_rook_moves();
            add_queen_art();
            break;
        case KING:
            add_king_moves();
            add_king_art();
            break;
        default:
            throw std::invalid_argument("Program bug: Invalid piece type in Piece(): " + std::to_string(type()));
    }
}

//! Return a row of the ASCII art representation of the piece.

//! \param row Which row of the square to return, with 0 being the top.
//!        If the height is above or below the piece's picture, then an
//!        empty string is returned.
//! \param square_height The height of the square in characters.
//! \returns One row of text that forms a picture of the piece.
//! \throws Debug assert fail if the square height is smaller than the piece height.
//!
//! Piece design by VK (?) and taken from http://ascii.co.uk/art/chess.
std::string Piece::ascii_art(int row, int square_height) const
{
    assert(square_height >= maximum_piece_height);

    int empty_bottom_rows = (square_height - maximum_piece_height)/2;
    int empty_top_rows = square_height - int(ascii_art_lines.size()) - empty_bottom_rows;
    int line = row - empty_top_rows;
    if(0 <= line && line < int(ascii_art_lines.size()))
    {
        return ascii_art_lines[line];
    }
    return {};
}

//! The color of the piece.

//! \returns The Color of the player that controls the piece.
Color Piece::color() const
{
    return my_color;
}

//! Get the PGN symbol for the piece.

//! \returns The symbol for the moving piece when writing a game record. A pawn is represented by an empty string.
std::string Piece::pgn_symbol() const
{
    return type() == PAWN ? std::string{} : std::string(1, std::toupper(fen_symbol()));
}

// Get the piece symbol when writing an FEN string.

//! \returns A single character symbol for the piece. Uppercase is white, lowercase is black.
char Piece::fen_symbol() const
{
    static auto symbols = "PRNBQK";
    auto symbol = symbols[type()];
    return (my_color == WHITE ? symbol : std::tolower(symbol));
}

//! Check that a piece is allowed to make a certain move.

//! \param move A pointer to a prospective move.
//! \returns Whether or not the piece is allowed to move in the manner described by the parameter.
bool Piece::can_move(const Move* move) const
{
    return std::find_if(possible_moves.begin(),
                        possible_moves.end(),
                        [move](const auto& x){ return x.get() == move; }) != possible_moves.end();
}

//! Get all possibly legal moves of a piece starting from a given square.

//! \param file The file of the starting square.
//! \param rank The rank of the starting square.
//! \returns A list of legal moves starting from that square.
const std::vector<const Move*>& Piece::move_list(char file, int rank) const
{
    return legal_moves[Board::square_index(file, rank)];
}

//! Get the type of the piece.

//! \returns The kind of piece, i.e., PAWN, ROOK, etc.
Piece_Type Piece::type() const
{
    return my_type;
}

void Piece::add_standard_legal_move(int file_step, int rank_step)
{
    for(char start_file = 'a'; start_file <= 'h'; ++start_file)
    {
        for(int start_rank = 1; start_rank <= 8; ++start_rank)
        {
            char end_file = start_file + file_step;
            int  end_rank = start_rank + rank_step;

            if(Board::inside_board(end_file, end_rank))
            {
                add_legal_move<Move>(start_file, start_rank,
                                     end_file, end_rank);
            }
        }
    }
}

void Piece::add_pawn_moves()
{
    auto base_rank = (color() == WHITE ? 2 : 7);
    auto no_normal_move_rank = (color() == WHITE ? 7 : 2);
    auto direction = (color() == WHITE ? 1 : -1);
    for(char file = 'a'; file <= 'h'; ++file)
    {
        for(int rank = base_rank; rank != no_normal_move_rank; rank += direction)
        {
            add_legal_move<Pawn_Move>(color(), file, rank);
        }
    }

    for(char file = 'a'; file <= 'h'; ++file)
    {
        add_legal_move<Pawn_Double_Move>(color(), file);
    }

    auto possible_promotions = {QUEEN, KNIGHT, ROOK, BISHOP};

    for(auto dir : {RIGHT, LEFT})
    {
        auto first_file = (dir == RIGHT ? 'a' : 'b');
        auto last_file = (dir == RIGHT ? 'g' : 'h');
        for(char file = first_file; file <= last_file; ++file)
        {
            for(int rank = base_rank; rank != no_normal_move_rank; rank += direction)
            {
                add_legal_move<Pawn_Capture>(color(), dir, file, rank);
            }
        }

        for(char file = first_file; file <= last_file; ++file)
        {
            add_legal_move<En_Passant>(color(), dir, file);
        }

        for(auto promote : possible_promotions)
        {
            for(auto file = first_file; file <= last_file; ++file)
            {
                add_legal_move<Pawn_Promotion_by_Capture>(promote, color(), dir, file);
            }
        }
    }

    for(auto promote : possible_promotions)
    {
        for(auto file = 'a'; file <= 'h'; ++file)
        {
            add_legal_move<Pawn_Promotion>(promote, color(), file);
        }
    }
}

void Piece::add_rook_moves()
{
    for(int d_file = -1; d_file <= 1; ++d_file)
    {
        for(int d_rank = -1; d_rank <= 1; ++d_rank)
        {
            if(d_file == 0 && d_rank == 0) { continue; }
            if(d_file != 0 && d_rank != 0) { continue; }

            for(int move_size = 1; move_size <= 7; ++move_size)
            {
                add_standard_legal_move(move_size*d_file, move_size*d_rank);
            }
        }
    }
}

void Piece::add_knight_moves()
{
    for(auto d_file : {1, 2})
    {
        auto d_rank = 3 - d_file;
        for(auto file_direction : {-1, 1})
        {
            for(auto rank_direction : {-1, 1})
            {
                add_standard_legal_move(d_file*file_direction, d_rank*rank_direction);
            }
        }
    }
}

void Piece::add_bishop_moves()
{
    for(int d_rank : {-1, 1})
    {
        for(int d_file : {-1, 1})
        {
            for(int move_size = 1; move_size <= 7; ++move_size)
            {
                add_standard_legal_move(move_size*d_file, move_size*d_rank);
            }
        }
    }
}

void Piece::add_king_moves()
{
    for(int d_rank = -1; d_rank <= 1; ++d_rank)
    {
        for(int d_file = -1; d_file <= 1; ++d_file)
        {
            if(d_rank == 0 && d_file == 0) { continue; }

            add_standard_legal_move(d_file, d_rank);
        }
    }

    int base_rank = (color() == WHITE ? 1 : 8);
    add_legal_move<Castle>(base_rank, LEFT);
    add_legal_move<Castle>(base_rank, RIGHT);
}

void Piece::add_pawn_art()
{
    // ASCII Art http://ascii.co.uk/art/chess (VK)
    ascii_art_lines.push_back("( )");
    ascii_art_lines.push_back("/___\\");
    add_color();
}

void Piece::add_rook_art()
{
    // ASCII Art http://ascii.co.uk/art/chess (VK)
    ascii_art_lines.push_back("|U|");
    ascii_art_lines.push_back("| |");
    ascii_art_lines.push_back("/___\\");
    add_color();
}

void Piece::add_knight_art()
{
    // ASCII Art http://ascii.co.uk/art/chess (VK)
    ascii_art_lines.push_back("/\")");
    ascii_art_lines.push_back("7 (");
    ascii_art_lines.push_back("/___\\");
    add_color();
}

void Piece::add_bishop_art()
{
    // ASCII art http://ascii.co.uk/art/chess (VK)
    ascii_art_lines.push_back("(V)");
    ascii_art_lines.push_back(") (");
    ascii_art_lines.push_back("/___\\");
    add_color();
}

void Piece::add_queen_art()
{
    // ASCII Art http://ascii.co.uk/art/chess (VK)
    ascii_art_lines.push_back("\\^/");
    ascii_art_lines.push_back(") (");
    ascii_art_lines.push_back("(___)");
    add_color();
}

void Piece::add_king_art()
{
    // ASCII Art http://ascii.co.uk/art/chess (VK)
    ascii_art_lines.push_back("\\+/");
    ascii_art_lines.push_back("| |");
    ascii_art_lines.push_back("/___\\");
    add_color();
}

void Piece::add_color()
{
    maximum_piece_height = std::max(maximum_piece_height, int(ascii_art_lines.size()));

    if(color() == WHITE)
    {
        return;
    }

    for(auto& line : ascii_art_lines)
    {
        for(auto& c : line)
        {
            if(c == ' ' || c == '_')
            {
                c = '#';
            }
        }
    }
}

//! Gives a list of moves that are allowed to capture other pieces.

//! \param file The file of the square where the attacking move starts.
//! \param rank The rank of the square where the attacking move starts.
const std::vector<const Move*>& Piece::attacking_moves(char file, int rank) const
{
    return attack_moves[Board::square_index(file, rank)];
}
<commit_msg>Simpler Piece::can_move()<commit_after>#include "Game/Piece.h"

#include <cctype>
#include <vector>
#include <algorithm>
#include <cassert>
#include <array>

#include "Game/Board.h"
#include "Game/Piece_Types.h"

#include "Moves/Move.h"
#include "Moves/Direction.h"
#include "Moves/Pawn_Move.h"
#include "Moves/Pawn_Capture.h"
#include "Moves/Pawn_Double_Move.h"
#include "Moves/Pawn_Promotion.h"
#include "Moves/Pawn_Promotion_by_Capture.h"
#include "Moves/En_Passant.h"
#include "Moves/Castle.h"

#include "Utility/String.h"

int Piece::maximum_piece_height = 0;

//! Create a piece.

//! \param color_in The color of the piece.
//! \param type_in The type of piece.
Piece::Piece(Color color_in, Piece_Type type_in) :
    my_color(color_in),
    my_type(type_in)
{
    switch(type())
    {
        case PAWN:
            add_pawn_moves();
            add_pawn_art();
            break;
        case ROOK:
            add_rook_moves();
            add_rook_art();
            break;
        case KNIGHT:
            add_knight_moves();
            add_knight_art();
            break;
        case BISHOP:
            add_bishop_moves();
            add_bishop_art();
            break;
        case QUEEN:
            add_bishop_moves();
            add_rook_moves();
            add_queen_art();
            break;
        case KING:
            add_king_moves();
            add_king_art();
            break;
        default:
            throw std::invalid_argument("Program bug: Invalid piece type in Piece(): " + std::to_string(type()));
    }
}

//! Return a row of the ASCII art representation of the piece.

//! \param row Which row of the square to return, with 0 being the top.
//!        If the height is above or below the piece's picture, then an
//!        empty string is returned.
//! \param square_height The height of the square in characters.
//! \returns One row of text that forms a picture of the piece.
//! \throws Debug assert fail if the square height is smaller than the piece height.
//!
//! Piece design by VK (?) and taken from http://ascii.co.uk/art/chess.
std::string Piece::ascii_art(int row, int square_height) const
{
    assert(square_height >= maximum_piece_height);

    int empty_bottom_rows = (square_height - maximum_piece_height)/2;
    int empty_top_rows = square_height - int(ascii_art_lines.size()) - empty_bottom_rows;
    int line = row - empty_top_rows;
    if(0 <= line && line < int(ascii_art_lines.size()))
    {
        return ascii_art_lines[line];
    }
    return {};
}

//! The color of the piece.

//! \returns The Color of the player that controls the piece.
Color Piece::color() const
{
    return my_color;
}

//! Get the PGN symbol for the piece.

//! \returns The symbol for the moving piece when writing a game record. A pawn is represented by an empty string.
std::string Piece::pgn_symbol() const
{
    return type() == PAWN ? std::string{} : std::string(1, std::toupper(fen_symbol()));
}

// Get the piece symbol when writing an FEN string.

//! \returns A single character symbol for the piece. Uppercase is white, lowercase is black.
char Piece::fen_symbol() const
{
    static auto symbols = "PRNBQK";
    auto symbol = symbols[type()];
    return (my_color == WHITE ? symbol : std::tolower(symbol));
}

//! Check that a piece is allowed to make a certain move.

//! \param move A pointer to a prospective move.
//! \returns Whether or not the piece is allowed to move in the manner described by the parameter.
bool Piece::can_move(const Move* move) const
{
    const auto& moves = move_list(move->start_file(), move->start_rank());
    return std::find(moves.begin(), moves.end(), move) != moves.end();
}

//! Get all possibly legal moves of a piece starting from a given square.

//! \param file The file of the starting square.
//! \param rank The rank of the starting square.
//! \returns A list of legal moves starting from that square.
const std::vector<const Move*>& Piece::move_list(char file, int rank) const
{
    return legal_moves[Board::square_index(file, rank)];
}

//! Get the type of the piece.

//! \returns The kind of piece, i.e., PAWN, ROOK, etc.
Piece_Type Piece::type() const
{
    return my_type;
}

void Piece::add_standard_legal_move(int file_step, int rank_step)
{
    for(char start_file = 'a'; start_file <= 'h'; ++start_file)
    {
        for(int start_rank = 1; start_rank <= 8; ++start_rank)
        {
            char end_file = start_file + file_step;
            int  end_rank = start_rank + rank_step;

            if(Board::inside_board(end_file, end_rank))
            {
                add_legal_move<Move>(start_file, start_rank,
                                     end_file, end_rank);
            }
        }
    }
}

void Piece::add_pawn_moves()
{
    auto base_rank = (color() == WHITE ? 2 : 7);
    auto no_normal_move_rank = (color() == WHITE ? 7 : 2);
    auto direction = (color() == WHITE ? 1 : -1);
    for(char file = 'a'; file <= 'h'; ++file)
    {
        for(int rank = base_rank; rank != no_normal_move_rank; rank += direction)
        {
            add_legal_move<Pawn_Move>(color(), file, rank);
        }
    }

    for(char file = 'a'; file <= 'h'; ++file)
    {
        add_legal_move<Pawn_Double_Move>(color(), file);
    }

    auto possible_promotions = {QUEEN, KNIGHT, ROOK, BISHOP};

    for(auto dir : {RIGHT, LEFT})
    {
        auto first_file = (dir == RIGHT ? 'a' : 'b');
        auto last_file = (dir == RIGHT ? 'g' : 'h');
        for(char file = first_file; file <= last_file; ++file)
        {
            for(int rank = base_rank; rank != no_normal_move_rank; rank += direction)
            {
                add_legal_move<Pawn_Capture>(color(), dir, file, rank);
            }
        }

        for(char file = first_file; file <= last_file; ++file)
        {
            add_legal_move<En_Passant>(color(), dir, file);
        }

        for(auto promote : possible_promotions)
        {
            for(auto file = first_file; file <= last_file; ++file)
            {
                add_legal_move<Pawn_Promotion_by_Capture>(promote, color(), dir, file);
            }
        }
    }

    for(auto promote : possible_promotions)
    {
        for(auto file = 'a'; file <= 'h'; ++file)
        {
            add_legal_move<Pawn_Promotion>(promote, color(), file);
        }
    }
}

void Piece::add_rook_moves()
{
    for(int d_file = -1; d_file <= 1; ++d_file)
    {
        for(int d_rank = -1; d_rank <= 1; ++d_rank)
        {
            if(d_file == 0 && d_rank == 0) { continue; }
            if(d_file != 0 && d_rank != 0) { continue; }

            for(int move_size = 1; move_size <= 7; ++move_size)
            {
                add_standard_legal_move(move_size*d_file, move_size*d_rank);
            }
        }
    }
}

void Piece::add_knight_moves()
{
    for(auto d_file : {1, 2})
    {
        auto d_rank = 3 - d_file;
        for(auto file_direction : {-1, 1})
        {
            for(auto rank_direction : {-1, 1})
            {
                add_standard_legal_move(d_file*file_direction, d_rank*rank_direction);
            }
        }
    }
}

void Piece::add_bishop_moves()
{
    for(int d_rank : {-1, 1})
    {
        for(int d_file : {-1, 1})
        {
            for(int move_size = 1; move_size <= 7; ++move_size)
            {
                add_standard_legal_move(move_size*d_file, move_size*d_rank);
            }
        }
    }
}

void Piece::add_king_moves()
{
    for(int d_rank = -1; d_rank <= 1; ++d_rank)
    {
        for(int d_file = -1; d_file <= 1; ++d_file)
        {
            if(d_rank == 0 && d_file == 0) { continue; }

            add_standard_legal_move(d_file, d_rank);
        }
    }

    int base_rank = (color() == WHITE ? 1 : 8);
    add_legal_move<Castle>(base_rank, LEFT);
    add_legal_move<Castle>(base_rank, RIGHT);
}

void Piece::add_pawn_art()
{
    // ASCII Art http://ascii.co.uk/art/chess (VK)
    ascii_art_lines.push_back("( )");
    ascii_art_lines.push_back("/___\\");
    add_color();
}

void Piece::add_rook_art()
{
    // ASCII Art http://ascii.co.uk/art/chess (VK)
    ascii_art_lines.push_back("|U|");
    ascii_art_lines.push_back("| |");
    ascii_art_lines.push_back("/___\\");
    add_color();
}

void Piece::add_knight_art()
{
    // ASCII Art http://ascii.co.uk/art/chess (VK)
    ascii_art_lines.push_back("/\")");
    ascii_art_lines.push_back("7 (");
    ascii_art_lines.push_back("/___\\");
    add_color();
}

void Piece::add_bishop_art()
{
    // ASCII art http://ascii.co.uk/art/chess (VK)
    ascii_art_lines.push_back("(V)");
    ascii_art_lines.push_back(") (");
    ascii_art_lines.push_back("/___\\");
    add_color();
}

void Piece::add_queen_art()
{
    // ASCII Art http://ascii.co.uk/art/chess (VK)
    ascii_art_lines.push_back("\\^/");
    ascii_art_lines.push_back(") (");
    ascii_art_lines.push_back("(___)");
    add_color();
}

void Piece::add_king_art()
{
    // ASCII Art http://ascii.co.uk/art/chess (VK)
    ascii_art_lines.push_back("\\+/");
    ascii_art_lines.push_back("| |");
    ascii_art_lines.push_back("/___\\");
    add_color();
}

void Piece::add_color()
{
    maximum_piece_height = std::max(maximum_piece_height, int(ascii_art_lines.size()));

    if(color() == WHITE)
    {
        return;
    }

    for(auto& line : ascii_art_lines)
    {
        for(auto& c : line)
        {
            if(c == ' ' || c == '_')
            {
                c = '#';
            }
        }
    }
}

//! Gives a list of moves that are allowed to capture other pieces.

//! \param file The file of the square where the attacking move starts.
//! \param rank The rank of the square where the attacking move starts.
const std::vector<const Move*>& Piece::attacking_moves(char file, int rank) const
{
    return attack_moves[Board::square_index(file, rank)];
}
<|endoftext|>


<commit_before>#include "Image.h"

#include "../File/File.h"

#include "Color.h"
#include <png.h>
#include <stdlib.h>

#define PNGSIGSIZE 8

static void pngReadData(png_structp pngPointer, png_bytep data, png_size_t length)
{
    char** fileData = (char**)png_get_io_ptr(pngPointer);
    memcpy(data, *fileData, length);
    *fileData += length;
}

/*bool Jatta::Png::isValid(const char* buffer, unsigned int length)
{
    return png_sig_cmp((png_bytep)buffer, 0, PNGSIGSIZE) == 0;
}

bool Jatta::Png::isValid(const std::string& fileName)
{
    // TODO: clean this up / check for errors
    unsigned int size;
    File::getFileSize(fileName, &size);
    char* buffer = new char[size];
    File::getData(fileName, buffer, size);
    bool valid = isValid(buffer, size);
    delete[] buffer;
    return valid;
}*/

bool Jatta::Image::loadPng(const std::string& fileName)
{
    unsigned int size;
    File::getFileSize(fileName, &size);
    char* buffer = new char[size];
    File::getData(fileName, buffer, size);

    //if (!isValid(buffer, size))
    //{
        //return false;
    //}

    //Here we create the png read struct. The 3 NULL's at the end can be used
    //for your own custom error handling functions, but we'll just use the default.
    //if the function fails, NULL is returned. Always check the return values!
    png_structp pngPtr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
    if (!pngPtr) {
        std::cerr << "ERROR: Couldn't initialize png read struct" << std::endl;
        return false; //Do your own error recovery/handling here
    }

    //Here we create the png info struct.
    //Note that this time, if this function fails, we have to clean up the read struct!
    png_infop infoPtr = png_create_info_struct(pngPtr);
    if (!infoPtr) {
        std::cerr << "ERROR: Couldn't initialize png info struct" << std::endl;
        png_destroy_read_struct(&pngPtr, (png_infopp)0, (png_infopp)0);
        return false; //Do your own error recovery/handling here
    }

    //Here I've defined 2 pointers up front, so I can use them in error handling.
    //I will explain these 2 later. Just making sure these get deleted on error.
    png_bytep* rowPtrs = NULL;
    char* data = NULL;

    if (setjmp(png_jmpbuf(pngPtr))) {
        //An error occured, so clean up what we have allocated so far...
        png_destroy_read_struct(&pngPtr, &infoPtr,(png_infopp)0);
        if (rowPtrs != NULL) delete [] rowPtrs;
        if (data != NULL) delete [] data;

        std::cout << "ERROR: An error occured while reading the PNG file\n";

        //Make sure you return here. libPNG will jump to here if something
        //goes wrong, and if you continue with your normal code, you might
        //End up with an infinite loop.
        return false; // Do your own error handling here.
    }

    buffer += 8;
    png_set_read_fn(pngPtr, (png_voidp)&buffer, pngReadData);

    //Set the amount signature bytes we've already read:
    //We've defined PNGSIGSIZE as 8;
    png_set_sig_bytes(pngPtr, PNGSIGSIZE);

    //Now call png_read_info with our pngPtr as image handle, and infoPtr to receive the file info.
    png_read_info(pngPtr, infoPtr);

    png_uint_32 imgWidth =  png_get_image_width(pngPtr, infoPtr);
    png_uint_32 imgHeight = png_get_image_height(pngPtr, infoPtr);

    //bits per CHANNEL! note: not per pixel!
    png_uint_32 bitdepth   = png_get_bit_depth(pngPtr, infoPtr);
    //Number of channels
    png_uint_32 channels   = png_get_channels(pngPtr, infoPtr);
    //Color type. (RGB, RGBA, Luminance, luminance alpha... palette... etc)
    png_uint_32 color_type = png_get_color_type(pngPtr, infoPtr);

    width = imgWidth;
    height = imgHeight;

    std::cout << imgWidth << "x" << imgHeight << std::endl;
    std::cout << "Bit Depth: " << bitdepth << std::endl;
    std::cout << "Channels: " << channels << std::endl;
    std::cout << "Color Type: " << color_type << std::endl;

    png_bytep* row_pointers = (png_bytep*) malloc(sizeof(png_bytep) * imgHeight);
    for (int y = 0; y < imgHeight; y++)
    {
        row_pointers[y] = (png_byte*) malloc(png_get_rowbytes(pngPtr,infoPtr));
    }

    delete[] buffer;

    png_read_image(pngPtr, row_pointers);

    if (png_get_color_type(pngPtr, infoPtr) == PNG_COLOR_TYPE_RGB)
    {
        colors = (Color*)new char[imgHeight * imgWidth * sizeof(Color)];

        for (int y=0; y<imgHeight; y++)
        {
            png_byte* row = row_pointers[y];
            for (int x=0; x<imgWidth; x++)
            {
                png_byte* ptr = &(row[x*3]);
                colors[x + y * imgWidth].r = ptr[0];
                colors[x + y * imgWidth].g = ptr[1];
                colors[x + y * imgWidth].b = ptr[2];
                colors[x + y * imgWidth].a = 255;
            }
        }
        return true;
    }

    if (png_get_color_type(pngPtr, infoPtr) == PNG_COLOR_TYPE_RGBA)
    {
        colors = (Color*)new char[imgHeight * imgWidth * sizeof(Color)];

        for (int y=0; y<imgHeight; y++)
        {
            png_byte* row = row_pointers[y];
            for (int x=0; x<imgWidth; x++)
            {
                png_byte* ptr = &(row[x*4]);
                colors[x + y * imgWidth].r = ptr[0];
                colors[x + y * imgWidth].g = ptr[1];
                colors[x + y * imgWidth].b = ptr[2];
                colors[x + y * imgWidth].a = ptr[3];
            }
        }
        return true;
    }

    std::cout << "[process_file] color_type of input file must be PNG_COLOR_TYPE_RGBA (" << PNG_COLOR_TYPE_RGBA << ") (is " << png_get_color_type(pngPtr, infoPtr) << ")" << std::endl;
    return false;
}

bool Jatta::Image::savePng(const std::string& fileName)
{
    // do STUFF
}<commit_msg>Ghetto fixed something. Will properly fix later.<commit_after>#include "Image.h"

#include "../File/File.h"

#include "Color.h"
#include <png.h>
#include <stdlib.h>

#define PNGSIGSIZE 8

static void pngReadData(png_structp pngPointer, png_bytep data, png_size_t length)
{
    char** fileData = (char**)png_get_io_ptr(pngPointer);
    memcpy(data, *fileData, length);
    *fileData += length;
}

/*bool Jatta::Png::isValid(const char* buffer, unsigned int length)
{
    return png_sig_cmp((png_bytep)buffer, 0, PNGSIGSIZE) == 0;
}

bool Jatta::Png::isValid(const std::string& fileName)
{
    // TODO: clean this up / check for errors
    unsigned int size;
    File::getFileSize(fileName, &size);
    char* buffer = new char[size];
    File::getData(fileName, buffer, size);
    bool valid = isValid(buffer, size);
    delete[] buffer;
    return valid;
}*/

bool Jatta::Image::loadPng(const std::string& fileName)
{
    unsigned int size;
    File::getFileSize(fileName, &size);
    char* buffer = new char[size];
    File::getData(fileName, buffer, size);

    //if (!isValid(buffer, size))
    //{
        //return false;
    //}

    //Here we create the png read struct. The 3 NULL's at the end can be used
    //for your own custom error handling functions, but we'll just use the default.
    //if the function fails, NULL is returned. Always check the return values!
    png_structp pngPtr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
    if (!pngPtr) {
        std::cerr << "ERROR: Couldn't initialize png read struct" << std::endl;
        return false; //Do your own error recovery/handling here
    }

    //Here we create the png info struct.
    //Note that this time, if this function fails, we have to clean up the read struct!
    png_infop infoPtr = png_create_info_struct(pngPtr);
    if (!infoPtr) {
        std::cerr << "ERROR: Couldn't initialize png info struct" << std::endl;
        png_destroy_read_struct(&pngPtr, (png_infopp)0, (png_infopp)0);
        return false; //Do your own error recovery/handling here
    }

    //Here I've defined 2 pointers up front, so I can use them in error handling.
    //I will explain these 2 later. Just making sure these get deleted on error.
    png_bytep* rowPtrs = NULL;
    char* data = NULL;

    if (setjmp(png_jmpbuf(pngPtr))) {
        //An error occured, so clean up what we have allocated so far...
        png_destroy_read_struct(&pngPtr, &infoPtr,(png_infopp)0);
        if (rowPtrs != NULL) delete [] rowPtrs;
        if (data != NULL) delete [] data;

        std::cout << "ERROR: An error occured while reading the PNG file\n";

        //Make sure you return here. libPNG will jump to here if something
        //goes wrong, and if you continue with your normal code, you might
        //End up with an infinite loop.
        return false; // Do your own error handling here.
    }

    buffer += 8;
    png_set_read_fn(pngPtr, (png_voidp)&buffer, pngReadData);

    //Set the amount signature bytes we've already read:
    //We've defined PNGSIGSIZE as 8;
    png_set_sig_bytes(pngPtr, PNGSIGSIZE);

    //Now call png_read_info with our pngPtr as image handle, and infoPtr to receive the file info.
    png_read_info(pngPtr, infoPtr);

    png_uint_32 imgWidth =  png_get_image_width(pngPtr, infoPtr);
    png_uint_32 imgHeight = png_get_image_height(pngPtr, infoPtr);

    //bits per CHANNEL! note: not per pixel!
    png_uint_32 bitdepth   = png_get_bit_depth(pngPtr, infoPtr);
    //Number of channels
    png_uint_32 channels   = png_get_channels(pngPtr, infoPtr);
    //Color type. (RGB, RGBA, Luminance, luminance alpha... palette... etc)
    png_uint_32 color_type = png_get_color_type(pngPtr, infoPtr);

    width = imgWidth;
    height = imgHeight;

    std::cout << imgWidth << "x" << imgHeight << std::endl;
    std::cout << "Bit Depth: " << bitdepth << std::endl;
    std::cout << "Channels: " << channels << std::endl;
    std::cout << "Color Type: " << color_type << std::endl;

    png_bytep* row_pointers = (png_bytep*) malloc(sizeof(png_bytep) * imgHeight);
    for (int y = 0; y < imgHeight; y++)
    {
        row_pointers[y] = (png_byte*) malloc(png_get_rowbytes(pngPtr,infoPtr));
    }

    //delete[] buffer;

    png_read_image(pngPtr, row_pointers);

    if (png_get_color_type(pngPtr, infoPtr) == PNG_COLOR_TYPE_RGB)
    {
        colors = (Color*)new char[imgHeight * imgWidth * sizeof(Color)];

        for (int y=0; y<imgHeight; y++)
        {
            png_byte* row = row_pointers[y];
            for (int x=0; x<imgWidth; x++)
            {
                png_byte* ptr = &(row[x*3]);
                colors[x + y * imgWidth].r = ptr[0];
                colors[x + y * imgWidth].g = ptr[1];
                colors[x + y * imgWidth].b = ptr[2];
                colors[x + y * imgWidth].a = 255;
            }
        }
        return true;
    }

    if (png_get_color_type(pngPtr, infoPtr) == PNG_COLOR_TYPE_RGBA)
    {
        colors = (Color*)new char[imgHeight * imgWidth * sizeof(Color)];

        for (int y=0; y<imgHeight; y++)
        {
            png_byte* row = row_pointers[y];
            for (int x=0; x<imgWidth; x++)
            {
                png_byte* ptr = &(row[x*4]);
                colors[x + y * imgWidth].r = ptr[0];
                colors[x + y * imgWidth].g = ptr[1];
                colors[x + y * imgWidth].b = ptr[2];
                colors[x + y * imgWidth].a = ptr[3];
            }
        }
        return true;
    }

    std::cout << "[process_file] color_type of input file must be PNG_COLOR_TYPE_RGBA (" << PNG_COLOR_TYPE_RGBA << ") (is " << png_get_color_type(pngPtr, infoPtr) << ")" << std::endl;
    return false;
}

bool Jatta::Image::savePng(const std::string& fileName)
{
    // do STUFF
}
<|endoftext|>


<commit_before>///
/// @file  MemoryPool.cpp
///        EratMedium and EratBig may use millions of buckets for
///        storing the sieving primes that are required to cross off
///        multiples. As many memory allocations/deallocations are
///        bad for performance the MemoryPool initially allocates a
///        large number of buckets (using a single memory allocation)
///        and puts the buckets into its stock. The MemoryPool can
///        then serve buckets to EratMedium and EratBig without
///        doing any memory allocation as long as the MemoryPool's
///        stock is not empty.
///
/// Copyright (C) 2018 Kim Walisch, <kim.walisch@gmail.com>
///
/// This file is distributed under the BSD License. See the COPYING
/// file in the top level directory.
///

#include <primesieve/MemoryPool.hpp>
#include <primesieve/config.hpp>
#include <primesieve/Bucket.hpp>

#include <stdint.h>
#include <algorithm>
#include <memory>
#include <vector>

namespace primesieve {

void MemoryPool::allocateBuckets()
{
  if (memory_.empty())
    memory_.reserve(128);

  using std::unique_ptr;
  Bucket* buckets = new Bucket[count_];
  memory_.emplace_back(unique_ptr<Bucket[]>(buckets));

  // initialize buckets
  for (uint64_t i = 0; i < count_; i++)
  {
    Bucket* next = nullptr;
    if (i + 1 < count_)
      next = &buckets[i + 1];

    buckets[i].reset();
    buckets[i].setNext(next);
  }

  stock_ = buckets;
  increaseAllocCount();
}

void MemoryPool::increaseAllocCount()
{
  count_ += count_ / 8;
  uint64_t maxCount = config::MAX_ALLOC_BYTES / sizeof(Bucket);
  count_ = std::min(count_, maxCount);
}

void MemoryPool::addBucket(Bucket*& list)
{
  if (!stock_)
    allocateBuckets();

  // get first bucket
  Bucket& bucket = *stock_;
  stock_ = stock_->next();
  bucket.setNext(list);
  list = &bucket;
}

void MemoryPool::freeBucket(Bucket* b)
{
  Bucket& bucket = *b;
  bucket.reset();
  bucket.setNext(stock_);
  stock_ = &bucket;
}

} // namespace
<commit_msg>Refactor<commit_after>///
/// @file  MemoryPool.cpp
///        EratMedium and EratBig may use millions of buckets for
///        storing the sieving primes that are required to cross off
///        multiples. As many memory allocations/deallocations are
///        bad for performance the MemoryPool initially allocates a
///        large number of buckets (using a single memory allocation)
///        and puts the buckets into its stock. The MemoryPool can
///        then serve buckets to EratMedium and EratBig without
///        doing any memory allocation as long as the MemoryPool's
///        stock is not empty.
///
/// Copyright (C) 2018 Kim Walisch, <kim.walisch@gmail.com>
///
/// This file is distributed under the BSD License. See the COPYING
/// file in the top level directory.
///

#include <primesieve/MemoryPool.hpp>
#include <primesieve/config.hpp>
#include <primesieve/Bucket.hpp>

#include <stdint.h>
#include <algorithm>
#include <memory>
#include <vector>

namespace primesieve {

void MemoryPool::addBucket(Bucket*& list)
{
  if (!stock_)
    allocateBuckets();

  // get first bucket
  Bucket& bucket = *stock_;
  stock_ = stock_->next();
  bucket.setNext(list);
  list = &bucket;
}

void MemoryPool::freeBucket(Bucket* b)
{
  Bucket& bucket = *b;
  bucket.reset();
  bucket.setNext(stock_);
  stock_ = &bucket;
}

void MemoryPool::allocateBuckets()
{
  if (memory_.empty())
    memory_.reserve(128);

  using std::unique_ptr;
  Bucket* buckets = new Bucket[count_];
  memory_.emplace_back(unique_ptr<Bucket[]>(buckets));

  // initialize buckets
  for (uint64_t i = 0; i < count_; i++)
  {
    Bucket* next = nullptr;
    if (i + 1 < count_)
      next = &buckets[i + 1];

    buckets[i].reset();
    buckets[i].setNext(next);
  }

  stock_ = buckets;
  increaseAllocCount();
}

void MemoryPool::increaseAllocCount()
{
  count_ += count_ / 8;
  uint64_t maxCount = config::MAX_ALLOC_BYTES / sizeof(Bucket);
  count_ = std::min(count_, maxCount);
}

} // namespace
<|endoftext|>


<commit_before>
#include "Globals.h"  // NOTE: MSVC stupidness requires this to be the same across all modules

#include "MobSpawner.h"
#include "Mobs/IncludeAllMonsters.h"

cMobSpawner::cMobSpawner(cMonster::eFamily a_MonsterFamily, const std::set<eMonsterType>& a_AllowedTypes) :
	m_MonsterFamily(a_MonsterFamily),
	m_NewPack(true),
	m_MobType(mtInvalidType)
{
	for (std::set<eMonsterType>::const_iterator itr = a_AllowedTypes.begin(); itr != a_AllowedTypes.end(); ++itr)
	{
		if (cMonster::FamilyFromType(*itr) == a_MonsterFamily)
		{
			m_AllowedTypes.insert(*itr);
		}
	}
}

bool cMobSpawner::CheckPackCenter(BLOCKTYPE a_BlockType)
{
	// Packs of non-water mobs can only be centered on an air block
	// Packs of water mobs can only be centered on a water block
	if (m_MonsterFamily == cMonster::mfWater)
	{
		return IsBlockWater(a_BlockType);
	}
	else
	{
		return a_BlockType == E_BLOCK_AIR;
	}
}

void cMobSpawner::addIfAllowed(eMonsterType toAdd, std::set<eMonsterType>& toAddIn)
{
	std::set<eMonsterType>::iterator itr = m_AllowedTypes.find(toAdd);
	if (itr != m_AllowedTypes.end())
	{
		toAddIn.insert(toAdd);
	}
}

eMonsterType cMobSpawner::ChooseMobType(EMCSBiome a_Biome)
{
	std::set<eMonsterType> allowedMobs;

	if (a_Biome == biMushroomIsland || a_Biome == biMushroomShore)
	{
		addIfAllowed(mtMooshroom, allowedMobs);
	}
	else if (a_Biome == biNether)
	{
		addIfAllowed(mtGhast, allowedMobs);
		addIfAllowed(mtZombiePigman, allowedMobs);
		addIfAllowed(mtMagmaCube, allowedMobs);
	}
	else if (a_Biome == biEnd)
	{
		addIfAllowed(mtEnderman, allowedMobs);
	}
	else
	{
		addIfAllowed(mtBat, allowedMobs);
		addIfAllowed(mtSpider, allowedMobs);
		addIfAllowed(mtZombie, allowedMobs);
		addIfAllowed(mtSkeleton, allowedMobs);
		addIfAllowed(mtCreeper, allowedMobs);
		addIfAllowed(mtSquid, allowedMobs);

		if (a_Biome != biDesert && a_Biome != biBeach && a_Biome != biOcean)
		{
			addIfAllowed(mtSheep, allowedMobs);
			addIfAllowed(mtPig, allowedMobs);
			addIfAllowed(mtCow, allowedMobs);
			addIfAllowed(mtChicken, allowedMobs);
			addIfAllowed(mtEnderman, allowedMobs);
			addIfAllowed(mtSlime, allowedMobs);  // MG TODO : much more complicated rule

			if (a_Biome == biForest || a_Biome == biForestHills || a_Biome == biTaiga  || a_Biome == biTaigaHills)
			{
				addIfAllowed(mtWolf, allowedMobs);
			}
			else if (a_Biome == biJungle || a_Biome == biJungleHills)
			{
				addIfAllowed(mtOcelot, allowedMobs);
			}
		}
	}

	size_t allowedMobsSize = allowedMobs.size();
	if (allowedMobsSize > 0)
	{
		std::set<eMonsterType>::iterator itr = allowedMobs.begin();
		int iRandom = m_Random.NextInt((int)allowedMobsSize, a_Biome);

		for (int i = 0; i < iRandom; i++)
		{
			++itr;
		}

		return *itr;
	}
	return mtInvalidType;
}

bool cMobSpawner::CanSpawnHere(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ, eMonsterType a_MobType, EMCSBiome a_Biome)
{
	cFastRandom Random;
	BLOCKTYPE TargetBlock = E_BLOCK_AIR;
	if (a_Chunk->UnboundedRelGetBlockType(a_RelX, a_RelY, a_RelZ, TargetBlock))
	{
		if ((a_RelY + 1 > cChunkDef::Height) || (a_RelY - 1 < 0))
		{
			return false;
		}

		NIBBLETYPE BlockLight = a_Chunk->GetBlockLight(a_RelX, a_RelY, a_RelZ);
		NIBBLETYPE SkyLight = a_Chunk->GetSkyLight(a_RelX, a_RelY, a_RelZ);
		BLOCKTYPE BlockAbove = a_Chunk->GetBlock(a_RelX, a_RelY + 1, a_RelZ);
		BLOCKTYPE BlockBelow = a_Chunk->GetBlock(a_RelX, a_RelY - 1, a_RelZ);

		SkyLight = a_Chunk->GetTimeAlteredLight(SkyLight);

		switch (a_MobType)
		{
			case mtSquid:
			{
				return IsBlockWater(TargetBlock) && (a_RelY >= 45) && (a_RelY <= 62);
			}

			case mtBat:
			{
				return (a_RelY <= 63) && (BlockLight <= 4) && (SkyLight <= 4) && (TargetBlock == E_BLOCK_AIR) && !cBlockInfo::IsTransparent(BlockAbove);
			}

			case mtChicken:
			case mtCow:
			case mtPig:
			case mtHorse:
			case mtSheep:
			{
				return (
					(TargetBlock == E_BLOCK_AIR) &&
					(BlockAbove == E_BLOCK_AIR) &&
					(!cBlockInfo::IsTransparent(BlockBelow)) &&
					(BlockBelow == E_BLOCK_GRASS) &&
					(SkyLight >= 9)
				);
			}

			case mtOcelot:
			{
				return (
					(TargetBlock == E_BLOCK_AIR) &&
					(BlockAbove == E_BLOCK_AIR) &&
					(
						(BlockBelow == E_BLOCK_GRASS) || (BlockBelow == E_BLOCK_LEAVES) || (BlockBelow == E_BLOCK_NEW_LEAVES)
					) &&
					(a_RelY >= 62) &&
					(Random.NextInt(3, a_Biome) != 0)
				);
			}

			case mtEnderman:
			{
				if (a_RelY < 250)
				{
					BLOCKTYPE BlockTop = a_Chunk->GetBlock(a_RelX, a_RelY + 2, a_RelZ);
					if (BlockTop == E_BLOCK_AIR)
					{
						BlockTop = a_Chunk->GetBlock(a_RelX, a_RelY + 3, a_RelZ);
						return (
							(TargetBlock == E_BLOCK_AIR) &&
							(BlockAbove == E_BLOCK_AIR) &&
							(BlockTop == E_BLOCK_AIR) &&
							(!cBlockInfo::IsTransparent(BlockBelow)) &&
							(SkyLight <= 7) &&
							(BlockLight <= 7)
						);
					}
				}
				break;
			}

			case mtSpider:
			{
				bool CanSpawn = true;
				bool HasFloor = false;
				for (int x = 0; x < 2; ++x)
				{
					for (int z = 0; z < 2; ++z)
					{
						CanSpawn = a_Chunk->UnboundedRelGetBlockType(a_RelX + x, a_RelY, a_RelZ + z, TargetBlock);
						CanSpawn = CanSpawn && (TargetBlock == E_BLOCK_AIR);
						if (!CanSpawn)
						{
							return false;
						}
						HasFloor = (
							HasFloor ||
							(
								a_Chunk->UnboundedRelGetBlockType(a_RelX + x, a_RelY - 1, a_RelZ + z, TargetBlock) &&
								!cBlockInfo::IsTransparent(TargetBlock)
							)
						);
					}
				}
				return CanSpawn && HasFloor && (SkyLight <= 7) && (BlockLight <= 7);
			}

			case mtCreeper:
			case mtSkeleton:
			case mtZombie:
			{
				return (
					(TargetBlock == E_BLOCK_AIR) &&
					(BlockAbove == E_BLOCK_AIR) &&
					(!cBlockInfo::IsTransparent(BlockBelow)) &&
					(SkyLight <= 7) &&
					(BlockLight <= 7) &&
					(Random.NextInt(2, a_Biome) == 0)
				);
			}

			case mtMagmaCube:
			case mtSlime:
			{
				return (
					(TargetBlock == E_BLOCK_AIR) &&
					(BlockAbove == E_BLOCK_AIR) &&
					(!cBlockInfo::IsTransparent(BlockBelow)) &&
					(
						(a_RelY <= 40) || (a_Biome == biSwampland)
					)
				);
			}

			case mtGhast:
			case mtZombiePigman:
			{
				return (
					(TargetBlock == E_BLOCK_AIR) &&
					(BlockAbove == E_BLOCK_AIR) &&
					(!cBlockInfo::IsTransparent(BlockBelow)) &&
					(Random.NextInt(20, a_Biome) == 0)
				);
			}

			case mtWolf:
			{
				return (
					(TargetBlock == E_BLOCK_GRASS) &&
					(BlockAbove == E_BLOCK_AIR) &&
					(
						(a_Biome == biTaiga) ||
						(a_Biome == biTaigaHills) ||
						(a_Biome == biForest) ||
						(a_Biome == biForestHills) ||
						(a_Biome == biColdTaiga) ||
						(a_Biome == biColdTaigaHills) ||
						(a_Biome == biTaigaM) ||
						(a_Biome == biMegaTaiga) ||
						(a_Biome == biMegaTaigaHills)
					)
				);
			}

			case mtMooshroom:
			{
				return (
					(TargetBlock == E_BLOCK_AIR) &&
					(BlockAbove == E_BLOCK_AIR) &&
					(!cBlockInfo::IsTransparent(BlockBelow)) &&
					(
						(a_Biome == biMushroomShore) ||
						(a_Biome == biMushroomIsland)
					)
				);
			}

			default:
			{
				LOGD("MG TODO: Write spawning rule for mob type %d", a_MobType);
				return false;
			}
		}
	}
	return false;
}

cMonster* cMobSpawner::TryToSpawnHere(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ, EMCSBiome a_Biome, int& a_MaxPackSize)
{
	cMonster* toReturn = nullptr;
	if (m_NewPack)
	{
		m_MobType = ChooseMobType(a_Biome);
		if (m_MobType == mtInvalidType)
		{
			return toReturn;
		}
		if (m_MobType == mtWolf)
		{
			a_MaxPackSize = 8;
		}
		else if (m_MobType == mtGhast)
		{
			a_MaxPackSize = 1;
		}
		m_NewPack = false;
	}

	// Make sure we are looking at the right chunk to spawn in
	a_Chunk = a_Chunk->GetRelNeighborChunkAdjustCoords(a_RelX, a_RelZ);

	if ((m_AllowedTypes.find(m_MobType) != m_AllowedTypes.end()) && CanSpawnHere(a_Chunk, a_RelX, a_RelY, a_RelZ, m_MobType, a_Biome))
	{
		cMonster * newMob = cMonster::NewMonsterFromType(m_MobType);
		if (newMob)
		{
			m_Spawned.insert(newMob);
		}
		toReturn = newMob;
	}
	return toReturn;
}

void cMobSpawner::NewPack()
{
	m_NewPack = true;
}

cMobSpawner::tSpawnedContainer & cMobSpawner::getSpawned(void)
{
	return m_Spawned;
}

bool cMobSpawner::CanSpawnAnything(void)
{
	return !m_AllowedTypes.empty();
}

<commit_msg>updated mooshroom check for mycelium<commit_after>
#include "Globals.h"  // NOTE: MSVC stupidness requires this to be the same across all modules

#include "MobSpawner.h"
#include "Mobs/IncludeAllMonsters.h"

cMobSpawner::cMobSpawner(cMonster::eFamily a_MonsterFamily, const std::set<eMonsterType>& a_AllowedTypes) :
	m_MonsterFamily(a_MonsterFamily),
	m_NewPack(true),
	m_MobType(mtInvalidType)
{
	for (std::set<eMonsterType>::const_iterator itr = a_AllowedTypes.begin(); itr != a_AllowedTypes.end(); ++itr)
	{
		if (cMonster::FamilyFromType(*itr) == a_MonsterFamily)
		{
			m_AllowedTypes.insert(*itr);
		}
	}
}

bool cMobSpawner::CheckPackCenter(BLOCKTYPE a_BlockType)
{
	// Packs of non-water mobs can only be centered on an air block
	// Packs of water mobs can only be centered on a water block
	if (m_MonsterFamily == cMonster::mfWater)
	{
		return IsBlockWater(a_BlockType);
	}
	else
	{
		return a_BlockType == E_BLOCK_AIR;
	}
}

void cMobSpawner::addIfAllowed(eMonsterType toAdd, std::set<eMonsterType>& toAddIn)
{
	std::set<eMonsterType>::iterator itr = m_AllowedTypes.find(toAdd);
	if (itr != m_AllowedTypes.end())
	{
		toAddIn.insert(toAdd);
	}
}

eMonsterType cMobSpawner::ChooseMobType(EMCSBiome a_Biome)
{
	std::set<eMonsterType> allowedMobs;

	if (a_Biome == biMushroomIsland || a_Biome == biMushroomShore)
	{
		addIfAllowed(mtMooshroom, allowedMobs);
	}
	else if (a_Biome == biNether)
	{
		addIfAllowed(mtGhast, allowedMobs);
		addIfAllowed(mtZombiePigman, allowedMobs);
		addIfAllowed(mtMagmaCube, allowedMobs);
	}
	else if (a_Biome == biEnd)
	{
		addIfAllowed(mtEnderman, allowedMobs);
	}
	else
	{
		addIfAllowed(mtBat, allowedMobs);
		addIfAllowed(mtSpider, allowedMobs);
		addIfAllowed(mtZombie, allowedMobs);
		addIfAllowed(mtSkeleton, allowedMobs);
		addIfAllowed(mtCreeper, allowedMobs);
		addIfAllowed(mtSquid, allowedMobs);

		if (a_Biome != biDesert && a_Biome != biBeach && a_Biome != biOcean)
		{
			addIfAllowed(mtSheep, allowedMobs);
			addIfAllowed(mtPig, allowedMobs);
			addIfAllowed(mtCow, allowedMobs);
			addIfAllowed(mtChicken, allowedMobs);
			addIfAllowed(mtEnderman, allowedMobs);
			addIfAllowed(mtSlime, allowedMobs);  // MG TODO : much more complicated rule

			if (a_Biome == biForest || a_Biome == biForestHills || a_Biome == biTaiga  || a_Biome == biTaigaHills)
			{
				addIfAllowed(mtWolf, allowedMobs);
			}
			else if (a_Biome == biJungle || a_Biome == biJungleHills)
			{
				addIfAllowed(mtOcelot, allowedMobs);
			}
		}
	}

	size_t allowedMobsSize = allowedMobs.size();
	if (allowedMobsSize > 0)
	{
		std::set<eMonsterType>::iterator itr = allowedMobs.begin();
		int iRandom = m_Random.NextInt((int)allowedMobsSize, a_Biome);

		for (int i = 0; i < iRandom; i++)
		{
			++itr;
		}

		return *itr;
	}
	return mtInvalidType;
}

bool cMobSpawner::CanSpawnHere(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ, eMonsterType a_MobType, EMCSBiome a_Biome)
{
	cFastRandom Random;
	BLOCKTYPE TargetBlock = E_BLOCK_AIR;
	if (a_Chunk->UnboundedRelGetBlockType(a_RelX, a_RelY, a_RelZ, TargetBlock))
	{
		if ((a_RelY + 1 > cChunkDef::Height) || (a_RelY - 1 < 0))
		{
			return false;
		}

		NIBBLETYPE BlockLight = a_Chunk->GetBlockLight(a_RelX, a_RelY, a_RelZ);
		NIBBLETYPE SkyLight = a_Chunk->GetSkyLight(a_RelX, a_RelY, a_RelZ);
		BLOCKTYPE BlockAbove = a_Chunk->GetBlock(a_RelX, a_RelY + 1, a_RelZ);
		BLOCKTYPE BlockBelow = a_Chunk->GetBlock(a_RelX, a_RelY - 1, a_RelZ);

		SkyLight = a_Chunk->GetTimeAlteredLight(SkyLight);

		switch (a_MobType)
		{
			case mtSquid:
			{
				return IsBlockWater(TargetBlock) && (a_RelY >= 45) && (a_RelY <= 62);
			}

			case mtBat:
			{
				return (a_RelY <= 63) && (BlockLight <= 4) && (SkyLight <= 4) && (TargetBlock == E_BLOCK_AIR) && !cBlockInfo::IsTransparent(BlockAbove);
			}

			case mtChicken:
			case mtCow:
			case mtPig:
			case mtHorse:
			case mtSheep:
			{
				return (
					(TargetBlock == E_BLOCK_AIR) &&
					(BlockAbove == E_BLOCK_AIR) &&
					(!cBlockInfo::IsTransparent(BlockBelow)) &&
					(BlockBelow == E_BLOCK_GRASS) &&
					(SkyLight >= 9)
				);
			}

			case mtOcelot:
			{
				return (
					(TargetBlock == E_BLOCK_AIR) &&
					(BlockAbove == E_BLOCK_AIR) &&
					(
						(BlockBelow == E_BLOCK_GRASS) || (BlockBelow == E_BLOCK_LEAVES) || (BlockBelow == E_BLOCK_NEW_LEAVES)
					) &&
					(a_RelY >= 62) &&
					(Random.NextInt(3, a_Biome) != 0)
				);
			}

			case mtEnderman:
			{
				if (a_RelY < 250)
				{
					BLOCKTYPE BlockTop = a_Chunk->GetBlock(a_RelX, a_RelY + 2, a_RelZ);
					if (BlockTop == E_BLOCK_AIR)
					{
						BlockTop = a_Chunk->GetBlock(a_RelX, a_RelY + 3, a_RelZ);
						return (
							(TargetBlock == E_BLOCK_AIR) &&
							(BlockAbove == E_BLOCK_AIR) &&
							(BlockTop == E_BLOCK_AIR) &&
							(!cBlockInfo::IsTransparent(BlockBelow)) &&
							(SkyLight <= 7) &&
							(BlockLight <= 7)
						);
					}
				}
				break;
			}

			case mtSpider:
			{
				bool CanSpawn = true;
				bool HasFloor = false;
				for (int x = 0; x < 2; ++x)
				{
					for (int z = 0; z < 2; ++z)
					{
						CanSpawn = a_Chunk->UnboundedRelGetBlockType(a_RelX + x, a_RelY, a_RelZ + z, TargetBlock);
						CanSpawn = CanSpawn && (TargetBlock == E_BLOCK_AIR);
						if (!CanSpawn)
						{
							return false;
						}
						HasFloor = (
							HasFloor ||
							(
								a_Chunk->UnboundedRelGetBlockType(a_RelX + x, a_RelY - 1, a_RelZ + z, TargetBlock) &&
								!cBlockInfo::IsTransparent(TargetBlock)
							)
						);
					}
				}
				return CanSpawn && HasFloor && (SkyLight <= 7) && (BlockLight <= 7);
			}

			case mtCreeper:
			case mtSkeleton:
			case mtZombie:
			{
				return (
					(TargetBlock == E_BLOCK_AIR) &&
					(BlockAbove == E_BLOCK_AIR) &&
					(!cBlockInfo::IsTransparent(BlockBelow)) &&
					(SkyLight <= 7) &&
					(BlockLight <= 7) &&
					(Random.NextInt(2, a_Biome) == 0)
				);
			}

			case mtMagmaCube:
			case mtSlime:
			{
				return (
					(TargetBlock == E_BLOCK_AIR) &&
					(BlockAbove == E_BLOCK_AIR) &&
					(!cBlockInfo::IsTransparent(BlockBelow)) &&
					(
						(a_RelY <= 40) || (a_Biome == biSwampland)
					)
				);
			}

			case mtGhast:
			case mtZombiePigman:
			{
				return (
					(TargetBlock == E_BLOCK_AIR) &&
					(BlockAbove == E_BLOCK_AIR) &&
					(!cBlockInfo::IsTransparent(BlockBelow)) &&
					(Random.NextInt(20, a_Biome) == 0)
				);
			}

			case mtWolf:
			{
				return (
					(TargetBlock == E_BLOCK_GRASS) &&
					(BlockAbove == E_BLOCK_AIR) &&
					(
						(a_Biome == biTaiga) ||
						(a_Biome == biTaigaHills) ||
						(a_Biome == biForest) ||
						(a_Biome == biForestHills) ||
						(a_Biome == biColdTaiga) ||
						(a_Biome == biColdTaigaHills) ||
						(a_Biome == biTaigaM) ||
						(a_Biome == biMegaTaiga) ||
						(a_Biome == biMegaTaigaHills)
					)
				);
			}

			case mtMooshroom:
			{
				return (
					(TargetBlock == E_BLOCK_AIR) &&
					(BlockAbove == E_BLOCK_AIR) &&
					(BlockBelow == E_BLOCK_MYCELIUM) &&
					(
						(a_Biome == biMushroomShore) ||
						(a_Biome == biMushroomIsland)
					)
				);
			}

			default:
			{
				LOGD("MG TODO: Write spawning rule for mob type %d", a_MobType);
				return false;
			}
		}
	}
	return false;
}

cMonster* cMobSpawner::TryToSpawnHere(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ, EMCSBiome a_Biome, int& a_MaxPackSize)
{
	cMonster* toReturn = nullptr;
	if (m_NewPack)
	{
		m_MobType = ChooseMobType(a_Biome);
		if (m_MobType == mtInvalidType)
		{
			return toReturn;
		}
		if (m_MobType == mtWolf)
		{
			a_MaxPackSize = 8;
		}
		else if (m_MobType == mtGhast)
		{
			a_MaxPackSize = 1;
		}
		m_NewPack = false;
	}

	// Make sure we are looking at the right chunk to spawn in
	a_Chunk = a_Chunk->GetRelNeighborChunkAdjustCoords(a_RelX, a_RelZ);

	if ((m_AllowedTypes.find(m_MobType) != m_AllowedTypes.end()) && CanSpawnHere(a_Chunk, a_RelX, a_RelY, a_RelZ, m_MobType, a_Biome))
	{
		cMonster * newMob = cMonster::NewMonsterFromType(m_MobType);
		if (newMob)
		{
			m_Spawned.insert(newMob);
		}
		toReturn = newMob;
	}
	return toReturn;
}

void cMobSpawner::NewPack()
{
	m_NewPack = true;
}

cMobSpawner::tSpawnedContainer & cMobSpawner::getSpawned(void)
{
	return m_Spawned;
}

bool cMobSpawner::CanSpawnAnything(void)
{
	return !m_AllowedTypes.empty();
}

<|endoftext|>