Welcome to lark3ri.com

Protection code for random kick votes

I wrote this addition to the Xonotic source code. This is added to both of my Xonotic servers. This addition prevents random players from kick voting me out of the server when i'm afk in my server. More player crypto_id values can be added to the sv_protected_players to prevent players to ever kick vote those players out of the server. First it was just a chatbot but it has many flaws. The chatbot was written in bash, it read the Xonotic server log file and worked based on that. I had a hard time to create a working regex code that would work on it so that it can not be manipulated through the player name text. Now this is straight addition to the Xonotic source code and simple and straightforward also in the code side.

The problem is that everybody can run multiple instances of the Xonotic game and join in the server with all of those instances at the same time. With all of those instances connected it is bossible to get votes through. Now with this addition, when someone tries to kick a player out which is in the variable sv_protected_players list, the code will reverse the kick and it start a vote to kick the voter itself out instead.

If you wanna add this code to your Xonotic server, follow these steps. Firstly you need to add the vote.qh code addition to the end of the file vote.qh and add players crypto_id values to the variable sv_protected_players to your server config file.

Separated by spaces like this sv_protected_players "crypto_id crypto_id crypto_id". You can find connected players crypto_id values by using who command.

Secondly you need to add the vote.qc code addition to the file vote.qc exactly under the line 714. The exact line number may change over time when the file got updates. The exact position should be inside and at the end of the if statement if (accepted > 0). Now you can recompile the server code by using this command ./all compile -r dedicated.

vote.qh
string autocvar_sv_protected_players;
string autocvar_sv_protected_players;
string autocvar_sv_protected_players;

vote.qc
if (etof(caller) == 0
|| !autocvar_sv_protected_players
|| autocvar_sv_protected_players == "")
{
break;
}
string identity_key = victim.playerstats_id;
if (identity_key == "")
{
identity_key = victim.crypto_idfp;
}
if (identity_key == "")
{
break;
}
float n = tokenizebyseparator(autocvar_sv_protected_players, " ");
float i;
for (i = 0; i < n; i++)
{
string protected_player_id = argv(i);
if (identity_key == protected_player_id)
{
string voter_caller_name = strzone(GetCallerName(caller));
int voter_caller_id = etof(caller);
GameLogEcho(sprintf("^2* ^1REVERSE KICK VOTE ^3%s ^2tried to call a vote for ^1kick ^1#%d ^3%s",
voter_caller_name,
etof(victim),
victim.netname));
vote_parsed_command = strcat(first_command, " # ", ftos(voter_caller_id), " ", command_arguments);
vote_parsed_display = sprintf("^1%s #%d ^7%s^1 %s", first_command, voter_caller_id, voter_caller_name, reason);
break;
}
}
if (etof(caller) == 0 || !autocvar_sv_protected_players || autocvar_sv_protected_players == "") { break; } string identity_key = victim.playerstats_id; if (identity_key == "") { identity_key = victim.crypto_idfp; } if (identity_key == "") { break; } float n = tokenizebyseparator(autocvar_sv_protected_players, " "); float i; for (i = 0; i < n; i++) { string protected_player_id = argv(i); if (identity_key == protected_player_id) { string voter_caller_name = strzone(GetCallerName(caller)); int voter_caller_id = etof(caller); GameLogEcho(sprintf("^2* ^1REVERSE KICK VOTE ^3%s ^2tried to call a vote for ^1kick ^1#%d ^3%s", voter_caller_name, etof(victim), victim.netname)); vote_parsed_command = strcat(first_command, " # ", ftos(voter_caller_id), " ", command_arguments); vote_parsed_display = sprintf("^1%s #%d ^7%s^1 %s", first_command, voter_caller_id, voter_caller_name, reason); break; } }
if (etof(caller) == 0
        || !autocvar_sv_protected_players
        || autocvar_sv_protected_players == "")
    {
        break;
    }
    
    string identity_key = victim.playerstats_id;
    if (identity_key == "")
    {
        identity_key = victim.crypto_idfp;
    }
    if (identity_key == "")
    {
        break;
    }
    
    float n = tokenizebyseparator(autocvar_sv_protected_players, " ");
    float i;
    for (i = 0; i < n; i++)
    {
        string protected_player_id = argv(i);
        if (identity_key == protected_player_id)
        {
            string voter_caller_name = strzone(GetCallerName(caller));
            int voter_caller_id = etof(caller);
    
            GameLogEcho(sprintf("^2* ^1REVERSE KICK VOTE ^3%s ^2tried to call a vote for ^1kick ^1#%d ^3%s",
                voter_caller_name,
                etof(victim),
                victim.netname));
    
            vote_parsed_command = strcat(first_command, " # ", ftos(voter_caller_id), " ", command_arguments);
            vote_parsed_display = sprintf("^1%s #%d ^7%s^1 %s", first_command, voter_caller_id, voter_caller_name, reason);
    
            break;
        }
    }
    

Financial management program with c++

28.11.2020

Financial management program which I program for more detailed financial management. Includes only the features I actually need so I can keep it simple to manage my finances. Includes enough features, as example, working command and command line parameters are:

./program.exe database-path=data start-date="15.11.2020 02:00:00" end-date="15.11.2020 02:00:03" credit-first no-color delete=1 it deletes the first result between those dates.

I use Cygwin to run this program on Windows. I have not tested functionality on any other platform. Default database path is data. You can find more default settings to be set in std::map<std::string, std::string> sett, line 57. Here is a picture of the program:


main.cpp
/*
Copyright(C) 2020 Lari Varjonen <[email protected]>
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
version 2 as published by the Free Software Foundation.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110 - 1301, USA.
Version: 1.2
*/
#include <iostream>
#include <fstream>
#include <vector>
#include <map>
#include <iomanip>
#ifdef _WIN32
#include <windows.h>
#pragma execution_character_set("utf-8")
#endif
//#define _DEBUG
class SETT
{
const std::string settings_fp = "settings";
const std::string translation_dp = "translations/";
bool veccmp(const std::vector<std::string> &arr, const std::string &val)
{
for (std::vector<std::string>::const_iterator it = arr.begin(); it != arr.end(); it++)
{
if (it->compare(val) == 0)
{
return true;
}
}
return false;
}
std::vector<std::string> cmd_arg = {
"SV_NEW",
"SV_DELETE"
};
std::map<std::string, std::string> sett = {
{"SV_DATABASE_PATH", "data"},
{"SV_LANGUAGE", "en"},
{"SV_TIMEZONE", "+0"},
{"SV_DATE_ORDER", "DMYhms"},
{"SV_CREDIT_COLOR", "1;32"},
{"SV_DEBIT_COLOR", "1;35"},
{"SV_ORDER_BY_DATE", "0"},
{"SV_DEBIT_FIRST", "0"},
{"SV_CREDIT_FIRST", "0"},
{"SV_ONLY_DEBIT", "0"},
{"SV_ONLY_CREDIT", "0"},
{"SV_NO_COLOR", "0"},
{"SV_NEW", "0"},
{"SV_DELETE", ""},
{"SV_START_DATE", ""},
{"SV_END_DATE", ""},
{"SV_FORMAT", "%d.%m.%Y %H:%M:%S"},
{"SV_ORDER", "cod-dat-nam-amo-ref-des-tzo"},
{"SV_CURRENCY", "$"},
{"SV_CURRENCY_SIDE", "1"},
{"T_CREDIT_OR_DEBIT", "Credit or Debit?(credit/debit): "},
{"T_CREDIT_OR_DEBIT_A", "credit"},
{"T_DAY", "Day:\t"},
{"T_MONTH", "Month:\t"},
{"T_YEAR", "Year:\t"},
{"T_HOURS", "Hours:\t"},
{"T_MINUTES", "Minutes:\t"},
{"T_SECONDS", "Seconds:\t"},
{"T_NAME", "Name:\t\t"},
{"T_AMOUNT", "Amount:\t\t"},
{"T_REF", "Ref:\t\t"},
{"T_DESCRIPTION", "Description:\t"},
{"T_TIMEZONE", "Timezone:\t"},
{"T_DATE", "Date:\t\t"},
{"T_BALANCE", "Balance: "},
{"T_CREDIT", "CREDIT"},
{"T_DEBIT", "DEBIT"}
};
public:
SETT();
std::string get(const std::string &key);
bool getb(const std::string& key);
bool add(std::string key, const std::string &val);
};
SETT::SETT()
{
// Read settings file
std::ifstream file(settings_fp);
if (!file.good())
{
return;
}
if (file.peek() == std::ifstream::traits_type::eof())
{
return;
}
std::string current_line;
while (std::getline(file, current_line))
{
if (current_line.empty()) continue;
std::string key;
std::string val;
int index = current_line.find("=");
if (index != -1)
{
key = current_line.substr(0, index);
val = current_line.substr(index + 1, current_line.size() - 1);
}
else
{
key = current_line;
val = "1";
}
for (unsigned int i = 0; i < key.size(); i++)
{
char c = key.at(i);
// abc --> ABC
if (c > 96 && c < 123)
{
c -= 32;
key.at(i) = c;
}
}
if (!veccmp(cmd_arg, key) && key.at(0) == 'S' && key.at(1) == 'V')
{
std::map<std::string, std::string>::iterator it = sett.find(key);
if (it != sett.end())
{
it->second = val;
}
}
}
// Read translations file
std::string langauge_fn = get("SV_LANGUAGE");
if (!langauge_fn.empty())
{
std::ifstream file0(translation_dp + langauge_fn);
if (!file0.good())
{
return;
}
if (file0.peek() == std::ifstream::traits_type::eof())
{
return;
}
while (std::getline(file0, current_line))
{
if (current_line.empty()) continue;
std::string key;
std::string val;
int index = current_line.find("=");
if (index != -1)
{
key = current_line.substr(0, index);
val = current_line.substr(index + 1, current_line.size() - 1);
}
else
{
key = current_line;
val = "1";
}
for (unsigned int i = 0; i < key.size(); i++)
{
char c = key.at(i);
// abc --> ABC
if (c > 96 && c < 123)
{
c -= 32;
key.at(i) = c;
}
}
if (!veccmp(cmd_arg, key) && key.at(0) == 'T')
{
std::map<std::string, std::string>::iterator it = sett.find(key);
if (it != sett.end())
{
it->second = val;
}
}
}
}
#ifdef _DEBUG
for (std::map<std::string, std::string>::iterator it = sett.begin(); it != sett.end(); it++)
{
std::cout << it->first << "=" << it->second << std::endl;
}
#endif
}
std::string SETT::get(const std::string &key)
{
std::map<std::string, std::string>::iterator it = sett.find(key);
if (it != sett.end())
{
return it->second;
}
return std::string("");
}
bool SETT::getb(const std::string& key)
{
std::map<std::string, std::string>::iterator it = sett.find(key);
if (it != sett.end())
{
return std::stoi(it->second) > 0;
}
return false;
}
bool SETT::add(std::string key, const std::string &val)
{
for (unsigned int i = 0; i < key.size(); i++)
{
char c = key.at(i);
if (c == '-')
{
key.at(i) = '_';
continue;
}
// abc --> ABC
if (c > 96 && c < 123)
{
c -= 32;
key.at(i) = c;
}
}
key = std::string("SV_") + key;
std::map<std::string, std::string>::iterator it = sett.find(key);
if (it != sett.end())
{
it->second = val;
return false;
}
return true;
}
struct transaction {
time_t date;
time_t date_current;
std::string reference_num;
int timezone;
bool credit_or_debit;
double amount;
std::string name;
std::string description;
unsigned int index;
};
std::vector<transaction> data; // All data.
std::vector<transaction> data0; // Data visible.
void read_database(const std::string &fn);
bool save_database(const std::string &fn);
int set_timezone(const std::string &tz);
std::string get_timezone(int timezone);
time_t date_to_seconds(const std::string& date)
{
int yy, month, dd, hh, mm, ss;
const char* datec = date.c_str();
sscanf(datec, "%d.%d.%d %d:%d:%d", &dd, &month, &yy, &hh, &mm, &ss);
struct tm tm;
tm.tm_year = yy - 1900;
tm.tm_mon = month - 1;
tm.tm_mday = dd;
tm.tm_hour = hh;
tm.tm_min = mm;
tm.tm_sec = ss;
tm.tm_isdst = -1;
time_t t = mktime(&tm);
return t;
}
time_t date_to_seconds(const std::string& date, std::string format)
{
char u[6] = {0};
int i = 0;
for (std::string::iterator it = format.begin(); it != format.end(); it++)
{
if (*it == '%')
{
u[i] = *(it + 1);
*(it + 1) = 'd';
i++;
}
}
int y[6] = {0};
const char* datec = date.c_str();
sscanf(datec, format.c_str(), &y[0], &y[1], &y[2], &y[3], &y[4], &y[5]);
int yy, month, dd, hh, mm, ss;
for (i = 0; i < 6; i++)
{
if (u[i] == 'd')
{
dd = y[i];
continue;
}
if (u[i] == 'm')
{
month = y[i];
continue;
}
if (u[i] == 'Y')
{
yy = y[i];
continue;
}
if (u[i] == 'H')
{
hh = y[i];
continue;
}
if (u[i] == 'M')
{
mm = y[i];
continue;
}
if (u[i] == 'S')
{
ss = y[i];
}
}
struct tm tm;
tm.tm_year = yy - 1900;
tm.tm_mon = month - 1;
tm.tm_mday = dd;
tm.tm_hour = hh;
tm.tm_min = mm;
tm.tm_sec = ss;
tm.tm_isdst = -1;
time_t t = mktime(&tm);
return t;
}
void get_word(std::string *val)
{
std::string usr_input;
std::getline(std::cin, usr_input);
if (usr_input.empty())
{
*val = "";
return;
}
int find_pos = usr_input.find(" ");
if (find_pos != -1)
{
usr_input = usr_input.substr(0, find_pos);
}
*val = usr_input;
}
int main(int argc, char *argv[])
{
#ifdef _WIN32
SetConsoleOutputCP(65001);
#endif
std::string fp;
std::vector<std::string> cmd_arg;
SETT sett;
if (argc > 1)
{
cmd_arg = std::vector<std::string>(argv, argv + argc);
cmd_arg.erase(cmd_arg.begin());
cmd_arg.shrink_to_fit();
for (std::vector<std::string>::iterator it = cmd_arg.begin(); it != cmd_arg.end(); it++)
{
std::string lhs;
std::string rhs;
int index = it->find('=');
if (index != -1)
{
lhs = it->substr(0, index);
rhs = it->substr(index + 1, it->size());
}
else
{
static bool prevent_double = false;
if (!prevent_double)
{
prevent_double = true;
std::vector<std::string>::iterator it0 = it;
for (; it0 != cmd_arg.end(); it0++)
{
int index0 = it0->find('=');
if (index0 != -1)
return 0;
}
}
lhs = *it;
rhs = "1";
}
sett.add(lhs, rhs);
}
}
int timezone = set_timezone(sett.get("SV_TIMEZONE"));
const std::string format = sett.get("SV_FORMAT");
const std::string order = sett.get("SV_ORDER");
fp = sett.get("SV_DATABASE_PATH");
read_database(fp);
// Order by date
if (!data.empty())
{
for (unsigned int i = 0; i < data.size() - 1; i++)
{
if (data.at(i).date > data.at(i + 1).date)
{
for (unsigned int u = i; u < data.size() - 1; u++)
{
if (data.at(u).date < data.at(u + 1).date) break;
std::vector<transaction>::iterator it = data.begin() + u;
data.insert(data.begin() + u + 2, *it);
data.erase(data.begin() + u);
}
if (i > 0) i -= 2;
else i--;
}
}
data0 = data;
}
if (argc > 1)
{
// SV_NEW - cmd arg
if (sett.getb("SV_NEW"))
{
transaction new_data;
for (unsigned int i = 0; i < 7; i++)
{
std::string usr_input;
char current[4];
current[0] = order.at(4 * i);
current[1] = order.at(4 * i + 1);
current[2] = order.at(4 * i + 2);
current[3] = '\0';
// credit or debit
if (strcmp(current, "cod") == 0)
{
std::cout << sett.get("T_CREDIT_OR_DEBIT");
get_word(&usr_input);
if (usr_input.empty())
return 0;
if (strcmp(usr_input.c_str(), sett.get("T_CREDIT_OR_DEBIT_A").c_str()) == 0)
{
usr_input = "1";
}
else
{
usr_input = "0";
}
new_data.credit_or_debit = std::stoi(usr_input.c_str()) > 0;
}
// date
if (strcmp(current, "dat") == 0)
{
std::string dmy = sett.get("SV_DATE_ORDER");
std::string dd, month, yy, mm, hh, ss;
for (unsigned int i = 0; i < 6; i++)
{
if (dmy.at(i) == 'D')
{
std::cout << sett.get("T_DAY");
get_word(&dd);
if (dd.empty())
return 0;
continue;
}
if (dmy.at(i) == 'M')
{
std::cout << sett.get("T_MONTH");
get_word(&month);
if (month.empty())
return 0;
continue;
}
if (dmy.at(i) == 'Y')
{
std::cout << sett.get("T_YEAR");
get_word(&yy);
if (yy.empty())
return 0;
continue;
}
if (dmy.at(i) == 'h')
{
std::cout << sett.get("T_HOURS");
get_word(&hh);
if (hh.empty())
hh = "0";
continue;
}
if (dmy.at(i) == 'm')
{
std::cout << sett.get("T_MINUTES");
get_word(&mm);
if (mm.empty())
mm = "0";
continue;
}
if (dmy.at(i) == 's')
{
std::cout << sett.get("T_SECONDS");
get_word(&ss);
if (ss.empty())
ss = "0";
continue;
}
}
std::string date = dd;
date += ".";
date += month;
date += ".";
date += yy;
date += " ";
date += hh;
date += ":";
date += mm;
date += ":";
date += ss;
new_data.date = date_to_seconds(date);
}
// name
if (strcmp(current, "nam") == 0)
{
std::cout << sett.get("T_NAME");
get_word(&usr_input);
if (usr_input.empty())
usr_input = "-";
new_data.name = usr_input;
}
// amount
if (strcmp(current, "amo") == 0)
{
std::cout << sett.get("T_AMOUNT");
get_word(&usr_input);
if (usr_input.empty())
usr_input = "-";
new_data.amount = std::atof(usr_input.c_str());
}
// reference
if (strcmp(current, "ref") == 0)
{
std::cout << sett.get("T_REF");
get_word(&usr_input);
if (usr_input.empty())
usr_input = "-";
new_data.reference_num = usr_input;
}
// description
if (strcmp(current, "des") == 0)
{
std::cout << sett.get("T_DESCRIPTION");
get_word(&usr_input);
if (usr_input.empty())
usr_input = "-";
new_data.description = usr_input;
}
// timezone
if (strcmp(current, "tzo") == 0)
{
std::cout << sett.get("T_TIMEZONE");
get_word(&usr_input);
if (usr_input.empty())
{
new_data.timezone = timezone;
}
else
{
new_data.timezone = set_timezone(usr_input);
}
new_data.date -= new_data.timezone;
}
std::cin.clear();
fflush(stdin);
}
new_data.date_current = std::time(nullptr);
#ifdef _DEBUG
std::cout << std::endl;
std::cout << "----- NEW DATA -----" << std::endl;
std::cout << "date: " << new_data.date << std::endl;
std::cout << "date_current: " << new_data.date_current << std::endl;
std::cout << "reference_num: " << new_data.reference_num << std::endl;
std::cout << "timezone: " << new_data.timezone << std::endl;
std::cout << "credit_or_debit: " << new_data.credit_or_debit << std::endl;
std::cout << "amount: " << new_data.amount << std::endl;
std::cout << "name: " << new_data.name << std::endl;
std::cout << "description: " << new_data.description << std::endl;
time_t tt = new_data.date_current + timezone;
std::cout << "ct: " << std::put_time(gmtime(&tt), format.c_str()) << std::endl;
tt = new_data.date + new_data.timezone;
std::cout << "dt: " << std::put_time(gmtime(&tt), format.c_str()) << std::endl;
return 0;
#endif
data.push_back(new_data);
save_database(fp);
return 0;
}
if (data0.empty())
{
return 0;
}
// SV_START_DATE - cmd arg
std::string start_date = sett.get("SV_START_DATE");
if (!start_date.empty())
{
time_t date = date_to_seconds(start_date, format.c_str());
for (std::vector<transaction>::iterator it = data0.begin(); it != data0.end(); it++)
{
if (it->date + it->timezone < date)
{
data0.erase(it);
it--;
}
}
}
// SV_END_DATE - cmd arg
std::string end_date = sett.get("SV_END_DATE");
if (!end_date.empty())
{
time_t date = date_to_seconds(end_date, format.c_str());
for (std::vector<transaction>::iterator it = data0.begin(); it != data0.end(); it++)
{
if (it->date + it->timezone > date)
{
data0.erase(it);
it--;
}
}
}
// SV_ORDER_BY_DATE - cmd arg
if (sett.getb("SV_ORDER_BY_DATE"))
{
for (unsigned int i = 0; i < data0.size() - 1; i++)
{
if (data0.at(i).date_current > data0.at(i + 1).date_current)
{
for (unsigned int u = i; u < data0.size() - 1; u++)
{
if (data0.at(u).date_current < data0.at(u + 1).date_current) break;
std::vector<transaction>::iterator it = data0.begin() + u;
data0.insert(data0.begin() + u + 2, *it);
data0.erase(data0.begin() + u);
}
if (i > 0) i -= 2;
else i--;
}
}
}
// SV_DEBIT_FIRST - cmd arg
if (sett.getb("SV_DEBIT_FIRST"))
{
std::vector<transaction> temp;
for (std::vector<transaction>::iterator it = data0.begin(); it != data0.end(); it++)
{
if (it->credit_or_debit == 0)
{
transaction tra = *it;
data0.erase(it);
temp.push_back(tra);
it--;
}
}
data0.insert(data0.end(), temp.begin(), temp.end());
}
// SV_CREDIT_FIRST - cmd arg
else if (sett.getb("SV_CREDIT_FIRST"))
{
std::vector<transaction> temp;
for (std::vector<transaction>::iterator it = data0.begin(); it != data0.end(); it++)
{
if (it->credit_or_debit == 1)
{
transaction tra = *it;
data0.erase(it);
temp.push_back(tra);
it--;
}
}
data0.insert(data0.end(), temp.begin(), temp.end());
}
// SV_ONLY_DEBIT - cmd arg
else if (sett.getb("SV_ONLY_DEBIT"))
{
for (std::vector<transaction>::iterator it = data0.begin(); it != data0.end(); it++)
{
if (it->credit_or_debit == 1)
{
data0.erase(it);
it--;
}
}
}
// SV_ONLY_CREDIT - cmd arg
else if (sett.getb("SV_ONLY_CREDIT"))
{
for (std::vector<transaction>::iterator it = data0.begin(); it != data0.end(); it++)
{
if (it->credit_or_debit == 0)
{
data0.erase(it);
it--;
}
}
}
// SV_DELETE - cmd arg
std::string delete_cmd = sett.get("SV_DELETE");
if (!delete_cmd.empty())
{
if (data0.empty())
{
return 0;
}
for (std::string::iterator it = delete_cmd.begin(); it != delete_cmd.end(); it++)
{
if (*it > 57 || *it < 48)
{
return 0;
}
}
unsigned int index = std::stoi(delete_cmd);
if (index < 1 || index > data0.size())
{
return 0;
}
transaction tra = data0.at(data0.size() - index);
std::vector<transaction>::const_iterator it;
it = data.begin() + tra.index;
data.erase(it);
save_database(fp);
return 0;
}
}
if (data0.empty())
{
return 0;
}
const std::string currency = sett.get("SV_CURRENCY");
double balance = 0;
bool currency_side = sett.getb("SV_CURRENCY_SIDE");
std::cout << std::fixed << std::setprecision(2);
unsigned int index = 0;
for (std::vector<transaction>::const_iterator it = data0.begin(); it != data0.end(); it++)
{
std::cout << "\n[ " << data0.size() - index++ << " ]" << std::endl;
for (unsigned int i = 0; i < 7; i++)
{
char current[4];
current[0] = order.at(4 * i);
current[1] = order.at(4 * i + 1);
current[2] = order.at(4 * i + 2);
current[3] = '\0';
// timezone
if (strcmp(current, "tzo") == 0)
{
continue;
}
// credit or debit
if (strcmp(current, "cod") == 0)
{
bool colored_text = sett.getb("SV_NO_COLOR");
if (it->credit_or_debit)
{
std::string text = sett.get("T_CREDIT");
if (colored_text)
{
std::cout << text << std::endl;
}
else
{
std::string color = "\033[";
color += sett.get("SV_CREDIT_COLOR");
color += "m";
std::cout << color << text << "\033[0m" << std::endl;
}
}
else
{
std::string text = sett.get("T_DEBIT");
if (colored_text)
{
std::cout << text << std::endl;
}
else
{
std::string color = "\033[";
color += sett.get("SV_DEBIT_COLOR");
color += "m";
std::cout << color << text << "\033[0m" << std::endl;
}
}
continue;
}
// date
if (strcmp(current, "dat") == 0)
{
std::cout << sett.get("T_DATE");
time_t date = it->date + it->timezone;
std::cout << std::put_time(gmtime(&date), format.c_str()) << std::endl;
if (it->credit_or_debit)
{
balance += it->amount;
}
else
{
balance -= it->amount;
}
continue;
}
// name
if (strcmp(current, "nam") == 0)
{
std::cout << sett.get("T_NAME") << it->name << std::endl;
continue;
}
// amount
if (strcmp(current, "amo") == 0)
{
std::cout << sett.get("T_AMOUNT");
if (currency_side)
{
std::cout << currency << it->amount << std::endl;
}
else
{
std::cout << it->amount << currency << std::endl;
}
continue;
}
// reference
if (strcmp(current, "ref") == 0)
{
std::cout << sett.get("T_REF");
std::cout << it->reference_num << std::endl;
continue;
}
// description
if (strcmp(current, "des") == 0)
{
std::cout << sett.get("T_DESCRIPTION") << it->description << std::endl;
}
}
}
std::cout << "\n" << sett.get("T_BALANCE");
if (currency_side)
{
std::cout << currency << balance << std::endl;
}
else
{
std::cout << balance << currency << std::endl;
}
return 0;
}
void read_database(const std::string &fn)
{
std::ifstream file(fn);
if (!file.good())
{
return;
}
if (file.peek() == std::ifstream::traits_type::eof())
{
return;
}
std::string current_line;
unsigned int index = 0;
while (std::getline(file, current_line))
{
data.push_back(transaction());
data.back().date = std::stoi(current_line.c_str());
std::getline(file, current_line);
data.back().date_current = std::stoi(current_line.c_str());
std::getline(file, current_line);
data.back().name = current_line;
std::getline(file, current_line);
data.back().amount = std::atof(current_line.c_str());
std::getline(file, current_line);
data.back().reference_num = current_line;
std::getline(file, current_line);
data.back().description = current_line;
std::getline(file, current_line);
int timezone = set_timezone(current_line);
data.back().timezone = timezone;
std::getline(file, current_line);
data.back().credit_or_debit = std::stoi(current_line.c_str()) > 0;
data.back().index = index;
index++;
}
}
bool save_database(const std::string &fn)
{
std::ofstream file(fn);
if (!file.good())
{
std::cout << "\n\nERROR! The database can not be saved." << std::endl;
return true;
}
for (std::vector<transaction>::const_iterator it = data.begin(); it != data.end(); it++)
{
file << it->date << "\n";
file << it->date_current << "\n";
file << it->name << "\n";
file << it->amount << "\n";
file << it->reference_num << "\n";
file << it->description << "\n";
file << get_timezone(it->timezone) << "\n";
file << it->credit_or_debit << "\n";
}
return false;
}
int set_timezone(const std::string& tz)
{
std::string sn(1, tz.at(1));
if (tz.size() > 2)
{
std::string c(1, tz.at(2));
sn += c;
}
int n = std::stoi(sn.c_str());
if (n < -12) n = -12;
else if (n > 12) n = 12;
int timezone;
timezone = 3600 * n;
if (tz.at(0) == '-')
{
timezone *= -1;
}
return timezone;
}
std::string get_timezone(int timezone)
{
std::string return_str;
if (timezone < 0)
{
timezone *= -1;
return_str = "-";
}
else
{
return_str = "+";
}
int n = timezone / 3600;
return_str += std::to_string(n);
return return_str;
}
/* Copyright(C) 2020 Lari Varjonen <[email protected]> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 as published by the Free Software Foundation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110 - 1301, USA. Version: 1.2 */ #include <iostream> #include <fstream> #include <vector> #include <map> #include <iomanip> #ifdef _WIN32 #include <windows.h> #pragma execution_character_set("utf-8") #endif //#define _DEBUG class SETT { const std::string settings_fp = "settings"; const std::string translation_dp = "translations/"; bool veccmp(const std::vector<std::string> &arr, const std::string &val) { for (std::vector<std::string>::const_iterator it = arr.begin(); it != arr.end(); it++) { if (it->compare(val) == 0) { return true; } } return false; } std::vector<std::string> cmd_arg = { "SV_NEW", "SV_DELETE" }; std::map<std::string, std::string> sett = { {"SV_DATABASE_PATH", "data"}, {"SV_LANGUAGE", "en"}, {"SV_TIMEZONE", "+0"}, {"SV_DATE_ORDER", "DMYhms"}, {"SV_CREDIT_COLOR", "1;32"}, {"SV_DEBIT_COLOR", "1;35"}, {"SV_ORDER_BY_DATE", "0"}, {"SV_DEBIT_FIRST", "0"}, {"SV_CREDIT_FIRST", "0"}, {"SV_ONLY_DEBIT", "0"}, {"SV_ONLY_CREDIT", "0"}, {"SV_NO_COLOR", "0"}, {"SV_NEW", "0"}, {"SV_DELETE", ""}, {"SV_START_DATE", ""}, {"SV_END_DATE", ""}, {"SV_FORMAT", "%d.%m.%Y %H:%M:%S"}, {"SV_ORDER", "cod-dat-nam-amo-ref-des-tzo"}, {"SV_CURRENCY", "$"}, {"SV_CURRENCY_SIDE", "1"}, {"T_CREDIT_OR_DEBIT", "Credit or Debit?(credit/debit): "}, {"T_CREDIT_OR_DEBIT_A", "credit"}, {"T_DAY", "Day:\t"}, {"T_MONTH", "Month:\t"}, {"T_YEAR", "Year:\t"}, {"T_HOURS", "Hours:\t"}, {"T_MINUTES", "Minutes:\t"}, {"T_SECONDS", "Seconds:\t"}, {"T_NAME", "Name:\t\t"}, {"T_AMOUNT", "Amount:\t\t"}, {"T_REF", "Ref:\t\t"}, {"T_DESCRIPTION", "Description:\t"}, {"T_TIMEZONE", "Timezone:\t"}, {"T_DATE", "Date:\t\t"}, {"T_BALANCE", "Balance: "}, {"T_CREDIT", "CREDIT"}, {"T_DEBIT", "DEBIT"} }; public: SETT(); std::string get(const std::string &key); bool getb(const std::string& key); bool add(std::string key, const std::string &val); }; SETT::SETT() { // Read settings file std::ifstream file(settings_fp); if (!file.good()) { return; } if (file.peek() == std::ifstream::traits_type::eof()) { return; } std::string current_line; while (std::getline(file, current_line)) { if (current_line.empty()) continue; std::string key; std::string val; int index = current_line.find("="); if (index != -1) { key = current_line.substr(0, index); val = current_line.substr(index + 1, current_line.size() - 1); } else { key = current_line; val = "1"; } for (unsigned int i = 0; i < key.size(); i++) { char c = key.at(i); // abc --> ABC if (c > 96 && c < 123) { c -= 32; key.at(i) = c; } } if (!veccmp(cmd_arg, key) && key.at(0) == 'S' && key.at(1) == 'V') { std::map<std::string, std::string>::iterator it = sett.find(key); if (it != sett.end()) { it->second = val; } } } // Read translations file std::string langauge_fn = get("SV_LANGUAGE"); if (!langauge_fn.empty()) { std::ifstream file0(translation_dp + langauge_fn); if (!file0.good()) { return; } if (file0.peek() == std::ifstream::traits_type::eof()) { return; } while (std::getline(file0, current_line)) { if (current_line.empty()) continue; std::string key; std::string val; int index = current_line.find("="); if (index != -1) { key = current_line.substr(0, index); val = current_line.substr(index + 1, current_line.size() - 1); } else { key = current_line; val = "1"; } for (unsigned int i = 0; i < key.size(); i++) { char c = key.at(i); // abc --> ABC if (c > 96 && c < 123) { c -= 32; key.at(i) = c; } } if (!veccmp(cmd_arg, key) && key.at(0) == 'T') { std::map<std::string, std::string>::iterator it = sett.find(key); if (it != sett.end()) { it->second = val; } } } } #ifdef _DEBUG for (std::map<std::string, std::string>::iterator it = sett.begin(); it != sett.end(); it++) { std::cout << it->first << "=" << it->second << std::endl; } #endif } std::string SETT::get(const std::string &key) { std::map<std::string, std::string>::iterator it = sett.find(key); if (it != sett.end()) { return it->second; } return std::string(""); } bool SETT::getb(const std::string& key) { std::map<std::string, std::string>::iterator it = sett.find(key); if (it != sett.end()) { return std::stoi(it->second) > 0; } return false; } bool SETT::add(std::string key, const std::string &val) { for (unsigned int i = 0; i < key.size(); i++) { char c = key.at(i); if (c == '-') { key.at(i) = '_'; continue; } // abc --> ABC if (c > 96 && c < 123) { c -= 32; key.at(i) = c; } } key = std::string("SV_") + key; std::map<std::string, std::string>::iterator it = sett.find(key); if (it != sett.end()) { it->second = val; return false; } return true; } struct transaction { time_t date; time_t date_current; std::string reference_num; int timezone; bool credit_or_debit; double amount; std::string name; std::string description; unsigned int index; }; std::vector<transaction> data; // All data. std::vector<transaction> data0; // Data visible. void read_database(const std::string &fn); bool save_database(const std::string &fn); int set_timezone(const std::string &tz); std::string get_timezone(int timezone); time_t date_to_seconds(const std::string& date) { int yy, month, dd, hh, mm, ss; const char* datec = date.c_str(); sscanf(datec, "%d.%d.%d %d:%d:%d", &dd, &month, &yy, &hh, &mm, &ss); struct tm tm; tm.tm_year = yy - 1900; tm.tm_mon = month - 1; tm.tm_mday = dd; tm.tm_hour = hh; tm.tm_min = mm; tm.tm_sec = ss; tm.tm_isdst = -1; time_t t = mktime(&tm); return t; } time_t date_to_seconds(const std::string& date, std::string format) { char u[6] = {0}; int i = 0; for (std::string::iterator it = format.begin(); it != format.end(); it++) { if (*it == '%') { u[i] = *(it + 1); *(it + 1) = 'd'; i++; } } int y[6] = {0}; const char* datec = date.c_str(); sscanf(datec, format.c_str(), &y[0], &y[1], &y[2], &y[3], &y[4], &y[5]); int yy, month, dd, hh, mm, ss; for (i = 0; i < 6; i++) { if (u[i] == 'd') { dd = y[i]; continue; } if (u[i] == 'm') { month = y[i]; continue; } if (u[i] == 'Y') { yy = y[i]; continue; } if (u[i] == 'H') { hh = y[i]; continue; } if (u[i] == 'M') { mm = y[i]; continue; } if (u[i] == 'S') { ss = y[i]; } } struct tm tm; tm.tm_year = yy - 1900; tm.tm_mon = month - 1; tm.tm_mday = dd; tm.tm_hour = hh; tm.tm_min = mm; tm.tm_sec = ss; tm.tm_isdst = -1; time_t t = mktime(&tm); return t; } void get_word(std::string *val) { std::string usr_input; std::getline(std::cin, usr_input); if (usr_input.empty()) { *val = ""; return; } int find_pos = usr_input.find(" "); if (find_pos != -1) { usr_input = usr_input.substr(0, find_pos); } *val = usr_input; } int main(int argc, char *argv[]) { #ifdef _WIN32 SetConsoleOutputCP(65001); #endif std::string fp; std::vector<std::string> cmd_arg; SETT sett; if (argc > 1) { cmd_arg = std::vector<std::string>(argv, argv + argc); cmd_arg.erase(cmd_arg.begin()); cmd_arg.shrink_to_fit(); for (std::vector<std::string>::iterator it = cmd_arg.begin(); it != cmd_arg.end(); it++) { std::string lhs; std::string rhs; int index = it->find('='); if (index != -1) { lhs = it->substr(0, index); rhs = it->substr(index + 1, it->size()); } else { static bool prevent_double = false; if (!prevent_double) { prevent_double = true; std::vector<std::string>::iterator it0 = it; for (; it0 != cmd_arg.end(); it0++) { int index0 = it0->find('='); if (index0 != -1) return 0; } } lhs = *it; rhs = "1"; } sett.add(lhs, rhs); } } int timezone = set_timezone(sett.get("SV_TIMEZONE")); const std::string format = sett.get("SV_FORMAT"); const std::string order = sett.get("SV_ORDER"); fp = sett.get("SV_DATABASE_PATH"); read_database(fp); // Order by date if (!data.empty()) { for (unsigned int i = 0; i < data.size() - 1; i++) { if (data.at(i).date > data.at(i + 1).date) { for (unsigned int u = i; u < data.size() - 1; u++) { if (data.at(u).date < data.at(u + 1).date) break; std::vector<transaction>::iterator it = data.begin() + u; data.insert(data.begin() + u + 2, *it); data.erase(data.begin() + u); } if (i > 0) i -= 2; else i--; } } data0 = data; } if (argc > 1) { // SV_NEW - cmd arg if (sett.getb("SV_NEW")) { transaction new_data; for (unsigned int i = 0; i < 7; i++) { std::string usr_input; char current[4]; current[0] = order.at(4 * i); current[1] = order.at(4 * i + 1); current[2] = order.at(4 * i + 2); current[3] = '\0'; // credit or debit if (strcmp(current, "cod") == 0) { std::cout << sett.get("T_CREDIT_OR_DEBIT"); get_word(&usr_input); if (usr_input.empty()) return 0; if (strcmp(usr_input.c_str(), sett.get("T_CREDIT_OR_DEBIT_A").c_str()) == 0) { usr_input = "1"; } else { usr_input = "0"; } new_data.credit_or_debit = std::stoi(usr_input.c_str()) > 0; } // date if (strcmp(current, "dat") == 0) { std::string dmy = sett.get("SV_DATE_ORDER"); std::string dd, month, yy, mm, hh, ss; for (unsigned int i = 0; i < 6; i++) { if (dmy.at(i) == 'D') { std::cout << sett.get("T_DAY"); get_word(&dd); if (dd.empty()) return 0; continue; } if (dmy.at(i) == 'M') { std::cout << sett.get("T_MONTH"); get_word(&month); if (month.empty()) return 0; continue; } if (dmy.at(i) == 'Y') { std::cout << sett.get("T_YEAR"); get_word(&yy); if (yy.empty()) return 0; continue; } if (dmy.at(i) == 'h') { std::cout << sett.get("T_HOURS"); get_word(&hh); if (hh.empty()) hh = "0"; continue; } if (dmy.at(i) == 'm') { std::cout << sett.get("T_MINUTES"); get_word(&mm); if (mm.empty()) mm = "0"; continue; } if (dmy.at(i) == 's') { std::cout << sett.get("T_SECONDS"); get_word(&ss); if (ss.empty()) ss = "0"; continue; } } std::string date = dd; date += "."; date += month; date += "."; date += yy; date += " "; date += hh; date += ":"; date += mm; date += ":"; date += ss; new_data.date = date_to_seconds(date); } // name if (strcmp(current, "nam") == 0) { std::cout << sett.get("T_NAME"); get_word(&usr_input); if (usr_input.empty()) usr_input = "-"; new_data.name = usr_input; } // amount if (strcmp(current, "amo") == 0) { std::cout << sett.get("T_AMOUNT"); get_word(&usr_input); if (usr_input.empty()) usr_input = "-"; new_data.amount = std::atof(usr_input.c_str()); } // reference if (strcmp(current, "ref") == 0) { std::cout << sett.get("T_REF"); get_word(&usr_input); if (usr_input.empty()) usr_input = "-"; new_data.reference_num = usr_input; } // description if (strcmp(current, "des") == 0) { std::cout << sett.get("T_DESCRIPTION"); get_word(&usr_input); if (usr_input.empty()) usr_input = "-"; new_data.description = usr_input; } // timezone if (strcmp(current, "tzo") == 0) { std::cout << sett.get("T_TIMEZONE"); get_word(&usr_input); if (usr_input.empty()) { new_data.timezone = timezone; } else { new_data.timezone = set_timezone(usr_input); } new_data.date -= new_data.timezone; } std::cin.clear(); fflush(stdin); } new_data.date_current = std::time(nullptr); #ifdef _DEBUG std::cout << std::endl; std::cout << "----- NEW DATA -----" << std::endl; std::cout << "date: " << new_data.date << std::endl; std::cout << "date_current: " << new_data.date_current << std::endl; std::cout << "reference_num: " << new_data.reference_num << std::endl; std::cout << "timezone: " << new_data.timezone << std::endl; std::cout << "credit_or_debit: " << new_data.credit_or_debit << std::endl; std::cout << "amount: " << new_data.amount << std::endl; std::cout << "name: " << new_data.name << std::endl; std::cout << "description: " << new_data.description << std::endl; time_t tt = new_data.date_current + timezone; std::cout << "ct: " << std::put_time(gmtime(&tt), format.c_str()) << std::endl; tt = new_data.date + new_data.timezone; std::cout << "dt: " << std::put_time(gmtime(&tt), format.c_str()) << std::endl; return 0; #endif data.push_back(new_data); save_database(fp); return 0; } if (data0.empty()) { return 0; } // SV_START_DATE - cmd arg std::string start_date = sett.get("SV_START_DATE"); if (!start_date.empty()) { time_t date = date_to_seconds(start_date, format.c_str()); for (std::vector<transaction>::iterator it = data0.begin(); it != data0.end(); it++) { if (it->date + it->timezone < date) { data0.erase(it); it--; } } } // SV_END_DATE - cmd arg std::string end_date = sett.get("SV_END_DATE"); if (!end_date.empty()) { time_t date = date_to_seconds(end_date, format.c_str()); for (std::vector<transaction>::iterator it = data0.begin(); it != data0.end(); it++) { if (it->date + it->timezone > date) { data0.erase(it); it--; } } } // SV_ORDER_BY_DATE - cmd arg if (sett.getb("SV_ORDER_BY_DATE")) { for (unsigned int i = 0; i < data0.size() - 1; i++) { if (data0.at(i).date_current > data0.at(i + 1).date_current) { for (unsigned int u = i; u < data0.size() - 1; u++) { if (data0.at(u).date_current < data0.at(u + 1).date_current) break; std::vector<transaction>::iterator it = data0.begin() + u; data0.insert(data0.begin() + u + 2, *it); data0.erase(data0.begin() + u); } if (i > 0) i -= 2; else i--; } } } // SV_DEBIT_FIRST - cmd arg if (sett.getb("SV_DEBIT_FIRST")) { std::vector<transaction> temp; for (std::vector<transaction>::iterator it = data0.begin(); it != data0.end(); it++) { if (it->credit_or_debit == 0) { transaction tra = *it; data0.erase(it); temp.push_back(tra); it--; } } data0.insert(data0.end(), temp.begin(), temp.end()); } // SV_CREDIT_FIRST - cmd arg else if (sett.getb("SV_CREDIT_FIRST")) { std::vector<transaction> temp; for (std::vector<transaction>::iterator it = data0.begin(); it != data0.end(); it++) { if (it->credit_or_debit == 1) { transaction tra = *it; data0.erase(it); temp.push_back(tra); it--; } } data0.insert(data0.end(), temp.begin(), temp.end()); } // SV_ONLY_DEBIT - cmd arg else if (sett.getb("SV_ONLY_DEBIT")) { for (std::vector<transaction>::iterator it = data0.begin(); it != data0.end(); it++) { if (it->credit_or_debit == 1) { data0.erase(it); it--; } } } // SV_ONLY_CREDIT - cmd arg else if (sett.getb("SV_ONLY_CREDIT")) { for (std::vector<transaction>::iterator it = data0.begin(); it != data0.end(); it++) { if (it->credit_or_debit == 0) { data0.erase(it); it--; } } } // SV_DELETE - cmd arg std::string delete_cmd = sett.get("SV_DELETE"); if (!delete_cmd.empty()) { if (data0.empty()) { return 0; } for (std::string::iterator it = delete_cmd.begin(); it != delete_cmd.end(); it++) { if (*it > 57 || *it < 48) { return 0; } } unsigned int index = std::stoi(delete_cmd); if (index < 1 || index > data0.size()) { return 0; } transaction tra = data0.at(data0.size() - index); std::vector<transaction>::const_iterator it; it = data.begin() + tra.index; data.erase(it); save_database(fp); return 0; } } if (data0.empty()) { return 0; } const std::string currency = sett.get("SV_CURRENCY"); double balance = 0; bool currency_side = sett.getb("SV_CURRENCY_SIDE"); std::cout << std::fixed << std::setprecision(2); unsigned int index = 0; for (std::vector<transaction>::const_iterator it = data0.begin(); it != data0.end(); it++) { std::cout << "\n[ " << data0.size() - index++ << " ]" << std::endl; for (unsigned int i = 0; i < 7; i++) { char current[4]; current[0] = order.at(4 * i); current[1] = order.at(4 * i + 1); current[2] = order.at(4 * i + 2); current[3] = '\0'; // timezone if (strcmp(current, "tzo") == 0) { continue; } // credit or debit if (strcmp(current, "cod") == 0) { bool colored_text = sett.getb("SV_NO_COLOR"); if (it->credit_or_debit) { std::string text = sett.get("T_CREDIT"); if (colored_text) { std::cout << text << std::endl; } else { std::string color = "\033["; color += sett.get("SV_CREDIT_COLOR"); color += "m"; std::cout << color << text << "\033[0m" << std::endl; } } else { std::string text = sett.get("T_DEBIT"); if (colored_text) { std::cout << text << std::endl; } else { std::string color = "\033["; color += sett.get("SV_DEBIT_COLOR"); color += "m"; std::cout << color << text << "\033[0m" << std::endl; } } continue; } // date if (strcmp(current, "dat") == 0) { std::cout << sett.get("T_DATE"); time_t date = it->date + it->timezone; std::cout << std::put_time(gmtime(&date), format.c_str()) << std::endl; if (it->credit_or_debit) { balance += it->amount; } else { balance -= it->amount; } continue; } // name if (strcmp(current, "nam") == 0) { std::cout << sett.get("T_NAME") << it->name << std::endl; continue; } // amount if (strcmp(current, "amo") == 0) { std::cout << sett.get("T_AMOUNT"); if (currency_side) { std::cout << currency << it->amount << std::endl; } else { std::cout << it->amount << currency << std::endl; } continue; } // reference if (strcmp(current, "ref") == 0) { std::cout << sett.get("T_REF"); std::cout << it->reference_num << std::endl; continue; } // description if (strcmp(current, "des") == 0) { std::cout << sett.get("T_DESCRIPTION") << it->description << std::endl; } } } std::cout << "\n" << sett.get("T_BALANCE"); if (currency_side) { std::cout << currency << balance << std::endl; } else { std::cout << balance << currency << std::endl; } return 0; } void read_database(const std::string &fn) { std::ifstream file(fn); if (!file.good()) { return; } if (file.peek() == std::ifstream::traits_type::eof()) { return; } std::string current_line; unsigned int index = 0; while (std::getline(file, current_line)) { data.push_back(transaction()); data.back().date = std::stoi(current_line.c_str()); std::getline(file, current_line); data.back().date_current = std::stoi(current_line.c_str()); std::getline(file, current_line); data.back().name = current_line; std::getline(file, current_line); data.back().amount = std::atof(current_line.c_str()); std::getline(file, current_line); data.back().reference_num = current_line; std::getline(file, current_line); data.back().description = current_line; std::getline(file, current_line); int timezone = set_timezone(current_line); data.back().timezone = timezone; std::getline(file, current_line); data.back().credit_or_debit = std::stoi(current_line.c_str()) > 0; data.back().index = index; index++; } } bool save_database(const std::string &fn) { std::ofstream file(fn); if (!file.good()) { std::cout << "\n\nERROR! The database can not be saved." << std::endl; return true; } for (std::vector<transaction>::const_iterator it = data.begin(); it != data.end(); it++) { file << it->date << "\n"; file << it->date_current << "\n"; file << it->name << "\n"; file << it->amount << "\n"; file << it->reference_num << "\n"; file << it->description << "\n"; file << get_timezone(it->timezone) << "\n"; file << it->credit_or_debit << "\n"; } return false; } int set_timezone(const std::string& tz) { std::string sn(1, tz.at(1)); if (tz.size() > 2) { std::string c(1, tz.at(2)); sn += c; } int n = std::stoi(sn.c_str()); if (n < -12) n = -12; else if (n > 12) n = 12; int timezone; timezone = 3600 * n; if (tz.at(0) == '-') { timezone *= -1; } return timezone; } std::string get_timezone(int timezone) { std::string return_str; if (timezone < 0) { timezone *= -1; return_str = "-"; } else { return_str = "+"; } int n = timezone / 3600; return_str += std::to_string(n); return return_str; }
/*
	Copyright(C) 2020 Lari Varjonen <[email protected]>

	This program is free software; you can redistribute it and/or
	modify it under the terms of the GNU General Public License
	version 2 as published by the Free Software Foundation.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with this program; if not, write to the Free Software
	Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110 - 1301, USA.

	Version: 1.2
*/

#include <iostream>
#include <fstream>
#include <vector>
#include <map>
#include <iomanip>

#ifdef _WIN32
#include <windows.h>
#pragma execution_character_set("utf-8")
#endif

//#define _DEBUG



class SETT
{
	const std::string settings_fp = "settings";
	const std::string translation_dp = "translations/";

	bool veccmp(const std::vector<std::string> &arr, const std::string &val)
	{
		for (std::vector<std::string>::const_iterator it = arr.begin(); it != arr.end(); it++)
		{
			if (it->compare(val) == 0)
			{
				return true;
			}
		}
		return false;
	}

	std::vector<std::string> cmd_arg = {
		"SV_NEW",
		"SV_DELETE"
	};

	std::map<std::string, std::string> sett = {
		{"SV_DATABASE_PATH",    "data"},
		{"SV_LANGUAGE",         "en"},
		{"SV_TIMEZONE",         "+0"},
		{"SV_DATE_ORDER",       "DMYhms"},
		{"SV_CREDIT_COLOR",     "1;32"},
		{"SV_DEBIT_COLOR",      "1;35"},
		{"SV_ORDER_BY_DATE",    "0"},
		{"SV_DEBIT_FIRST",      "0"},
		{"SV_CREDIT_FIRST",     "0"},
		{"SV_ONLY_DEBIT",       "0"},
		{"SV_ONLY_CREDIT",      "0"},
		{"SV_NO_COLOR",         "0"},
		{"SV_NEW",              "0"},
		{"SV_DELETE",           ""},
		{"SV_START_DATE",       ""},
		{"SV_END_DATE",         ""},
		{"SV_FORMAT",           "%d.%m.%Y %H:%M:%S"},
		{"SV_ORDER",            "cod-dat-nam-amo-ref-des-tzo"},
		{"SV_CURRENCY",         "$"},
		{"SV_CURRENCY_SIDE",    "1"},

		{"T_CREDIT_OR_DEBIT",   "Credit or Debit?(credit/debit): "},
		{"T_CREDIT_OR_DEBIT_A", "credit"},
		{"T_DAY",               "Day:\t"},
		{"T_MONTH",             "Month:\t"},
		{"T_YEAR",              "Year:\t"},
		{"T_HOURS",             "Hours:\t"},
		{"T_MINUTES",           "Minutes:\t"},
		{"T_SECONDS",           "Seconds:\t"},
		{"T_NAME",              "Name:\t\t"},
		{"T_AMOUNT",            "Amount:\t\t"},
		{"T_REF",               "Ref:\t\t"},
		{"T_DESCRIPTION",       "Description:\t"},
		{"T_TIMEZONE",          "Timezone:\t"},
		{"T_DATE",              "Date:\t\t"},
		{"T_BALANCE",           "Balance: "},
		{"T_CREDIT",            "CREDIT"},
		{"T_DEBIT",             "DEBIT"}
	};

	public:
		SETT();
		std::string get(const std::string &key);
		bool getb(const std::string& key);
		bool add(std::string key, const std::string &val);
};

SETT::SETT()
{
	// Read settings file
	std::ifstream file(settings_fp);
	if (!file.good())
	{
		return;
	}
	if (file.peek() == std::ifstream::traits_type::eof())
	{
		return;
	}

	std::string current_line;
	while (std::getline(file, current_line))
	{
		if (current_line.empty()) continue;

		std::string key;
		std::string val;

		int index = current_line.find("=");
		if (index != -1)
		{
			key = current_line.substr(0, index);
			val = current_line.substr(index + 1, current_line.size() - 1);
		}
		else
		{
			key = current_line;
			val = "1";
		}

		for (unsigned int i = 0; i < key.size(); i++)
		{
			char c = key.at(i);
			// abc --> ABC
			if (c > 96 && c < 123)
			{
				c -= 32;
				key.at(i) = c;
			}
		}

		if (!veccmp(cmd_arg, key) && key.at(0) == 'S' && key.at(1) == 'V')
		{
			std::map<std::string, std::string>::iterator it = sett.find(key);
			if (it != sett.end())
			{
				it->second = val;
			}
		}
	}

	// Read translations file
	std::string langauge_fn = get("SV_LANGUAGE");
	if (!langauge_fn.empty())
	{
		std::ifstream file0(translation_dp + langauge_fn);
		if (!file0.good())
		{
			return;
		}
		if (file0.peek() == std::ifstream::traits_type::eof())
		{
			return;
		}

		while (std::getline(file0, current_line))
		{
			if (current_line.empty()) continue;

			std::string key;
			std::string val;

			int index = current_line.find("=");
			if (index != -1)
			{
				key = current_line.substr(0, index);
				val = current_line.substr(index + 1, current_line.size() - 1);
			}
			else
			{
				key = current_line;
				val = "1";
			}

			for (unsigned int i = 0; i < key.size(); i++)
			{
				char c = key.at(i);
				// abc --> ABC
				if (c > 96 && c < 123)
				{
					c -= 32;
					key.at(i) = c;
				}
			}

			if (!veccmp(cmd_arg, key) && key.at(0) == 'T')
			{
				std::map<std::string, std::string>::iterator it = sett.find(key);
				if (it != sett.end())
				{
					it->second = val;
				}
			}
		}
	}

#ifdef _DEBUG
	for (std::map<std::string, std::string>::iterator it = sett.begin(); it != sett.end(); it++)
	{
		std::cout << it->first << "=" << it->second << std::endl;
	}
#endif
}

std::string SETT::get(const std::string &key)
{
	std::map<std::string, std::string>::iterator it = sett.find(key);
	if (it != sett.end())
	{
		return it->second;
	}
	return std::string("");
}

bool SETT::getb(const std::string& key)
{
	std::map<std::string, std::string>::iterator it = sett.find(key);
	if (it != sett.end())
	{
		return std::stoi(it->second) > 0;
	}
	return false;
}

bool SETT::add(std::string key, const std::string &val)
{
	for (unsigned int i = 0; i < key.size(); i++)
	{
		char c = key.at(i);
		if (c == '-')
		{
			key.at(i) = '_';
			continue;
		}
		// abc --> ABC
		if (c > 96 && c < 123)
		{
			c -= 32;
			key.at(i) = c;
		}
	}
	key = std::string("SV_") + key;
	std::map<std::string, std::string>::iterator it = sett.find(key);
	if (it != sett.end())
	{
		it->second = val;
		return false;
	}
	return true;
}



struct transaction {
	time_t date;
	time_t date_current;
	std::string reference_num;
	int timezone;
	bool credit_or_debit;
	double amount;
	std::string name;
	std::string description;
	unsigned int index;
};
std::vector<transaction> data; // All data.
std::vector<transaction> data0; // Data visible.

void read_database(const std::string &fn);
bool save_database(const std::string &fn);
int set_timezone(const std::string &tz);
std::string get_timezone(int timezone);

time_t date_to_seconds(const std::string& date)
{
	int yy, month, dd, hh, mm, ss;
	const char* datec = date.c_str();
	sscanf(datec, "%d.%d.%d %d:%d:%d", &dd, &month, &yy, &hh, &mm, &ss);
	
	struct tm tm;
	tm.tm_year = yy - 1900;
	tm.tm_mon = month - 1;
	tm.tm_mday = dd;
	tm.tm_hour = hh;
	tm.tm_min = mm;
	tm.tm_sec = ss;
	tm.tm_isdst = -1;

	time_t t = mktime(&tm);
	return t;
}

time_t date_to_seconds(const std::string& date, std::string format)
{
	char u[6] = {0};
	int i = 0;
	for (std::string::iterator it = format.begin(); it != format.end(); it++)
	{
		if (*it == '%')
		{
			u[i] = *(it + 1);
			*(it + 1) = 'd';
			i++;
		}
	}

	int y[6] = {0};
	const char* datec = date.c_str();
	sscanf(datec, format.c_str(), &y[0], &y[1], &y[2], &y[3], &y[4], &y[5]);

	int yy, month, dd, hh, mm, ss;
	for (i = 0; i < 6; i++)
	{
		if (u[i] == 'd')
		{
			dd = y[i];
			continue;
		}
		if (u[i] == 'm')
		{
			month = y[i];
			continue;
		}
		if (u[i] == 'Y')
		{
			yy = y[i];
			continue;
		}
		if (u[i] == 'H')
		{
			hh = y[i];
			continue;
		}
		if (u[i] == 'M')
		{
			mm = y[i];
			continue;
		}
		if (u[i] == 'S')
		{
			ss = y[i];
		}
	}

	struct tm tm;
	tm.tm_year = yy - 1900;
	tm.tm_mon = month - 1;
	tm.tm_mday = dd;
	tm.tm_hour = hh;
	tm.tm_min = mm;
	tm.tm_sec = ss;
	tm.tm_isdst = -1;

	time_t t = mktime(&tm);
	return t;
}

void get_word(std::string *val)
{
	std::string usr_input;
	std::getline(std::cin, usr_input);
	if (usr_input.empty())
	{
		*val = "";
		return;
	}
	int find_pos = usr_input.find(" ");
	if (find_pos != -1)
	{
		usr_input = usr_input.substr(0, find_pos);
	}
	*val = usr_input;
}



int main(int argc, char *argv[])
{
#ifdef _WIN32
	SetConsoleOutputCP(65001);
#endif

	std::string fp;
	std::vector<std::string> cmd_arg;
	SETT sett;

	if (argc > 1)
	{
		cmd_arg = std::vector<std::string>(argv, argv + argc);
		cmd_arg.erase(cmd_arg.begin());
		cmd_arg.shrink_to_fit();

		for (std::vector<std::string>::iterator it = cmd_arg.begin(); it != cmd_arg.end(); it++)
		{
			std::string lhs;
			std::string rhs;
			int index = it->find('=');
			if (index != -1)
			{
				lhs = it->substr(0, index);
				rhs = it->substr(index + 1, it->size());
			}
			else
			{
				static bool prevent_double = false;
				if (!prevent_double)
				{
					prevent_double = true;
					std::vector<std::string>::iterator it0 = it;
					for (; it0 != cmd_arg.end(); it0++)
					{
						int index0 = it0->find('=');
						if (index0 != -1)
							return 0;
					}
				}
				lhs = *it;
				rhs = "1";
			}
			sett.add(lhs, rhs);
		}
	}

	int timezone = set_timezone(sett.get("SV_TIMEZONE"));
	const std::string format = sett.get("SV_FORMAT");
	const std::string order = sett.get("SV_ORDER");
	
	fp = sett.get("SV_DATABASE_PATH");
	read_database(fp);

	// Order by date
	if (!data.empty())
	{
		for (unsigned int i = 0; i < data.size() - 1; i++)
		{
			if (data.at(i).date > data.at(i + 1).date)
			{
				for (unsigned int u = i; u < data.size() - 1; u++)
				{
					if (data.at(u).date < data.at(u + 1).date) break;

					std::vector<transaction>::iterator it = data.begin() + u;
					data.insert(data.begin() + u + 2, *it);
					data.erase(data.begin() + u);
				}
				if (i > 0) i -= 2;
				else i--;
			}
		}
		data0 = data;
	}

	if (argc > 1)
	{
		// SV_NEW - cmd arg
		if (sett.getb("SV_NEW"))
		{
			transaction new_data;
			for (unsigned int i = 0; i < 7; i++)
			{
				std::string usr_input;

				char current[4];
				current[0] = order.at(4 * i);
				current[1] = order.at(4 * i + 1);
				current[2] = order.at(4 * i + 2);
				current[3] = '\0';

				// credit or debit
				if (strcmp(current, "cod") == 0)
				{
					std::cout << sett.get("T_CREDIT_OR_DEBIT");
					get_word(&usr_input);
					if (usr_input.empty())
						return 0;
					if (strcmp(usr_input.c_str(), sett.get("T_CREDIT_OR_DEBIT_A").c_str()) == 0)
					{
						usr_input = "1";
					}
					else
					{
						usr_input = "0";
					}
					new_data.credit_or_debit = std::stoi(usr_input.c_str()) > 0;
				}
				// date
				if (strcmp(current, "dat") == 0)
				{
					std::string dmy = sett.get("SV_DATE_ORDER");
					std::string dd, month, yy, mm, hh, ss;
					for (unsigned int i = 0; i < 6; i++)
					{
						if (dmy.at(i) == 'D')
						{
							std::cout << sett.get("T_DAY");
							get_word(&dd);
							if (dd.empty())
								return 0;
							continue;
						}
						if (dmy.at(i) == 'M')
						{
							std::cout << sett.get("T_MONTH");
							get_word(&month);
							if (month.empty())
								return 0;
							continue;
						}
						if (dmy.at(i) == 'Y')
						{
							std::cout << sett.get("T_YEAR");
							get_word(&yy);
							if (yy.empty())
								return 0;
							continue;
						}
						if (dmy.at(i) == 'h')
						{
							std::cout << sett.get("T_HOURS");
							get_word(&hh);
							if (hh.empty())
								hh = "0";
							continue;
						}
						if (dmy.at(i) == 'm')
						{
							std::cout << sett.get("T_MINUTES");
							get_word(&mm);
							if (mm.empty())
								mm = "0";
							continue;
						}
						if (dmy.at(i) == 's')
						{
							std::cout << sett.get("T_SECONDS");
							get_word(&ss);
							if (ss.empty())
								ss = "0";
							continue;
						}
					}
					std::string date = dd;
					date += ".";
					date += month;
					date += ".";
					date += yy;
					date += " ";
					date += hh;
					date += ":";
					date += mm;
					date += ":";
					date += ss;
					new_data.date = date_to_seconds(date);
				}
				// name
				if (strcmp(current, "nam") == 0)
				{
					std::cout << sett.get("T_NAME");
					get_word(&usr_input);
					if (usr_input.empty())
						usr_input = "-";
					new_data.name = usr_input;
				}
				// amount
				if (strcmp(current, "amo") == 0)
				{
					std::cout << sett.get("T_AMOUNT");
					get_word(&usr_input);
					if (usr_input.empty())
						usr_input = "-";
					new_data.amount = std::atof(usr_input.c_str());
				}
				// reference
				if (strcmp(current, "ref") == 0)
				{
					std::cout << sett.get("T_REF");
					get_word(&usr_input);
					if (usr_input.empty())
						usr_input = "-";
					new_data.reference_num = usr_input;
				}
				// description
				if (strcmp(current, "des") == 0)
				{
					std::cout << sett.get("T_DESCRIPTION");
					get_word(&usr_input);
					if (usr_input.empty())
						usr_input = "-";
					new_data.description = usr_input;
				}
				// timezone
				if (strcmp(current, "tzo") == 0)
				{
					std::cout << sett.get("T_TIMEZONE");
					get_word(&usr_input);
					if (usr_input.empty())
					{
						new_data.timezone = timezone;
					}
					else
					{
						new_data.timezone = set_timezone(usr_input);
					}
					new_data.date -= new_data.timezone;
				}
				std::cin.clear();
				fflush(stdin);
			}
			new_data.date_current = std::time(nullptr);
#ifdef _DEBUG
			std::cout << std::endl;
			std::cout << "----- NEW DATA -----" << std::endl;
			std::cout << "date:            " << new_data.date << std::endl;
			std::cout << "date_current:    " << new_data.date_current << std::endl;
			std::cout << "reference_num:   " << new_data.reference_num << std::endl;
			std::cout << "timezone:        " << new_data.timezone << std::endl;
			std::cout << "credit_or_debit: " << new_data.credit_or_debit << std::endl;
			std::cout << "amount:          " << new_data.amount << std::endl;
			std::cout << "name:            " << new_data.name << std::endl;
			std::cout << "description:     " << new_data.description << std::endl;
			time_t tt = new_data.date_current + timezone;
			std::cout << "ct: " << std::put_time(gmtime(&tt), format.c_str()) << std::endl;
			tt = new_data.date + new_data.timezone;
			std::cout << "dt: " << std::put_time(gmtime(&tt), format.c_str()) << std::endl;
			return 0;
#endif
			data.push_back(new_data);

			save_database(fp);
			return 0;
		}

		if (data0.empty())
		{
			return 0;
		}

		// SV_START_DATE - cmd arg
		std::string start_date = sett.get("SV_START_DATE");
		if (!start_date.empty())
		{
			time_t date = date_to_seconds(start_date, format.c_str());
			for (std::vector<transaction>::iterator it = data0.begin(); it != data0.end(); it++)
			{
				if (it->date + it->timezone < date)
				{
					data0.erase(it);
					it--;
				}
			}
		}
		// SV_END_DATE - cmd arg
		std::string end_date = sett.get("SV_END_DATE");
		if (!end_date.empty())
		{
			time_t date = date_to_seconds(end_date, format.c_str());
			for (std::vector<transaction>::iterator it = data0.begin(); it != data0.end(); it++)
			{
				if (it->date + it->timezone > date)
				{
					data0.erase(it);
					it--;
				}
			}
		}

		// SV_ORDER_BY_DATE - cmd arg
		if (sett.getb("SV_ORDER_BY_DATE"))
		{
			for (unsigned int i = 0; i < data0.size() - 1; i++)
			{
				if (data0.at(i).date_current > data0.at(i + 1).date_current)
				{
					for (unsigned int u = i; u < data0.size() - 1; u++)
					{
						if (data0.at(u).date_current < data0.at(u + 1).date_current) break;

						std::vector<transaction>::iterator it = data0.begin() + u;
						data0.insert(data0.begin() + u + 2, *it);
						data0.erase(data0.begin() + u);
					}
					if (i > 0) i -= 2;
					else i--;
				}
			}
		}

		// SV_DEBIT_FIRST - cmd arg
		if (sett.getb("SV_DEBIT_FIRST"))
		{
			std::vector<transaction> temp;
			for (std::vector<transaction>::iterator it = data0.begin(); it != data0.end(); it++)
			{
				if (it->credit_or_debit == 0)
				{
					transaction tra = *it;
					data0.erase(it);
					temp.push_back(tra);
					it--;
				}
			}
			data0.insert(data0.end(), temp.begin(), temp.end());
		}
		// SV_CREDIT_FIRST - cmd arg
		else if (sett.getb("SV_CREDIT_FIRST"))
		{
			std::vector<transaction> temp;
			for (std::vector<transaction>::iterator it = data0.begin(); it != data0.end(); it++)
			{
				if (it->credit_or_debit == 1)
				{
					transaction tra = *it;
					data0.erase(it);
					temp.push_back(tra);
					it--;
				}
			}
			data0.insert(data0.end(), temp.begin(), temp.end());
		}
		// SV_ONLY_DEBIT - cmd arg
		else if (sett.getb("SV_ONLY_DEBIT"))
		{
			for (std::vector<transaction>::iterator it = data0.begin(); it != data0.end(); it++)
			{
				if (it->credit_or_debit == 1)
				{
					data0.erase(it);
					it--;
				}
			}
		}
		// SV_ONLY_CREDIT - cmd arg
		else if (sett.getb("SV_ONLY_CREDIT"))
		{
			for (std::vector<transaction>::iterator it = data0.begin(); it != data0.end(); it++)
			{
				if (it->credit_or_debit == 0)
				{
					data0.erase(it);
					it--;
				}
			}
		}

		// SV_DELETE - cmd arg
		std::string delete_cmd = sett.get("SV_DELETE");
		if (!delete_cmd.empty())
		{
			if (data0.empty())
			{
				return 0;
			}
			for (std::string::iterator it = delete_cmd.begin(); it != delete_cmd.end(); it++)
			{
				if (*it > 57 || *it < 48)
				{
					return 0;
				}
			}
			unsigned int index = std::stoi(delete_cmd);
			if (index < 1 || index > data0.size())
			{
				return 0;
			}

			transaction tra = data0.at(data0.size() - index);

			std::vector<transaction>::const_iterator it;
			it = data.begin() + tra.index;
			data.erase(it);

			save_database(fp);
			return 0;
		}
	}

	if (data0.empty())
	{
		return 0;
	}

	const std::string currency = sett.get("SV_CURRENCY");
	double balance = 0;
	bool currency_side = sett.getb("SV_CURRENCY_SIDE");

	std::cout << std::fixed << std::setprecision(2);

	unsigned int index = 0;
	for (std::vector<transaction>::const_iterator it = data0.begin(); it != data0.end(); it++)
	{
		std::cout << "\n[ " << data0.size() - index++  << " ]" << std::endl;
		for (unsigned int i = 0; i < 7; i++)
		{
			char current[4];
			current[0] = order.at(4 * i);
			current[1] = order.at(4 * i + 1);
			current[2] = order.at(4 * i + 2);
			current[3] = '\0';

			// timezone
			if (strcmp(current, "tzo") == 0)
			{
				continue;
			}

			// credit or debit
			if (strcmp(current, "cod") == 0)
			{
				bool colored_text = sett.getb("SV_NO_COLOR");
				if (it->credit_or_debit)
				{
					std::string text = sett.get("T_CREDIT");
					if (colored_text)
					{
						std::cout << text << std::endl;
					}
					else
					{
						std::string color = "\033[";
						color += sett.get("SV_CREDIT_COLOR");
						color += "m";
						std::cout << color << text << "\033[0m" << std::endl;
					}
				}
				else
				{
					std::string text = sett.get("T_DEBIT");
					if (colored_text)
					{
						std::cout << text << std::endl;
					}
					else
					{
						std::string color = "\033[";
						color += sett.get("SV_DEBIT_COLOR");
						color += "m";
						std::cout << color << text << "\033[0m" << std::endl;
					}
				}
				continue;
			}
			// date
			if (strcmp(current, "dat") == 0)
			{
				std::cout << sett.get("T_DATE");
				time_t date = it->date + it->timezone;
				std::cout << std::put_time(gmtime(&date), format.c_str()) << std::endl;
				if (it->credit_or_debit)
				{
					balance += it->amount;
				}
				else
				{
					balance -= it->amount;
				}
				continue;
			}
			// name
			if (strcmp(current, "nam") == 0)
			{
				std::cout << sett.get("T_NAME") << it->name << std::endl;
				continue;
			}
			// amount
			if (strcmp(current, "amo") == 0)
			{
				std::cout << sett.get("T_AMOUNT");
				if (currency_side)
				{
					std::cout << currency << it->amount << std::endl;
				}
				else
				{
					std::cout << it->amount << currency << std::endl;
				}
				continue;
			}
			// reference
			if (strcmp(current, "ref") == 0)
			{
				std::cout << sett.get("T_REF");
				std::cout << it->reference_num << std::endl;
				continue;
			}
			// description
			if (strcmp(current, "des") == 0)
			{
				std::cout << sett.get("T_DESCRIPTION") << it->description << std::endl;
			}
		}
	}

	std::cout << "\n" << sett.get("T_BALANCE");
	if (currency_side)
	{
		std::cout << currency << balance << std::endl;
	}
	else
	{
		std::cout << balance << currency << std::endl;
	}

	return 0;
}

void read_database(const std::string &fn)
{
	std::ifstream file(fn);

	if (!file.good())
	{
		return;
	}
	if (file.peek() == std::ifstream::traits_type::eof())
	{
		return;
	}

	std::string current_line;
	unsigned int index = 0;
	while (std::getline(file, current_line))
	{
		data.push_back(transaction());
		data.back().date = std::stoi(current_line.c_str());

		std::getline(file, current_line);
		data.back().date_current = std::stoi(current_line.c_str());

		std::getline(file, current_line);
		data.back().name = current_line;

		std::getline(file, current_line);
		data.back().amount = std::atof(current_line.c_str());

		std::getline(file, current_line);
		data.back().reference_num = current_line;

		std::getline(file, current_line);
		data.back().description = current_line;
		
		std::getline(file, current_line);
		int timezone = set_timezone(current_line);
		data.back().timezone = timezone;

		std::getline(file, current_line);
		data.back().credit_or_debit = std::stoi(current_line.c_str()) > 0;

		data.back().index = index;
		index++;
	}
}

bool save_database(const std::string &fn)
{
	std::ofstream file(fn);
	if (!file.good())
	{
		std::cout << "\n\nERROR! The database can not be saved." << std::endl;
		return true;
	}
	for (std::vector<transaction>::const_iterator it = data.begin(); it != data.end(); it++)
	{
		file << it->date << "\n";
		file << it->date_current << "\n";
		file << it->name << "\n";
		file << it->amount << "\n";
		file << it->reference_num << "\n";
		file << it->description << "\n";
		file << get_timezone(it->timezone) << "\n";
		file << it->credit_or_debit << "\n";
	}
	return false;
}

int set_timezone(const std::string& tz)
{
	std::string sn(1, tz.at(1));
	if (tz.size() > 2)
	{
		std::string c(1, tz.at(2));
		sn += c;
	}
	
	int n = std::stoi(sn.c_str());
	if (n < -12) n = -12;
	else if (n > 12) n = 12;
	
	int timezone;
	timezone = 3600 * n;
	if (tz.at(0) == '-')
	{
		timezone *= -1;
	}
	return timezone;
}

std::string get_timezone(int timezone)
{
	std::string return_str;
	if (timezone < 0)
	{
		timezone *= -1;
		return_str = "-";
	}
	else
	{
		return_str = "+";
	}
	int n = timezone / 3600;
	return_str += std::to_string(n);
	return return_str;
}

Supports also translations into a different languages. Translated into Finnish, the file may look like this for example:

translations/fi
T_DATE=Päivämäärä:
T_AMOUNT=Summa:
T_NAME=Nimi:
T_DESCRIPTION=Kuvaus:
T_BALANCE=Yhteensä:
T_REF=Viitenumero:
T_CREDIT_OR_DEBIT=Tulo vai meno?(tulo/meno):
T_CREDIT_OR_DEBIT_A=tulo
T_DAY=Päivä:
T_MONTH=Kuukausi:
T_YEAR=Vuosi:
T_MINUTES=Minuutit:
T_HOURS=Tunnit:
T_SECONDS=Sekunnit:
T_TIMEZONE=Aikavyöhyke:
T_DEBIT=MENO
T_CREDIT=TULO
T_DATE=Päivämäärä: T_AMOUNT=Summa: T_NAME=Nimi: T_DESCRIPTION=Kuvaus: T_BALANCE=Yhteensä: T_REF=Viitenumero: T_CREDIT_OR_DEBIT=Tulo vai meno?(tulo/meno): T_CREDIT_OR_DEBIT_A=tulo T_DAY=Päivä: T_MONTH=Kuukausi: T_YEAR=Vuosi: T_MINUTES=Minuutit: T_HOURS=Tunnit: T_SECONDS=Sekunnit: T_TIMEZONE=Aikavyöhyke: T_DEBIT=MENO T_CREDIT=TULO
T_DATE=Päivämäärä:	
T_AMOUNT=Summa:		
T_NAME=Nimi:		
T_DESCRIPTION=Kuvaus:		
T_BALANCE=Yhteensä: 
T_REF=Viitenumero:	
T_CREDIT_OR_DEBIT=Tulo vai meno?(tulo/meno): 
T_CREDIT_OR_DEBIT_A=tulo
T_DAY=Päivä: 		
T_MONTH=Kuukausi: 	
T_YEAR=Vuosi: 		
T_MINUTES=Minuutit: 	
T_HOURS=Tunnit: 	
T_SECONDS=Sekunnit: 	
T_TIMEZONE=Aikavyöhyke:	
T_DEBIT=MENO
T_CREDIT=TULO

The Settings file may look like this for example:

settings
SV_LANGUAGE=fi
SV_TIMEZONE=+2
SV_FORMAT=%d.%m.%Y %H:%M:%S
SV_CURRENCY=€
SV_CURRENCY_SIDE=0
SV_LANGUAGE=fi SV_TIMEZONE=+2 SV_FORMAT=%d.%m.%Y %H:%M:%S SV_CURRENCY=€ SV_CURRENCY_SIDE=0
SV_LANGUAGE=fi
SV_TIMEZONE=+2
SV_FORMAT=%d.%m.%Y %H:%M:%S
SV_CURRENCY=€
SV_CURRENCY_SIDE=0

Matrix and vector classes with c++

28.10.2020

I wrote these classes a couple of years ago. They became a byproduct of when I developed my own 3d game engine. I never got to finish it, still, not yet. It loaded 3d objects from files and drew those in 3d and it was fun to make. It was made using only SDL2 2d library. There was also a cube in the game. The camera was able to rotate around the cube by moving the mouse. Also the cube could be moved with the WASD keys.

I remember that I have tested the functionality of these classes. I remember some bug was in the matrix class, somewhere on top of it. The bug didn't affect its use in any way so I left it there.

matrix.h
/*
Copyright (C) 2017 Lari Varjonen <[email protected]>
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
version 2 as published by the Free Software Foundation.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef MATRIX_H
#define MATRIX_H
#include <cstring>
#include "vector.h"
/* A B C D
* a { 00, 01, 02, 03 }
* b { 10, 11, 12, 13 }
* c { 20, 21, 22, 23 }
* d { 30, 31, 32, 33 }
*
* aA, bA, cA = x, y, z = Forward Vector,
* aB, bB, cB = x, y, z = Up Vector,
* aC, bC, cC = x, y, z = Right Vector,
* aD, bD, cD = x, y, z = Translation Vector.
*
* Matrix = Matrix +/- Vector
* Matrix +=/-= Vector
* Adding or subtracting translation.
*
* T *get_row(unsigned int p) const;
* T *get_column(unsigned int p) const;
* Return a dynamically allocated array. Remember to delete[] it.
*/
template<class T>
class Matrix
{
public:
Matrix() {identity();}
Matrix(T x0,T y0,T z0,
T x1,T y1,T z1,
T x2,T y2,T z2,
T xT,T yT,T zT,
T w0,T w1,T w2,T w3);
Matrix(const Vector<T> &forward,
const Vector<T> &up,
const Vector<T> &right);
Matrix(const T (&forward)[3],
const T (&up)[3],
const T (&right)[3]);
Matrix(const T (&array)[4][4]);
Matrix(const Matrix &_m);
Matrix<T>& operator =(const T (&rhs)[4][4]);
Matrix<T>& operator =(const T rhs);
Matrix<T>& operator =(const Matrix &rhs);
Matrix<T>& operator *=(const T rhs);
Matrix<T>& operator *=(const Matrix &rhs);
Matrix<T> operator *(const T rhs);
Matrix<T> operator *(const Matrix &rhs);
Vector<T> operator *(const Vector<T> &rhs);
Vector<T> transform_vector(const Vector<T> &rhs);
Matrix<T>& operator +=(const T rhs);
Matrix<T>& operator +=(const Matrix<T> &rhs);
Matrix<T>& operator +=(const Vector<T> &rhs);
Matrix<T> operator +(const T rhs);
Matrix<T> operator +(const Matrix<T> &rhs);
Matrix<T> operator +(const Vector<T> &rhs);
Matrix<T>& operator -=(const T rhs);
Matrix<T>& operator -=(const Matrix<T> &rhs);
Matrix<T>& operator -=(const Vector<T> &rhs);
Matrix<T> operator -(const T rhs);
Matrix<T> operator -(const Matrix<T> &rhs);
Matrix<T> operator -(const Vector<T> &rhs);
bool operator ==(const T (&rhs)[4][4]) const;
bool operator ==(const T rhs) const;
bool operator ==(const Matrix &rhs) const;
bool operator ==(const Vector<T> &rhs) const;
bool operator !=(const T (&rhs)[4][4]) const;
bool operator !=(const T rhs) const;
bool operator !=(const Matrix &rhs) const;
bool operator !=(const Vector<T> &rhs) const;
void identity();
bool is_identity() const;
Vector<T> get_forward_vector() const;
Vector<T> get_up_vector() const;
Vector<T> get_right_vector() const;
Vector<T> get_translation_vector() const;
void set_scale(T x,T y,T z);
void set_scale(const Vector<T> &v);
void set_rotation(double a, T x, T y, T z);
void set_rotation(double a, const Vector<T> &v);
void set_translation(T x,T y,T z);
void set_translation(const Vector<T> &v);
void set_forward(T x,T y,T z);
void set_forward(const Vector<T> &v);
void set_up(T x,T y,T z);
void set_up(const Vector<T> &v);
void set_right(T x,T y,T z);
void set_right(const Vector<T> &v);
T *get_row(unsigned int p) const;
void set_row(unsigned int p,T r0,T r1,T r2,T r3);
T *get_column(unsigned int p) const;
void set_column(unsigned int p,T c0,T c1,T c2,T c3);
void set_column(unsigned int p, const Vector<T> &v,T c3);
T m[4][4];
const unsigned int ROW_SIZE = sizeof(m)/sizeof(m[0]);
const unsigned int COLUMN_SIZE = sizeof(m[0])/sizeof(T);
private:
template<class U>
friend std::ostream &operator <<(std::ostream &, const Matrix<U> &);
};
template<class T>
std::ostream& operator <<(std::ostream &os, const Matrix<T> &m)
{
os << "Matrix\n";
for (unsigned int r = 0; r < m.ROW_SIZE; r++)
{
os << "[";
for (unsigned int c = 0; c < m.COLUMN_SIZE; c++)
os << m.m[r][c] << ((c<3)?",":"]\n");
}
return os << "\n";
}
template<class T>
Matrix<T>::Matrix(T x0,T y0,T z0,
T x1,T y1,T z1,
T x2,T y2,T z2,
T xT,T yT,T zT,
T w0,T w1,T w2,T w3)
{
m[0][0] = x0; m[0][1] = x1; m[0][2] = x2; m[0][3] = xT;
m[1][0] = y0; m[1][1] = y1; m[1][2] = y2; m[1][3] = yT;
m[2][0] = z0; m[2][1] = z1; m[2][2] = z2; m[2][3] = zT;
m[3][0] = w0; m[3][1] = w1; m[3][2] = w2; m[3][3] = w3;
}
template<class T>
Matrix<T>::Matrix(const Matrix &_m)
{
memcpy(m, _m.m, sizeof m);
}
template<class T>
Matrix<T>::Matrix(const Vector<T> &f,
const Vector<T> &u,
const Vector<T> &r)
{
*this = {{f.x,u.x,r.x,0},
{f.y,u.y,r.y,0},
{f.z,u.z,r.z,0},
{0,0,0,1}};
}
template<class T>
Matrix<T>::Matrix(const T (&f)[3],
const T (&u)[3],
const T (&r)[3])
{
*this = {{f[0],u[0],r[0],0},
{f[1],u[1],r[1],0},
{f[2],u[2],r[2],0},
{0,0,0,1}};
}
template<class T>
Matrix<T>::Matrix(const T (&array)[4][4])
{
*this = array;
}
// Basic assignment
template<class T>
Matrix<T>& Matrix<T>::operator =(const T (&rhs)[4][4])
{
if (*this != rhs) memcpy(m, rhs, sizeof m);
return *this;
}
template<class T>
Matrix<T>& Matrix<T>::operator =(const T rhs)
{
return *this = {{rhs,rhs,rhs,rhs},
{rhs,rhs,rhs,rhs},
{rhs,rhs,rhs,rhs},
{rhs,rhs,rhs,rhs}};
}
template<class T>
Matrix<T>& Matrix<T>::operator =(const Matrix<T> &rhs)
{
return *this = rhs.m;
}
// Multiplication
template<class T>
Matrix<T>& Matrix<T>::operator *=(const T rhs)
{
m[0][0] *= rhs; m[0][1] *= rhs; m[0][2] *= rhs; m[0][3] *= rhs;
m[1][0] *= rhs; m[1][1] *= rhs; m[1][2] *= rhs; m[1][3] *= rhs;
m[2][0] *= rhs; m[2][1] *= rhs; m[2][2] *= rhs; m[2][3] *= rhs;
m[3][0] *= rhs; m[3][1] *= rhs; m[3][2] *= rhs; m[3][3] *= rhs;
return *this;
}
template<class T>
Matrix<T>& Matrix<T>::operator *=(const Matrix &rhs)
{
T r[ROW_SIZE][COLUMN_SIZE];
r[0][0] = m[0][0]*rhs.m[0][0] + m[0][1]*rhs.m[1][0] + m[0][2]*rhs.m[2][0] + m[0][3]*rhs.m[3][0];
r[0][1] = m[0][0]*rhs.m[0][1] + m[0][1]*rhs.m[1][1] + m[0][2]*rhs.m[2][1] + m[0][3]*rhs.m[3][1];
r[0][2] = m[0][0]*rhs.m[0][2] + m[0][1]*rhs.m[1][2] + m[0][2]*rhs.m[2][2] + m[0][3]*rhs.m[3][2];
r[0][3] = m[0][0]*rhs.m[0][3] + m[0][1]*rhs.m[1][3] + m[0][2]*rhs.m[2][3] + m[0][3]*rhs.m[3][3];
r[1][0] = m[1][0]*rhs.m[0][0] + m[1][1]*rhs.m[1][0] + m[1][2]*rhs.m[2][0] + m[1][3]*rhs.m[3][0];
r[1][1] = m[1][0]*rhs.m[0][1] + m[1][1]*rhs.m[1][1] + m[1][2]*rhs.m[2][1] + m[1][3]*rhs.m[3][1];
r[1][2] = m[1][0]*rhs.m[0][2] + m[1][1]*rhs.m[1][2] + m[1][2]*rhs.m[2][2] + m[1][3]*rhs.m[3][2];
r[1][3] = m[1][0]*rhs.m[0][3] + m[1][1]*rhs.m[1][3] + m[1][2]*rhs.m[2][3] + m[1][3]*rhs.m[3][3];
r[2][0] = m[2][0]*rhs.m[0][0] + m[2][1]*rhs.m[1][0] + m[2][2]*rhs.m[2][0] + m[2][3]*rhs.m[3][0];
r[2][1] = m[2][0]*rhs.m[0][1] + m[2][1]*rhs.m[1][1] + m[2][2]*rhs.m[2][1] + m[2][3]*rhs.m[3][1];
r[2][2] = m[2][0]*rhs.m[0][2] + m[2][1]*rhs.m[1][2] + m[2][2]*rhs.m[2][2] + m[2][3]*rhs.m[3][2];
r[2][3] = m[2][0]*rhs.m[0][3] + m[2][1]*rhs.m[1][3] + m[2][2]*rhs.m[2][3] + m[2][3]*rhs.m[3][3];
r[3][0] = m[3][0]*rhs.m[0][0] + m[3][1]*rhs.m[1][0] + m[3][2]*rhs.m[2][0] + m[3][3]*rhs.m[3][0];
r[3][1] = m[3][0]*rhs.m[0][1] + m[3][1]*rhs.m[1][1] + m[3][2]*rhs.m[2][1] + m[3][3]*rhs.m[3][1];
r[3][2] = m[3][0]*rhs.m[0][2] + m[3][1]*rhs.m[1][2] + m[3][2]*rhs.m[2][2] + m[3][3]*rhs.m[3][2];
r[3][3] = m[3][0]*rhs.m[0][3] + m[3][1]*rhs.m[1][3] + m[3][2]*rhs.m[2][3] + m[3][3]*rhs.m[3][3];
memcpy(m, r, sizeof m);
return *this;
}
template<class T>
Matrix<T> Matrix<T>::operator *(const T rhs)
{
return Matrix(*this) *= rhs;
}
template<class T>
Matrix<T> Matrix<T>::operator *(const Matrix &rhs)
{
return Matrix(*this) *= rhs;
}
template<class T>
Vector<T> Matrix<T>::operator *(const Vector<T> &rhs)
{
return Vector<T>(m[0][0]*rhs.x + m[0][1]*rhs.y + m[0][2]*rhs.z + m[0][3]*m[0][3],
m[1][0]*rhs.x + m[1][1]*rhs.y + m[1][2]*rhs.z + m[1][3]*m[1][3],
m[2][0]*rhs.x + m[2][1]*rhs.y + m[2][2]*rhs.z + m[2][3]*m[2][3]);
}
template<class T>
Vector<T> Matrix<T>::transform_vector(const Vector<T> &rhs)
{
return Vector<T>(m[0][0]*rhs.x + m[0][1]*rhs.y + m[0][2]*rhs.z + m[0][3],
m[1][0]*rhs.x + m[1][1]*rhs.y + m[1][2]*rhs.z + m[1][3],
m[2][0]*rhs.x + m[2][1]*rhs.y + m[2][2]*rhs.z + m[2][3]);
}
// Addition
template<class T>
Matrix<T>& Matrix<T>::operator +=(const T rhs)
{
m[0][0] += rhs; m[0][1] += rhs; m[0][2] += rhs; m[0][3] += rhs;
m[1][0] += rhs; m[1][1] += rhs; m[1][2] += rhs; m[1][3] += rhs;
m[2][0] += rhs; m[2][1] += rhs; m[2][2] += rhs; m[2][3] += rhs;
m[3][0] += rhs; m[3][1] += rhs; m[3][2] += rhs; m[3][3] += rhs;
return *this;
}
template<class T>
Matrix<T>& Matrix<T>::operator +=(const Matrix<T> &rhs)
{
m[0][0] += rhs.m[0][0]; m[0][1] += rhs.m[0][1]; m[0][2] += rhs.m[0][2]; m[0][3] += rhs.m[0][3];
m[1][0] += rhs.m[1][0]; m[1][1] += rhs.m[1][1]; m[1][2] += rhs.m[1][2]; m[1][3] += rhs.m[1][3];
m[2][0] += rhs.m[2][0]; m[2][1] += rhs.m[2][1]; m[2][2] += rhs.m[2][2]; m[2][3] += rhs.m[2][3];
m[3][0] += rhs.m[3][0]; m[3][1] += rhs.m[3][1]; m[3][2] += rhs.m[3][2]; m[3][3] += rhs.m[3][3];
return *this;
}
template<class T>
Matrix<T>& Matrix<T>::operator +=(const Vector<T> &rhs)
{
m[0][3] += rhs.x;
m[1][3] += rhs.y;
m[2][3] += rhs.z;
return *this;
}
template<class T>
Matrix<T> Matrix<T>::operator +(const T rhs)
{
return Matrix(*this) += rhs;
}
template<class T>
Matrix<T> Matrix<T>::operator +(const Matrix<T> &rhs)
{
return Matrix(*this) += rhs;
}
template<class T>
Matrix<T> Matrix<T>::operator +(const Vector<T> &rhs)
{
return Matrix(*this) += rhs;
}
// Subtraction
template<class T>
Matrix<T>& Matrix<T>::operator -=(const T rhs)
{
return *this += -rhs;
}
template<class T>
Matrix<T>& Matrix<T>::operator -=(const Matrix<T> &rhs)
{
m[0][0] -= rhs.m[0][0]; m[0][1] -= rhs.m[0][1]; m[0][2] -= rhs.m[0][2]; m[0][3] -= rhs.m[0][3];
m[1][0] -= rhs.m[1][0]; m[1][1] -= rhs.m[1][1]; m[1][2] -= rhs.m[1][2]; m[1][3] -= rhs.m[1][3];
m[2][0] -= rhs.m[2][0]; m[2][1] -= rhs.m[2][1]; m[2][2] -= rhs.m[2][2]; m[2][3] -= rhs.m[2][3];
m[3][0] -= rhs.m[3][0]; m[3][1] -= rhs.m[3][1]; m[3][2] -= rhs.m[3][2]; m[3][3] -= rhs.m[3][3];
return *this;
}
template<class T>
Matrix<T>& Matrix<T>::operator -=(const Vector<T> &rhs)
{
m[0][3] -= rhs.x;
m[1][3] -= rhs.y;
m[2][3] -= rhs.z;
return *this;
}
template<class T>
Matrix<T> Matrix<T>::operator -(const T rhs)
{
return Matrix(*this) -= rhs;
}
template<class T>
Matrix<T> Matrix<T>::operator -(const Matrix<T> &rhs)
{
return Matrix(*this) -= rhs;
}
template<class T>
Matrix<T> Matrix<T>::operator -(const Vector<T> &rhs)
{
return Matrix(*this) -= rhs;
}
// Comparison operators
template<class T>
bool Matrix<T>::operator ==(const T (&rhs)[4][4]) const
{
return m[0][0] == rhs[0][0] && m[0][1] == rhs[0][1] && m[0][2] == rhs[0][2] && m[0][3] == rhs[0][3] &&
m[1][0] == rhs[1][0] && m[1][1] == rhs[1][1] && m[1][2] == rhs[1][2] && m[1][3] == rhs[1][3] &&
m[2][0] == rhs[2][0] && m[2][1] == rhs[2][1] && m[2][2] == rhs[2][2] && m[2][3] == rhs[2][3] &&
m[3][0] == rhs[3][0] && m[3][1] == rhs[3][1] && m[3][2] == rhs[3][2] && m[3][3] == rhs[3][3];
}
template<class T>
bool Matrix<T>::operator ==(const T rhs) const
{
return this->operator ==({{rhs,rhs,rhs,rhs},
{rhs,rhs,rhs,rhs},
{rhs,rhs,rhs,rhs},
{rhs,rhs,rhs,rhs}});
}
template<class T>
bool Matrix<T>::operator ==(const Matrix &rhs) const
{
return *this == rhs.m;
}
template<class T>
bool Matrix<T>::operator ==(const Vector<T> &rhs) const
{
return m[0][3] == rhs.x && m[1][3] == rhs.y && m[2][3] == rhs.z;
}
template<class T>
bool Matrix<T>::operator !=(const T (&rhs)[4][4]) const
{
return !(*this == rhs);
}
template<class T>
bool Matrix<T>::operator !=(const T rhs) const
{
return !(*this == rhs);
}
template<class T>
bool Matrix<T>::operator !=(const Matrix &rhs) const
{
return !(*this == rhs);
}
template<class T>
bool Matrix<T>::operator !=(const Vector<T> &rhs) const
{
return !(*this == rhs);
}
template<class T>
void Matrix<T>::identity()
{
memset(m, 0, sizeof m);
m[0][0] = m[1][1] = m[2][2] = m[3][3] = 1;
}
template<class T>
bool Matrix<T>::is_identity() const
{
return m[0][0] == 1 && m[0][1] == 0 && m[0][2] == 0 && m[0][3] == 0 &&
m[1][0] == 0 && m[1][1] == 1 && m[1][2] == 0 && m[1][3] == 0 &&
m[2][0] == 0 && m[2][1] == 0 && m[2][2] == 1 && m[2][3] == 0 &&
m[3][0] == 0 && m[3][1] == 0 && m[3][2] == 0 && m[3][3] == 1;
}
// Get vector
template<class T>
Vector<T> Matrix<T>::get_forward_vector() const
{
return Vector<T>(m[0][0],m[1][0],m[2][0]);
}
template<class T>
Vector<T> Matrix<T>::get_up_vector() const
{
return Vector<T>(m[0][1],m[1][1],m[2][1]);
}
template<class T>
Vector<T> Matrix<T>::get_right_vector() const
{
return Vector<T>(m[0][2],m[1][2],m[2][2]);
}
template<class T>
Vector<T> Matrix<T>::get_translation_vector() const
{
return Vector<T>(m[0][3],m[1][3],m[2][3]);
}
// Set scale
template<class T>
void Matrix<T>::set_scale(T x,T y,T z)
{
m[0][0] = x;
m[1][1] = y;
m[2][2] = z;
}
template<class T>
void Matrix<T>::set_scale(const Vector<T> &v)
{
set_scale(v.x, v.y, v.z);
}
// Set rotation
template<class T>
void Matrix<T>::set_rotation(double a,T x,T y,T z)
{
double c = cos(a);
double s = sin(a);
double t = 1-c;
m[0][0] = x*x*t + c;
m[1][0] = y*x*t + z*s;
m[2][0] = z*x*t - y*s;
m[0][1] = x*y*t - z*s;
m[1][1] = y*y*t + c;
m[2][1] = z*y*t + x*s;
m[0][2] = x*z*t + y*s;
m[1][2] = y*z*t - x*s;
m[2][2] = z*z*t + c;
}
template<class T>
void Matrix<T>::set_rotation(double a, const Vector<T> &v)
{
set_rotation(a, v.x, v.y, v.z);
}
// Set translation
template<class T>
void Matrix<T>::set_translation(T x,T y,T z)
{
m[0][3] = x;
m[1][3] = y;
m[2][3] = z;
}
template<class T>
void Matrix<T>::set_translation(const Vector<T> &v)
{
set_translation(v.x, v.y, v.z);
}
// Set FUR vectors
template<class T>
void Matrix<T>::set_forward(T x,T y,T z)
{
m[0][0] = x;
m[1][0] = y;
m[2][0] = z;
}
template<class T>
void Matrix<T>::set_forward(const Vector<T> &v)
{
set_forward_vector(v.x, v.y, v.z);
}
template<class T>
void Matrix<T>::set_up(T x,T y,T z)
{
m[0][1] = x;
m[1][1] = y;
m[2][1] = z;
}
template<class T>
void Matrix<T>::set_up(const Vector<T> &v)
{
set_up_vector(v.x, v.y, v.z);
}
template<class T>
void Matrix<T>::set_right(T x,T y,T z)
{
m[0][2] = x;
m[1][2] = y;
m[2][2] = z;
}
template<class T>
void Matrix<T>::set_right(const Vector<T> &v)
{
set_right_vector(v.x, v.y, v.z);
}
// Get/Set row
template<class T>
T *Matrix<T>::get_row(unsigned int p) const
{
if (p > ROW_SIZE) return NULL;
T *r = new T[ROW_SIZE];
r[0] = m[p][0];
r[1] = m[p][1];
r[2] = m[p][2];
r[3] = m[p][3];
return r;
}
template<class T>
void Matrix<T>::set_row(unsigned int p,T r0,T r1,T r2,T r3)
{
if (p > ROW_SIZE) return;
m[p][0] = r0;
m[p][1] = r1;
m[p][2] = r2;
m[p][3] = r3;
}
// Get/Set column
template<class T>
T *Matrix<T>::get_column(unsigned int p) const
{
if (p > COLUMN_SIZE) return NULL;
T *r = new T[COLUMN_SIZE];
r[0] = m[0][p];
r[1] = m[1][p];
r[2] = m[2][p];
r[3] = m[3][p];
return r;
}
template<class T>
void Matrix<T>::set_column(unsigned int p,T c0,T c1,T c2,T c3)
{
if (p > COLUMN_SIZE) return;
m[0][p] = c0;
m[1][p] = c1;
m[2][p] = c2;
m[3][p] = c3;
}
template<class T>
void Matrix<T>::set_column(unsigned int p, const Vector<T> &v,T c3)
{
set_column(p, v.x, v.y, v.z, c3);
}
#endif // MATRIX_H
/* Copyright (C) 2017 Lari Varjonen <[email protected]> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 as published by the Free Software Foundation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef MATRIX_H #define MATRIX_H #include <cstring> #include "vector.h" /* A B C D * a { 00, 01, 02, 03 } * b { 10, 11, 12, 13 } * c { 20, 21, 22, 23 } * d { 30, 31, 32, 33 } * * aA, bA, cA = x, y, z = Forward Vector, * aB, bB, cB = x, y, z = Up Vector, * aC, bC, cC = x, y, z = Right Vector, * aD, bD, cD = x, y, z = Translation Vector. * * Matrix = Matrix +/- Vector * Matrix +=/-= Vector * Adding or subtracting translation. * * T *get_row(unsigned int p) const; * T *get_column(unsigned int p) const; * Return a dynamically allocated array. Remember to delete[] it. */ template<class T> class Matrix { public: Matrix() {identity();} Matrix(T x0,T y0,T z0, T x1,T y1,T z1, T x2,T y2,T z2, T xT,T yT,T zT, T w0,T w1,T w2,T w3); Matrix(const Vector<T> &forward, const Vector<T> &up, const Vector<T> &right); Matrix(const T (&forward)[3], const T (&up)[3], const T (&right)[3]); Matrix(const T (&array)[4][4]); Matrix(const Matrix &_m); Matrix<T>& operator =(const T (&rhs)[4][4]); Matrix<T>& operator =(const T rhs); Matrix<T>& operator =(const Matrix &rhs); Matrix<T>& operator *=(const T rhs); Matrix<T>& operator *=(const Matrix &rhs); Matrix<T> operator *(const T rhs); Matrix<T> operator *(const Matrix &rhs); Vector<T> operator *(const Vector<T> &rhs); Vector<T> transform_vector(const Vector<T> &rhs); Matrix<T>& operator +=(const T rhs); Matrix<T>& operator +=(const Matrix<T> &rhs); Matrix<T>& operator +=(const Vector<T> &rhs); Matrix<T> operator +(const T rhs); Matrix<T> operator +(const Matrix<T> &rhs); Matrix<T> operator +(const Vector<T> &rhs); Matrix<T>& operator -=(const T rhs); Matrix<T>& operator -=(const Matrix<T> &rhs); Matrix<T>& operator -=(const Vector<T> &rhs); Matrix<T> operator -(const T rhs); Matrix<T> operator -(const Matrix<T> &rhs); Matrix<T> operator -(const Vector<T> &rhs); bool operator ==(const T (&rhs)[4][4]) const; bool operator ==(const T rhs) const; bool operator ==(const Matrix &rhs) const; bool operator ==(const Vector<T> &rhs) const; bool operator !=(const T (&rhs)[4][4]) const; bool operator !=(const T rhs) const; bool operator !=(const Matrix &rhs) const; bool operator !=(const Vector<T> &rhs) const; void identity(); bool is_identity() const; Vector<T> get_forward_vector() const; Vector<T> get_up_vector() const; Vector<T> get_right_vector() const; Vector<T> get_translation_vector() const; void set_scale(T x,T y,T z); void set_scale(const Vector<T> &v); void set_rotation(double a, T x, T y, T z); void set_rotation(double a, const Vector<T> &v); void set_translation(T x,T y,T z); void set_translation(const Vector<T> &v); void set_forward(T x,T y,T z); void set_forward(const Vector<T> &v); void set_up(T x,T y,T z); void set_up(const Vector<T> &v); void set_right(T x,T y,T z); void set_right(const Vector<T> &v); T *get_row(unsigned int p) const; void set_row(unsigned int p,T r0,T r1,T r2,T r3); T *get_column(unsigned int p) const; void set_column(unsigned int p,T c0,T c1,T c2,T c3); void set_column(unsigned int p, const Vector<T> &v,T c3); T m[4][4]; const unsigned int ROW_SIZE = sizeof(m)/sizeof(m[0]); const unsigned int COLUMN_SIZE = sizeof(m[0])/sizeof(T); private: template<class U> friend std::ostream &operator <<(std::ostream &, const Matrix<U> &); }; template<class T> std::ostream& operator <<(std::ostream &os, const Matrix<T> &m) { os << "Matrix\n"; for (unsigned int r = 0; r < m.ROW_SIZE; r++) { os << "["; for (unsigned int c = 0; c < m.COLUMN_SIZE; c++) os << m.m[r][c] << ((c<3)?",":"]\n"); } return os << "\n"; } template<class T> Matrix<T>::Matrix(T x0,T y0,T z0, T x1,T y1,T z1, T x2,T y2,T z2, T xT,T yT,T zT, T w0,T w1,T w2,T w3) { m[0][0] = x0; m[0][1] = x1; m[0][2] = x2; m[0][3] = xT; m[1][0] = y0; m[1][1] = y1; m[1][2] = y2; m[1][3] = yT; m[2][0] = z0; m[2][1] = z1; m[2][2] = z2; m[2][3] = zT; m[3][0] = w0; m[3][1] = w1; m[3][2] = w2; m[3][3] = w3; } template<class T> Matrix<T>::Matrix(const Matrix &_m) { memcpy(m, _m.m, sizeof m); } template<class T> Matrix<T>::Matrix(const Vector<T> &f, const Vector<T> &u, const Vector<T> &r) { *this = {{f.x,u.x,r.x,0}, {f.y,u.y,r.y,0}, {f.z,u.z,r.z,0}, {0,0,0,1}}; } template<class T> Matrix<T>::Matrix(const T (&f)[3], const T (&u)[3], const T (&r)[3]) { *this = {{f[0],u[0],r[0],0}, {f[1],u[1],r[1],0}, {f[2],u[2],r[2],0}, {0,0,0,1}}; } template<class T> Matrix<T>::Matrix(const T (&array)[4][4]) { *this = array; } // Basic assignment template<class T> Matrix<T>& Matrix<T>::operator =(const T (&rhs)[4][4]) { if (*this != rhs) memcpy(m, rhs, sizeof m); return *this; } template<class T> Matrix<T>& Matrix<T>::operator =(const T rhs) { return *this = {{rhs,rhs,rhs,rhs}, {rhs,rhs,rhs,rhs}, {rhs,rhs,rhs,rhs}, {rhs,rhs,rhs,rhs}}; } template<class T> Matrix<T>& Matrix<T>::operator =(const Matrix<T> &rhs) { return *this = rhs.m; } // Multiplication template<class T> Matrix<T>& Matrix<T>::operator *=(const T rhs) { m[0][0] *= rhs; m[0][1] *= rhs; m[0][2] *= rhs; m[0][3] *= rhs; m[1][0] *= rhs; m[1][1] *= rhs; m[1][2] *= rhs; m[1][3] *= rhs; m[2][0] *= rhs; m[2][1] *= rhs; m[2][2] *= rhs; m[2][3] *= rhs; m[3][0] *= rhs; m[3][1] *= rhs; m[3][2] *= rhs; m[3][3] *= rhs; return *this; } template<class T> Matrix<T>& Matrix<T>::operator *=(const Matrix &rhs) { T r[ROW_SIZE][COLUMN_SIZE]; r[0][0] = m[0][0]*rhs.m[0][0] + m[0][1]*rhs.m[1][0] + m[0][2]*rhs.m[2][0] + m[0][3]*rhs.m[3][0]; r[0][1] = m[0][0]*rhs.m[0][1] + m[0][1]*rhs.m[1][1] + m[0][2]*rhs.m[2][1] + m[0][3]*rhs.m[3][1]; r[0][2] = m[0][0]*rhs.m[0][2] + m[0][1]*rhs.m[1][2] + m[0][2]*rhs.m[2][2] + m[0][3]*rhs.m[3][2]; r[0][3] = m[0][0]*rhs.m[0][3] + m[0][1]*rhs.m[1][3] + m[0][2]*rhs.m[2][3] + m[0][3]*rhs.m[3][3]; r[1][0] = m[1][0]*rhs.m[0][0] + m[1][1]*rhs.m[1][0] + m[1][2]*rhs.m[2][0] + m[1][3]*rhs.m[3][0]; r[1][1] = m[1][0]*rhs.m[0][1] + m[1][1]*rhs.m[1][1] + m[1][2]*rhs.m[2][1] + m[1][3]*rhs.m[3][1]; r[1][2] = m[1][0]*rhs.m[0][2] + m[1][1]*rhs.m[1][2] + m[1][2]*rhs.m[2][2] + m[1][3]*rhs.m[3][2]; r[1][3] = m[1][0]*rhs.m[0][3] + m[1][1]*rhs.m[1][3] + m[1][2]*rhs.m[2][3] + m[1][3]*rhs.m[3][3]; r[2][0] = m[2][0]*rhs.m[0][0] + m[2][1]*rhs.m[1][0] + m[2][2]*rhs.m[2][0] + m[2][3]*rhs.m[3][0]; r[2][1] = m[2][0]*rhs.m[0][1] + m[2][1]*rhs.m[1][1] + m[2][2]*rhs.m[2][1] + m[2][3]*rhs.m[3][1]; r[2][2] = m[2][0]*rhs.m[0][2] + m[2][1]*rhs.m[1][2] + m[2][2]*rhs.m[2][2] + m[2][3]*rhs.m[3][2]; r[2][3] = m[2][0]*rhs.m[0][3] + m[2][1]*rhs.m[1][3] + m[2][2]*rhs.m[2][3] + m[2][3]*rhs.m[3][3]; r[3][0] = m[3][0]*rhs.m[0][0] + m[3][1]*rhs.m[1][0] + m[3][2]*rhs.m[2][0] + m[3][3]*rhs.m[3][0]; r[3][1] = m[3][0]*rhs.m[0][1] + m[3][1]*rhs.m[1][1] + m[3][2]*rhs.m[2][1] + m[3][3]*rhs.m[3][1]; r[3][2] = m[3][0]*rhs.m[0][2] + m[3][1]*rhs.m[1][2] + m[3][2]*rhs.m[2][2] + m[3][3]*rhs.m[3][2]; r[3][3] = m[3][0]*rhs.m[0][3] + m[3][1]*rhs.m[1][3] + m[3][2]*rhs.m[2][3] + m[3][3]*rhs.m[3][3]; memcpy(m, r, sizeof m); return *this; } template<class T> Matrix<T> Matrix<T>::operator *(const T rhs) { return Matrix(*this) *= rhs; } template<class T> Matrix<T> Matrix<T>::operator *(const Matrix &rhs) { return Matrix(*this) *= rhs; } template<class T> Vector<T> Matrix<T>::operator *(const Vector<T> &rhs) { return Vector<T>(m[0][0]*rhs.x + m[0][1]*rhs.y + m[0][2]*rhs.z + m[0][3]*m[0][3], m[1][0]*rhs.x + m[1][1]*rhs.y + m[1][2]*rhs.z + m[1][3]*m[1][3], m[2][0]*rhs.x + m[2][1]*rhs.y + m[2][2]*rhs.z + m[2][3]*m[2][3]); } template<class T> Vector<T> Matrix<T>::transform_vector(const Vector<T> &rhs) { return Vector<T>(m[0][0]*rhs.x + m[0][1]*rhs.y + m[0][2]*rhs.z + m[0][3], m[1][0]*rhs.x + m[1][1]*rhs.y + m[1][2]*rhs.z + m[1][3], m[2][0]*rhs.x + m[2][1]*rhs.y + m[2][2]*rhs.z + m[2][3]); } // Addition template<class T> Matrix<T>& Matrix<T>::operator +=(const T rhs) { m[0][0] += rhs; m[0][1] += rhs; m[0][2] += rhs; m[0][3] += rhs; m[1][0] += rhs; m[1][1] += rhs; m[1][2] += rhs; m[1][3] += rhs; m[2][0] += rhs; m[2][1] += rhs; m[2][2] += rhs; m[2][3] += rhs; m[3][0] += rhs; m[3][1] += rhs; m[3][2] += rhs; m[3][3] += rhs; return *this; } template<class T> Matrix<T>& Matrix<T>::operator +=(const Matrix<T> &rhs) { m[0][0] += rhs.m[0][0]; m[0][1] += rhs.m[0][1]; m[0][2] += rhs.m[0][2]; m[0][3] += rhs.m[0][3]; m[1][0] += rhs.m[1][0]; m[1][1] += rhs.m[1][1]; m[1][2] += rhs.m[1][2]; m[1][3] += rhs.m[1][3]; m[2][0] += rhs.m[2][0]; m[2][1] += rhs.m[2][1]; m[2][2] += rhs.m[2][2]; m[2][3] += rhs.m[2][3]; m[3][0] += rhs.m[3][0]; m[3][1] += rhs.m[3][1]; m[3][2] += rhs.m[3][2]; m[3][3] += rhs.m[3][3]; return *this; } template<class T> Matrix<T>& Matrix<T>::operator +=(const Vector<T> &rhs) { m[0][3] += rhs.x; m[1][3] += rhs.y; m[2][3] += rhs.z; return *this; } template<class T> Matrix<T> Matrix<T>::operator +(const T rhs) { return Matrix(*this) += rhs; } template<class T> Matrix<T> Matrix<T>::operator +(const Matrix<T> &rhs) { return Matrix(*this) += rhs; } template<class T> Matrix<T> Matrix<T>::operator +(const Vector<T> &rhs) { return Matrix(*this) += rhs; } // Subtraction template<class T> Matrix<T>& Matrix<T>::operator -=(const T rhs) { return *this += -rhs; } template<class T> Matrix<T>& Matrix<T>::operator -=(const Matrix<T> &rhs) { m[0][0] -= rhs.m[0][0]; m[0][1] -= rhs.m[0][1]; m[0][2] -= rhs.m[0][2]; m[0][3] -= rhs.m[0][3]; m[1][0] -= rhs.m[1][0]; m[1][1] -= rhs.m[1][1]; m[1][2] -= rhs.m[1][2]; m[1][3] -= rhs.m[1][3]; m[2][0] -= rhs.m[2][0]; m[2][1] -= rhs.m[2][1]; m[2][2] -= rhs.m[2][2]; m[2][3] -= rhs.m[2][3]; m[3][0] -= rhs.m[3][0]; m[3][1] -= rhs.m[3][1]; m[3][2] -= rhs.m[3][2]; m[3][3] -= rhs.m[3][3]; return *this; } template<class T> Matrix<T>& Matrix<T>::operator -=(const Vector<T> &rhs) { m[0][3] -= rhs.x; m[1][3] -= rhs.y; m[2][3] -= rhs.z; return *this; } template<class T> Matrix<T> Matrix<T>::operator -(const T rhs) { return Matrix(*this) -= rhs; } template<class T> Matrix<T> Matrix<T>::operator -(const Matrix<T> &rhs) { return Matrix(*this) -= rhs; } template<class T> Matrix<T> Matrix<T>::operator -(const Vector<T> &rhs) { return Matrix(*this) -= rhs; } // Comparison operators template<class T> bool Matrix<T>::operator ==(const T (&rhs)[4][4]) const { return m[0][0] == rhs[0][0] && m[0][1] == rhs[0][1] && m[0][2] == rhs[0][2] && m[0][3] == rhs[0][3] && m[1][0] == rhs[1][0] && m[1][1] == rhs[1][1] && m[1][2] == rhs[1][2] && m[1][3] == rhs[1][3] && m[2][0] == rhs[2][0] && m[2][1] == rhs[2][1] && m[2][2] == rhs[2][2] && m[2][3] == rhs[2][3] && m[3][0] == rhs[3][0] && m[3][1] == rhs[3][1] && m[3][2] == rhs[3][2] && m[3][3] == rhs[3][3]; } template<class T> bool Matrix<T>::operator ==(const T rhs) const { return this->operator ==({{rhs,rhs,rhs,rhs}, {rhs,rhs,rhs,rhs}, {rhs,rhs,rhs,rhs}, {rhs,rhs,rhs,rhs}}); } template<class T> bool Matrix<T>::operator ==(const Matrix &rhs) const { return *this == rhs.m; } template<class T> bool Matrix<T>::operator ==(const Vector<T> &rhs) const { return m[0][3] == rhs.x && m[1][3] == rhs.y && m[2][3] == rhs.z; } template<class T> bool Matrix<T>::operator !=(const T (&rhs)[4][4]) const { return !(*this == rhs); } template<class T> bool Matrix<T>::operator !=(const T rhs) const { return !(*this == rhs); } template<class T> bool Matrix<T>::operator !=(const Matrix &rhs) const { return !(*this == rhs); } template<class T> bool Matrix<T>::operator !=(const Vector<T> &rhs) const { return !(*this == rhs); } template<class T> void Matrix<T>::identity() { memset(m, 0, sizeof m); m[0][0] = m[1][1] = m[2][2] = m[3][3] = 1; } template<class T> bool Matrix<T>::is_identity() const { return m[0][0] == 1 && m[0][1] == 0 && m[0][2] == 0 && m[0][3] == 0 && m[1][0] == 0 && m[1][1] == 1 && m[1][2] == 0 && m[1][3] == 0 && m[2][0] == 0 && m[2][1] == 0 && m[2][2] == 1 && m[2][3] == 0 && m[3][0] == 0 && m[3][1] == 0 && m[3][2] == 0 && m[3][3] == 1; } // Get vector template<class T> Vector<T> Matrix<T>::get_forward_vector() const { return Vector<T>(m[0][0],m[1][0],m[2][0]); } template<class T> Vector<T> Matrix<T>::get_up_vector() const { return Vector<T>(m[0][1],m[1][1],m[2][1]); } template<class T> Vector<T> Matrix<T>::get_right_vector() const { return Vector<T>(m[0][2],m[1][2],m[2][2]); } template<class T> Vector<T> Matrix<T>::get_translation_vector() const { return Vector<T>(m[0][3],m[1][3],m[2][3]); } // Set scale template<class T> void Matrix<T>::set_scale(T x,T y,T z) { m[0][0] = x; m[1][1] = y; m[2][2] = z; } template<class T> void Matrix<T>::set_scale(const Vector<T> &v) { set_scale(v.x, v.y, v.z); } // Set rotation template<class T> void Matrix<T>::set_rotation(double a,T x,T y,T z) { double c = cos(a); double s = sin(a); double t = 1-c; m[0][0] = x*x*t + c; m[1][0] = y*x*t + z*s; m[2][0] = z*x*t - y*s; m[0][1] = x*y*t - z*s; m[1][1] = y*y*t + c; m[2][1] = z*y*t + x*s; m[0][2] = x*z*t + y*s; m[1][2] = y*z*t - x*s; m[2][2] = z*z*t + c; } template<class T> void Matrix<T>::set_rotation(double a, const Vector<T> &v) { set_rotation(a, v.x, v.y, v.z); } // Set translation template<class T> void Matrix<T>::set_translation(T x,T y,T z) { m[0][3] = x; m[1][3] = y; m[2][3] = z; } template<class T> void Matrix<T>::set_translation(const Vector<T> &v) { set_translation(v.x, v.y, v.z); } // Set FUR vectors template<class T> void Matrix<T>::set_forward(T x,T y,T z) { m[0][0] = x; m[1][0] = y; m[2][0] = z; } template<class T> void Matrix<T>::set_forward(const Vector<T> &v) { set_forward_vector(v.x, v.y, v.z); } template<class T> void Matrix<T>::set_up(T x,T y,T z) { m[0][1] = x; m[1][1] = y; m[2][1] = z; } template<class T> void Matrix<T>::set_up(const Vector<T> &v) { set_up_vector(v.x, v.y, v.z); } template<class T> void Matrix<T>::set_right(T x,T y,T z) { m[0][2] = x; m[1][2] = y; m[2][2] = z; } template<class T> void Matrix<T>::set_right(const Vector<T> &v) { set_right_vector(v.x, v.y, v.z); } // Get/Set row template<class T> T *Matrix<T>::get_row(unsigned int p) const { if (p > ROW_SIZE) return NULL; T *r = new T[ROW_SIZE]; r[0] = m[p][0]; r[1] = m[p][1]; r[2] = m[p][2]; r[3] = m[p][3]; return r; } template<class T> void Matrix<T>::set_row(unsigned int p,T r0,T r1,T r2,T r3) { if (p > ROW_SIZE) return; m[p][0] = r0; m[p][1] = r1; m[p][2] = r2; m[p][3] = r3; } // Get/Set column template<class T> T *Matrix<T>::get_column(unsigned int p) const { if (p > COLUMN_SIZE) return NULL; T *r = new T[COLUMN_SIZE]; r[0] = m[0][p]; r[1] = m[1][p]; r[2] = m[2][p]; r[3] = m[3][p]; return r; } template<class T> void Matrix<T>::set_column(unsigned int p,T c0,T c1,T c2,T c3) { if (p > COLUMN_SIZE) return; m[0][p] = c0; m[1][p] = c1; m[2][p] = c2; m[3][p] = c3; } template<class T> void Matrix<T>::set_column(unsigned int p, const Vector<T> &v,T c3) { set_column(p, v.x, v.y, v.z, c3); } #endif // MATRIX_H
/*
    Copyright (C) 2017 Lari Varjonen <[email protected]>

    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License
    version 2 as published by the Free Software Foundation.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
*/

#ifndef MATRIX_H
#define MATRIX_H

#include <cstring>

#include "vector.h"

/*     A   B   C   D
 * a { 00, 01, 02, 03 }
 * b { 10, 11, 12, 13 }
 * c { 20, 21, 22, 23 }
 * d { 30, 31, 32, 33 }
 *
 * aA, bA, cA = x, y, z = Forward Vector,
 * aB, bB, cB = x, y, z = Up Vector,
 * aC, bC, cC = x, y, z = Right Vector,
 * aD, bD, cD = x, y, z = Translation Vector.
 *
 * Matrix = Matrix +/- Vector
 * Matrix +=/-= Vector
 * Adding or subtracting translation.
 *
 * T *get_row(unsigned int p) const;
 * T *get_column(unsigned int p) const;
 * Return a dynamically allocated array. Remember to delete[] it.
*/

template<class T>
class Matrix
{
public:
    Matrix() {identity();}
    Matrix(T x0,T y0,T z0,
           T x1,T y1,T z1,
           T x2,T y2,T z2,
           T xT,T yT,T zT,
           T w0,T w1,T w2,T w3);
    Matrix(const Vector<T> &forward,
           const Vector<T> &up,
           const Vector<T> &right);
    Matrix(const T (&forward)[3],
           const T (&up)[3],
           const T (&right)[3]);
    Matrix(const T (&array)[4][4]);
    Matrix(const Matrix &_m);

    Matrix<T>& operator =(const T (&rhs)[4][4]);
    Matrix<T>& operator =(const T rhs);
    Matrix<T>& operator =(const Matrix &rhs);

    Matrix<T>& operator *=(const T rhs);
    Matrix<T>& operator *=(const Matrix &rhs);
    Matrix<T> operator *(const T rhs);
    Matrix<T> operator *(const Matrix &rhs);
    Vector<T> operator *(const Vector<T> &rhs);
    Vector<T> transform_vector(const Vector<T> &rhs);

    Matrix<T>& operator +=(const T rhs);
    Matrix<T>& operator +=(const Matrix<T> &rhs);
    Matrix<T>& operator +=(const Vector<T> &rhs);
    Matrix<T> operator +(const T rhs);
    Matrix<T> operator +(const Matrix<T> &rhs);
    Matrix<T> operator +(const Vector<T> &rhs);

    Matrix<T>& operator -=(const T rhs);
    Matrix<T>& operator -=(const Matrix<T> &rhs);
    Matrix<T>& operator -=(const Vector<T> &rhs);
    Matrix<T> operator -(const T rhs);
    Matrix<T> operator -(const Matrix<T> &rhs);
    Matrix<T> operator -(const Vector<T> &rhs);

    bool operator ==(const T (&rhs)[4][4]) const;
    bool operator ==(const T rhs) const;
    bool operator ==(const Matrix &rhs) const;
    bool operator ==(const Vector<T> &rhs) const;
    bool operator !=(const T (&rhs)[4][4]) const;
    bool operator !=(const T rhs) const;
    bool operator !=(const Matrix &rhs) const;
    bool operator !=(const Vector<T> &rhs) const;

    void identity();
    bool is_identity() const;

    Vector<T> get_forward_vector() const;
    Vector<T> get_up_vector() const;
    Vector<T> get_right_vector() const;
    Vector<T> get_translation_vector() const;

    void set_scale(T x,T y,T z);
    void set_scale(const Vector<T> &v);
    void set_rotation(double a, T x, T y, T z);
    void set_rotation(double a, const Vector<T> &v);
    void set_translation(T x,T y,T z);
    void set_translation(const Vector<T> &v);
    void set_forward(T x,T y,T z);
    void set_forward(const Vector<T> &v);
    void set_up(T x,T y,T z);
    void set_up(const Vector<T> &v);
    void set_right(T x,T y,T z);
    void set_right(const Vector<T> &v);

    T *get_row(unsigned int p) const;
    void set_row(unsigned int p,T r0,T r1,T r2,T r3);
    T *get_column(unsigned int p) const;
    void set_column(unsigned int p,T c0,T c1,T c2,T c3);
    void set_column(unsigned int p, const Vector<T> &v,T c3);

    T m[4][4];

    const unsigned int ROW_SIZE = sizeof(m)/sizeof(m[0]);
    const unsigned int COLUMN_SIZE = sizeof(m[0])/sizeof(T);

private:
    template<class U>
    friend std::ostream &operator <<(std::ostream &, const Matrix<U> &);
};

template<class T>
std::ostream& operator <<(std::ostream &os, const Matrix<T> &m)
{
    os << "Matrix\n";
    for (unsigned int r = 0; r < m.ROW_SIZE; r++)
    {
        os << "[";
        for (unsigned int c = 0; c < m.COLUMN_SIZE; c++)
            os << m.m[r][c] << ((c<3)?",":"]\n");
    }
    return os << "\n";
}

template<class T>
Matrix<T>::Matrix(T x0,T y0,T z0,
                  T x1,T y1,T z1,
                  T x2,T y2,T z2,
                  T xT,T yT,T zT,
                  T w0,T w1,T w2,T w3)
{
    m[0][0] = x0; m[0][1] = x1; m[0][2] = x2; m[0][3] = xT;
    m[1][0] = y0; m[1][1] = y1; m[1][2] = y2; m[1][3] = yT;
    m[2][0] = z0; m[2][1] = z1; m[2][2] = z2; m[2][3] = zT;
    m[3][0] = w0; m[3][1] = w1; m[3][2] = w2; m[3][3] = w3;
}

template<class T>
Matrix<T>::Matrix(const Matrix &_m)
{
    memcpy(m, _m.m, sizeof m);
}

template<class T>
Matrix<T>::Matrix(const Vector<T> &f,
               const Vector<T> &u,
               const Vector<T> &r)
{
    *this = {{f.x,u.x,r.x,0},
             {f.y,u.y,r.y,0},
             {f.z,u.z,r.z,0},
             {0,0,0,1}};
}

template<class T>
Matrix<T>::Matrix(const T (&f)[3],
                  const T (&u)[3],
                  const T (&r)[3])
{
    *this = {{f[0],u[0],r[0],0},
             {f[1],u[1],r[1],0},
             {f[2],u[2],r[2],0},
             {0,0,0,1}};
}

template<class T>
Matrix<T>::Matrix(const T (&array)[4][4])
{
    *this = array;
}

// Basic assignment
template<class T>
Matrix<T>& Matrix<T>::operator =(const T (&rhs)[4][4])
{
    if (*this != rhs) memcpy(m, rhs, sizeof m);
    return *this;
}
template<class T>
Matrix<T>& Matrix<T>::operator =(const T rhs)
{
    return *this = {{rhs,rhs,rhs,rhs},
                    {rhs,rhs,rhs,rhs},
                    {rhs,rhs,rhs,rhs},
                    {rhs,rhs,rhs,rhs}};
}
template<class T>
Matrix<T>& Matrix<T>::operator =(const Matrix<T> &rhs)
{
    return *this = rhs.m;
}

// Multiplication
template<class T>
Matrix<T>& Matrix<T>::operator *=(const T rhs)
{
    m[0][0] *= rhs; m[0][1] *= rhs; m[0][2] *= rhs; m[0][3] *= rhs;
    m[1][0] *= rhs; m[1][1] *= rhs; m[1][2] *= rhs; m[1][3] *= rhs;
    m[2][0] *= rhs; m[2][1] *= rhs; m[2][2] *= rhs; m[2][3] *= rhs;
    m[3][0] *= rhs; m[3][1] *= rhs; m[3][2] *= rhs; m[3][3] *= rhs;
    return *this;
}
template<class T>
Matrix<T>& Matrix<T>::operator *=(const Matrix &rhs)
{
    T r[ROW_SIZE][COLUMN_SIZE];
    r[0][0] = m[0][0]*rhs.m[0][0] + m[0][1]*rhs.m[1][0] + m[0][2]*rhs.m[2][0] + m[0][3]*rhs.m[3][0];
    r[0][1] = m[0][0]*rhs.m[0][1] + m[0][1]*rhs.m[1][1] + m[0][2]*rhs.m[2][1] + m[0][3]*rhs.m[3][1];
    r[0][2] = m[0][0]*rhs.m[0][2] + m[0][1]*rhs.m[1][2] + m[0][2]*rhs.m[2][2] + m[0][3]*rhs.m[3][2];
    r[0][3] = m[0][0]*rhs.m[0][3] + m[0][1]*rhs.m[1][3] + m[0][2]*rhs.m[2][3] + m[0][3]*rhs.m[3][3];

    r[1][0] = m[1][0]*rhs.m[0][0] + m[1][1]*rhs.m[1][0] + m[1][2]*rhs.m[2][0] + m[1][3]*rhs.m[3][0];
    r[1][1] = m[1][0]*rhs.m[0][1] + m[1][1]*rhs.m[1][1] + m[1][2]*rhs.m[2][1] + m[1][3]*rhs.m[3][1];
    r[1][2] = m[1][0]*rhs.m[0][2] + m[1][1]*rhs.m[1][2] + m[1][2]*rhs.m[2][2] + m[1][3]*rhs.m[3][2];
    r[1][3] = m[1][0]*rhs.m[0][3] + m[1][1]*rhs.m[1][3] + m[1][2]*rhs.m[2][3] + m[1][3]*rhs.m[3][3];

    r[2][0] = m[2][0]*rhs.m[0][0] + m[2][1]*rhs.m[1][0] + m[2][2]*rhs.m[2][0] + m[2][3]*rhs.m[3][0];
    r[2][1] = m[2][0]*rhs.m[0][1] + m[2][1]*rhs.m[1][1] + m[2][2]*rhs.m[2][1] + m[2][3]*rhs.m[3][1];
    r[2][2] = m[2][0]*rhs.m[0][2] + m[2][1]*rhs.m[1][2] + m[2][2]*rhs.m[2][2] + m[2][3]*rhs.m[3][2];
    r[2][3] = m[2][0]*rhs.m[0][3] + m[2][1]*rhs.m[1][3] + m[2][2]*rhs.m[2][3] + m[2][3]*rhs.m[3][3];

    r[3][0] = m[3][0]*rhs.m[0][0] + m[3][1]*rhs.m[1][0] + m[3][2]*rhs.m[2][0] + m[3][3]*rhs.m[3][0];
    r[3][1] = m[3][0]*rhs.m[0][1] + m[3][1]*rhs.m[1][1] + m[3][2]*rhs.m[2][1] + m[3][3]*rhs.m[3][1];
    r[3][2] = m[3][0]*rhs.m[0][2] + m[3][1]*rhs.m[1][2] + m[3][2]*rhs.m[2][2] + m[3][3]*rhs.m[3][2];
    r[3][3] = m[3][0]*rhs.m[0][3] + m[3][1]*rhs.m[1][3] + m[3][2]*rhs.m[2][3] + m[3][3]*rhs.m[3][3];

    memcpy(m, r, sizeof m);
    return *this;
}
template<class T>
Matrix<T> Matrix<T>::operator *(const T rhs)
{
    return Matrix(*this) *= rhs;
}
template<class T>
Matrix<T> Matrix<T>::operator *(const Matrix &rhs)
{
    return Matrix(*this) *= rhs;
}
template<class T>
Vector<T> Matrix<T>::operator *(const Vector<T> &rhs)
{
    return Vector<T>(m[0][0]*rhs.x + m[0][1]*rhs.y + m[0][2]*rhs.z + m[0][3]*m[0][3],
                     m[1][0]*rhs.x + m[1][1]*rhs.y + m[1][2]*rhs.z + m[1][3]*m[1][3],
                     m[2][0]*rhs.x + m[2][1]*rhs.y + m[2][2]*rhs.z + m[2][3]*m[2][3]);
}
template<class T>
Vector<T> Matrix<T>::transform_vector(const Vector<T> &rhs)
{
    return Vector<T>(m[0][0]*rhs.x + m[0][1]*rhs.y + m[0][2]*rhs.z + m[0][3],
                     m[1][0]*rhs.x + m[1][1]*rhs.y + m[1][2]*rhs.z + m[1][3],
                     m[2][0]*rhs.x + m[2][1]*rhs.y + m[2][2]*rhs.z + m[2][3]);
}

// Addition
template<class T>
Matrix<T>& Matrix<T>::operator +=(const T rhs)
{
    m[0][0] += rhs; m[0][1] += rhs; m[0][2] += rhs; m[0][3] += rhs;
    m[1][0] += rhs; m[1][1] += rhs; m[1][2] += rhs; m[1][3] += rhs;
    m[2][0] += rhs; m[2][1] += rhs; m[2][2] += rhs; m[2][3] += rhs;
    m[3][0] += rhs; m[3][1] += rhs; m[3][2] += rhs; m[3][3] += rhs;
    return *this;
}
template<class T>
Matrix<T>& Matrix<T>::operator +=(const Matrix<T> &rhs)
{
    m[0][0] += rhs.m[0][0]; m[0][1] += rhs.m[0][1]; m[0][2] += rhs.m[0][2]; m[0][3] += rhs.m[0][3];
    m[1][0] += rhs.m[1][0]; m[1][1] += rhs.m[1][1]; m[1][2] += rhs.m[1][2]; m[1][3] += rhs.m[1][3];
    m[2][0] += rhs.m[2][0]; m[2][1] += rhs.m[2][1]; m[2][2] += rhs.m[2][2]; m[2][3] += rhs.m[2][3];
    m[3][0] += rhs.m[3][0]; m[3][1] += rhs.m[3][1]; m[3][2] += rhs.m[3][2]; m[3][3] += rhs.m[3][3];
    return *this;
}
template<class T>
Matrix<T>& Matrix<T>::operator +=(const Vector<T> &rhs)
{
    m[0][3] += rhs.x;
    m[1][3] += rhs.y;
    m[2][3] += rhs.z;
    return *this;
}
template<class T>
Matrix<T> Matrix<T>::operator +(const T rhs)
{
    return Matrix(*this) += rhs;
}
template<class T>
Matrix<T> Matrix<T>::operator +(const Matrix<T> &rhs)
{
    return Matrix(*this) += rhs;
}
template<class T>
Matrix<T> Matrix<T>::operator +(const Vector<T> &rhs)
{
    return Matrix(*this) += rhs;
}

// Subtraction
template<class T>
Matrix<T>& Matrix<T>::operator -=(const T rhs)
{
    return *this += -rhs;
}
template<class T>
Matrix<T>& Matrix<T>::operator -=(const Matrix<T> &rhs)
{
    m[0][0] -= rhs.m[0][0]; m[0][1] -= rhs.m[0][1]; m[0][2] -= rhs.m[0][2]; m[0][3] -= rhs.m[0][3];
    m[1][0] -= rhs.m[1][0]; m[1][1] -= rhs.m[1][1]; m[1][2] -= rhs.m[1][2]; m[1][3] -= rhs.m[1][3];
    m[2][0] -= rhs.m[2][0]; m[2][1] -= rhs.m[2][1]; m[2][2] -= rhs.m[2][2]; m[2][3] -= rhs.m[2][3];
    m[3][0] -= rhs.m[3][0]; m[3][1] -= rhs.m[3][1]; m[3][2] -= rhs.m[3][2]; m[3][3] -= rhs.m[3][3];
    return *this;
}
template<class T>
Matrix<T>& Matrix<T>::operator -=(const Vector<T> &rhs)
{
    m[0][3] -= rhs.x;
    m[1][3] -= rhs.y;
    m[2][3] -= rhs.z;
    return *this;
}
template<class T>
Matrix<T> Matrix<T>::operator -(const T rhs)
{
    return Matrix(*this) -= rhs;
}
template<class T>
Matrix<T> Matrix<T>::operator -(const Matrix<T> &rhs)
{
    return Matrix(*this) -= rhs;
}
template<class T>
Matrix<T> Matrix<T>::operator -(const Vector<T> &rhs)
{
    return Matrix(*this) -= rhs;
}

// Comparison operators
template<class T>
bool Matrix<T>::operator ==(const T (&rhs)[4][4]) const
{
    return m[0][0] == rhs[0][0] && m[0][1] == rhs[0][1] && m[0][2] == rhs[0][2] && m[0][3] == rhs[0][3] &&
           m[1][0] == rhs[1][0] && m[1][1] == rhs[1][1] && m[1][2] == rhs[1][2] && m[1][3] == rhs[1][3] &&
           m[2][0] == rhs[2][0] && m[2][1] == rhs[2][1] && m[2][2] == rhs[2][2] && m[2][3] == rhs[2][3] &&
           m[3][0] == rhs[3][0] && m[3][1] == rhs[3][1] && m[3][2] == rhs[3][2] && m[3][3] == rhs[3][3];
}
template<class T>
bool Matrix<T>::operator ==(const T rhs) const
{
    return this->operator ==({{rhs,rhs,rhs,rhs},
                              {rhs,rhs,rhs,rhs},
                              {rhs,rhs,rhs,rhs},
                              {rhs,rhs,rhs,rhs}});
}
template<class T>
bool Matrix<T>::operator ==(const Matrix &rhs) const
{
    return *this == rhs.m;
}
template<class T>
bool Matrix<T>::operator ==(const Vector<T> &rhs) const
{
    return m[0][3] == rhs.x && m[1][3] == rhs.y && m[2][3] == rhs.z;
}
template<class T>
bool Matrix<T>::operator !=(const T (&rhs)[4][4]) const
{
    return !(*this == rhs);
}
template<class T>
bool Matrix<T>::operator !=(const T rhs) const
{
    return !(*this == rhs);
}
template<class T>
bool Matrix<T>::operator !=(const Matrix &rhs) const
{
    return !(*this == rhs);
}
template<class T>
bool Matrix<T>::operator !=(const Vector<T> &rhs) const
{
    return !(*this == rhs);
}

template<class T>
void Matrix<T>::identity()
{
    memset(m, 0, sizeof m);
    m[0][0] = m[1][1] = m[2][2] = m[3][3] = 1;
}

template<class T>
bool Matrix<T>::is_identity() const
{
    return m[0][0] == 1 && m[0][1] == 0 && m[0][2] == 0 && m[0][3] == 0 &&
           m[1][0] == 0 && m[1][1] == 1 && m[1][2] == 0 && m[1][3] == 0 &&
           m[2][0] == 0 && m[2][1] == 0 && m[2][2] == 1 && m[2][3] == 0 &&
           m[3][0] == 0 && m[3][1] == 0 && m[3][2] == 0 && m[3][3] == 1;
}

// Get vector
template<class T>
Vector<T> Matrix<T>::get_forward_vector() const
{
    return Vector<T>(m[0][0],m[1][0],m[2][0]);
}
template<class T>
Vector<T> Matrix<T>::get_up_vector() const
{
    return Vector<T>(m[0][1],m[1][1],m[2][1]);
}
template<class T>
Vector<T> Matrix<T>::get_right_vector() const
{
    return Vector<T>(m[0][2],m[1][2],m[2][2]);
}
template<class T>
Vector<T> Matrix<T>::get_translation_vector() const
{
    return Vector<T>(m[0][3],m[1][3],m[2][3]);
}

// Set scale
template<class T>
void Matrix<T>::set_scale(T x,T y,T z)
{
    m[0][0] = x;
    m[1][1] = y;
    m[2][2] = z;
}
template<class T>
void Matrix<T>::set_scale(const Vector<T> &v)
{
    set_scale(v.x, v.y, v.z);
}

// Set rotation
template<class T>
void Matrix<T>::set_rotation(double a,T x,T y,T z)
{
    double c = cos(a);
    double s = sin(a);
    double t = 1-c;

    m[0][0] = x*x*t + c;
    m[1][0] = y*x*t + z*s;
    m[2][0] = z*x*t - y*s;

    m[0][1] = x*y*t - z*s;
    m[1][1] = y*y*t + c;
    m[2][1] = z*y*t + x*s;

    m[0][2] = x*z*t + y*s;
    m[1][2] = y*z*t - x*s;
    m[2][2] = z*z*t + c;
}
template<class T>
void Matrix<T>::set_rotation(double a, const Vector<T> &v)
{
    set_rotation(a, v.x, v.y, v.z);
}

// Set translation
template<class T>
void Matrix<T>::set_translation(T x,T y,T z)
{
    m[0][3] = x;
    m[1][3] = y;
    m[2][3] = z;
}
template<class T>
void Matrix<T>::set_translation(const Vector<T> &v)
{
    set_translation(v.x, v.y, v.z);
}

// Set FUR vectors
template<class T>
void Matrix<T>::set_forward(T x,T y,T z)
{
    m[0][0] = x;
    m[1][0] = y;
    m[2][0] = z;
}
template<class T>
void Matrix<T>::set_forward(const Vector<T> &v)
{
    set_forward_vector(v.x, v.y, v.z);
}
template<class T>
void Matrix<T>::set_up(T x,T y,T z)
{
    m[0][1] = x;
    m[1][1] = y;
    m[2][1] = z;
}
template<class T>
void Matrix<T>::set_up(const Vector<T> &v)
{
    set_up_vector(v.x, v.y, v.z);
}
template<class T>
void Matrix<T>::set_right(T x,T y,T z)
{
    m[0][2] = x;
    m[1][2] = y;
    m[2][2] = z;
}
template<class T>
void Matrix<T>::set_right(const Vector<T> &v)
{
    set_right_vector(v.x, v.y, v.z);
}

// Get/Set row
template<class T>
T *Matrix<T>::get_row(unsigned int p) const
{
    if (p > ROW_SIZE) return NULL;
    T *r = new T[ROW_SIZE];
    r[0] = m[p][0];
    r[1] = m[p][1];
    r[2] = m[p][2];
    r[3] = m[p][3];
    return r;
}
template<class T>
void Matrix<T>::set_row(unsigned int p,T r0,T r1,T r2,T r3)
{
    if (p > ROW_SIZE) return;
    m[p][0] = r0;
    m[p][1] = r1;
    m[p][2] = r2;
    m[p][3] = r3;
}
// Get/Set column
template<class T>
T *Matrix<T>::get_column(unsigned int p) const
{
    if (p > COLUMN_SIZE) return NULL;
    T *r = new T[COLUMN_SIZE];
    r[0] = m[0][p];
    r[1] = m[1][p];
    r[2] = m[2][p];
    r[3] = m[3][p];
    return r;
}
template<class T>
void Matrix<T>::set_column(unsigned int p,T c0,T c1,T c2,T c3)
{
    if (p > COLUMN_SIZE) return;
    m[0][p] = c0;
    m[1][p] = c1;
    m[2][p] = c2;
    m[3][p] = c3;
}
template<class T>
void Matrix<T>::set_column(unsigned int p, const Vector<T> &v,T c3)
{
    set_column(p, v.x, v.y, v.z, c3);
}

#endif // MATRIX_H

vector.h
/*
Copyright (C) 2017 Lari Varjonen <[email protected]>
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
version 2 as published by the Free Software Foundation.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef VECTOR_H
#define VECTOR_H
#include <math.h>
template<class T>
class Vector
{
public:
Vector();
Vector(T rhs);
Vector(T x, T y, T z);
Vector(const T (&array)[3]);
Vector<T>& operator =(const T rhs);
Vector<T>& operator =(const T (&rhs)[3]);
Vector<T>& operator =(const Vector &rhs);
Vector<T>& operator *=(const T rhs);
Vector<T>& operator *=(const Vector &rhs);
Vector<T> operator *(const T rhs);
Vector<T> operator *(const Vector &rhs);
Vector<T>& operator /=(const T rhs);
Vector<T>& operator /=(const Vector &rhs);
Vector<T> operator /(const T rhs);
Vector<T> operator /(const Vector &rhs);
Vector<T>& operator +=(const T rhs);
Vector<T>& operator +=(const Vector &rhs);
Vector<T> operator +(const T rhs);
Vector<T> operator +(const Vector &rhs);
Vector<T>& operator -=(const T rhs);
Vector<T>& operator -=(const Vector &rhs);
Vector<T> operator -(const T rhs);
Vector<T> operator -(const Vector &rhs);
bool operator ==(const T (&rhs)[3]) const;
bool operator ==(const T rhs) const;
bool operator ==(const Vector &rhs) const;
bool operator !=(const T (&rhs)[3]) const;
bool operator !=(const T rhs) const;
bool operator !=(const Vector &rhs) const;
T dot(const Vector &rhs) const;
Vector<T> cross(const Vector &rhs) const;
T length() const;
T length_sqr() const;
Vector<T> normalized() const;
void normalize();
T x, y, z;
private:
template<class U>
friend std::ostream &operator <<(std::ostream &, const Vector<U> &);
};
template<class T>
std::ostream& operator <<(std::ostream &os, const Vector<T> &vec)
{
return os << "Vector " << vec.x << ", " << vec.y << ", " << vec.z << "\n";
}
template<class T>
Vector<T>::Vector()
{
x = y = z = 0;
}
template<class T>
Vector<T>::Vector(T rhs)
{
x = rhs;
y = rhs;
z = rhs;
}
template<class T>
Vector<T>::Vector(T x, T y, T z)
{
this->x = x;
this->y = y;
this->z = z;
}
template<class T>
Vector<T>::Vector(const T (&array)[3])
{
x = array[0];
y = array[1];
z = array[2];
}
// Basic assignment
template<class T>
Vector<T>& Vector<T>::operator =(const T (&rhs)[3])
{
if (*this != rhs)
{
x = rhs[0];
y = rhs[1];
z = rhs[2];
}
return *this;
}
template<class T>
Vector<T>& Vector<T>::operator =(const T rhs)
{
return *this = { rhs, rhs, rhs };
}
template<class T>
Vector<T>& Vector<T>::operator =(const Vector<T> &rhs)
{
return *this = { rhs.x, rhs.y, rhs.z };
}
// Multiplication
template<class T>
Vector<T>& Vector<T>::operator *=(const T rhs)
{
x *= rhs;
y *= rhs;
z *= rhs;
return *this;
}
template<class T>
Vector<T>& Vector<T>::operator *=(const Vector &rhs)
{
x *= rhs.x;
y *= rhs.y;
z *= rhs.z;
return *this;
}
template<class T>
Vector<T> Vector<T>::operator *(const T rhs)
{
return Vector(*this) *= rhs;
}
template<class T>
Vector<T> Vector<T>::operator *(const Vector &rhs)
{
return Vector(*this) *= rhs;
}
// Division
template<class T>
Vector<T>& Vector<T>::operator /=(const T rhs)
{
x /= rhs;
y /= rhs;
z /= rhs;
return *this;
}
template<class T>
Vector<T>& Vector<T>::operator /=(const Vector &rhs)
{
x /= rhs.x;
y /= rhs.y;
z /= rhs.z;
return *this;
}
template<class T>
Vector<T> Vector<T>::operator /(const T rhs)
{
return Vector(*this) /= rhs;
}
template<class T>
Vector<T> Vector<T>::operator /(const Vector &rhs)
{
return Vector(*this) /= rhs;
}
// Addition
template<class T>
Vector<T>& Vector<T>::operator +=(const T rhs)
{
x += rhs;
y += rhs;
z += rhs;
return *this;
}
template<class T>
Vector<T>& Vector<T>::operator +=(const Vector &rhs)
{
x += rhs.x;
y += rhs.y;
z += rhs.z;
return *this;
}
template<class T>
Vector<T> Vector<T>::operator +(const T rhs)
{
return Vector(*this) += rhs;
}
template<class T>
Vector<T> Vector<T>::operator +(const Vector &rhs)
{
return Vector(*this) += rhs;
}
// Subtraction
template<class T>
Vector<T>& Vector<T>::operator -=(const T rhs)
{
return *this += -rhs;
}
template<class T>
Vector<T>& Vector<T>::operator -=(const Vector &rhs)
{
x -= rhs.x;
y -= rhs.y;
z -= rhs.z;
return *this;
}
template<class T>
Vector<T> Vector<T>::operator -(const T rhs)
{
return Vector(*this) -= rhs;
}
template<class T>
Vector<T> Vector<T>::operator -(const Vector &rhs)
{
return Vector(*this) -= rhs;
}
// Comparison operators
template<class T>
bool Vector<T>::operator ==(const T (&rhs)[3]) const
{
return x == rhs[0] && y == rhs[1] && z == rhs[2];
}
template<class T>
bool Vector<T>::operator ==(const T rhs) const
{
return x == rhs && y == rhs && z == rhs;
}
template<class T>
bool Vector<T>::operator ==(const Vector &rhs) const
{
return x == rhs.x && y == rhs.y && z == rhs.z;
}
template<class T>
bool Vector<T>::operator !=(const T (&rhs)[3]) const
{
return !(*this == rhs);
}
template<class T>
bool Vector<T>::operator !=(const T rhs) const
{
return !(*this == rhs);
}
template<class T>
bool Vector<T>::operator !=(const Vector &rhs) const
{
return !(*this == rhs);
}
// Products
template<class T>
T Vector<T>::dot(const Vector &rhs) const
{
return x * rhs.x + y * rhs.y + z * rhs.z;
}
template<class T>
Vector<T> Vector<T>::cross(const Vector &rhs) const
{
T _x = y * rhs.z - z * rhs.y;
T _y = z * rhs.x - x * rhs.z;
T _z = x * rhs.y - y * rhs.x;
return Vector(_x, _y, _z);
}
template<class T>
T Vector<T>::length() const
{
return sqrt(x*x + y*y + z*z);
}
template<class T>
T Vector<T>::length_sqr() const
{
return (x*x + y*y + z*z);
}
template<class T>
Vector<T> Vector<T>::normalized() const
{
T l = length();
Vector<T> r = 0;
if (l == 0) return r;
r = (*this);
return r /= l;
}
template<class T>
void Vector<T>::normalize()
{
T l = length();
if (l == 0) x = y = z = 0;
else (*this) /= l;
}
#endif // VECTOR_H
/* Copyright (C) 2017 Lari Varjonen <[email protected]> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 as published by the Free Software Foundation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef VECTOR_H #define VECTOR_H #include <math.h> template<class T> class Vector { public: Vector(); Vector(T rhs); Vector(T x, T y, T z); Vector(const T (&array)[3]); Vector<T>& operator =(const T rhs); Vector<T>& operator =(const T (&rhs)[3]); Vector<T>& operator =(const Vector &rhs); Vector<T>& operator *=(const T rhs); Vector<T>& operator *=(const Vector &rhs); Vector<T> operator *(const T rhs); Vector<T> operator *(const Vector &rhs); Vector<T>& operator /=(const T rhs); Vector<T>& operator /=(const Vector &rhs); Vector<T> operator /(const T rhs); Vector<T> operator /(const Vector &rhs); Vector<T>& operator +=(const T rhs); Vector<T>& operator +=(const Vector &rhs); Vector<T> operator +(const T rhs); Vector<T> operator +(const Vector &rhs); Vector<T>& operator -=(const T rhs); Vector<T>& operator -=(const Vector &rhs); Vector<T> operator -(const T rhs); Vector<T> operator -(const Vector &rhs); bool operator ==(const T (&rhs)[3]) const; bool operator ==(const T rhs) const; bool operator ==(const Vector &rhs) const; bool operator !=(const T (&rhs)[3]) const; bool operator !=(const T rhs) const; bool operator !=(const Vector &rhs) const; T dot(const Vector &rhs) const; Vector<T> cross(const Vector &rhs) const; T length() const; T length_sqr() const; Vector<T> normalized() const; void normalize(); T x, y, z; private: template<class U> friend std::ostream &operator <<(std::ostream &, const Vector<U> &); }; template<class T> std::ostream& operator <<(std::ostream &os, const Vector<T> &vec) { return os << "Vector " << vec.x << ", " << vec.y << ", " << vec.z << "\n"; } template<class T> Vector<T>::Vector() { x = y = z = 0; } template<class T> Vector<T>::Vector(T rhs) { x = rhs; y = rhs; z = rhs; } template<class T> Vector<T>::Vector(T x, T y, T z) { this->x = x; this->y = y; this->z = z; } template<class T> Vector<T>::Vector(const T (&array)[3]) { x = array[0]; y = array[1]; z = array[2]; } // Basic assignment template<class T> Vector<T>& Vector<T>::operator =(const T (&rhs)[3]) { if (*this != rhs) { x = rhs[0]; y = rhs[1]; z = rhs[2]; } return *this; } template<class T> Vector<T>& Vector<T>::operator =(const T rhs) { return *this = { rhs, rhs, rhs }; } template<class T> Vector<T>& Vector<T>::operator =(const Vector<T> &rhs) { return *this = { rhs.x, rhs.y, rhs.z }; } // Multiplication template<class T> Vector<T>& Vector<T>::operator *=(const T rhs) { x *= rhs; y *= rhs; z *= rhs; return *this; } template<class T> Vector<T>& Vector<T>::operator *=(const Vector &rhs) { x *= rhs.x; y *= rhs.y; z *= rhs.z; return *this; } template<class T> Vector<T> Vector<T>::operator *(const T rhs) { return Vector(*this) *= rhs; } template<class T> Vector<T> Vector<T>::operator *(const Vector &rhs) { return Vector(*this) *= rhs; } // Division template<class T> Vector<T>& Vector<T>::operator /=(const T rhs) { x /= rhs; y /= rhs; z /= rhs; return *this; } template<class T> Vector<T>& Vector<T>::operator /=(const Vector &rhs) { x /= rhs.x; y /= rhs.y; z /= rhs.z; return *this; } template<class T> Vector<T> Vector<T>::operator /(const T rhs) { return Vector(*this) /= rhs; } template<class T> Vector<T> Vector<T>::operator /(const Vector &rhs) { return Vector(*this) /= rhs; } // Addition template<class T> Vector<T>& Vector<T>::operator +=(const T rhs) { x += rhs; y += rhs; z += rhs; return *this; } template<class T> Vector<T>& Vector<T>::operator +=(const Vector &rhs) { x += rhs.x; y += rhs.y; z += rhs.z; return *this; } template<class T> Vector<T> Vector<T>::operator +(const T rhs) { return Vector(*this) += rhs; } template<class T> Vector<T> Vector<T>::operator +(const Vector &rhs) { return Vector(*this) += rhs; } // Subtraction template<class T> Vector<T>& Vector<T>::operator -=(const T rhs) { return *this += -rhs; } template<class T> Vector<T>& Vector<T>::operator -=(const Vector &rhs) { x -= rhs.x; y -= rhs.y; z -= rhs.z; return *this; } template<class T> Vector<T> Vector<T>::operator -(const T rhs) { return Vector(*this) -= rhs; } template<class T> Vector<T> Vector<T>::operator -(const Vector &rhs) { return Vector(*this) -= rhs; } // Comparison operators template<class T> bool Vector<T>::operator ==(const T (&rhs)[3]) const { return x == rhs[0] && y == rhs[1] && z == rhs[2]; } template<class T> bool Vector<T>::operator ==(const T rhs) const { return x == rhs && y == rhs && z == rhs; } template<class T> bool Vector<T>::operator ==(const Vector &rhs) const { return x == rhs.x && y == rhs.y && z == rhs.z; } template<class T> bool Vector<T>::operator !=(const T (&rhs)[3]) const { return !(*this == rhs); } template<class T> bool Vector<T>::operator !=(const T rhs) const { return !(*this == rhs); } template<class T> bool Vector<T>::operator !=(const Vector &rhs) const { return !(*this == rhs); } // Products template<class T> T Vector<T>::dot(const Vector &rhs) const { return x * rhs.x + y * rhs.y + z * rhs.z; } template<class T> Vector<T> Vector<T>::cross(const Vector &rhs) const { T _x = y * rhs.z - z * rhs.y; T _y = z * rhs.x - x * rhs.z; T _z = x * rhs.y - y * rhs.x; return Vector(_x, _y, _z); } template<class T> T Vector<T>::length() const { return sqrt(x*x + y*y + z*z); } template<class T> T Vector<T>::length_sqr() const { return (x*x + y*y + z*z); } template<class T> Vector<T> Vector<T>::normalized() const { T l = length(); Vector<T> r = 0; if (l == 0) return r; r = (*this); return r /= l; } template<class T> void Vector<T>::normalize() { T l = length(); if (l == 0) x = y = z = 0; else (*this) /= l; } #endif // VECTOR_H
/*
    Copyright (C) 2017 Lari Varjonen <[email protected]>

    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License
    version 2 as published by the Free Software Foundation.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
*/

#ifndef VECTOR_H
#define VECTOR_H

#include <math.h>


template<class T>
class Vector
{
public:
    Vector();
    Vector(T rhs);
    Vector(T x, T y, T z);
    Vector(const T (&array)[3]);

    Vector<T>& operator =(const T rhs);
    Vector<T>& operator =(const T (&rhs)[3]);
    Vector<T>& operator =(const Vector &rhs);

    Vector<T>& operator *=(const T rhs);
    Vector<T>& operator *=(const Vector &rhs);
    Vector<T> operator *(const T rhs);
    Vector<T> operator *(const Vector &rhs);

    Vector<T>& operator /=(const T rhs);
    Vector<T>& operator /=(const Vector &rhs);
    Vector<T> operator /(const T rhs);
    Vector<T> operator /(const Vector &rhs);

    Vector<T>& operator +=(const T rhs);
    Vector<T>& operator +=(const Vector &rhs);
    Vector<T> operator +(const T rhs);
    Vector<T> operator +(const Vector &rhs);

    Vector<T>& operator -=(const T rhs);
    Vector<T>& operator -=(const Vector &rhs);
    Vector<T> operator -(const T rhs);
    Vector<T> operator -(const Vector &rhs);

    bool operator ==(const T (&rhs)[3]) const;
    bool operator ==(const T rhs) const;
    bool operator ==(const Vector &rhs) const;
    bool operator !=(const T (&rhs)[3]) const;
    bool operator !=(const T rhs) const;
    bool operator !=(const Vector &rhs) const;

    T dot(const Vector &rhs) const;
    Vector<T> cross(const Vector &rhs) const;

    T length() const;
    T length_sqr() const;
    Vector<T> normalized() const;
    void normalize();

    T x, y, z;

private:
    template<class U>
    friend std::ostream &operator <<(std::ostream &, const Vector<U> &);
};

template<class T>
std::ostream& operator <<(std::ostream &os, const Vector<T> &vec)
{
    return os << "Vector " << vec.x << ", " << vec.y << ", " << vec.z << "\n";
}

template<class T>
Vector<T>::Vector()
{
    x = y = z = 0;
}

template<class T>
Vector<T>::Vector(T rhs)
{
    x = rhs;
    y = rhs;
    z = rhs;
}

template<class T>
Vector<T>::Vector(T x, T y, T z)
{
    this->x = x;
    this->y = y;
    this->z = z;
}

template<class T>
Vector<T>::Vector(const T (&array)[3])
{
    x = array[0];
    y = array[1];
    z = array[2];
}

// Basic assignment
template<class T>
Vector<T>& Vector<T>::operator =(const T (&rhs)[3])
{
    if (*this != rhs)
    {
        x = rhs[0];
        y = rhs[1];
        z = rhs[2];
    }
    return *this;
}
template<class T>
Vector<T>& Vector<T>::operator =(const T rhs)
{
    return *this = { rhs, rhs, rhs };
}
template<class T>
Vector<T>& Vector<T>::operator =(const Vector<T> &rhs)
{
    return *this = { rhs.x, rhs.y, rhs.z };
}

// Multiplication
template<class T>
Vector<T>& Vector<T>::operator *=(const T rhs)
{
    x *= rhs;
    y *= rhs;
    z *= rhs;
    return *this;
}
template<class T>
Vector<T>& Vector<T>::operator *=(const Vector &rhs)
{
    x *= rhs.x;
    y *= rhs.y;
    z *= rhs.z;
    return *this;
}
template<class T>
Vector<T> Vector<T>::operator *(const T rhs)
{
    return Vector(*this) *= rhs;
}
template<class T>
Vector<T> Vector<T>::operator *(const Vector &rhs)
{
    return Vector(*this) *= rhs;
}

// Division
template<class T>
Vector<T>& Vector<T>::operator /=(const T rhs)
{
    x /= rhs;
    y /= rhs;
    z /= rhs;
    return *this;
}
template<class T>
Vector<T>& Vector<T>::operator /=(const Vector &rhs)
{
    x /= rhs.x;
    y /= rhs.y;
    z /= rhs.z;
    return *this;
}
template<class T>
Vector<T> Vector<T>::operator /(const T rhs)
{
    return Vector(*this) /= rhs;
}
template<class T>
Vector<T> Vector<T>::operator /(const Vector &rhs)
{
    return Vector(*this) /= rhs;
}

// Addition
template<class T>
Vector<T>& Vector<T>::operator +=(const T rhs)
{
    x += rhs;
    y += rhs;
    z += rhs;
    return *this;
}
template<class T>
Vector<T>& Vector<T>::operator +=(const Vector &rhs)
{
    x += rhs.x;
    y += rhs.y;
    z += rhs.z;
    return *this;
}
template<class T>
Vector<T> Vector<T>::operator +(const T rhs)
{
    return Vector(*this) += rhs;
}
template<class T>
Vector<T> Vector<T>::operator +(const Vector &rhs)
{
    return Vector(*this) += rhs;
}

// Subtraction
template<class T>
Vector<T>& Vector<T>::operator -=(const T rhs)
{
    return *this += -rhs;
}
template<class T>
Vector<T>& Vector<T>::operator -=(const Vector &rhs)
{
    x -= rhs.x;
    y -= rhs.y;
    z -= rhs.z;
    return *this;
}
template<class T>
Vector<T> Vector<T>::operator -(const T rhs)
{
    return Vector(*this) -= rhs;
}
template<class T>
Vector<T> Vector<T>::operator -(const Vector &rhs)
{
    return Vector(*this) -= rhs;
}

// Comparison operators
template<class T>
bool Vector<T>::operator ==(const T (&rhs)[3]) const
{
    return x == rhs[0] && y == rhs[1] && z == rhs[2];
}
template<class T>
bool Vector<T>::operator ==(const T rhs) const
{
    return x == rhs && y == rhs && z == rhs;
}
template<class T>
bool Vector<T>::operator ==(const Vector &rhs) const
{
    return x == rhs.x && y == rhs.y && z == rhs.z;
}
template<class T>
bool Vector<T>::operator !=(const T (&rhs)[3]) const
{
    return !(*this == rhs);
}
template<class T>
bool Vector<T>::operator !=(const T rhs) const
{
    return !(*this == rhs);
}
template<class T>
bool Vector<T>::operator !=(const Vector &rhs) const
{
    return !(*this == rhs);
}

// Products
template<class T>
T Vector<T>::dot(const Vector &rhs) const
{
    return x * rhs.x + y * rhs.y + z * rhs.z;
}
template<class T>
Vector<T> Vector<T>::cross(const Vector &rhs) const
{
    T _x = y * rhs.z - z * rhs.y;
    T _y = z * rhs.x - x * rhs.z;
    T _z = x * rhs.y - y * rhs.x;
    return Vector(_x, _y, _z);
}

template<class T>
T Vector<T>::length() const
{
    return sqrt(x*x + y*y + z*z);
}
template<class T>
T Vector<T>::length_sqr() const
{
    return (x*x + y*y + z*z);
}
template<class T>
Vector<T> Vector<T>::normalized() const
{
    T l = length();
    Vector<T> r = 0;
    if (l == 0) return r;
    r = (*this);
    return r /= l;
}
template<class T>
void Vector<T>::normalize()
{
    T l = length();
    if (l == 0) x = y = z = 0;
    else (*this) /= l;
}

#endif // VECTOR_H

Visitor counter with PHP and MySQL, Version 2

19.09.2020

Here is a second version of the previous one. A feature has been added to this version that it will re-register the same IP address after 86400 seconds which is 24 hours.

EDIT: 25.09.2020: Improved how to fetch the total number of visitors. Added hash. Ip-addresses are not visible anymore in the database.

EDIT: 01.04.2022: Completely rewritten code. Same functionality.

sqlcommand2.sql
--
-- Database: `website_visitorcounter`
--
CREATE DATABASE IF NOT EXISTS `website_visitorcounter` DEFAULT CHARACTER SET latin1 COLLATE latin1_swedish_ci;
USE `website_visitorcounter`;
-- --------------------------------------------------------
--
-- Table structure for table `count`
--
CREATE TABLE `count` (
`count` bigint(20) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-- --------------------------------------------------------
--
-- Table structure for table `temp`
--
CREATE TABLE `temp` (
`id` text NOT NULL,
`date` datetime NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-- -- Database: `website_visitorcounter` -- CREATE DATABASE IF NOT EXISTS `website_visitorcounter` DEFAULT CHARACTER SET latin1 COLLATE latin1_swedish_ci; USE `website_visitorcounter`; -- -------------------------------------------------------- -- -- Table structure for table `count` -- CREATE TABLE `count` ( `count` bigint(20) NOT NULL ) ENGINE=InnoDB DEFAULT CHARSET=latin1; -- -------------------------------------------------------- -- -- Table structure for table `temp` -- CREATE TABLE `temp` ( `id` text NOT NULL, `date` datetime NOT NULL ) ENGINE=InnoDB DEFAULT CHARSET=latin1;
--
-- Database: `website_visitorcounter`
--

CREATE DATABASE IF NOT EXISTS `website_visitorcounter` DEFAULT CHARACTER SET latin1 COLLATE latin1_swedish_ci;
USE `website_visitorcounter`;

-- --------------------------------------------------------

--
-- Table structure for table `count`
--

CREATE TABLE `count` (
  `count` bigint(20) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

-- --------------------------------------------------------

--
-- Table structure for table `temp`
--

CREATE TABLE `temp` (
  `id` text NOT NULL,
  `date` datetime NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

visitorcounter_credentials.php
<?php namespace mysql_visitorcounter_credentials;
class MySQLConnect
{
private $user_name = "user";
private $password = "password";
private $server_name;
private $db_name;
function __construct($server_name, $db_name) {
$this->server_name = $server_name;
$this->db_name = $db_name;
}
function connect() {
return new \mysqli($this->server_name,
$this->user_name,
$this->password,
$this->db_name);
}
}
<?php namespace mysql_visitorcounter_credentials; class MySQLConnect { private $user_name = "user"; private $password = "password"; private $server_name; private $db_name; function __construct($server_name, $db_name) { $this->server_name = $server_name; $this->db_name = $db_name; } function connect() { return new \mysqli($this->server_name, $this->user_name, $this->password, $this->db_name); } }
<?php namespace mysql_visitorcounter_credentials;
class MySQLConnect
{
	private $user_name = "user";
	private $password = "password";
	private $server_name;
	private $db_name;

	function __construct($server_name, $db_name) {
		$this->server_name = $server_name;
		$this->db_name = $db_name;
	}
	
	function connect() {
		return new \mysqli($this->server_name,
						  $this->user_name,
						  $this->password,
						  $this->db_name);
	}
}

phpvisitorcounter2.php
<?php
function get_visitor_count() {
require_once("visitorcounter_credentials.php");
$mysqlc = new \mysql_visitorcounter_credentials\MySQLConnect("localhost", "website_visitorcounter");
$conn = $mysqlc->connect();
if($conn->ping() == false) {
return "SQLERROR";
}
$ip = $_SERVER["REMOTE_ADDR"];
$id = hash("sha256", $ip);
$stmt = $conn->prepare("SELECT IF(EXISTS(SELECT `id` FROM `temp` WHERE `id` = ?), 1, 0)");
$stmt->bind_param("s", $id);
$stmt->execute();
$res = $stmt->get_result();
$res_fa = $res->fetch_assoc();
$id_exists = array_values($res_fa)[0];
if ($id_exists == 0) {
$stmt = $conn->prepare("UPDATE `count` SET `count` = `count` + 1");
$stmt->execute();
$stmt = $conn->prepare("INSERT INTO `temp`(`id`,`date`) VALUES (?,now())");
$stmt->bind_param("s", $id);
$stmt->execute();
}
$stmt = $conn->prepare("SELECT `count` FROM `count`");
$stmt->execute();
$res = $stmt->get_result();
$res_fa = $res->fetch_assoc();
$count = $res_fa["count"];
$conn->close();
return str_pad($count, 8, "0", STR_PAD_LEFT);
}
<?php function get_visitor_count() { require_once("visitorcounter_credentials.php"); $mysqlc = new \mysql_visitorcounter_credentials\MySQLConnect("localhost", "website_visitorcounter"); $conn = $mysqlc->connect(); if($conn->ping() == false) { return "SQLERROR"; } $ip = $_SERVER["REMOTE_ADDR"]; $id = hash("sha256", $ip); $stmt = $conn->prepare("SELECT IF(EXISTS(SELECT `id` FROM `temp` WHERE `id` = ?), 1, 0)"); $stmt->bind_param("s", $id); $stmt->execute(); $res = $stmt->get_result(); $res_fa = $res->fetch_assoc(); $id_exists = array_values($res_fa)[0]; if ($id_exists == 0) { $stmt = $conn->prepare("UPDATE `count` SET `count` = `count` + 1"); $stmt->execute(); $stmt = $conn->prepare("INSERT INTO `temp`(`id`,`date`) VALUES (?,now())"); $stmt->bind_param("s", $id); $stmt->execute(); } $stmt = $conn->prepare("SELECT `count` FROM `count`"); $stmt->execute(); $res = $stmt->get_result(); $res_fa = $res->fetch_assoc(); $count = $res_fa["count"]; $conn->close(); return str_pad($count, 8, "0", STR_PAD_LEFT); }
<?php
function get_visitor_count() {
	require_once("visitorcounter_credentials.php");
	$mysqlc = new \mysql_visitorcounter_credentials\MySQLConnect("localhost", "website_visitorcounter");
	$conn = $mysqlc->connect();

	if($conn->ping() == false) {
		return "SQLERROR";
	}

	$ip = $_SERVER["REMOTE_ADDR"];
	$id = hash("sha256", $ip);

	$stmt = $conn->prepare("SELECT IF(EXISTS(SELECT `id` FROM `temp` WHERE `id` = ?), 1, 0)");
	$stmt->bind_param("s", $id);
	$stmt->execute();

	$res = $stmt->get_result();
	$res_fa = $res->fetch_assoc();
	$id_exists = array_values($res_fa)[0];

	if ($id_exists == 0) {
		$stmt = $conn->prepare("UPDATE `count` SET `count` = `count` + 1");
		$stmt->execute();

		$stmt = $conn->prepare("INSERT INTO `temp`(`id`,`date`) VALUES (?,now())");
		$stmt->bind_param("s", $id);
		$stmt->execute();
	}

	$stmt = $conn->prepare("SELECT `count` FROM `count`");
	$stmt->execute();
	$res = $stmt->get_result();
	$res_fa = $res->fetch_assoc();
	$count = $res_fa["count"];

	$conn->close();
	return str_pad($count, 8, "0", STR_PAD_LEFT);
}

Code for crontab to run each hour. Keeping the temp section of the database clean and updated.

visitorcounter_cleaner.php
<?php
require_once("visitorcounter_credentials.php");
$mysqlc = new \mysql_visitorcounter_credentials\MySQLConnect("localhost", "website_visitorcounter");
$conn = $mysqlc->connect();
if($conn->ping() == false) {
die("mysql error");
}
$vc_recount_cooldown = 86400;
$stmt = $conn->prepare("DELETE FROM `temp` WHERE `date` < now() - interval ? SECOND");
$stmt->bind_param("s", $vc_recount_cooldown);
$stmt->execute();
$conn->close();
<?php require_once("visitorcounter_credentials.php"); $mysqlc = new \mysql_visitorcounter_credentials\MySQLConnect("localhost", "website_visitorcounter"); $conn = $mysqlc->connect(); if($conn->ping() == false) { die("mysql error"); } $vc_recount_cooldown = 86400; $stmt = $conn->prepare("DELETE FROM `temp` WHERE `date` < now() - interval ? SECOND"); $stmt->bind_param("s", $vc_recount_cooldown); $stmt->execute(); $conn->close();
<?php
require_once("visitorcounter_credentials.php");
$mysqlc = new \mysql_visitorcounter_credentials\MySQLConnect("localhost", "website_visitorcounter");
$conn = $mysqlc->connect();

if($conn->ping() == false) {
	die("mysql error");
}

$vc_recount_cooldown = 86400;

$stmt = $conn->prepare("DELETE FROM `temp` WHERE `date` < now() - interval ? SECOND");
$stmt->bind_param("s", $vc_recount_cooldown);
$stmt->execute();

$conn->close();

Visitor counter with PHP and MySQL

18.09.2020

Do you remember, in the old days, many sites had those little numbers that at its finest even looked like a 7seg screens with few digits that counted the number of times that the page was loaded. Each time you reloaded the page, one more was added to that counter number what it showed.

I made exactly that, and here is the code of it and how you can get it to work on your own website too. This code also includes that it prevents users to increase the counter number by repeatedly reloading the page again and again. However, this doesn't look like a 7seg screen but is just plain numbers. You need MySQL and PHP to get this to work on your website. Next, a little guide on how to make this working. You still need at least a basic understanding of how PHP and MySQL works:

Firstly, create a MySQL user account which you plan to use with this. Remember to use strong passwords, always. Secondly, create a new database and name it ip-addresses and add basic SELECT,INSERT,UPDATE,DELETE privileges to your firstly created user to access this database. Thirdly, run the MySQL code below in the MySQL console, inside that database:

sqlcommand.sql
CREATE TABLE IF NOT EXISTS `ip-addresses` (
ip VARCHAR(100) NOT NULL,
UNIQUE KEY unique_ip(ip)
);
CREATE TABLE IF NOT EXISTS `ip-addresses` ( ip VARCHAR(100) NOT NULL, UNIQUE KEY unique_ip(ip) );
CREATE TABLE IF NOT EXISTS `ip-addresses` (
	ip VARCHAR(100) NOT NULL,
	UNIQUE KEY unique_ip(ip)
);

Lastly, you can put this PHP code somewhere in the code of your web pages, fill it in with the correct login information, and use a variable $ip_counter_value to show the number of visitors on your webpage.

phpvisitorcounter.php
<?php
$ip_counter_value = "00000000";
$server_name = "localhost";
$user_name = "user";
$password = "password";
$db_name = "ip-addresses";
$conn = mysqli_connect($server_name, $user_name, $password, $db_name);
if($conn !== false) {
$client_ip = htmlspecialchars($_SERVER['REMOTE_ADDR'], ENT_QUOTES);
$client_ip = mysqli_real_escape_string($conn, $client_ip);
$sql = "INSERT INTO `" . $db_name . "`(`ip`) VALUES ('" . $client_ip . "')";
mysqli_query($conn, $sql);
$sql = "SELECT `ip` FROM `" . $db_name . "` ORDER BY `ip`";
$res = mysqli_query($conn, $sql);
$row_count = mysqli_num_rows($res);
mysqli_close($conn);
$ip_counter_value = str_pad($row_count, 8, "0", STR_PAD_LEFT);
}
?>
<?php $ip_counter_value = "00000000"; $server_name = "localhost"; $user_name = "user"; $password = "password"; $db_name = "ip-addresses"; $conn = mysqli_connect($server_name, $user_name, $password, $db_name); if($conn !== false) { $client_ip = htmlspecialchars($_SERVER['REMOTE_ADDR'], ENT_QUOTES); $client_ip = mysqli_real_escape_string($conn, $client_ip); $sql = "INSERT INTO `" . $db_name . "`(`ip`) VALUES ('" . $client_ip . "')"; mysqli_query($conn, $sql); $sql = "SELECT `ip` FROM `" . $db_name . "` ORDER BY `ip`"; $res = mysqli_query($conn, $sql); $row_count = mysqli_num_rows($res); mysqli_close($conn); $ip_counter_value = str_pad($row_count, 8, "0", STR_PAD_LEFT); } ?>
<?php
$ip_counter_value = "00000000";
$server_name = "localhost";
$user_name = "user";
$password = "password";
$db_name = "ip-addresses";

$conn = mysqli_connect($server_name, $user_name, $password, $db_name);
if($conn !== false) {
	$client_ip = htmlspecialchars($_SERVER['REMOTE_ADDR'], ENT_QUOTES);
	$client_ip = mysqli_real_escape_string($conn, $client_ip);
	
    $sql = "INSERT INTO `" . $db_name . "`(`ip`) VALUES ('" . $client_ip . "')";
	mysqli_query($conn, $sql);
	
	$sql = "SELECT `ip` FROM `" . $db_name . "` ORDER BY `ip`";
	$res = mysqli_query($conn, $sql);
	
	$row_count = mysqli_num_rows($res);
	mysqli_close($conn);
	$ip_counter_value = str_pad($row_count, 8, "0", STR_PAD_LEFT);
}
?>

Convert F-Secure KEY exported json file to csv

07.09.2020

If you need to transfer accounts from the F-Secure KEY to the KeePassXC, this is the solution to go. The F-Secure KEY does not have the Linux support. In the Linux i'm using the KeePassXC application so i needed to move all my stored accounts from the F-Secure KEY to the KeePassXC application.

Turned out that the F-Secure KEY does not support any other format to export the accounts than json and the KeePassXC wont support json format so i needed to make this little Python code to make the convert from json to csv. Here it is if someone is in a similar situation and needs this.

fsk2csv.py
import json
if __name__ == '__main__':
with open('ExportedPasswords.fsk', 'r', encoding='utf-8') as json_file:
data = json.load(json_file)
str_ = ''
for key, value in data['data'].items():
service = value['service'].replace('"', '""')
username = value['username'].replace('"', '""')
password = value['password'].replace('"', '""')
url = value['url'].replace('"', '""')
notes = value['notes'].replace('"', '""')
str_ += f'"Root","{service}","{username}","{password}","{url}","{notes}"\n'
file = open('output.csv', 'w', encoding='utf-8')
file.write(str_)
import json if __name__ == '__main__': with open('ExportedPasswords.fsk', 'r', encoding='utf-8') as json_file: data = json.load(json_file) str_ = '' for key, value in data['data'].items(): service = value['service'].replace('"', '""') username = value['username'].replace('"', '""') password = value['password'].replace('"', '""') url = value['url'].replace('"', '""') notes = value['notes'].replace('"', '""') str_ += f'"Root","{service}","{username}","{password}","{url}","{notes}"\n' file = open('output.csv', 'w', encoding='utf-8') file.write(str_)
import json

if __name__ == '__main__':
	with open('ExportedPasswords.fsk', 'r', encoding='utf-8') as json_file:
		data = json.load(json_file)
		str_ = ''
		for key, value in data['data'].items():
			service =   value['service'].replace('"', '""')
			username =  value['username'].replace('"', '""')
			password =  value['password'].replace('"', '""')
			url =       value['url'].replace('"', '""')
			notes =     value['notes'].replace('"', '""')
			str_ += f'"Root","{service}","{username}","{password}","{url}","{notes}"\n'

		file = open('output.csv', 'w', encoding='utf-8')
		file.write(str_)

String sequence guesser with c++

27.05.2020

This program uses the full power of the CPU to compute the string given to it. You can adjust the CPU usage from the variables. It calculates the ASCII characters as numbers and it goes through every possibility until it finds the correct combination of characters, without knowing it beforehand. It just comparing against the correct one without knowing it. It only knows the length from the given correct character set because i don't want it to compute unlimitedly. You can download the code from this link.

It also has nice console loading bar while calculating and after it finds the correct string sequence, it prints the time for how long it took in nanoseconds as shown in the picture. 513041023200 is 8.55 minutes.

How long the process can be, it depends how fast your computer is where you run it and what is the length of the string that you give to this program. Longer strings take longer to go through.

It also matters where the given character is in the ASCII character set. If the character is at the end then it can significantly extend the processing time, because it needs to go through and try every wrong answer too between the beginning and the end, where the correct character is located(as we know, but the program does not know). It doesn't know the positions of the correct characters. It just calculates through everything until it finds the correct combination of characters.

ATTENTION! This program is for educational purposes only. I created it because i'm interested of how things works. About illegal aspects of this kind of programs, i don't take any responsibilities of how you use this program. I don't recommend to use it illegally and i don't take any responsibilities from that kind of actions.

passwordguesser.cpp
/*
Copyright(C) 2020 Lari Varjonen <[email protected]>
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
version 2 as published by the Free Software Foundation.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110 - 1301, USA.
*/
#include <boost/multiprecision/cpp_dec_float.hpp>
using boost::multiprecision::cpp_dec_float_50;
#include <chrono>
using namespace std::literals::chrono_literals;
#include <iostream>
#include <string>
#include <vector>
#include <thread>
#include <mutex>
#include <atomic>
unsigned int max_simultaneous_threads = 16;
size_t max_single_thread_length = 4;
cpp_dec_float_50 biggest_possible_count_dec = 1;
cpp_dec_float_50 current_position_dec = 0;
std::atomic_bool process_is_running = true;
unsigned int active_threads = 0;
std::vector<int> password_found;
std::mutex lock;
void update_console(cpp_dec_float_50, cpp_dec_float_50);
void create_new_thread(std::vector<int>*, std::vector<int>*, std::vector<int>*);
void thread_work(std::vector<int>, std::vector<int>, std::vector<int>);
void add_one(std::vector<int>&, unsigned int);
bool is_equal(std::vector<int> const &v1, std::vector<int> const &v2)
{
return (v1.size() == v2.size() &&
std::equal(v1.begin(), v1.end(), v2.begin()));
}
int main(int argc, char* argv[])
{
if (argc > 2)
{
max_simultaneous_threads = atoi(argv[1]);
max_single_thread_length = atoi(argv[2]);
}
std::string user_passwd_str;
if (argc > 3)
{
user_passwd_str = argv[3];
for (int i = 4; i < argc; i++)
{
user_passwd_str += (char)32;
user_passwd_str += argv[i];
}
}
else
{
std::getline(std::cin, user_passwd_str);
}
if (user_passwd_str.size() <= max_single_thread_length)
{
max_single_thread_length = user_passwd_str.size()-1;
}
if (user_passwd_str.size() > max_single_thread_length)
{
for (unsigned int i = 0; i < user_passwd_str.size() - max_single_thread_length; i++)
{
biggest_possible_count_dec *= 223;
}
}
std::vector<int> user_passwd;
for (unsigned int i = 0; i < user_passwd_str.size(); i++)
{
int u = (int)user_passwd_str.at(i);
if (u < 0)
{
u *= -1;
u = 127 + (127 - u) + 2;
}
user_passwd.push_back(u);
}
std::vector<int> start;
std::vector<int> end;
std::vector<int> final_end;
start.resize(user_passwd.size(), 32);
end.resize(user_passwd.size(), 32);
final_end.resize(user_passwd.size(), 255);
std::chrono::high_resolution_clock::time_point t1 = std::chrono::high_resolution_clock::now();
while(process_is_running)
{
if (active_threads < max_simultaneous_threads)
{
create_new_thread(&user_passwd, &start, &end);
if (is_equal(end, final_end))
{
while (active_threads > 0 && process_is_running);
break;
}
add_one(end, 0);
start = end;
}
}
std::chrono::high_resolution_clock::time_point t2 = std::chrono::high_resolution_clock::now();
update_console(100, 2);
std::cout << std::endl;
std::cout << "Password is ";
for (unsigned int i = 0; i < password_found.size(); i++)
{
std::cout << (char)password_found.at(i);
}
std::cout << std::endl;
auto duration = std::chrono::duration_cast<std::chrono::nanoseconds>(t2 - t1).count();
std::cout << duration << std::endl;
return 0;
}
void update_console(cpp_dec_float_50 p, cpp_dec_float_50 scale)
{
for (int i = 0; i < floor(p/scale); i++)
{
std::cout << (char)219;
}
for (int i = 0; i < 50 - floor(p/scale); i++)
{
std::cout << (char)176;
}
std::cout << (char)32 << floor(p) << "%\r";
}
void create_new_thread(std::vector<int>* user_passwd,
std::vector<int>* start_pos,
std::vector<int>* end_pos)
{
for (unsigned int i = 0; i < max_single_thread_length; i++)
{
(*end_pos)[i] = 255;
}
current_position_dec += 1.0;
cpp_dec_float_50 p = current_position_dec / (biggest_possible_count_dec / 100.0);
update_console(p, 2);
active_threads++;
std::thread t(thread_work, *user_passwd, *start_pos, *end_pos);
t.detach();
}
void thread_work(std::vector<int> user_passwd,
std::vector<int> start_pos,
std::vector<int> end_pos)
{
while (process_is_running && !is_equal(start_pos, end_pos))
{
add_one(start_pos, 0);
if (is_equal(start_pos, user_passwd))
{
std::lock_guard <std::mutex> lock(lock);
for (unsigned int i = 0; i < start_pos.size(); i++)
{
password_found.push_back(start_pos.at(i));
}
process_is_running = false;
break;
}
}
std::lock_guard <std::mutex> lock(lock);
active_threads--;
}
void add_one(std::vector<int> &passwd, unsigned int i)
{
if (passwd[i] < 255)
{
passwd[i]++;
if (passwd[i] == 127)
passwd[i]++;
return;
}
passwd[i] = 32;
i++;
if (i < passwd.size())
{
add_one(passwd, i);
}
}
/* Copyright(C) 2020 Lari Varjonen <[email protected]> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 as published by the Free Software Foundation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110 - 1301, USA. */ #include <boost/multiprecision/cpp_dec_float.hpp> using boost::multiprecision::cpp_dec_float_50; #include <chrono> using namespace std::literals::chrono_literals; #include <iostream> #include <string> #include <vector> #include <thread> #include <mutex> #include <atomic> unsigned int max_simultaneous_threads = 16; size_t max_single_thread_length = 4; cpp_dec_float_50 biggest_possible_count_dec = 1; cpp_dec_float_50 current_position_dec = 0; std::atomic_bool process_is_running = true; unsigned int active_threads = 0; std::vector<int> password_found; std::mutex lock; void update_console(cpp_dec_float_50, cpp_dec_float_50); void create_new_thread(std::vector<int>*, std::vector<int>*, std::vector<int>*); void thread_work(std::vector<int>, std::vector<int>, std::vector<int>); void add_one(std::vector<int>&, unsigned int); bool is_equal(std::vector<int> const &v1, std::vector<int> const &v2) { return (v1.size() == v2.size() && std::equal(v1.begin(), v1.end(), v2.begin())); } int main(int argc, char* argv[]) { if (argc > 2) { max_simultaneous_threads = atoi(argv[1]); max_single_thread_length = atoi(argv[2]); } std::string user_passwd_str; if (argc > 3) { user_passwd_str = argv[3]; for (int i = 4; i < argc; i++) { user_passwd_str += (char)32; user_passwd_str += argv[i]; } } else { std::getline(std::cin, user_passwd_str); } if (user_passwd_str.size() <= max_single_thread_length) { max_single_thread_length = user_passwd_str.size()-1; } if (user_passwd_str.size() > max_single_thread_length) { for (unsigned int i = 0; i < user_passwd_str.size() - max_single_thread_length; i++) { biggest_possible_count_dec *= 223; } } std::vector<int> user_passwd; for (unsigned int i = 0; i < user_passwd_str.size(); i++) { int u = (int)user_passwd_str.at(i); if (u < 0) { u *= -1; u = 127 + (127 - u) + 2; } user_passwd.push_back(u); } std::vector<int> start; std::vector<int> end; std::vector<int> final_end; start.resize(user_passwd.size(), 32); end.resize(user_passwd.size(), 32); final_end.resize(user_passwd.size(), 255); std::chrono::high_resolution_clock::time_point t1 = std::chrono::high_resolution_clock::now(); while(process_is_running) { if (active_threads < max_simultaneous_threads) { create_new_thread(&user_passwd, &start, &end); if (is_equal(end, final_end)) { while (active_threads > 0 && process_is_running); break; } add_one(end, 0); start = end; } } std::chrono::high_resolution_clock::time_point t2 = std::chrono::high_resolution_clock::now(); update_console(100, 2); std::cout << std::endl; std::cout << "Password is "; for (unsigned int i = 0; i < password_found.size(); i++) { std::cout << (char)password_found.at(i); } std::cout << std::endl; auto duration = std::chrono::duration_cast<std::chrono::nanoseconds>(t2 - t1).count(); std::cout << duration << std::endl; return 0; } void update_console(cpp_dec_float_50 p, cpp_dec_float_50 scale) { for (int i = 0; i < floor(p/scale); i++) { std::cout << (char)219; } for (int i = 0; i < 50 - floor(p/scale); i++) { std::cout << (char)176; } std::cout << (char)32 << floor(p) << "%\r"; } void create_new_thread(std::vector<int>* user_passwd, std::vector<int>* start_pos, std::vector<int>* end_pos) { for (unsigned int i = 0; i < max_single_thread_length; i++) { (*end_pos)[i] = 255; } current_position_dec += 1.0; cpp_dec_float_50 p = current_position_dec / (biggest_possible_count_dec / 100.0); update_console(p, 2); active_threads++; std::thread t(thread_work, *user_passwd, *start_pos, *end_pos); t.detach(); } void thread_work(std::vector<int> user_passwd, std::vector<int> start_pos, std::vector<int> end_pos) { while (process_is_running && !is_equal(start_pos, end_pos)) { add_one(start_pos, 0); if (is_equal(start_pos, user_passwd)) { std::lock_guard <std::mutex> lock(lock); for (unsigned int i = 0; i < start_pos.size(); i++) { password_found.push_back(start_pos.at(i)); } process_is_running = false; break; } } std::lock_guard <std::mutex> lock(lock); active_threads--; } void add_one(std::vector<int> &passwd, unsigned int i) { if (passwd[i] < 255) { passwd[i]++; if (passwd[i] == 127) passwd[i]++; return; } passwd[i] = 32; i++; if (i < passwd.size()) { add_one(passwd, i); } }
/*
Copyright(C) 2020 Lari Varjonen <[email protected]>

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
version 2 as published by the Free Software Foundation.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110 - 1301, USA.
*/

#include <boost/multiprecision/cpp_dec_float.hpp>
using boost::multiprecision::cpp_dec_float_50;

#include <chrono>
using namespace std::literals::chrono_literals;

#include <iostream>
#include <string>
#include <vector>
#include <thread>
#include <mutex>
#include <atomic>

unsigned int max_simultaneous_threads = 16;
size_t max_single_thread_length = 4;
cpp_dec_float_50 biggest_possible_count_dec = 1;
cpp_dec_float_50 current_position_dec = 0;
std::atomic_bool process_is_running = true;
unsigned int active_threads = 0;
std::vector<int> password_found;
std::mutex lock;

void update_console(cpp_dec_float_50, cpp_dec_float_50);
void create_new_thread(std::vector<int>*, std::vector<int>*, std::vector<int>*);
void thread_work(std::vector<int>, std::vector<int>, std::vector<int>);
void add_one(std::vector<int>&, unsigned int);
bool is_equal(std::vector<int> const &v1, std::vector<int> const &v2)
{
	return (v1.size() == v2.size() &&
		std::equal(v1.begin(), v1.end(), v2.begin()));
}

int main(int argc, char* argv[])
{
	if (argc > 2)
	{
		max_simultaneous_threads = atoi(argv[1]);
		max_single_thread_length = atoi(argv[2]);
	}
	std::string user_passwd_str;
	if (argc > 3)
	{
		user_passwd_str = argv[3];
		for (int i = 4; i < argc; i++)
		{
			user_passwd_str += (char)32;
			user_passwd_str += argv[i];
		}
	}
	else
	{
		std::getline(std::cin, user_passwd_str);
	}
	if (user_passwd_str.size() <= max_single_thread_length)
	{
		max_single_thread_length = user_passwd_str.size()-1;
	}
	if (user_passwd_str.size() > max_single_thread_length)
	{
		for (unsigned int i = 0; i < user_passwd_str.size() - max_single_thread_length; i++)
		{
			biggest_possible_count_dec *= 223;
		}
	}
	std::vector<int> user_passwd;
	for (unsigned int i = 0; i < user_passwd_str.size(); i++)
	{
		int u = (int)user_passwd_str.at(i);
		if (u < 0)
		{
			u *= -1;
			u = 127 + (127 - u) + 2;
		}
		user_passwd.push_back(u);
	}

	std::vector<int> start;
	std::vector<int> end;
	std::vector<int> final_end;
	start.resize(user_passwd.size(), 32);
	end.resize(user_passwd.size(), 32);
	final_end.resize(user_passwd.size(), 255);

	std::chrono::high_resolution_clock::time_point t1 = std::chrono::high_resolution_clock::now();
	while(process_is_running)
	{
		if (active_threads < max_simultaneous_threads)
		{
			create_new_thread(&user_passwd, &start, &end);
			if (is_equal(end, final_end))
			{
				while (active_threads > 0 && process_is_running);
				break;
			}
			add_one(end, 0);
			start = end;
		}
	}
	std::chrono::high_resolution_clock::time_point t2 = std::chrono::high_resolution_clock::now();

	update_console(100, 2);
	std::cout << std::endl;

	std::cout << "Password is ";
	for (unsigned int i = 0; i < password_found.size(); i++)
	{
		std::cout << (char)password_found.at(i);
	}
	std::cout << std::endl;

	auto duration = std::chrono::duration_cast<std::chrono::nanoseconds>(t2 - t1).count();
	std::cout << duration << std::endl;
	
	return 0;
}

void update_console(cpp_dec_float_50 p, cpp_dec_float_50 scale)
{
	for (int i = 0; i < floor(p/scale); i++)
	{
		std::cout << (char)219;
	}
	for (int i = 0; i < 50 - floor(p/scale); i++)
	{
		std::cout << (char)176;
	}
	std::cout << (char)32 << floor(p) << "%\r";
}

void create_new_thread(std::vector<int>* user_passwd,
					   std::vector<int>* start_pos,
					   std::vector<int>* end_pos)
{
	for (unsigned int i = 0; i < max_single_thread_length; i++)
	{
		(*end_pos)[i] = 255;
	}

	current_position_dec += 1.0;
	cpp_dec_float_50 p = current_position_dec / (biggest_possible_count_dec / 100.0);
	update_console(p, 2);

	active_threads++;
	std::thread t(thread_work, *user_passwd, *start_pos, *end_pos);
	t.detach();
}

void thread_work(std::vector<int> user_passwd,
				 std::vector<int> start_pos,
				 std::vector<int> end_pos)
{
	while (process_is_running && !is_equal(start_pos, end_pos))
	{
		add_one(start_pos, 0);
		if (is_equal(start_pos, user_passwd))
		{
			std::lock_guard <std::mutex> lock(lock);
			for (unsigned int i = 0; i < start_pos.size(); i++)
			{
				password_found.push_back(start_pos.at(i));
			}
			process_is_running = false;
			break;
		}
	}
	std::lock_guard <std::mutex> lock(lock);
	active_threads--;
}

void add_one(std::vector<int> &passwd, unsigned int i)
{
	if (passwd[i] < 255)
	{
		passwd[i]++;
		if (passwd[i] == 127)
			passwd[i]++;
		return;
	}
	passwd[i] = 32;
	i++;
	if (i < passwd.size())
	{
		add_one(passwd, i);
	}
}

Discord slot machine bot (using Twitch points as coins)

07.06.2020

I had an idea to test how to make a Discord bot and here is what it became. Still i was out of ideas that what kind of Discord bot i could create. Thanks for Janne(Jenkkemi) for the idea to make this kind of bot. It was very fun to create and program this. This bot was running on Shark Network Discord channel but due to too little usage, i closed it because the project is already starting to be so old that it would require partial rewriting, and nice addition would be also to use mysql database instead of just writing and reading from file.

It uses the Discord API to handle all the Discord side stuff, and the WebSockets to handle all the Twitch side stuff. It uses the Asyncio to processing all that together. You can download the full project from this link. It is licensed with GPL v2 license only. It is free to use under the license agreements. Here are all the codes that it includes.



Main file that runs the whole program.

bot.py
"""
Copyright (C) 2020 Lari Varjonen <[email protected]>
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
version 2 as published by the Free Software Foundation.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
Thanks for Janne(Jenkkemi) for the idea to make this kind of bot: https://twitter.com/JanneKuoksa
Version: 1.3
"""
from overload_builtins import print
import discord
import asyncio
import secret
import link_accounts
import get_channel_redemption
import commands.casino
link_accounts = link_accounts.LinkAccounts()
casino = commands.casino.Casino()
admins = {} # Admin Discord user IDs
class Client(discord.Client):
async def on_ready(self):
print(f'Logged on as {self.user}')
asyncio.create_task(link_accounts.init(client))
async def on_message(self, message):
if message.author == self.user:
return
# Private messages
if isinstance(message.channel, discord.channel.DMChannel):
# !link
if message.content == link_accounts.command[0]:
if await link_accounts.link(message):
await link_accounts.wait_user_response(message)
# !unlink
elif message.content == link_accounts.command[1]:
await link_accounts.unlink(message)
if int(message.author.id) not in admins:
return
# !casino
if message.content.startswith(casino.command[0]):
cut_casino = len(casino.command[0]) + 1
# !casino roll
if message.content[cut_casino:].startswith(casino.command[1]):
await casino.identify_author_id(message)
money_added = await link_accounts.check_cash_waiting(message, casino)
await casino.roll(message.content[cut_casino:].split(), message, money_added)
# !casino
elif message.content == casino.command[0]:
s = casino.print_rules(1)
await message.channel.send(s)
print(f'{message.author.id} {message.author.name}: {message.content}: print rules')
# !casino rules
elif message.content[cut_casino:] == casino.command[2]:
await casino.identify_author_id(message)
money_added = await link_accounts.check_cash_waiting(message, casino)
await casino.print_own_rules(message, money_added)
# !casino cash
elif message.content[cut_casino:] == casino.command[3]:
await casino.identify_author_id(message)
money_added = await link_accounts.check_cash_waiting(message, casino)
await casino.print_nice_balance(message, money_added)
# !casino set
elif message.content[cut_casino:].startswith(casino.command[4]):
await casino.identify_author_id(message)
await casino.set(message.content[cut_casino:].split(), message)
# !casino add
"""
elif message.content[cut_casino:].startswith(casino.command[5]):
await casino.identify_author_id(message)
await casino.add(message.content[cut_casino:].split(), message)
"""
if __name__ == '__main__':
casino.init()
loop = asyncio.get_event_loop()
loop.create_task(casino.save_database_interval())
loop.create_task(get_channel_redemption.connect(link_accounts.gcr_callback))
client = Client()
client.run(secret.discord_token)
casino.save_database()
""" Copyright (C) 2020 Lari Varjonen <[email protected]> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 as published by the Free Software Foundation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Thanks for Janne(Jenkkemi) for the idea to make this kind of bot: https://twitter.com/JanneKuoksa Version: 1.3 """ from overload_builtins import print import discord import asyncio import secret import link_accounts import get_channel_redemption import commands.casino link_accounts = link_accounts.LinkAccounts() casino = commands.casino.Casino() admins = {} # Admin Discord user IDs class Client(discord.Client): async def on_ready(self): print(f'Logged on as {self.user}') asyncio.create_task(link_accounts.init(client)) async def on_message(self, message): if message.author == self.user: return # Private messages if isinstance(message.channel, discord.channel.DMChannel): # !link if message.content == link_accounts.command[0]: if await link_accounts.link(message): await link_accounts.wait_user_response(message) # !unlink elif message.content == link_accounts.command[1]: await link_accounts.unlink(message) if int(message.author.id) not in admins: return # !casino if message.content.startswith(casino.command[0]): cut_casino = len(casino.command[0]) + 1 # !casino roll if message.content[cut_casino:].startswith(casino.command[1]): await casino.identify_author_id(message) money_added = await link_accounts.check_cash_waiting(message, casino) await casino.roll(message.content[cut_casino:].split(), message, money_added) # !casino elif message.content == casino.command[0]: s = casino.print_rules(1) await message.channel.send(s) print(f'{message.author.id} {message.author.name}: {message.content}: print rules') # !casino rules elif message.content[cut_casino:] == casino.command[2]: await casino.identify_author_id(message) money_added = await link_accounts.check_cash_waiting(message, casino) await casino.print_own_rules(message, money_added) # !casino cash elif message.content[cut_casino:] == casino.command[3]: await casino.identify_author_id(message) money_added = await link_accounts.check_cash_waiting(message, casino) await casino.print_nice_balance(message, money_added) # !casino set elif message.content[cut_casino:].startswith(casino.command[4]): await casino.identify_author_id(message) await casino.set(message.content[cut_casino:].split(), message) # !casino add """ elif message.content[cut_casino:].startswith(casino.command[5]): await casino.identify_author_id(message) await casino.add(message.content[cut_casino:].split(), message) """ if __name__ == '__main__': casino.init() loop = asyncio.get_event_loop() loop.create_task(casino.save_database_interval()) loop.create_task(get_channel_redemption.connect(link_accounts.gcr_callback)) client = Client() client.run(secret.discord_token) casino.save_database()
"""
	Copyright (C) 2020 Lari Varjonen <[email protected]>

	This program is free software; you can redistribute it and/or
	modify it under the terms of the GNU General Public License
	version 2 as published by the Free Software Foundation.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with this program; if not, write to the Free Software
	Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.

	Thanks for Janne(Jenkkemi) for the idea to make this kind of bot: https://twitter.com/JanneKuoksa

	Version: 1.3
"""

from overload_builtins import print
import discord
import asyncio
import secret
import link_accounts
import get_channel_redemption
import commands.casino

link_accounts = link_accounts.LinkAccounts()
casino = commands.casino.Casino()


admins = {} # Admin Discord user IDs


class Client(discord.Client):
	async def on_ready(self):
		print(f'Logged on as {self.user}')
		asyncio.create_task(link_accounts.init(client))

	async def on_message(self, message):
		if message.author == self.user:
			return

		# Private messages
		if isinstance(message.channel, discord.channel.DMChannel):
			# !link
			if message.content == link_accounts.command[0]:
				if await link_accounts.link(message):
					await link_accounts.wait_user_response(message)

			# !unlink
			elif message.content == link_accounts.command[1]:
				await link_accounts.unlink(message)

			if int(message.author.id) not in admins:
				return

		# !casino
		if message.content.startswith(casino.command[0]):
			cut_casino = len(casino.command[0]) + 1
			# !casino roll
			if message.content[cut_casino:].startswith(casino.command[1]):
				await casino.identify_author_id(message)
				money_added = await link_accounts.check_cash_waiting(message, casino)
				await casino.roll(message.content[cut_casino:].split(), message, money_added)

			# !casino
			elif message.content == casino.command[0]:
				s = casino.print_rules(1)
				await message.channel.send(s)
				print(f'{message.author.id} {message.author.name}: {message.content}: print rules')

			# !casino rules
			elif message.content[cut_casino:] == casino.command[2]:
				await casino.identify_author_id(message)
				money_added = await link_accounts.check_cash_waiting(message, casino)
				await casino.print_own_rules(message, money_added)

			# !casino cash
			elif message.content[cut_casino:] == casino.command[3]:
				await casino.identify_author_id(message)
				money_added = await link_accounts.check_cash_waiting(message, casino)
				await casino.print_nice_balance(message, money_added)

			# !casino set
			elif message.content[cut_casino:].startswith(casino.command[4]):
				await casino.identify_author_id(message)
				await casino.set(message.content[cut_casino:].split(), message)

			# !casino add
			"""
			elif message.content[cut_casino:].startswith(casino.command[5]):
				await casino.identify_author_id(message)
				await casino.add(message.content[cut_casino:].split(), message)
			"""


if __name__ == '__main__':
	casino.init()
	loop = asyncio.get_event_loop()
	loop.create_task(casino.save_database_interval())
	loop.create_task(get_channel_redemption.connect(link_accounts.gcr_callback))

	client = Client()
	client.run(secret.discord_token)
	casino.save_database()

The actual game logic that makes all the game actions, like rolling the reels and get the next reel positions and so on.

casino.py
"""
Copyright (C) 2020 Lari Varjonen <[email protected]>
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
version 2 as published by the Free Software Foundation.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
Thanks for Janne(Jenkkemi) for the idea to make this kind of bot: https://twitter.com/JanneKuoksa
"""
from overload_builtins import print
from random import randint
import asyncio
import time
import math
import json
import copy
bell = ':bell:'
heart = ':crown:'
diamond = ':diamond_shape_with_a_dot_inside:'
spade = ':hearts:'
horseshoe = ':dragon:'
star = ':star:'
wild = ':moneybag:'
class Casino:
command = ['!casino', 'roll', 'rules', 'cash', 'set', 'add']
data_file_name = 'casino-data.json'
roll_cost = 1
registered_author_ids = {}
reel = [0, 0, 0]
interval_time_to_save = 60 * 10
reel_ = [
[
bell,
horseshoe,
spade,
horseshoe,
diamond,
horseshoe,
spade,
horseshoe,
heart,
horseshoe
],
[
bell,
horseshoe,
spade,
horseshoe,
diamond,
horseshoe,
spade,
horseshoe,
heart,
wild
],
[
bell,
diamond,
star,
spade,
bell,
diamond,
heart,
star,
spade,
diamond
]
]
# Bell Heart Diamond Spade Horseshoe Star Wild
table_to_win = [
[[0], [8], [4], [2, 6], [1, 3, 5, 7, 9], [], []], # Reel 1
[[0], [8], [4], [2, 6], [1, 3, 5, 7], [], [9]], # Reel 2
[[0, 4], [6], [1, 5, 9], [3, 8], [], [2, 7], []] # Reel 3
]
pay_table = [
[20, table_to_win[0][0], table_to_win[1][0], [False, table_to_win[2][0]]], # $20 = Bell Bell Bell
[16, table_to_win[0][1], table_to_win[1][1], [False, table_to_win[2][1]]], # $16 = Heart Heart Heart
[12, table_to_win[0][2], table_to_win[1][2], [False, table_to_win[2][2]]], # $12 = Diamond Diamond Diamond
[8, table_to_win[0][3], table_to_win[1][3], [False, table_to_win[2][3]]], # $8 = Spade Spade Spade
[4, table_to_win[0][4], table_to_win[1][4], [False, table_to_win[2][5]]], # $4 = Horseshoe Horseshoe Star
[2, table_to_win[0][4], table_to_win[1][4], [True, table_to_win[2][5]]]
# $2 = Horseshoe Horseshoe Any(not Star)
]
def init(self):
try:
with open(self.data_file_name, 'r', encoding='utf-8') as f:
data = f.read()
self.registered_author_ids = json.loads(data)
except IOError:
pass
def save_database(self):
with open(self.data_file_name, 'w', encoding='utf-8') as f:
json.dump(self.registered_author_ids, f, ensure_ascii=False, indent=4)
async def save_database_interval(self):
registered_author_ids = copy.deepcopy(self.registered_author_ids)
while True:
await asyncio.sleep(self.interval_time_to_save)
if self.registered_author_ids.items() != registered_author_ids.items():
print('saving database')
self.save_database()
registered_author_ids = copy.deepcopy(self.registered_author_ids)
print('database saved')
async def roll(self, cut_msg, message, money_added):
current_author_id = str(message.author.id)
if len(cut_msg) > 4:
return
if len(cut_msg) > 1 and not str(cut_msg[1]).isdigit():
return
if len(cut_msg) > 2 and not str(cut_msg[2]).isdigit():
return
if len(cut_msg) > 3 and not str(cut_msg[3]).isdigit():
return
count = 1
# Set auto roll count
if len(cut_msg) > 1 and str(cut_msg[1]).isdigit() and int(cut_msg[1]) > 0:
count = int(cut_msg[1])
if count > 10:
count = 10
# Set multiplier: needs to be before set_payout_limit function
if len(cut_msg) > 2 and str(cut_msg[2]).isdigit():
self.set_multiplier(current_author_id, int(cut_msg[2]))
# Set payout limit
if len(cut_msg) > 3 and str(cut_msg[3]).isdigit():
self.set_payout_limit(current_author_id, int(cut_msg[3]))
# Do game
game_str = ''
for i in range(count):
if self.roll_cost * self.get_multiplier(current_author_id) > self.get_balance(current_author_id):
game_str += '\n :money_with_wings: `You don\'t have enough cash to roll`'
break
self.update_balance(current_author_id, -self.roll_cost * self.get_multiplier(current_author_id))
self.reel[0] = randint(0, 9)
self.reel[1] = randint(0, 9)
self.reel[2] = randint(0, 9)
game_str += f'\n{str(self.get_reels_position())}'
current_winnings = self.check_reels_update_balance(current_author_id)
game_str += str(self.get_nice_balance(current_author_id, current_winnings))
if current_winnings >= int(self.get_payout_limit(current_author_id)):
break
str_ = f'<@{current_author_id}> '
if money_added > 0:
str_ += f'{self.twitch_money_added_message(message, money_added)}'
str_ += f'{game_str}'
await message.channel.send(str_)
def get_reels_position(self):
s0 = self.reel_[0][self.reel[0]]
s1 = self.reel_[1][self.reel[1]]
s2 = self.reel_[2][self.reel[2]]
return s0 + s1 + s2
def check_reels_update_balance(self, current_author_id):
current_winnings = 0
for i in range(len(self.pay_table)):
# Reel 1 check
if self.reel[0] in self.pay_table[i][1]:
# Reel 2 check, and if WILD then just pass through
if self.reel[1] in self.table_to_win[1][6] \
or self.reel[1] in self.pay_table[i][2]:
# Reel 3 check and handling Any(not Star) case
if not self.pay_table[i][3][0]:
if self.reel[2] in self.pay_table[i][3][1]:
current_winnings = self.pay_table[i][0] * self.get_multiplier(current_author_id)
self.update_balance(current_author_id, current_winnings)
else:
if self.reel[2] not in self.pay_table[i][3][1]:
current_winnings = self.pay_table[i][0] * self.get_multiplier(current_author_id)
self.update_balance(current_author_id, current_winnings)
# Stop point handling
if current_winnings >= int(self.get_payout_limit(current_author_id)):
break
return current_winnings
def update_balance(self, current_author_id, i: int):
self.registered_author_ids[current_author_id][0] += i
def get_nice_balance(self, current_author_id, current_winnings=0):
s = f'`${self.registered_author_ids[current_author_id][0]}`'
s += f' `M:{self.get_multiplier(current_author_id)}`'
s += f' `L:{self.get_payout_limit(current_author_id)}`'
if current_winnings > 0:
s += f' `You won! ${current_winnings}`'
return s
async def print_nice_balance(self, message, money_added):
current_author_id = str(message.author.id)
str_ = f'{self.get_nice_balance(current_author_id)}'
if money_added > 0:
str_ = f'{self.twitch_money_added_message(message, money_added)}\n{str_}'
await message.channel.send(f'<@{current_author_id}> {str_}')
def get_balance(self, current_author_id):
return self.registered_author_ids[current_author_id][0]
def get_multiplier(self, current_author_id):
return self.registered_author_ids[current_author_id][1]
def set_multiplier(self, current_author_id, i):
if i < 1:
i = 1
elif i > 4:
i = 4
self.registered_author_ids[current_author_id][1] = i
def get_payout_limit(self, current_author_id):
return self.registered_author_ids[current_author_id][2]
def set_payout_limit(self, current_author_id, i):
maximum_payout = self.pay_table[0][0] * self.get_multiplier(current_author_id)
smallest_payout = self.pay_table[len(self.pay_table) - 1][0] * self.get_multiplier(current_author_id)
if i < smallest_payout:
i = smallest_payout # Smallest possible payout
elif i > maximum_payout:
i = maximum_payout # Maximum possible payout
self.registered_author_ids[current_author_id][2] = i
async def identify_author_id(self, message):
current_author_id = str(message.author.id)
# # Add new user
if current_author_id not in self.registered_author_ids:
# [balance, multiplier, stop if this or greater payout reached, current time]
self.registered_author_ids[current_author_id] = [20, 1, 20, time.time()]
self.save_database()
str_ = f'{message.author.id} {message.author.name}: {message.content}: '
str_ += self.get_nice_balance(current_author_id).replace('`', '')
print(str_)
async def print_own_rules(self, message, money_added):
print(f'{message.author.id} {message.author.name}: {message.content}: print own rules')
current_author_id = str(message.author.id)
str_ = f'{self.get_nice_balance(current_author_id)}\n'
str_ += f'{self.print_rules(self.get_multiplier(current_author_id))}'
if money_added > 0:
str_ = f'{self.twitch_money_added_message(message, money_added)}\n{str_}'
await message.channel.send(f'<@{current_author_id}> {str_}')
def print_rules(self, multiplier=1):
s = ''
for i in range(len(self.pay_table) - 1):
s += self.reel_[0][self.pay_table[i][1][0]]
s += self.reel_[1][self.pay_table[i][2][0]]
s += self.reel_[2][self.pay_table[i][3][1][0]]
s += f' `${self.pay_table[i][0] * multiplier}`\n'
s += f'{horseshoe}{horseshoe} Any(Not {star}) `${2 * multiplier}`\n\n'
s += f'{wild} **WILD** symbol on the center reel.'
s += ' Wild can substitute for any other symbol, with no exceptions.\n\n'
s += '**Get more money!** Redeem specific Twitch reward from'
s += ' [twitch channel name here] Twitch channel using your channel points'
s += ' to get more money. Link your Discord and Twitch account first by sending `!link`'
s += ' private message for this bot. You can also unlink your Discord and Twitch connection by'
s += ' sending `!unlink` private message for this bot.\n\n'
s += '`!casino rules` or `!casino` Shows you the casino rules.\n'
s += '`!casino roll` Roll the reels once.\n'
s += '`!casino roll 10` Roll the reels multiple times at once. Number can be between 1 - 10.\n'
s += '`!casino roll 10 4` Set the multiplier. Number can be between 1 - 4.\n'
s += '`!casino roll 10 4 80` Set the payout limit. Stop if this or greater payout.\n'
s += '`!casino cash` Show how much money you have at the moment.\n'
s += '`!casino set M` Set the multiplier. M can also be m, mult, multp or multiplier.\n'
s += '`!casino set L` Set the payout limit. L can also be l, limit or payoutlimit.\n'
# s += '`!casino add M` Add $100 money. Every 10 minutes you can add money. M can also be m or money.'
return s
async def set(self, cut_msg, message):
current_author_id = str(message.author.id)
print(f'{message.author.id} {message.author.name}: {message.content}')
if len(cut_msg) < 3 or len(cut_msg) > 3:
return
str_ = ''
# Set Multiplier
if cut_msg[1] in ['m', 'M', 'mult', 'multp', 'multiplier'] and str(cut_msg[2]).isdigit():
self.set_multiplier(current_author_id, int(cut_msg[2]))
str_ = f' `Multiplier set to {self.get_multiplier(current_author_id)}`'
# Set stop point
elif cut_msg[1] in ['l', 'L', 'limit', 'payoutlimit'] and str(cut_msg[2]).isdigit():
self.set_payout_limit(current_author_id, int(cut_msg[2]))
str_ = f' `Payout limit set to {self.get_payout_limit(current_author_id)}`'
if str_ == '':
return
await message.channel.send(f'<@{current_author_id}>{str_}')
print(f'{message.author.id} {message.author.name}: {message.content}:{str_.lower().replace("`", "")}')
async def add(self, cut_msg, message):
current_author_id = str(message.author.id)
print(f'{message.author.id} {message.author.name}: {message.content}')
if len(cut_msg) < 2:
return
if not cut_msg[1] in ['m', 'M', 'money']:
return
# Add money
if self.permission_to_add_100(current_author_id):
self.update_timer_time(current_author_id)
await self.send_money_added_message(message)
self.save_database()
return
p = time.time() - self.registered_author_ids[current_author_id][3]
m = math.floor(((60.0 * 10.0) - p) / 60.0)
s = math.floor(((60.0 * 10.0) - p) - (m * 60))
str_ = f' :money_with_wings: `You have to wait {m} minutes and {s} seconds before you can add more money.`'
await message.channel.send(f'<@{current_author_id}>{str_}')
async def send_money_added_message(self, message):
current_author_id = str(message.author.id)
str_ = f' :money_mouth: `Money added! Your current balance: ${self.get_balance(current_author_id)}`'
await message.channel.send(f'<@{current_author_id}>{str_}')
str_ = f'{message.author.id} {message.author.name}: {message.content}: '
str_ += f'money added, current balance {self.get_balance(current_author_id)}'
print(str_)
def twitch_money_added_message(self, message, cost):
current_author_id = str(message.author.id)
str_ = f'{message.author.id} {message.author.name}: {message.content}: '
str_ += f'twitch money added {cost}, current balance {self.get_balance(current_author_id)}'
print(str_)
str_ = f':money_mouth: `You redeemed a Twitch reward and gained ${cost} of cash! Your current balance:'
str_ += f' ${self.get_balance(current_author_id)}`'
return str_
def permission_to_add_100(self, current_author_id):
if time.time() - self.registered_author_ids[current_author_id][3] > 60 * 10:
self.update_balance(current_author_id, 100)
return True
return False
def update_timer_time(self, current_author_id):
self.registered_author_ids[current_author_id][3] = time.time()
""" Copyright (C) 2020 Lari Varjonen <[email protected]> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 as published by the Free Software Foundation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Thanks for Janne(Jenkkemi) for the idea to make this kind of bot: https://twitter.com/JanneKuoksa """ from overload_builtins import print from random import randint import asyncio import time import math import json import copy bell = ':bell:' heart = ':crown:' diamond = ':diamond_shape_with_a_dot_inside:' spade = ':hearts:' horseshoe = ':dragon:' star = ':star:' wild = ':moneybag:' class Casino: command = ['!casino', 'roll', 'rules', 'cash', 'set', 'add'] data_file_name = 'casino-data.json' roll_cost = 1 registered_author_ids = {} reel = [0, 0, 0] interval_time_to_save = 60 * 10 reel_ = [ [ bell, horseshoe, spade, horseshoe, diamond, horseshoe, spade, horseshoe, heart, horseshoe ], [ bell, horseshoe, spade, horseshoe, diamond, horseshoe, spade, horseshoe, heart, wild ], [ bell, diamond, star, spade, bell, diamond, heart, star, spade, diamond ] ] # Bell Heart Diamond Spade Horseshoe Star Wild table_to_win = [ [[0], [8], [4], [2, 6], [1, 3, 5, 7, 9], [], []], # Reel 1 [[0], [8], [4], [2, 6], [1, 3, 5, 7], [], [9]], # Reel 2 [[0, 4], [6], [1, 5, 9], [3, 8], [], [2, 7], []] # Reel 3 ] pay_table = [ [20, table_to_win[0][0], table_to_win[1][0], [False, table_to_win[2][0]]], # $20 = Bell Bell Bell [16, table_to_win[0][1], table_to_win[1][1], [False, table_to_win[2][1]]], # $16 = Heart Heart Heart [12, table_to_win[0][2], table_to_win[1][2], [False, table_to_win[2][2]]], # $12 = Diamond Diamond Diamond [8, table_to_win[0][3], table_to_win[1][3], [False, table_to_win[2][3]]], # $8 = Spade Spade Spade [4, table_to_win[0][4], table_to_win[1][4], [False, table_to_win[2][5]]], # $4 = Horseshoe Horseshoe Star [2, table_to_win[0][4], table_to_win[1][4], [True, table_to_win[2][5]]] # $2 = Horseshoe Horseshoe Any(not Star) ] def init(self): try: with open(self.data_file_name, 'r', encoding='utf-8') as f: data = f.read() self.registered_author_ids = json.loads(data) except IOError: pass def save_database(self): with open(self.data_file_name, 'w', encoding='utf-8') as f: json.dump(self.registered_author_ids, f, ensure_ascii=False, indent=4) async def save_database_interval(self): registered_author_ids = copy.deepcopy(self.registered_author_ids) while True: await asyncio.sleep(self.interval_time_to_save) if self.registered_author_ids.items() != registered_author_ids.items(): print('saving database') self.save_database() registered_author_ids = copy.deepcopy(self.registered_author_ids) print('database saved') async def roll(self, cut_msg, message, money_added): current_author_id = str(message.author.id) if len(cut_msg) > 4: return if len(cut_msg) > 1 and not str(cut_msg[1]).isdigit(): return if len(cut_msg) > 2 and not str(cut_msg[2]).isdigit(): return if len(cut_msg) > 3 and not str(cut_msg[3]).isdigit(): return count = 1 # Set auto roll count if len(cut_msg) > 1 and str(cut_msg[1]).isdigit() and int(cut_msg[1]) > 0: count = int(cut_msg[1]) if count > 10: count = 10 # Set multiplier: needs to be before set_payout_limit function if len(cut_msg) > 2 and str(cut_msg[2]).isdigit(): self.set_multiplier(current_author_id, int(cut_msg[2])) # Set payout limit if len(cut_msg) > 3 and str(cut_msg[3]).isdigit(): self.set_payout_limit(current_author_id, int(cut_msg[3])) # Do game game_str = '' for i in range(count): if self.roll_cost * self.get_multiplier(current_author_id) > self.get_balance(current_author_id): game_str += '\n :money_with_wings: `You don\'t have enough cash to roll`' break self.update_balance(current_author_id, -self.roll_cost * self.get_multiplier(current_author_id)) self.reel[0] = randint(0, 9) self.reel[1] = randint(0, 9) self.reel[2] = randint(0, 9) game_str += f'\n{str(self.get_reels_position())}' current_winnings = self.check_reels_update_balance(current_author_id) game_str += str(self.get_nice_balance(current_author_id, current_winnings)) if current_winnings >= int(self.get_payout_limit(current_author_id)): break str_ = f'<@{current_author_id}> ' if money_added > 0: str_ += f'{self.twitch_money_added_message(message, money_added)}' str_ += f'{game_str}' await message.channel.send(str_) def get_reels_position(self): s0 = self.reel_[0][self.reel[0]] s1 = self.reel_[1][self.reel[1]] s2 = self.reel_[2][self.reel[2]] return s0 + s1 + s2 def check_reels_update_balance(self, current_author_id): current_winnings = 0 for i in range(len(self.pay_table)): # Reel 1 check if self.reel[0] in self.pay_table[i][1]: # Reel 2 check, and if WILD then just pass through if self.reel[1] in self.table_to_win[1][6] \ or self.reel[1] in self.pay_table[i][2]: # Reel 3 check and handling Any(not Star) case if not self.pay_table[i][3][0]: if self.reel[2] in self.pay_table[i][3][1]: current_winnings = self.pay_table[i][0] * self.get_multiplier(current_author_id) self.update_balance(current_author_id, current_winnings) else: if self.reel[2] not in self.pay_table[i][3][1]: current_winnings = self.pay_table[i][0] * self.get_multiplier(current_author_id) self.update_balance(current_author_id, current_winnings) # Stop point handling if current_winnings >= int(self.get_payout_limit(current_author_id)): break return current_winnings def update_balance(self, current_author_id, i: int): self.registered_author_ids[current_author_id][0] += i def get_nice_balance(self, current_author_id, current_winnings=0): s = f'`${self.registered_author_ids[current_author_id][0]}`' s += f' `M:{self.get_multiplier(current_author_id)}`' s += f' `L:{self.get_payout_limit(current_author_id)}`' if current_winnings > 0: s += f' `You won! ${current_winnings}`' return s async def print_nice_balance(self, message, money_added): current_author_id = str(message.author.id) str_ = f'{self.get_nice_balance(current_author_id)}' if money_added > 0: str_ = f'{self.twitch_money_added_message(message, money_added)}\n{str_}' await message.channel.send(f'<@{current_author_id}> {str_}') def get_balance(self, current_author_id): return self.registered_author_ids[current_author_id][0] def get_multiplier(self, current_author_id): return self.registered_author_ids[current_author_id][1] def set_multiplier(self, current_author_id, i): if i < 1: i = 1 elif i > 4: i = 4 self.registered_author_ids[current_author_id][1] = i def get_payout_limit(self, current_author_id): return self.registered_author_ids[current_author_id][2] def set_payout_limit(self, current_author_id, i): maximum_payout = self.pay_table[0][0] * self.get_multiplier(current_author_id) smallest_payout = self.pay_table[len(self.pay_table) - 1][0] * self.get_multiplier(current_author_id) if i < smallest_payout: i = smallest_payout # Smallest possible payout elif i > maximum_payout: i = maximum_payout # Maximum possible payout self.registered_author_ids[current_author_id][2] = i async def identify_author_id(self, message): current_author_id = str(message.author.id) # # Add new user if current_author_id not in self.registered_author_ids: # [balance, multiplier, stop if this or greater payout reached, current time] self.registered_author_ids[current_author_id] = [20, 1, 20, time.time()] self.save_database() str_ = f'{message.author.id} {message.author.name}: {message.content}: ' str_ += self.get_nice_balance(current_author_id).replace('`', '') print(str_) async def print_own_rules(self, message, money_added): print(f'{message.author.id} {message.author.name}: {message.content}: print own rules') current_author_id = str(message.author.id) str_ = f'{self.get_nice_balance(current_author_id)}\n' str_ += f'{self.print_rules(self.get_multiplier(current_author_id))}' if money_added > 0: str_ = f'{self.twitch_money_added_message(message, money_added)}\n{str_}' await message.channel.send(f'<@{current_author_id}> {str_}') def print_rules(self, multiplier=1): s = '' for i in range(len(self.pay_table) - 1): s += self.reel_[0][self.pay_table[i][1][0]] s += self.reel_[1][self.pay_table[i][2][0]] s += self.reel_[2][self.pay_table[i][3][1][0]] s += f' `${self.pay_table[i][0] * multiplier}`\n' s += f'{horseshoe}{horseshoe} Any(Not {star}) `${2 * multiplier}`\n\n' s += f'{wild} **WILD** symbol on the center reel.' s += ' Wild can substitute for any other symbol, with no exceptions.\n\n' s += '**Get more money!** Redeem specific Twitch reward from' s += ' [twitch channel name here] Twitch channel using your channel points' s += ' to get more money. Link your Discord and Twitch account first by sending `!link`' s += ' private message for this bot. You can also unlink your Discord and Twitch connection by' s += ' sending `!unlink` private message for this bot.\n\n' s += '`!casino rules` or `!casino` Shows you the casino rules.\n' s += '`!casino roll` Roll the reels once.\n' s += '`!casino roll 10` Roll the reels multiple times at once. Number can be between 1 - 10.\n' s += '`!casino roll 10 4` Set the multiplier. Number can be between 1 - 4.\n' s += '`!casino roll 10 4 80` Set the payout limit. Stop if this or greater payout.\n' s += '`!casino cash` Show how much money you have at the moment.\n' s += '`!casino set M` Set the multiplier. M can also be m, mult, multp or multiplier.\n' s += '`!casino set L` Set the payout limit. L can also be l, limit or payoutlimit.\n' # s += '`!casino add M` Add $100 money. Every 10 minutes you can add money. M can also be m or money.' return s async def set(self, cut_msg, message): current_author_id = str(message.author.id) print(f'{message.author.id} {message.author.name}: {message.content}') if len(cut_msg) < 3 or len(cut_msg) > 3: return str_ = '' # Set Multiplier if cut_msg[1] in ['m', 'M', 'mult', 'multp', 'multiplier'] and str(cut_msg[2]).isdigit(): self.set_multiplier(current_author_id, int(cut_msg[2])) str_ = f' `Multiplier set to {self.get_multiplier(current_author_id)}`' # Set stop point elif cut_msg[1] in ['l', 'L', 'limit', 'payoutlimit'] and str(cut_msg[2]).isdigit(): self.set_payout_limit(current_author_id, int(cut_msg[2])) str_ = f' `Payout limit set to {self.get_payout_limit(current_author_id)}`' if str_ == '': return await message.channel.send(f'<@{current_author_id}>{str_}') print(f'{message.author.id} {message.author.name}: {message.content}:{str_.lower().replace("`", "")}') async def add(self, cut_msg, message): current_author_id = str(message.author.id) print(f'{message.author.id} {message.author.name}: {message.content}') if len(cut_msg) < 2: return if not cut_msg[1] in ['m', 'M', 'money']: return # Add money if self.permission_to_add_100(current_author_id): self.update_timer_time(current_author_id) await self.send_money_added_message(message) self.save_database() return p = time.time() - self.registered_author_ids[current_author_id][3] m = math.floor(((60.0 * 10.0) - p) / 60.0) s = math.floor(((60.0 * 10.0) - p) - (m * 60)) str_ = f' :money_with_wings: `You have to wait {m} minutes and {s} seconds before you can add more money.`' await message.channel.send(f'<@{current_author_id}>{str_}') async def send_money_added_message(self, message): current_author_id = str(message.author.id) str_ = f' :money_mouth: `Money added! Your current balance: ${self.get_balance(current_author_id)}`' await message.channel.send(f'<@{current_author_id}>{str_}') str_ = f'{message.author.id} {message.author.name}: {message.content}: ' str_ += f'money added, current balance {self.get_balance(current_author_id)}' print(str_) def twitch_money_added_message(self, message, cost): current_author_id = str(message.author.id) str_ = f'{message.author.id} {message.author.name}: {message.content}: ' str_ += f'twitch money added {cost}, current balance {self.get_balance(current_author_id)}' print(str_) str_ = f':money_mouth: `You redeemed a Twitch reward and gained ${cost} of cash! Your current balance:' str_ += f' ${self.get_balance(current_author_id)}`' return str_ def permission_to_add_100(self, current_author_id): if time.time() - self.registered_author_ids[current_author_id][3] > 60 * 10: self.update_balance(current_author_id, 100) return True return False def update_timer_time(self, current_author_id): self.registered_author_ids[current_author_id][3] = time.time()
"""
	Copyright (C) 2020 Lari Varjonen <[email protected]>

	This program is free software; you can redistribute it and/or
	modify it under the terms of the GNU General Public License
	version 2 as published by the Free Software Foundation.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with this program; if not, write to the Free Software
	Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.

	Thanks for Janne(Jenkkemi) for the idea to make this kind of bot: https://twitter.com/JanneKuoksa
"""

from overload_builtins import print
from random import randint
import asyncio
import time
import math
import json
import copy

bell = ':bell:'
heart = ':crown:'
diamond = ':diamond_shape_with_a_dot_inside:'
spade = ':hearts:'
horseshoe = ':dragon:'
star = ':star:'
wild = ':moneybag:'


class Casino:
	command = ['!casino', 'roll', 'rules', 'cash', 'set', 'add']
	data_file_name = 'casino-data.json'
	roll_cost = 1
	registered_author_ids = {}
	reel = [0, 0, 0]
	interval_time_to_save = 60 * 10
	reel_ = [
		[
			bell,
			horseshoe,
			spade,
			horseshoe,
			diamond,
			horseshoe,
			spade,
			horseshoe,
			heart,
			horseshoe
		],
		[
			bell,
			horseshoe,
			spade,
			horseshoe,
			diamond,
			horseshoe,
			spade,
			horseshoe,
			heart,
			wild
		],
		[
			bell,
			diamond,
			star,
			spade,
			bell,
			diamond,
			heart,
			star,
			spade,
			diamond
		]
	]

	# Bell Heart Diamond Spade Horseshoe Star Wild
	table_to_win = [
		[[0], [8], [4], [2, 6], [1, 3, 5, 7, 9], [], []],  # Reel 1
		[[0], [8], [4], [2, 6], [1, 3, 5, 7], [], [9]],  # Reel 2
		[[0, 4], [6], [1, 5, 9], [3, 8], [], [2, 7], []]  # Reel 3
	]

	pay_table = [
		[20, table_to_win[0][0], table_to_win[1][0], [False, table_to_win[2][0]]],  # $20 = Bell Bell Bell
		[16, table_to_win[0][1], table_to_win[1][1], [False, table_to_win[2][1]]],  # $16 = Heart Heart Heart
		[12, table_to_win[0][2], table_to_win[1][2], [False, table_to_win[2][2]]],  # $12 = Diamond Diamond Diamond
		[8, table_to_win[0][3], table_to_win[1][3], [False, table_to_win[2][3]]],  # $8  = Spade Spade Spade
		[4, table_to_win[0][4], table_to_win[1][4], [False, table_to_win[2][5]]],  # $4  = Horseshoe Horseshoe Star
		[2, table_to_win[0][4], table_to_win[1][4], [True, table_to_win[2][5]]]
		# $2  = Horseshoe Horseshoe Any(not Star)
	]

	def init(self):
		try:
			with open(self.data_file_name, 'r', encoding='utf-8') as f:
				data = f.read()
				self.registered_author_ids = json.loads(data)
		except IOError:
			pass

	def save_database(self):
		with open(self.data_file_name, 'w', encoding='utf-8') as f:
			json.dump(self.registered_author_ids, f, ensure_ascii=False, indent=4)

	async def save_database_interval(self):
		registered_author_ids = copy.deepcopy(self.registered_author_ids)
		while True:
			await asyncio.sleep(self.interval_time_to_save)
			if self.registered_author_ids.items() != registered_author_ids.items():
				print('saving database')
				self.save_database()
				registered_author_ids = copy.deepcopy(self.registered_author_ids)
				print('database saved')

	async def roll(self, cut_msg, message, money_added):
		current_author_id = str(message.author.id)
		if len(cut_msg) > 4:
			return
		if len(cut_msg) > 1 and not str(cut_msg[1]).isdigit():
			return
		if len(cut_msg) > 2 and not str(cut_msg[2]).isdigit():
			return
		if len(cut_msg) > 3 and not str(cut_msg[3]).isdigit():
			return

		count = 1
		# Set auto roll count
		if len(cut_msg) > 1 and str(cut_msg[1]).isdigit() and int(cut_msg[1]) > 0:
			count = int(cut_msg[1])
			if count > 10:
				count = 10
		# Set multiplier: needs to be before set_payout_limit function
		if len(cut_msg) > 2 and str(cut_msg[2]).isdigit():
			self.set_multiplier(current_author_id, int(cut_msg[2]))
		# Set payout limit
		if len(cut_msg) > 3 and str(cut_msg[3]).isdigit():
			self.set_payout_limit(current_author_id, int(cut_msg[3]))

		# Do game
		game_str = ''
		for i in range(count):
			if self.roll_cost * self.get_multiplier(current_author_id) > self.get_balance(current_author_id):
				game_str += '\n :money_with_wings: `You don\'t have enough cash to roll`'
				break
			self.update_balance(current_author_id, -self.roll_cost * self.get_multiplier(current_author_id))
			self.reel[0] = randint(0, 9)
			self.reel[1] = randint(0, 9)
			self.reel[2] = randint(0, 9)
			game_str += f'\n{str(self.get_reels_position())}'
			current_winnings = self.check_reels_update_balance(current_author_id)
			game_str += str(self.get_nice_balance(current_author_id, current_winnings))
			if current_winnings >= int(self.get_payout_limit(current_author_id)):
				break

		str_ = f'<@{current_author_id}> '
		if money_added > 0:
			str_ += f'{self.twitch_money_added_message(message, money_added)}'
		str_ += f'{game_str}'
		await message.channel.send(str_)

	def get_reels_position(self):
		s0 = self.reel_[0][self.reel[0]]
		s1 = self.reel_[1][self.reel[1]]
		s2 = self.reel_[2][self.reel[2]]
		return s0 + s1 + s2

	def check_reels_update_balance(self, current_author_id):
		current_winnings = 0
		for i in range(len(self.pay_table)):
			# Reel 1 check
			if self.reel[0] in self.pay_table[i][1]:
				# Reel 2 check, and if WILD then just pass through
				if self.reel[1] in self.table_to_win[1][6] \
						or self.reel[1] in self.pay_table[i][2]:
					# Reel 3 check and handling Any(not Star) case
					if not self.pay_table[i][3][0]:
						if self.reel[2] in self.pay_table[i][3][1]:
							current_winnings = self.pay_table[i][0] * self.get_multiplier(current_author_id)
							self.update_balance(current_author_id, current_winnings)
					else:
						if self.reel[2] not in self.pay_table[i][3][1]:
							current_winnings = self.pay_table[i][0] * self.get_multiplier(current_author_id)
							self.update_balance(current_author_id, current_winnings)
			# Stop point handling
			if current_winnings >= int(self.get_payout_limit(current_author_id)):
				break

		return current_winnings

	def update_balance(self, current_author_id, i: int):
		self.registered_author_ids[current_author_id][0] += i

	def get_nice_balance(self, current_author_id, current_winnings=0):
		s = f'`${self.registered_author_ids[current_author_id][0]}`'
		s += f' `M:{self.get_multiplier(current_author_id)}`'
		s += f' `L:{self.get_payout_limit(current_author_id)}`'
		if current_winnings > 0:
			s += f' `You won! ${current_winnings}`'
		return s

	async def print_nice_balance(self, message, money_added):
		current_author_id = str(message.author.id)
		str_ = f'{self.get_nice_balance(current_author_id)}'
		if money_added > 0:
			str_ = f'{self.twitch_money_added_message(message, money_added)}\n{str_}'
		await message.channel.send(f'<@{current_author_id}> {str_}')

	def get_balance(self, current_author_id):
		return self.registered_author_ids[current_author_id][0]

	def get_multiplier(self, current_author_id):
		return self.registered_author_ids[current_author_id][1]

	def set_multiplier(self, current_author_id, i):
		if i < 1:
			i = 1
		elif i > 4:
			i = 4
		self.registered_author_ids[current_author_id][1] = i

	def get_payout_limit(self, current_author_id):
		return self.registered_author_ids[current_author_id][2]

	def set_payout_limit(self, current_author_id, i):
		maximum_payout = self.pay_table[0][0] * self.get_multiplier(current_author_id)
		smallest_payout = self.pay_table[len(self.pay_table) - 1][0] * self.get_multiplier(current_author_id)
		if i < smallest_payout:
			i = smallest_payout  # Smallest possible payout
		elif i > maximum_payout:
			i = maximum_payout  # Maximum possible payout
		self.registered_author_ids[current_author_id][2] = i

	async def identify_author_id(self, message):
		current_author_id = str(message.author.id)
		# # Add new user
		if current_author_id not in self.registered_author_ids:
			# [balance, multiplier, stop if this or greater payout reached, current time]
			self.registered_author_ids[current_author_id] = [20, 1, 20, time.time()]
			self.save_database()

		str_ = f'{message.author.id} {message.author.name}: {message.content}: '
		str_ += self.get_nice_balance(current_author_id).replace('`', '')
		print(str_)

	async def print_own_rules(self, message, money_added):
		print(f'{message.author.id} {message.author.name}: {message.content}: print own rules')
		current_author_id = str(message.author.id)
		str_ = f'{self.get_nice_balance(current_author_id)}\n'
		str_ += f'{self.print_rules(self.get_multiplier(current_author_id))}'
		if money_added > 0:
			str_ = f'{self.twitch_money_added_message(message, money_added)}\n{str_}'
		await message.channel.send(f'<@{current_author_id}> {str_}')

	def print_rules(self, multiplier=1):
		s = ''
		for i in range(len(self.pay_table) - 1):
			s += self.reel_[0][self.pay_table[i][1][0]]
			s += self.reel_[1][self.pay_table[i][2][0]]
			s += self.reel_[2][self.pay_table[i][3][1][0]]
			s += f' `${self.pay_table[i][0] * multiplier}`\n'
		s += f'{horseshoe}{horseshoe} Any(Not {star}) `${2 * multiplier}`\n\n'

		s += f'{wild} **WILD** symbol on the center reel.'
		s += ' Wild can substitute for any other symbol, with no exceptions.\n\n'

		s += '**Get more money!** Redeem specific Twitch reward from'
        s += ' [twitch channel name here] Twitch channel using your channel points'
		s += ' to get more money. Link your Discord and Twitch account first by sending `!link`'
		s += ' private message for this bot. You can also unlink your Discord and Twitch connection by'
		s += ' sending `!unlink` private message for this bot.\n\n'

		s += '`!casino rules` or `!casino` Shows you the casino rules.\n'
		s += '`!casino roll` Roll the reels once.\n'
		s += '`!casino roll 10` Roll the reels multiple times at once. Number can be between 1 - 10.\n'
		s += '`!casino roll 10 4` Set the multiplier. Number can be between 1 - 4.\n'
		s += '`!casino roll 10 4 80` Set the payout limit. Stop if this or greater payout.\n'
		s += '`!casino cash` Show how much money you have at the moment.\n'
		s += '`!casino set M` Set the multiplier. M can also be m, mult, multp or multiplier.\n'
		s += '`!casino set L` Set the payout limit. L can also be l, limit or payoutlimit.\n'
		# s += '`!casino add M` Add $100 money. Every 10 minutes you can add money. M can also be m or money.'
		return s

	async def set(self, cut_msg, message):
		current_author_id = str(message.author.id)

		print(f'{message.author.id} {message.author.name}: {message.content}')

		if len(cut_msg) < 3 or len(cut_msg) > 3:
			return

		str_ = ''
		# Set Multiplier
		if cut_msg[1] in ['m', 'M', 'mult', 'multp', 'multiplier'] and str(cut_msg[2]).isdigit():
			self.set_multiplier(current_author_id, int(cut_msg[2]))
			str_ = f' `Multiplier set to {self.get_multiplier(current_author_id)}`'

		# Set stop point
		elif cut_msg[1] in ['l', 'L', 'limit', 'payoutlimit'] and str(cut_msg[2]).isdigit():
			self.set_payout_limit(current_author_id, int(cut_msg[2]))
			str_ = f' `Payout limit set to {self.get_payout_limit(current_author_id)}`'

		if str_ == '':
			return

		await message.channel.send(f'<@{current_author_id}>{str_}')
		print(f'{message.author.id} {message.author.name}: {message.content}:{str_.lower().replace("`", "")}')

	async def add(self, cut_msg, message):
		current_author_id = str(message.author.id)

		print(f'{message.author.id} {message.author.name}: {message.content}')

		if len(cut_msg) < 2:
			return
		if not cut_msg[1] in ['m', 'M', 'money']:
			return

		# Add money
		if self.permission_to_add_100(current_author_id):
			self.update_timer_time(current_author_id)
			await self.send_money_added_message(message)
			self.save_database()
			return

		p = time.time() - self.registered_author_ids[current_author_id][3]
		m = math.floor(((60.0 * 10.0) - p) / 60.0)
		s = math.floor(((60.0 * 10.0) - p) - (m * 60))
		str_ = f' :money_with_wings: `You have to wait {m} minutes and {s} seconds before you can add more money.`'

		await message.channel.send(f'<@{current_author_id}>{str_}')

	async def send_money_added_message(self, message):
		current_author_id = str(message.author.id)
		str_ = f' :money_mouth: `Money added! Your current balance: ${self.get_balance(current_author_id)}`'
		await message.channel.send(f'<@{current_author_id}>{str_}')
		str_ = f'{message.author.id} {message.author.name}: {message.content}: '
		str_ += f'money added, current balance {self.get_balance(current_author_id)}'
		print(str_)

	def twitch_money_added_message(self, message, cost):
		current_author_id = str(message.author.id)

		str_ = f'{message.author.id} {message.author.name}: {message.content}: '
		str_ += f'twitch money added {cost}, current balance {self.get_balance(current_author_id)}'
		print(str_)

		str_ = f':money_mouth: `You redeemed a Twitch reward and gained ${cost} of cash! Your current balance:'
		str_ += f' ${self.get_balance(current_author_id)}`'

		return str_

	def permission_to_add_100(self, current_author_id):
		if time.time() - self.registered_author_ids[current_author_id][3] > 60 * 10:
			self.update_balance(current_author_id, 100)
			return True
		return False

	def update_timer_time(self, current_author_id):
		self.registered_author_ids[current_author_id][3] = time.time()

This is the key file between Discord and Twitch. It handels the Discord and Twitch accounts linking process. If the user asks to link the Discord and Twitch accounts, the bot asks the user to send a generated unique identifier ID through Twitch, back to the bot, that it can know that this user have this Twitch account, and so link those together. Partly it also handles the action of users to redeem a specific reward from Twitch account to add more money to the bot cash for that specific user that asked it.

It also handles the situation if someone redeems the specific reward in Twitch to add more money, but the user doesn't have lineked the Discord and Twitch accounts yet, and the bot does not know yet to from which user this is from. It saves the reward redeem action and before every user gaming action it checks the user from remeeded rewards, if the user had linked the accounts and if it have some rewards waiting. It handles also the timing how users can whisper at the same time to the bot, while keeping their own time limits active.

link_accounts.py
"""
Copyright (C) 2020 Lari Varjonen <[email protected]>
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
version 2 as published by the Free Software Foundation.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
Thanks for Janne(Jenkkemi) for the idea to make this kind of bot: https://twitter.com/JanneKuoksa
"""
from overload_builtins import print
import time
import asyncio
import json
import nonce
import get_whispers
class LinkAccounts:
command = ['!link', '!unlink']
linked_accounts_fn = 'linked-account.json'
cash_waiting_fn = 'cash-waiting-requests.json'
linked_accounts = {}
expect_response = {}
cash_waiting = {}
dc_client = None
get_whispers = None
init_done = False
async def init(self, dc_client):
if self.init_done:
print('init already done')
return
self.init_done = True
self.dc_client = dc_client
await self.load_linked_accounts()
await self.load_cash_waiting()
self.get_whispers = get_whispers.WebSocketClient(self.gw_callback)
async def load_linked_accounts(self):
try:
with open(self.linked_accounts_fn, 'r', encoding='utf-8') as f:
data = f.read()
self.linked_accounts = json.loads(data)
except IOError:
pass
def save_linked_accounts(self):
with open(self.linked_accounts_fn, 'w', encoding='utf-8') as f:
json.dump(self.linked_accounts, f, ensure_ascii=False, indent=4)
async def load_cash_waiting(self):
try:
with open(self.cash_waiting_fn, 'r', encoding='utf-8') as f:
data = f.read()
self.cash_waiting = json.loads(data)
except IOError:
pass
def save_cash_waiting(self):
with open(self.cash_waiting_fn, 'w', encoding='utf-8') as f:
json.dump(self.cash_waiting, f, ensure_ascii=False, indent=4)
async def gcr_callback(self, display_name: str, tw_uid: str, cost: int):
print(f'{display_name} {tw_uid} {cost}')
if tw_uid in self.cash_waiting:
self.cash_waiting[tw_uid] += cost
else:
self.cash_waiting[tw_uid] = cost
self.save_cash_waiting()
for dc_uid, tw_uid_ in self.linked_accounts.items():
if tw_uid_ == tw_uid:
dc_user = self.dc_client.get_user(int(dc_uid))
await dc_user.send(f'Successfully transferred {cost} Twitch channel points to your '
f'slot machine cash.')
return
async def gw_callback(self, tw_uid: str, received_token: str):
print(f'{tw_uid} {received_token}')
if received_token in self.expect_response:
print('expected token received. user added to the linked accounts')
self.linked_accounts[self.expect_response[received_token]] = str(tw_uid)
del self.expect_response[received_token]
if not self.expect_response and self.get_whispers.connection.open:
await self.get_whispers.connection.close()
await self.get_whispers.connection.wait_closed()
self.save_linked_accounts()
else:
print('expected token NOT received')
async def check_cash_waiting(self, message, casino):
message_author_id = str(message.author.id)
if message_author_id not in self.linked_accounts:
return 0
for tw_uid, cost in self.cash_waiting.items():
if tw_uid == self.linked_accounts[message_author_id]:
print(f'casino add {cost}')
# Add new twitch reward money to user current balance
casino.update_balance(message_author_id, int(cost))
casino.save_database()
del self.cash_waiting[tw_uid]
self.save_cash_waiting()
return int(cost)
return 0
async def unlink(self, message):
str_ = f'{message.author.id} {message.author.name}: {message.content}:'
current_author_id = str(message.author.id)
if current_author_id in self.linked_accounts:
del self.linked_accounts[current_author_id]
print(f'{str_} account successfully unlinked')
await message.channel.send('Account successfully unlinked.')
self.save_linked_accounts()
return
print(f'{str_} your account is not linked')
await message.channel.send('Your account is not linked.')
async def link(self, message):
str_ = f'{message.author.id} {message.author.name}: {message.content}:'
current_author_id = str(message.author.id)
if current_author_id in self.linked_accounts:
print(f'{str_} account already linked')
await message.channel.send('Account already linked.')
return 0
if current_author_id in self.expect_response.values():
print(f'{str_} the link process is already running for this user')
return 0
else:
print(f'{str_} link')
generated_token = nonce.get(16)
while generated_token in self.expect_response:
generated_token = nonce.get(16)
self.expect_response[generated_token] = current_author_id
await self.get_whispers.update_time()
if self.get_whispers.connection is None or not self.get_whispers.connection.open:
await self.get_whispers.connect()
asyncio.create_task(self.get_whispers.heartbeat())
asyncio.create_task(self.get_whispers.on_message())
asyncio.create_task(self.get_whispers.timer())
s = 'In order to complete your linking process please whisper '
s += 'in Twitch within 1 minute. Copy this: '
s += f'`/w {get_whispers.bot_name} {generated_token}`'
await message.channel.send(s)
return 1
async def wait_user_response(self, message):
str_ = f'{message.author.id} {message.author.name}: {message.content}:'
current_author_id = str(message.author.id)
for key, value in self.expect_response.items():
if value == current_author_id:
print(f'{str_} waiting for user response: {key}')
break
time_ = self.get_whispers.time
while True:
await asyncio.sleep(1)
# If response is expect response
if current_author_id in self.linked_accounts:
print(f'{str_} successfully linked account')
await message.channel.send('You have successfully linked your Discord and Twitch accounts.')
return
# If waiting time ends
if time.time() > time_:
print(f'{str_} time ends')
# Remove expect response from the expect_response dict
for key, value in self.expect_response.items():
if value == current_author_id:
del self.expect_response[key]
break
break
await message.channel.send('Time limit reached. Try again: `!link`')
print(f'{str_} try again')
""" Copyright (C) 2020 Lari Varjonen <[email protected]> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 as published by the Free Software Foundation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Thanks for Janne(Jenkkemi) for the idea to make this kind of bot: https://twitter.com/JanneKuoksa """ from overload_builtins import print import time import asyncio import json import nonce import get_whispers class LinkAccounts: command = ['!link', '!unlink'] linked_accounts_fn = 'linked-account.json' cash_waiting_fn = 'cash-waiting-requests.json' linked_accounts = {} expect_response = {} cash_waiting = {} dc_client = None get_whispers = None init_done = False async def init(self, dc_client): if self.init_done: print('init already done') return self.init_done = True self.dc_client = dc_client await self.load_linked_accounts() await self.load_cash_waiting() self.get_whispers = get_whispers.WebSocketClient(self.gw_callback) async def load_linked_accounts(self): try: with open(self.linked_accounts_fn, 'r', encoding='utf-8') as f: data = f.read() self.linked_accounts = json.loads(data) except IOError: pass def save_linked_accounts(self): with open(self.linked_accounts_fn, 'w', encoding='utf-8') as f: json.dump(self.linked_accounts, f, ensure_ascii=False, indent=4) async def load_cash_waiting(self): try: with open(self.cash_waiting_fn, 'r', encoding='utf-8') as f: data = f.read() self.cash_waiting = json.loads(data) except IOError: pass def save_cash_waiting(self): with open(self.cash_waiting_fn, 'w', encoding='utf-8') as f: json.dump(self.cash_waiting, f, ensure_ascii=False, indent=4) async def gcr_callback(self, display_name: str, tw_uid: str, cost: int): print(f'{display_name} {tw_uid} {cost}') if tw_uid in self.cash_waiting: self.cash_waiting[tw_uid] += cost else: self.cash_waiting[tw_uid] = cost self.save_cash_waiting() for dc_uid, tw_uid_ in self.linked_accounts.items(): if tw_uid_ == tw_uid: dc_user = self.dc_client.get_user(int(dc_uid)) await dc_user.send(f'Successfully transferred {cost} Twitch channel points to your ' f'slot machine cash.') return async def gw_callback(self, tw_uid: str, received_token: str): print(f'{tw_uid} {received_token}') if received_token in self.expect_response: print('expected token received. user added to the linked accounts') self.linked_accounts[self.expect_response[received_token]] = str(tw_uid) del self.expect_response[received_token] if not self.expect_response and self.get_whispers.connection.open: await self.get_whispers.connection.close() await self.get_whispers.connection.wait_closed() self.save_linked_accounts() else: print('expected token NOT received') async def check_cash_waiting(self, message, casino): message_author_id = str(message.author.id) if message_author_id not in self.linked_accounts: return 0 for tw_uid, cost in self.cash_waiting.items(): if tw_uid == self.linked_accounts[message_author_id]: print(f'casino add {cost}') # Add new twitch reward money to user current balance casino.update_balance(message_author_id, int(cost)) casino.save_database() del self.cash_waiting[tw_uid] self.save_cash_waiting() return int(cost) return 0 async def unlink(self, message): str_ = f'{message.author.id} {message.author.name}: {message.content}:' current_author_id = str(message.author.id) if current_author_id in self.linked_accounts: del self.linked_accounts[current_author_id] print(f'{str_} account successfully unlinked') await message.channel.send('Account successfully unlinked.') self.save_linked_accounts() return print(f'{str_} your account is not linked') await message.channel.send('Your account is not linked.') async def link(self, message): str_ = f'{message.author.id} {message.author.name}: {message.content}:' current_author_id = str(message.author.id) if current_author_id in self.linked_accounts: print(f'{str_} account already linked') await message.channel.send('Account already linked.') return 0 if current_author_id in self.expect_response.values(): print(f'{str_} the link process is already running for this user') return 0 else: print(f'{str_} link') generated_token = nonce.get(16) while generated_token in self.expect_response: generated_token = nonce.get(16) self.expect_response[generated_token] = current_author_id await self.get_whispers.update_time() if self.get_whispers.connection is None or not self.get_whispers.connection.open: await self.get_whispers.connect() asyncio.create_task(self.get_whispers.heartbeat()) asyncio.create_task(self.get_whispers.on_message()) asyncio.create_task(self.get_whispers.timer()) s = 'In order to complete your linking process please whisper ' s += 'in Twitch within 1 minute. Copy this: ' s += f'`/w {get_whispers.bot_name} {generated_token}`' await message.channel.send(s) return 1 async def wait_user_response(self, message): str_ = f'{message.author.id} {message.author.name}: {message.content}:' current_author_id = str(message.author.id) for key, value in self.expect_response.items(): if value == current_author_id: print(f'{str_} waiting for user response: {key}') break time_ = self.get_whispers.time while True: await asyncio.sleep(1) # If response is expect response if current_author_id in self.linked_accounts: print(f'{str_} successfully linked account') await message.channel.send('You have successfully linked your Discord and Twitch accounts.') return # If waiting time ends if time.time() > time_: print(f'{str_} time ends') # Remove expect response from the expect_response dict for key, value in self.expect_response.items(): if value == current_author_id: del self.expect_response[key] break break await message.channel.send('Time limit reached. Try again: `!link`') print(f'{str_} try again')
"""
	Copyright (C) 2020 Lari Varjonen <[email protected]>

	This program is free software; you can redistribute it and/or
	modify it under the terms of the GNU General Public License
	version 2 as published by the Free Software Foundation.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with this program; if not, write to the Free Software
	Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.

	Thanks for Janne(Jenkkemi) for the idea to make this kind of bot: https://twitter.com/JanneKuoksa
"""

from overload_builtins import print
import time
import asyncio
import json

import nonce
import get_whispers


class LinkAccounts:
	command = ['!link', '!unlink']
	linked_accounts_fn = 'linked-account.json'
	cash_waiting_fn = 'cash-waiting-requests.json'
	linked_accounts = {}
	expect_response = {}
	cash_waiting = {}
	dc_client = None
	get_whispers = None
	init_done = False

	async def init(self, dc_client):
		if self.init_done:
			print('init already done')
			return
		self.init_done = True
		self.dc_client = dc_client
		await self.load_linked_accounts()
		await self.load_cash_waiting()
		self.get_whispers = get_whispers.WebSocketClient(self.gw_callback)

	async def load_linked_accounts(self):
		try:
			with open(self.linked_accounts_fn, 'r', encoding='utf-8') as f:
				data = f.read()
				self.linked_accounts = json.loads(data)
		except IOError:
			pass

	def save_linked_accounts(self):
		with open(self.linked_accounts_fn, 'w', encoding='utf-8') as f:
			json.dump(self.linked_accounts, f, ensure_ascii=False, indent=4)

	async def load_cash_waiting(self):
		try:
			with open(self.cash_waiting_fn, 'r', encoding='utf-8') as f:
				data = f.read()
				self.cash_waiting = json.loads(data)
		except IOError:
			pass

	def save_cash_waiting(self):
		with open(self.cash_waiting_fn, 'w', encoding='utf-8') as f:
			json.dump(self.cash_waiting, f, ensure_ascii=False, indent=4)

	async def gcr_callback(self, display_name: str, tw_uid: str, cost: int):
		print(f'{display_name} {tw_uid} {cost}')

		if tw_uid in self.cash_waiting:
			self.cash_waiting[tw_uid] += cost
		else:
			self.cash_waiting[tw_uid] = cost
		self.save_cash_waiting()

		for dc_uid, tw_uid_ in self.linked_accounts.items():
			if tw_uid_ == tw_uid:
				dc_user = self.dc_client.get_user(int(dc_uid))
				await dc_user.send(f'Successfully transferred {cost} Twitch channel points to your '
								   f'slot machine cash.')
				return

	async def gw_callback(self, tw_uid: str, received_token: str):
		print(f'{tw_uid} {received_token}')
		if received_token in self.expect_response:
			print('expected token received. user added to the linked accounts')
			self.linked_accounts[self.expect_response[received_token]] = str(tw_uid)
			del self.expect_response[received_token]
			if not self.expect_response and self.get_whispers.connection.open:
				await self.get_whispers.connection.close()
				await self.get_whispers.connection.wait_closed()
			self.save_linked_accounts()
		else:
			print('expected token NOT received')

	async def check_cash_waiting(self, message, casino):
		message_author_id = str(message.author.id)

		if message_author_id not in self.linked_accounts:
			return 0

		for tw_uid, cost in self.cash_waiting.items():
			if tw_uid == self.linked_accounts[message_author_id]:

				print(f'casino add {cost}')

				# Add new twitch reward money to user current balance
				casino.update_balance(message_author_id, int(cost))
				casino.save_database()

				del self.cash_waiting[tw_uid]
				self.save_cash_waiting()

				return int(cost)
		return 0

	async def unlink(self, message):
		str_ = f'{message.author.id} {message.author.name}: {message.content}:'
		current_author_id = str(message.author.id)
		if current_author_id in self.linked_accounts:
			del self.linked_accounts[current_author_id]
			print(f'{str_} account successfully unlinked')
			await message.channel.send('Account successfully unlinked.')
			self.save_linked_accounts()
			return
		print(f'{str_} your account is not linked')
		await message.channel.send('Your account is not linked.')

	async def link(self, message):
		str_ = f'{message.author.id} {message.author.name}: {message.content}:'
		current_author_id = str(message.author.id)
		if current_author_id in self.linked_accounts:
			print(f'{str_} account already linked')
			await message.channel.send('Account already linked.')
			return 0
		if current_author_id in self.expect_response.values():
			print(f'{str_} the link process is already running for this user')
			return 0
		else:
			print(f'{str_} link')

		generated_token = nonce.get(16)
		while generated_token in self.expect_response:
			generated_token = nonce.get(16)

		self.expect_response[generated_token] = current_author_id

		await self.get_whispers.update_time()
		if self.get_whispers.connection is None or not self.get_whispers.connection.open:
			await self.get_whispers.connect()
			asyncio.create_task(self.get_whispers.heartbeat())
			asyncio.create_task(self.get_whispers.on_message())
			asyncio.create_task(self.get_whispers.timer())

		s = 'In order to complete your linking process please whisper '
		s += 'in Twitch within 1 minute. Copy this: '
		s += f'`/w {get_whispers.bot_name} {generated_token}`'
		await message.channel.send(s)
		return 1

	async def wait_user_response(self, message):
		str_ = f'{message.author.id} {message.author.name}: {message.content}:'
		current_author_id = str(message.author.id)
		for key, value in self.expect_response.items():
			if value == current_author_id:
				print(f'{str_} waiting for user response: {key}')
				break
		time_ = self.get_whispers.time
		while True:
			await asyncio.sleep(1)
			# If response is expect response
			if current_author_id in self.linked_accounts:
				print(f'{str_} successfully linked account')
				await message.channel.send('You have successfully linked your Discord and Twitch accounts.')
				return

			# If waiting time ends
			if time.time() > time_:
				print(f'{str_} time ends')
				# Remove expect response from the expect_response dict
				for key, value in self.expect_response.items():
					if value == current_author_id:
						del self.expect_response[key]