mirror of
https://github.com/EQEmu/Server.git
synced 2026-02-16 21:12:26 +00:00
Merge branch 'master' into lsid
This commit is contained in:
commit
e0391dfcb8
@ -17,10 +17,7 @@
|
||||
|:---:|:---:|:---:|
|
||||
|**Install Count**|||
|
||||
### > Windows
|
||||
* [Easy Install](http://wiki.eqemulator.org/p?Akkas_PEQ_Server_Installer&frm=Main#from-scratch-installation-instructions-windows)
|
||||
* [Advanced Setup](http://wiki.eqemulator.org/p?Complete_Windows-based_Server_Setup_Guide)
|
||||
|
||||
|
||||
* [Install](https://github.com/EQEmu/Server/wiki/Windows-Server)
|
||||
|
||||
### > Debian/Ubuntu/CentOS/Fedora
|
||||
* You can use curl or wget to kick off the installer (whichever your OS has)
|
||||
@ -55,7 +52,7 @@ forum, although pull requests will be much quicker and easier on all parties.
|
||||
|
||||
## Resources
|
||||
- [EQEmulator Forums](http://www.eqemulator.org/forums)
|
||||
- [EQEmulator Wiki](http://wiki.eqemulator.org/i?M=Wiki)
|
||||
- [EQEmulator Wiki](https://github.com/EQEmu/Server/wiki)
|
||||
|
||||
## Related Repositories
|
||||
* [ProjectEQ Quests](https://github.com/ProjectEQ/projecteqquests)
|
||||
|
||||
@ -472,16 +472,6 @@ bool Database::CheckDatabaseConversions() {
|
||||
CheckDatabaseConvertPPDeblob();
|
||||
CheckDatabaseConvertCorpseDeblob();
|
||||
|
||||
/* Fetch EQEmu Server script */
|
||||
if (!std::ifstream("eqemu_server.pl")){
|
||||
std::cout << "Pulling down automatic database upgrade script..." << std::endl;
|
||||
#ifdef _WIN32
|
||||
system("perl -MLWP::UserAgent -e \"require LWP::UserAgent; my $ua = LWP::UserAgent->new; $ua->timeout(10); $ua->env_proxy; my $response = $ua->get('https://raw.githubusercontent.com/EQEmu/Server/master/utils/scripts/eqemu_server.pl'); if ($response->is_success){ open(FILE, '> eqemu_server.pl'); print FILE $response->decoded_content; close(FILE); }\"");
|
||||
#else
|
||||
system("wget --no-check-certificate -O eqemu_server.pl https://raw.githubusercontent.com/EQEmu/Server/master/utils/scripts/eqemu_server.pl");
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Run EQEmu Server script (Checks for database updates) */
|
||||
system("perl eqemu_server.pl ran_from_world");
|
||||
|
||||
|
||||
@ -23,351 +23,112 @@
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
|
||||
std::string EQEmuConfig::ConfigFile = "eqemu_config.xml";
|
||||
std::string EQEmuConfig::ConfigFile = "eqemu_config.json";
|
||||
EQEmuConfig *EQEmuConfig::_config = nullptr;
|
||||
|
||||
void EQEmuConfig::do_world(TiXmlElement *ele)
|
||||
{
|
||||
const char *text;
|
||||
TiXmlElement * sub_ele;;
|
||||
text = ParseTextBlock(ele, "shortname");
|
||||
if (text) {
|
||||
ShortName = text;
|
||||
}
|
||||
text = ParseTextBlock(ele, "longname");
|
||||
if (text) {
|
||||
LongName = text;
|
||||
}
|
||||
text = ParseTextBlock(ele, "address", true);
|
||||
if (text) {
|
||||
WorldAddress = text;
|
||||
}
|
||||
text = ParseTextBlock(ele, "localaddress", true);
|
||||
if (text) {
|
||||
LocalAddress = text;
|
||||
}
|
||||
text = ParseTextBlock(ele, "maxclients", true);
|
||||
if (text) {
|
||||
MaxClients = atoi(text);
|
||||
}
|
||||
// Get the <key> element
|
||||
text = ParseTextBlock(ele, "key", true);
|
||||
if (text) {
|
||||
SharedKey = text;
|
||||
}
|
||||
// Get the <loginserver> element
|
||||
sub_ele = ele->FirstChildElement("loginserver");
|
||||
if (sub_ele) {
|
||||
text = ParseTextBlock(sub_ele, "host", true);
|
||||
if (text) {
|
||||
LoginHost = text;
|
||||
}
|
||||
text = ParseTextBlock(sub_ele, "port", true);
|
||||
if (text) {
|
||||
LoginPort = atoi(text);
|
||||
}
|
||||
text = ParseTextBlock(sub_ele, "legacy", true);
|
||||
if (text) {
|
||||
LoginLegacy = atoi(text) > 0 ? true : false;
|
||||
}
|
||||
text = ParseTextBlock(sub_ele, "account", true);
|
||||
if (text) {
|
||||
LoginAccount = text;
|
||||
}
|
||||
text = ParseTextBlock(sub_ele, "password", true);
|
||||
if (text) {
|
||||
LoginPassword = text;
|
||||
}
|
||||
void EQEmuConfig::parse_config() {
|
||||
|
||||
ShortName = _root["server"]["world"].get("shortname", "").asString();
|
||||
LongName = _root["server"]["world"].get("longname", "").asString();
|
||||
WorldAddress = _root["server"]["world"].get("address", "").asString();
|
||||
LocalAddress = _root["server"]["world"].get("localaddress", "").asString();
|
||||
MaxClients = atoi(_root["server"]["world"].get("maxclients", "-1").asString().c_str());
|
||||
SharedKey = _root["server"]["world"].get("key", "").asString();
|
||||
LoginCount = 0;
|
||||
|
||||
if (_root["server"]["world"]["loginserver"].isObject()) {
|
||||
LoginHost = _root["server"]["world"]["loginserver"].get("host", "login.eqemulator.net").asString();
|
||||
LoginPort = atoi(_root["server"]["world"]["loginserver"].get("port", "5998").asString().c_str());
|
||||
LoginLegacy = false;
|
||||
if (_root["server"]["world"]["loginserver"].get("legacy", "0").asString() == "1") LoginLegacy = true;
|
||||
LoginAccount = _root["server"]["world"]["loginserver"].get("account", "").asString();
|
||||
LoginPassword = _root["server"]["world"]["loginserver"].get("password", "").asString();
|
||||
} else {
|
||||
char str[32];
|
||||
char str[32];
|
||||
loginlist.Clear();
|
||||
do {
|
||||
sprintf(str, "loginserver%i", ++LoginCount);
|
||||
sub_ele = ele->FirstChildElement(str);
|
||||
if (sub_ele) {
|
||||
auto loginconfig = new LoginConfig;
|
||||
text = ParseTextBlock(sub_ele, "host", true);
|
||||
if (text) {
|
||||
loginconfig->LoginHost = text;
|
||||
}
|
||||
text = ParseTextBlock(sub_ele, "port", true);
|
||||
if (text) {
|
||||
loginconfig->LoginPort = atoi(text);
|
||||
}
|
||||
text = ParseTextBlock(sub_ele, "legacy", true);
|
||||
if (text) {
|
||||
loginconfig->LoginLegacy = atoi(text) > 0 ? true : false;
|
||||
}
|
||||
text = ParseTextBlock(sub_ele, "account", true);
|
||||
if (text) {
|
||||
loginconfig->LoginAccount = text;
|
||||
}
|
||||
text = ParseTextBlock(sub_ele, "password", true);
|
||||
if (text) {
|
||||
loginconfig->LoginPassword = text;
|
||||
}
|
||||
loginlist.Insert(loginconfig);
|
||||
if (!_root["server"]["world"][str].isObject()) {
|
||||
break;
|
||||
}
|
||||
} while (sub_ele);
|
||||
}
|
||||
// Check for locked
|
||||
sub_ele = ele->FirstChildElement("locked");
|
||||
if (sub_ele != nullptr) {
|
||||
Locked = true;
|
||||
}
|
||||
// Get the <tcp> element
|
||||
sub_ele = ele->FirstChildElement("tcp");
|
||||
if (sub_ele != nullptr) {
|
||||
text = sub_ele->Attribute("ip");
|
||||
if (text) {
|
||||
WorldIP = text;
|
||||
}
|
||||
text = sub_ele->Attribute("port");
|
||||
if (text) {
|
||||
WorldTCPPort = atoi(text);
|
||||
}
|
||||
}
|
||||
|
||||
sub_ele = ele->FirstChildElement("telnet");
|
||||
if (sub_ele != nullptr) {
|
||||
text = sub_ele->Attribute("ip");
|
||||
if (text) {
|
||||
TelnetIP = text;
|
||||
}
|
||||
text = sub_ele->Attribute("port");
|
||||
if (text) {
|
||||
TelnetTCPPort = atoi(text);
|
||||
}
|
||||
text = sub_ele->Attribute("enabled");
|
||||
if (text && !strcasecmp(text, "true")) {
|
||||
TelnetEnabled = true;
|
||||
}
|
||||
}
|
||||
auto loginconfig = new LoginConfig;
|
||||
loginconfig->LoginHost = _root["server"]["world"][str].get("host", "login.eqemulator.net").asString();
|
||||
loginconfig->LoginPort = atoi(_root["server"]["world"][str].get("port", "5998").asString().c_str());
|
||||
loginconfig->LoginAccount = _root["server"]["world"][str].get("account", "").asString();
|
||||
loginconfig->LoginPassword = _root["server"]["world"][str].get("password", "").asString();
|
||||
|
||||
// Get the <http> element
|
||||
sub_ele = ele->FirstChildElement("http");
|
||||
if (sub_ele != nullptr) {
|
||||
// text = sub_ele->Attribute("ip");
|
||||
// if (text)
|
||||
// WorldIP=text;
|
||||
text = sub_ele->Attribute("mimefile");
|
||||
if (text) {
|
||||
WorldHTTPMimeFile = text;
|
||||
}
|
||||
text = sub_ele->Attribute("port");
|
||||
if (text) {
|
||||
WorldHTTPPort = atoi(text);
|
||||
}
|
||||
text = sub_ele->Attribute("enabled");
|
||||
if (text && !strcasecmp(text, "true")) {
|
||||
WorldHTTPEnabled = true;
|
||||
}
|
||||
loginconfig->LoginLegacy = false;
|
||||
if (_root["server"]["world"][str].get("legacy", "0").asString() == "1") loginconfig->LoginLegacy = true;
|
||||
loginlist.Insert(loginconfig);
|
||||
} while (LoginCount < 100);
|
||||
}
|
||||
|
||||
|
||||
//<locked> from xml converts to json as locked: "", so i default to "false".
|
||||
//The only way to enable locked is by switching to true, meaning this value is always false until manually set true
|
||||
Locked = false;
|
||||
if (_root["server"]["world"].get("locked", "false").asString() == "true") Locked = true;
|
||||
WorldIP = _root["server"]["world"]["tcp"].get("host", "127.0.0.1").asString();
|
||||
WorldTCPPort = atoi(_root["server"]["world"]["tcp"].get("port", "9000").asString().c_str());
|
||||
|
||||
TelnetIP = _root["server"]["world"]["telnet"].get("ip", "127.0.0.1").asString();
|
||||
TelnetTCPPort = atoi(_root["server"]["world"]["telnet"].get("port", "9001").asString().c_str());
|
||||
TelnetEnabled = false;
|
||||
if (_root["server"]["world"]["telnet"].get("enabled", "false").asString() == "true") TelnetEnabled = true;
|
||||
|
||||
WorldHTTPMimeFile = _root["server"]["world"]["http"].get("mimefile", "mime.types").asString();
|
||||
WorldHTTPPort = atoi(_root["server"]["world"]["http"].get("port", "9080").asString().c_str());
|
||||
WorldHTTPEnabled = false;
|
||||
if (_root["server"]["world"]["http"].get("enabled", "false").asString() == "true") WorldHTTPEnabled = true;
|
||||
|
||||
ChatHost = _root["server"]["chatserver"].get("host", "eqchat.eqemulator.net").asString();
|
||||
ChatPort = atoi(_root["server"]["chatserver"].get("port", "7778").asString().c_str());
|
||||
|
||||
MailHost = _root["server"]["mailserver"].get("host", "eqmail.eqemulator.net").asString();
|
||||
MailPort = atoi(_root["server"]["mailserver"].get("port", "7778").asString().c_str());
|
||||
|
||||
DatabaseUsername = _root["server"]["database"].get("username", "eq").asString();
|
||||
DatabasePassword = _root["server"]["database"].get("password", "eq").asString();
|
||||
DatabaseHost = _root["server"]["database"].get("host", "localhost").asString();
|
||||
DatabasePort = atoi(_root["server"]["database"].get("port", "3306").asString().c_str());
|
||||
DatabaseDB = _root["server"]["database"].get("db", "eq").asString();
|
||||
|
||||
QSDatabaseHost = _root["server"]["qsdatabase"].get("host", "localhost").asString();
|
||||
QSDatabasePort = atoi(_root["server"]["qsdatabase"].get("port", "3306").asString().c_str());
|
||||
QSDatabaseUsername = _root["server"]["qsdatabase"].get("username", "eq").asString();
|
||||
QSDatabasePassword = _root["server"]["qsdatabase"].get("password", "eq").asString();
|
||||
QSDatabaseDB = _root["server"]["qsdatabase"].get("db", "eq").asString();
|
||||
|
||||
DefaultStatus = atoi(_root["server"]["zones"].get("defaultstatus", 0).asString().c_str());
|
||||
ZonePortLow = atoi(_root["server"]["zones"]["ports"].get("low", "7000").asString().c_str());
|
||||
ZonePortHigh = atoi(_root["server"]["zones"]["ports"].get("high", "7999").asString().c_str());
|
||||
|
||||
SpellsFile = _root["server"]["files"].get("spells", "spells_us.txt").asString();
|
||||
OpCodesFile = _root["server"]["files"].get("opcodes", "opcodes.conf").asString();
|
||||
PluginPlFile = _root["server"]["files"].get("plugin.pl", "plugin.pl").asString();
|
||||
|
||||
MapDir = _root["server"]["directories"].get("maps", "Maps/").asString();
|
||||
QuestDir = _root["server"]["directories"].get("quests", "quests/").asString();
|
||||
PluginDir = _root["server"]["directories"].get("plugins", "plugins/").asString();
|
||||
LuaModuleDir = _root["server"]["directories"].get("lua_modules", "lua_modules/").asString();
|
||||
PatchDir = _root["server"]["directories"].get("patches", "./").asString();
|
||||
SharedMemDir = _root["server"]["directories"].get("shared_memory", "shared/").asString();
|
||||
LogDir = _root["server"]["directories"].get("logs", "logs/").asString();
|
||||
|
||||
LogPrefix = _root["server"]["launcher"].get("logprefix", "logs/zone-").asString();
|
||||
LogSuffix = _root["server"]["launcher"].get("logsuffix", ".log").asString();
|
||||
RestartWait = atoi(_root["server"]["launcher"]["timers"].get("restart", "10000").asString().c_str());
|
||||
TerminateWait = atoi(_root["server"]["launcher"]["timers"].get("reterminate", "10000").asString().c_str());
|
||||
InitialBootWait = atoi(_root["server"]["launcher"]["timers"].get("initial", "20000").asString().c_str());
|
||||
ZoneBootInterval = atoi(_root["server"]["launcher"]["timers"].get("interval", "2000").asString().c_str());
|
||||
#ifdef WIN32
|
||||
ZoneExe = _root["server"]["launcher"].get("exe", "zone.exe").asString();
|
||||
#else
|
||||
ZoneExe = _root["server"]["launcher"].get("exe", "./zone").asString();
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
void EQEmuConfig::do_chatserver(TiXmlElement *ele)
|
||||
{
|
||||
const char *text;
|
||||
text = ParseTextBlock(ele, "host", true);
|
||||
if (text) {
|
||||
ChatHost = text;
|
||||
}
|
||||
text = ParseTextBlock(ele, "port", true);
|
||||
if (text) {
|
||||
ChatPort = atoi(text);
|
||||
}
|
||||
}
|
||||
|
||||
void EQEmuConfig::do_mailserver(TiXmlElement *ele)
|
||||
{
|
||||
const char *text;
|
||||
text = ParseTextBlock(ele, "host", true);
|
||||
if (text) {
|
||||
MailHost = text;
|
||||
}
|
||||
text = ParseTextBlock(ele, "port", true);
|
||||
if (text) {
|
||||
MailPort = atoi(text);
|
||||
}
|
||||
}
|
||||
|
||||
void EQEmuConfig::do_database(TiXmlElement *ele)
|
||||
{
|
||||
const char *text;
|
||||
text = ParseTextBlock(ele, "host", true);
|
||||
if (text) {
|
||||
DatabaseHost = text;
|
||||
}
|
||||
text = ParseTextBlock(ele, "port", true);
|
||||
if (text) {
|
||||
DatabasePort = atoi(text);
|
||||
}
|
||||
text = ParseTextBlock(ele, "username", true);
|
||||
if (text) {
|
||||
DatabaseUsername = text;
|
||||
}
|
||||
text = ParseTextBlock(ele, "password", true);
|
||||
if (text) {
|
||||
DatabasePassword = text;
|
||||
}
|
||||
text = ParseTextBlock(ele, "db", true);
|
||||
if (text) {
|
||||
DatabaseDB = text;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void EQEmuConfig::do_qsdatabase(TiXmlElement *ele)
|
||||
{
|
||||
const char *text;
|
||||
text = ParseTextBlock(ele, "host", true);
|
||||
if (text) {
|
||||
QSDatabaseHost = text;
|
||||
}
|
||||
text = ParseTextBlock(ele, "port", true);
|
||||
if (text) {
|
||||
QSDatabasePort = atoi(text);
|
||||
}
|
||||
text = ParseTextBlock(ele, "username", true);
|
||||
if (text) {
|
||||
QSDatabaseUsername = text;
|
||||
}
|
||||
text = ParseTextBlock(ele, "password", true);
|
||||
if (text) {
|
||||
QSDatabasePassword = text;
|
||||
}
|
||||
text = ParseTextBlock(ele, "db", true);
|
||||
if (text) {
|
||||
QSDatabaseDB = text;
|
||||
}
|
||||
}
|
||||
|
||||
void EQEmuConfig::do_zones(TiXmlElement *ele)
|
||||
{
|
||||
const char *text;
|
||||
TiXmlElement *sub_ele;
|
||||
// TiXmlNode *node,*sub_node;
|
||||
text = ParseTextBlock(ele, "defaultstatus", true);
|
||||
if (text) {
|
||||
DefaultStatus = atoi(text);
|
||||
}
|
||||
// Get the <ports> element
|
||||
sub_ele = ele->FirstChildElement("ports");
|
||||
if (sub_ele != nullptr) {
|
||||
text = sub_ele->Attribute("low");
|
||||
if (text) {
|
||||
ZonePortLow = atoi(text);
|
||||
};
|
||||
text = sub_ele->Attribute("high");
|
||||
if (text) {
|
||||
ZonePortHigh = atoi(text);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EQEmuConfig::do_files(TiXmlElement *ele)
|
||||
{
|
||||
const char *text;
|
||||
text = ParseTextBlock(ele, "spells", true);
|
||||
if (text) {
|
||||
SpellsFile = text;
|
||||
}
|
||||
text = ParseTextBlock(ele, "opcodes", true);
|
||||
if (text) {
|
||||
OpCodesFile = text;
|
||||
}
|
||||
text = ParseTextBlock(ele, "plugin.pl", true);
|
||||
if (text) {
|
||||
PluginPlFile = text;
|
||||
}
|
||||
}
|
||||
|
||||
void EQEmuConfig::do_directories(TiXmlElement *ele)
|
||||
{
|
||||
const char *text;
|
||||
text = ParseTextBlock(ele, "maps", true);
|
||||
if (text) {
|
||||
MapDir = text;
|
||||
if ( MapDir.back() != '/' )
|
||||
MapDir += '/';
|
||||
}
|
||||
text = ParseTextBlock(ele, "quests", true);
|
||||
if (text) {
|
||||
QuestDir = text;
|
||||
if ( QuestDir.back() != '/' )
|
||||
QuestDir += '/';
|
||||
}
|
||||
text = ParseTextBlock(ele, "plugins", true);
|
||||
if (text) {
|
||||
PluginDir = text;
|
||||
if ( PluginDir.back() != '/' )
|
||||
PluginDir += '/';
|
||||
}
|
||||
text = ParseTextBlock(ele, "lua_modules", true);
|
||||
if (text) {
|
||||
LuaModuleDir = text;
|
||||
if ( LuaModuleDir.back() != '/' )
|
||||
LuaModuleDir += '/';
|
||||
}
|
||||
text = ParseTextBlock(ele, "patches", true);
|
||||
if (text) {
|
||||
PatchDir = text;
|
||||
if ( PatchDir.back() != '/' )
|
||||
PatchDir += '/';
|
||||
}
|
||||
text = ParseTextBlock(ele, "shared_memory", true);
|
||||
if (text) {
|
||||
SharedMemDir = text;
|
||||
if ( SharedMemDir.back() != '/' )
|
||||
SharedMemDir += '/';
|
||||
}
|
||||
//Not Fully Implemented yet LogDir
|
||||
text = ParseTextBlock(ele, "logs", true);
|
||||
if (text) {
|
||||
LogDir = text;
|
||||
if ( LogDir.back() != '/' )
|
||||
LogDir += '/';
|
||||
}
|
||||
}
|
||||
|
||||
void EQEmuConfig::do_launcher(TiXmlElement *ele)
|
||||
{
|
||||
const char *text;
|
||||
TiXmlElement *sub_ele;
|
||||
text = ParseTextBlock(ele, "logprefix", true);
|
||||
if (text) {
|
||||
LogPrefix = text;
|
||||
}
|
||||
text = ParseTextBlock(ele, "logsuffix", true);
|
||||
if (text) {
|
||||
LogSuffix = text;
|
||||
}
|
||||
// Get the <exe> element
|
||||
text = ParseTextBlock(ele, "exe", true);
|
||||
if (text) {
|
||||
ZoneExe = text;
|
||||
}
|
||||
// Get the <timers> element
|
||||
sub_ele = ele->FirstChildElement("timers");
|
||||
if (sub_ele != nullptr) {
|
||||
text = sub_ele->Attribute("restart");
|
||||
if (text) {
|
||||
RestartWait = atoi(text);
|
||||
}
|
||||
text = sub_ele->Attribute("reterminate");
|
||||
if (text) {
|
||||
TerminateWait = atoi(text);
|
||||
}
|
||||
text = sub_ele->Attribute("initial");
|
||||
if (text) {
|
||||
InitialBootWait = atoi(text);
|
||||
}
|
||||
text = sub_ele->Attribute("interval");
|
||||
if (text) {
|
||||
ZoneBootInterval = atoi(text);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string EQEmuConfig::GetByName(const std::string &var_name) const
|
||||
{
|
||||
if (var_name == "ShortName") {
|
||||
@ -564,4 +325,3 @@ void EQEmuConfig::Dump() const
|
||||
std::cout << "DefaultStatus = " << (int)DefaultStatus << std::endl;
|
||||
// std::cout << "DynamicCount = " << DynamicCount << std::endl;
|
||||
}
|
||||
|
||||
|
||||
@ -18,8 +18,9 @@
|
||||
#ifndef __EQEmuConfig_H
|
||||
#define __EQEmuConfig_H
|
||||
|
||||
#include "xml_parser.h"
|
||||
#include "json/json.h"
|
||||
#include "linked_list.h"
|
||||
#include <fstream>
|
||||
|
||||
struct LoginConfig {
|
||||
std::string LoginHost;
|
||||
@ -29,7 +30,7 @@ struct LoginConfig {
|
||||
bool LoginLegacy;
|
||||
};
|
||||
|
||||
class EQEmuConfig : public XMLParser
|
||||
class EQEmuConfig
|
||||
{
|
||||
public:
|
||||
virtual std::string GetByName(const std::string &var_name) const;
|
||||
@ -115,88 +116,14 @@ class EQEmuConfig : public XMLParser
|
||||
protected:
|
||||
|
||||
static EQEmuConfig *_config;
|
||||
|
||||
Json::Value _root;
|
||||
static std::string ConfigFile;
|
||||
|
||||
#define ELEMENT(name) \
|
||||
void do_##name(TiXmlElement *ele);
|
||||
#include "eqemu_config_elements.h"
|
||||
|
||||
void parse_config();
|
||||
|
||||
EQEmuConfig()
|
||||
{
|
||||
// import the needed handler prototypes
|
||||
#define ELEMENT(name) \
|
||||
Handlers[#name]=(ElementHandler)&EQEmuConfig::do_##name;
|
||||
#include "eqemu_config_elements.h"
|
||||
// Set sane defaults
|
||||
// Login server
|
||||
LoginHost = "login.eqemulator.net";
|
||||
LoginPort = 5998;
|
||||
LoginLegacy = false;
|
||||
// World
|
||||
Locked = false;
|
||||
WorldTCPPort = 9000;
|
||||
TelnetTCPPort = 9001;
|
||||
TelnetEnabled = false;
|
||||
WorldHTTPEnabled = false;
|
||||
WorldHTTPPort = 9080;
|
||||
WorldHTTPMimeFile = "mime.types";
|
||||
SharedKey = ""; //blank disables authentication
|
||||
// Mail
|
||||
ChatHost = "eqchat.eqemulator.net";
|
||||
ChatPort = 7778;
|
||||
// Mail
|
||||
MailHost = "eqmail.eqemulator.net";
|
||||
MailPort = 7779;
|
||||
// Mysql
|
||||
DatabaseHost = "localhost";
|
||||
DatabasePort = 3306;
|
||||
DatabaseUsername = "eq";
|
||||
DatabasePassword = "eq";
|
||||
DatabaseDB = "eq";
|
||||
// QueryServ Database
|
||||
QSDatabaseHost = "localhost";
|
||||
QSDatabasePort = 3306;
|
||||
QSDatabaseUsername = "eq";
|
||||
QSDatabasePassword = "eq";
|
||||
QSDatabaseDB = "eq";
|
||||
// Files
|
||||
SpellsFile = "spells_us.txt";
|
||||
OpCodesFile = "opcodes.conf";
|
||||
PluginPlFile = "plugin.pl";
|
||||
// Dirs
|
||||
MapDir = "Maps/";
|
||||
QuestDir = "quests/";
|
||||
PluginDir = "plugins/";
|
||||
LuaModuleDir = "lua_modules/";
|
||||
PatchDir = "./";
|
||||
SharedMemDir = "shared/";
|
||||
LogDir = "logs/";
|
||||
{
|
||||
|
||||
// Launcher
|
||||
LogPrefix = "logs/zone-";
|
||||
LogSuffix = ".log";
|
||||
RestartWait = 10000; //milliseconds
|
||||
TerminateWait = 10000; //milliseconds
|
||||
InitialBootWait = 20000; //milliseconds
|
||||
ZoneBootInterval = 2000; //milliseconds
|
||||
#ifdef WIN32
|
||||
ZoneExe = "zone.exe";
|
||||
#else
|
||||
ZoneExe = "./zone";
|
||||
#endif
|
||||
// Zones
|
||||
ZonePortLow = 7000;
|
||||
ZonePortHigh = 7999;
|
||||
DefaultStatus = 0;
|
||||
// For where zones need to connect to.
|
||||
WorldIP = "127.0.0.1";
|
||||
TelnetIP = "127.0.0.1";
|
||||
// Dynamics to start
|
||||
//DynamicCount=5;
|
||||
MaxClients = -1;
|
||||
LoginCount = 0;
|
||||
}
|
||||
virtual ~EQEmuConfig() {}
|
||||
|
||||
@ -205,9 +132,7 @@ class EQEmuConfig : public XMLParser
|
||||
// Produce a const singleton
|
||||
static const EQEmuConfig *get()
|
||||
{
|
||||
if (_config == nullptr) {
|
||||
LoadConfig();
|
||||
}
|
||||
LoadConfig();
|
||||
return (_config);
|
||||
}
|
||||
|
||||
@ -221,10 +146,28 @@ class EQEmuConfig : public XMLParser
|
||||
static bool LoadConfig()
|
||||
{
|
||||
if (_config != nullptr) {
|
||||
delete _config;
|
||||
return true;
|
||||
}
|
||||
_config = new EQEmuConfig;
|
||||
return _config->ParseFile(EQEmuConfig::ConfigFile.c_str(), "server");
|
||||
|
||||
return parseFile();
|
||||
}
|
||||
|
||||
// Load config file and parse data
|
||||
static bool parseFile() {
|
||||
if (_config == nullptr) {
|
||||
return LoadConfig();
|
||||
}
|
||||
|
||||
std::ifstream fconfig(EQEmuConfig::ConfigFile, std::ifstream::binary);
|
||||
try {
|
||||
fconfig >> _config->_root;
|
||||
_config->parse_config();
|
||||
}
|
||||
catch (std::exception) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void Dump() const;
|
||||
|
||||
@ -3847,9 +3847,12 @@ namespace RoF
|
||||
OUT(y);
|
||||
OUT(x);
|
||||
OUT(z)
|
||||
OUT(zone_reason);
|
||||
OUT(zone_reason);
|
||||
OUT(success);
|
||||
|
||||
if (eq->success < 0)
|
||||
eq->success -= 1;
|
||||
|
||||
FINISH_ENCODE();
|
||||
}
|
||||
|
||||
|
||||
@ -3991,9 +3991,12 @@ namespace RoF2
|
||||
OUT(y);
|
||||
OUT(x);
|
||||
OUT(z)
|
||||
OUT(zone_reason);
|
||||
OUT(zone_reason);
|
||||
OUT(success);
|
||||
|
||||
if (eq->success < 0)
|
||||
eq->success -= 1;
|
||||
|
||||
FINISH_ENCODE();
|
||||
}
|
||||
|
||||
|
||||
@ -164,7 +164,7 @@ namespace RoF2
|
||||
ItemPacket11 = 111,
|
||||
ItemPacket12 = 112,
|
||||
ItemPacketRecovery = 113,
|
||||
ItemPacket14 = 115
|
||||
ItemPacket14 = 115 // Parcel? adds to merchant window too
|
||||
};
|
||||
|
||||
} /*item*/
|
||||
|
||||
@ -198,6 +198,7 @@ RULE_CATEGORY(Pets)
|
||||
RULE_REAL(Pets, AttackCommandRange, 150)
|
||||
RULE_BOOL(Pets, UnTargetableSwarmPet, false)
|
||||
RULE_REAL(Pets, PetPowerLevelCap, 10) // Max number of levels your pet can go up with pet power
|
||||
RULE_BOOL(Pets, CanTakeNoDrop, false) // Can everyone trade nodrop gear to pets
|
||||
RULE_CATEGORY_END()
|
||||
|
||||
RULE_CATEGORY(GM)
|
||||
@ -508,6 +509,7 @@ RULE_INT(Combat, NPCAssistCapTimer, 6000) // Time in milliseconds a NPC will tak
|
||||
RULE_BOOL(Combat, UseRevampHandToHand, false) // use h2h revamped dmg/delays I believe this was implemented during SoF
|
||||
RULE_BOOL(Combat, ClassicMasterWu, false) // classic master wu uses a random special, modern doesn't
|
||||
RULE_INT(Combat, LevelToStopDamageCaps, 0) // 1 will effectively disable them, 20 should give basically same results as old incorrect system
|
||||
RULE_BOOL(Combat, ClassicNPCBackstab, false) // true disables npc facestab - npcs get normal attack if not behind
|
||||
RULE_CATEGORY_END()
|
||||
|
||||
RULE_CATEGORY(NPC)
|
||||
@ -674,6 +676,12 @@ RULE_CATEGORY_END()
|
||||
RULE_CATEGORY(AA)
|
||||
RULE_INT(AA, ExpPerPoint, 23976503) //Amount of exp per AA. Is the same as the amount of exp to go from level 51 to level 52.
|
||||
RULE_BOOL(AA, Stacking, true) //Allow AA that belong to the same group to stack on SOF+ clients.
|
||||
RULE_BOOL(AA, NormalizedAAEnabled, false) // TSS+ change to AA that normalizes AA XP to a fixed # of white con kills independent of level.
|
||||
RULE_INT(AA, NormalizedAANumberOfWhiteConPerAA, 25) // The number of white con kills per AA point.
|
||||
RULE_BOOL(AA, ModernAAScalingEnabled, false) // Are we linearly scaling AA XP based on total # of earned AA?
|
||||
RULE_REAL(AA, ModernAAScalingStartPercent, 1000) // 1000% or 10x AA XP at the start of the scaling range
|
||||
RULE_INT(AA, ModernAAScalingAAMinimum, 0) // The minimum number of earned AA before AA XP scaling begins.
|
||||
RULE_INT(AA, ModernAAScalingAALimit, 4000) // The number of earned AA when AA XP scaling ends
|
||||
RULE_CATEGORY_END()
|
||||
|
||||
RULE_CATEGORY(Console)
|
||||
|
||||
@ -1719,6 +1719,8 @@ FMT_DEFINE_INT_FORMATTERS(unsigned long)
|
||||
FMT_DEFINE_INT_FORMATTERS(LongLong)
|
||||
FMT_DEFINE_INT_FORMATTERS(ULongLong)
|
||||
|
||||
#define CHAR_WIDTH 1
|
||||
|
||||
/**
|
||||
\rst
|
||||
Returns a string formatter that pads the formatted argument with the fill
|
||||
@ -1823,7 +1825,7 @@ class ArgFormatterBase : public ArgVisitor<Impl, void> {
|
||||
typedef typename BasicWriter<Char>::CharPtr CharPtr;
|
||||
Char fill = internal::CharTraits<Char>::cast(spec_.fill());
|
||||
CharPtr out = CharPtr();
|
||||
const unsigned CHAR_WIDTH = 1;
|
||||
|
||||
if (spec_.width_ > CHAR_WIDTH) {
|
||||
out = writer_.grow_buffer(spec_.width_);
|
||||
if (spec_.align_ == ALIGN_RIGHT) {
|
||||
|
||||
@ -46,7 +46,7 @@ public:
|
||||
_chat_config=new queryservconfig;
|
||||
_config=_chat_config;
|
||||
|
||||
return _config->ParseFile(EQEmuConfig::ConfigFile.c_str(),"server");
|
||||
return _config->parseFile();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
@ -46,7 +46,7 @@ public:
|
||||
_chat_config=new ucsconfig;
|
||||
_config=_chat_config;
|
||||
|
||||
return _config->ParseFile(EQEmuConfig::ConfigFile.c_str(),"server");
|
||||
return _config->parseFile();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
8
utils/defaults/eqemu_config.json
Executable file
8
utils/defaults/eqemu_config.json
Executable file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"server": {
|
||||
"world": {
|
||||
"shortname": "setme",
|
||||
"longname": "I Forgot To Edit My Config"
|
||||
}
|
||||
}
|
||||
}
|
||||
54
utils/defaults/eqemu_config.json.full
Executable file
54
utils/defaults/eqemu_config.json.full
Executable file
@ -0,0 +1,54 @@
|
||||
{
|
||||
"server": {
|
||||
"zones": {
|
||||
"defaultstatus": "20",
|
||||
"ports": {
|
||||
"low": "7000",
|
||||
"high": "7100"
|
||||
}
|
||||
},
|
||||
"database": {
|
||||
"password": "eq",
|
||||
"db": "eq",
|
||||
"host": "127.0.0.1",
|
||||
"port": "3306",
|
||||
"username": "eq"
|
||||
},
|
||||
"world": {
|
||||
"shortname": "setme",
|
||||
"longname": "I Forgot To Edit My Config",
|
||||
"loginserver": {
|
||||
"password": "",
|
||||
"host": "login.eqemulator.net",
|
||||
"port": "5998",
|
||||
"account": ""
|
||||
},
|
||||
"tcp": {
|
||||
"port": "9000",
|
||||
"telnet": "disable",
|
||||
"ip": "127.0.0.1"
|
||||
},
|
||||
"key": "some long random string",
|
||||
"http": {
|
||||
"mimefile": "mime.types",
|
||||
"port": "9080",
|
||||
"enabled": "false"
|
||||
}
|
||||
},
|
||||
"mailserver": {
|
||||
"host": "channels.eqemulator.net",
|
||||
"port": "7778"
|
||||
},
|
||||
"chatserver": {
|
||||
"host": "channels.eqemulator.net",
|
||||
"port": "7778"
|
||||
},
|
||||
"qsdatabase": {
|
||||
"host": "127.0.0.1",
|
||||
"port": "3306",
|
||||
"username": "eq",
|
||||
"password": "eq",
|
||||
"db": "eq"
|
||||
}
|
||||
}
|
||||
}
|
||||
355
utils/doc_scripts/perlparse/main.go
Normal file
355
utils/doc_scripts/perlparse/main.go
Normal file
@ -0,0 +1,355 @@
|
||||
//Parses perl scripts
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func main() {
|
||||
path := "../../../zone/embparser_api.cpp"
|
||||
err := readFile(path)
|
||||
if err != nil {
|
||||
log.Panicf("Failed to read file: %s", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
type API struct {
|
||||
Function string
|
||||
Arguments []*Argument
|
||||
}
|
||||
|
||||
type Argument struct {
|
||||
Name string
|
||||
Type string
|
||||
API *API
|
||||
}
|
||||
|
||||
func readFile(path string) (err error) {
|
||||
inFile, err := os.Open(path)
|
||||
if err != nil {
|
||||
err = errors.Wrap(err, "Failed to open file")
|
||||
}
|
||||
defer inFile.Close()
|
||||
scanner := bufio.NewScanner(inFile)
|
||||
scanner.Split(bufio.ScanLines)
|
||||
|
||||
arguments := map[string][]*Argument{}
|
||||
functions := []*API{}
|
||||
reg, err := regexp.Compile(`\]+|\[+|\?+|[...]+`)
|
||||
if err != nil {
|
||||
err = errors.Wrap(err, "Failed to compile regex")
|
||||
return
|
||||
}
|
||||
regType, err := regexp.Compile(`(unsigned long|long|int32|bool|uint[0-9]+|int|auto|float|unsigned int|char[ \*]).+([. a-zA-Z]+=)`)
|
||||
if err != nil {
|
||||
err = errors.Wrap(err, "Failed to compile type regex")
|
||||
return
|
||||
}
|
||||
|
||||
lastArguments := []*Argument{}
|
||||
lastAPI := &API{}
|
||||
lineNum := 0
|
||||
for scanner.Scan() {
|
||||
lineNum++
|
||||
key := ""
|
||||
line := scanner.Text()
|
||||
if len(line) < 1 {
|
||||
continue
|
||||
}
|
||||
if len(lastArguments) > 0 { //existing args to parse
|
||||
for i, argument := range lastArguments {
|
||||
key = fmt.Sprintf("ST(%d)", i)
|
||||
if strings.Contains(line, key) {
|
||||
//We found a definition argument line
|
||||
if argument.Type != "" {
|
||||
continue
|
||||
}
|
||||
|
||||
match := regType.FindStringSubmatch(line)
|
||||
if len(match) < 2 {
|
||||
continue
|
||||
}
|
||||
|
||||
//key = `int`
|
||||
//function = line[strings.Index(line, key)+len(key):]
|
||||
newType := ""
|
||||
|
||||
switch v := strings.TrimSpace(match[1]); v {
|
||||
case "int":
|
||||
newType = "int"
|
||||
case "int32":
|
||||
newType = "int"
|
||||
case "float":
|
||||
newType = "float"
|
||||
case "unsigned int":
|
||||
newType = "uint"
|
||||
case "uint32":
|
||||
newType = "uint"
|
||||
case "uint8":
|
||||
newType = "uint"
|
||||
case "uint":
|
||||
newType = "uint"
|
||||
case "bool":
|
||||
newType = "bool"
|
||||
case "uint16":
|
||||
newType = "uint"
|
||||
case "long":
|
||||
newType = "long"
|
||||
case "unsigned long":
|
||||
newType = "unsigned long"
|
||||
case "char":
|
||||
newType = "string"
|
||||
case "auto":
|
||||
//Auto is tricky
|
||||
if strings.Contains(line, "glm::vec4") {
|
||||
newType = "float"
|
||||
}
|
||||
|
||||
default:
|
||||
log.Printf(`Unknown type: "%s" on line %d`, v, lineNum)
|
||||
}
|
||||
//log.Println("Found arg type", newType, "on index", i, argument.Name)
|
||||
lastArguments[i].Type = newType
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function := ""
|
||||
|
||||
argLine := ""
|
||||
args := []string{}
|
||||
//Find line
|
||||
key = `Perl_croak(aTHX_ "Usage:`
|
||||
if strings.Contains(line, key) {
|
||||
function = line[strings.Index(line, key)+len(key):]
|
||||
}
|
||||
|
||||
for _, argument := range lastArguments {
|
||||
arguments[argument.Name] = append(arguments[argument.Name], argument)
|
||||
}
|
||||
|
||||
lastArguments = []*Argument{}
|
||||
|
||||
//Trim off the endings
|
||||
key = `");`
|
||||
if strings.Contains(function, key) {
|
||||
function = function[0:strings.Index(function, key)]
|
||||
}
|
||||
//Strip out the arguments
|
||||
key = `(`
|
||||
if strings.Contains(function, key) {
|
||||
argLine = function[strings.Index(function, key)+len(key):]
|
||||
function = function[0:strings.Index(function, key)]
|
||||
key = `)`
|
||||
if strings.Contains(argLine, key) {
|
||||
argLine = argLine[:strings.Index(argLine, key)]
|
||||
}
|
||||
key = `=`
|
||||
if strings.Contains(argLine, key) {
|
||||
argLine = argLine[:strings.Index(argLine, key)]
|
||||
}
|
||||
argLine = reg.ReplaceAllString(argLine, "")
|
||||
}
|
||||
key = `,`
|
||||
argLine = strings.TrimSpace(argLine)
|
||||
|
||||
if strings.Contains(argLine, key) {
|
||||
args = strings.Split(argLine, key)
|
||||
}
|
||||
|
||||
if len(function) < 1 {
|
||||
continue
|
||||
}
|
||||
|
||||
newArgs := []string{}
|
||||
for j, _ := range args {
|
||||
args[j] = strings.TrimSpace(args[j])
|
||||
if len(args[j]) == 0 {
|
||||
continue
|
||||
}
|
||||
newArgs = append(newArgs, args[j])
|
||||
}
|
||||
|
||||
lastAPI = &API{
|
||||
Function: function,
|
||||
}
|
||||
|
||||
for _, arg := range newArgs {
|
||||
argType, _ := knownTypes[arg]
|
||||
argument := &Argument{
|
||||
Name: arg,
|
||||
Type: argType,
|
||||
API: lastAPI,
|
||||
}
|
||||
lastArguments = append(lastArguments, argument)
|
||||
}
|
||||
lastAPI.Arguments = lastArguments
|
||||
|
||||
functions = append(functions, lastAPI)
|
||||
}
|
||||
|
||||
foundCount := 0
|
||||
failCount := 0
|
||||
for key, val := range arguments {
|
||||
isMissing := false
|
||||
line := ""
|
||||
line = fmt.Sprintf("%s used by %d functions:", key, len(val))
|
||||
for _, fnc := range val {
|
||||
line += fmt.Sprintf("%s(%s %s), ", fnc.API.Function, fnc.Type, key)
|
||||
if fnc.Type == "" {
|
||||
isMissing = true
|
||||
}
|
||||
}
|
||||
if isMissing {
|
||||
fmt.Println(line)
|
||||
failCount++
|
||||
} else {
|
||||
foundCount++
|
||||
}
|
||||
}
|
||||
log.Println(foundCount, "functions properly identified,", failCount, "have errors")
|
||||
|
||||
line := ""
|
||||
for _, api := range functions {
|
||||
line += fmt.Sprintf("void %s(", strings.TrimSpace(api.Function))
|
||||
for _, argument := range api.Arguments {
|
||||
line += fmt.Sprintf("%s %s, ", argument.Type, argument.Name)
|
||||
}
|
||||
if len(api.Arguments) > 0 {
|
||||
line = line[0 : len(line)-2]
|
||||
}
|
||||
line += ")\n"
|
||||
}
|
||||
fmt.Println(line)
|
||||
return
|
||||
}
|
||||
|
||||
var knownTypes = map[string]string{
|
||||
"activity_id": "uint",
|
||||
"alt_mode": "bool",
|
||||
"anim_num": "int",
|
||||
"best_z": "float",
|
||||
"buttons": "int",
|
||||
"channel_id": "int",
|
||||
"char_id": "int",
|
||||
"charges": "int",
|
||||
"class_id": "int",
|
||||
"client_name": "string",
|
||||
"color": "int",
|
||||
"color_id": "int",
|
||||
"condition_id": "int",
|
||||
"copper": "int",
|
||||
"count": "int",
|
||||
"debug_level": "int",
|
||||
"decay_time": "int",
|
||||
"dest_heading": "float",
|
||||
"dest_x": "float",
|
||||
"dest_y": "float",
|
||||
"dest_z": "float",
|
||||
"distance": "int",
|
||||
"door_id": "int",
|
||||
"doorid": "uint",
|
||||
"duration": "int",
|
||||
"effect_id": "int",
|
||||
"elite_material_id": "int",
|
||||
"enforce_level_requirement": "bool",
|
||||
"explore_id": "uint",
|
||||
"faction_value": "int",
|
||||
"fade_in": "int",
|
||||
"fade_out": "int",
|
||||
"fadeout": "uint",
|
||||
"firstname": "string",
|
||||
"from": "string",
|
||||
"gender_id": "int",
|
||||
"gold": "int",
|
||||
"grid_id": "int",
|
||||
"guild_rank_id": "int",
|
||||
"heading": "float",
|
||||
"hero_forge_model_id": "int",
|
||||
"ignore_quest_update": "bool",
|
||||
"instance_id": "int",
|
||||
"int_unused": "int",
|
||||
"int_value": "int",
|
||||
"is_enabled": "bool",
|
||||
"is_strict": "bool",
|
||||
"item_id": "int",
|
||||
"key": "string",
|
||||
"language_id": "int",
|
||||
"lastname": "string",
|
||||
"leader_name": "string",
|
||||
"level": "int",
|
||||
"link_name": "string",
|
||||
"macro_id": "int",
|
||||
"max_level": "int",
|
||||
"max_x": "float",
|
||||
"max_y": "float",
|
||||
"max_z": "float",
|
||||
"message": "string",
|
||||
"milliseconds": "int",
|
||||
"min_level": "int",
|
||||
"min_x": "float",
|
||||
"min_y": "float",
|
||||
"min_z": "float",
|
||||
"name": "string",
|
||||
"new_hour": "int",
|
||||
"new_min": "int",
|
||||
"node1": "int",
|
||||
"node2": "int",
|
||||
"npc_id": "int",
|
||||
"npc_type_id": "int",
|
||||
"object_type": "int",
|
||||
"options": "int",
|
||||
"platinum": "int",
|
||||
"popup_id": "int",
|
||||
"priority": "int",
|
||||
"quantity": "int",
|
||||
"race_id": "int",
|
||||
"remove_item": "bool",
|
||||
"requested_id": "int",
|
||||
"reset_base": "bool",
|
||||
"saveguard": "bool",
|
||||
"seconds": "int",
|
||||
"send_to_world": "bool",
|
||||
"signal_id": "int",
|
||||
"silent": "bool",
|
||||
"silver": "int",
|
||||
"size": "int",
|
||||
"stat_id": "int",
|
||||
"str_value": "string",
|
||||
"subject": "string",
|
||||
"target_enum": "string",
|
||||
"target_id": "int",
|
||||
"task": "int",
|
||||
"task_id": "uint",
|
||||
"task_id1": "int",
|
||||
"task_id10": "int",
|
||||
"task_id2": "int",
|
||||
"task_set": "int",
|
||||
"taskid": "int",
|
||||
"taskid1": "int",
|
||||
"taskid2": "int",
|
||||
"taskid3": "int",
|
||||
"taskid4": "int",
|
||||
"teleport": "int",
|
||||
"temp": "int",
|
||||
"texture_id": "int",
|
||||
"theme_id": "int",
|
||||
"update_world": "int",
|
||||
"updated_time_till_repop": "uint",
|
||||
"version": "int",
|
||||
"wait_ms": "int",
|
||||
"window_title": "string",
|
||||
"x": "float",
|
||||
"y": "float",
|
||||
"z": "float",
|
||||
"zone_id": "int",
|
||||
"zone_short": "string",
|
||||
`task_id%i`: "int",
|
||||
}
|
||||
252
utils/scripts/database_dumper.pl
Normal file
252
utils/scripts/database_dumper.pl
Normal file
@ -0,0 +1,252 @@
|
||||
#!/usr/bin/perl
|
||||
|
||||
############################################################
|
||||
#::: Script: db_dumper.pl
|
||||
#::: Purpose: Utility to easily manage database backups and compress.
|
||||
#::: Export Individual DB Tables...
|
||||
#::: Export specific databases...
|
||||
#::: Built for both Windows and Linux
|
||||
#::: Windows uses WinRar or 7-Zip for compression
|
||||
#::: Linux uses tar for compression
|
||||
#::: Author: Akkadius
|
||||
############################################################
|
||||
|
||||
$localdrive = "C:"; #::: Where Windows and all Install Programs are...
|
||||
$linesep = "---------------------------------------";
|
||||
|
||||
use POSIX qw(strftime);
|
||||
my $date = strftime "%m_%d_%Y", localtime;
|
||||
print "\nTodays Date: " . $date . "\n";
|
||||
|
||||
use Config;
|
||||
print "Operating System is: $Config{osname}\n";
|
||||
if($Config{osname}=~/linux/i){ $OS = "Linux"; }
|
||||
if($Config{osname}=~/Win|MS/i){ $OS = "Windows"; }
|
||||
|
||||
if(!$ARGV[0]){
|
||||
print "\nERROR! Need arguments\n";
|
||||
print "#::: Help :::#\n";
|
||||
print "######################################################\n";
|
||||
print "Arguments\n";
|
||||
print " loc=\"C:\\File Location\" - File path location to backup...\n";
|
||||
print " database=\"dbname\" - Manually specify databasename, default is database in eqemu_config.xml\n";
|
||||
print " tables=\"table1,table2,table3\" - Manually specify tables, default is to dump all tables from database\n";
|
||||
print " compress - Compress Database with 7-ZIP, will fallback to WinRAR depending on what is installed (Must be installed to default program dir)...\n";
|
||||
print " nolock - Does not lock tables, meant for backuping while the server is running..\n";
|
||||
print " backup_name=\"name\" - Sets database backup prefix name\n";
|
||||
print ' Example: perl DB_Dumper.pl Loc="E:\Backups"' . "\n";
|
||||
print "######################################################\n";
|
||||
exit;
|
||||
}
|
||||
|
||||
sub read_eqemu_config_json {
|
||||
use JSON;
|
||||
my $json = new JSON();
|
||||
|
||||
my $content;
|
||||
open(my $fh, '<', "eqemu_config.json") or die "Unable to open config: eqemu_config.json - This must be in your EQEmu Server Folder\n"; {
|
||||
local $/;
|
||||
$content = <$fh>;
|
||||
}
|
||||
close($fh);
|
||||
|
||||
$config = $json->decode($content);
|
||||
|
||||
$db = $config->{"server"}{"database"}{"db"};
|
||||
$host = $config->{"server"}{"database"}{"host"};
|
||||
$user = $config->{"server"}{"database"}{"username"};
|
||||
$pass = $config->{"server"}{"database"}{"password"};
|
||||
$long_name = $config->{"server"}{"world"}{"longname"};
|
||||
|
||||
}
|
||||
|
||||
read_eqemu_config_json();
|
||||
|
||||
$Debug = 0;
|
||||
print "[db_dumper.pl] Arguments\n" if $Debug;
|
||||
$n = 0;
|
||||
while($ARGV[$n]){
|
||||
print $n . ': ' . $ARGV[$n] . "\n" if $Debug;
|
||||
if($ARGV[$n]=~/nolock/i){
|
||||
$no_lock = 1;
|
||||
}
|
||||
if($ARGV[$n]=~/compress/i){
|
||||
print "[db_dumper.pl] Compression SET\n";
|
||||
$Compress = 1;
|
||||
}
|
||||
if($ARGV[$n]=~/database=/i){
|
||||
@DB_NAME = split('=', $ARGV[$n]);
|
||||
print "[db_dumper.pl] Database is " . $DB_NAME[1] . "\n";
|
||||
$db = $DB_NAME[1];
|
||||
}
|
||||
if($ARGV[$n]=~/backup_name=/i){
|
||||
@data = split('=', $ARGV[$n]);
|
||||
print "[db_dumper.pl] Backup Name is " . $data[1] . "\n";
|
||||
$backup_name = $data[1];
|
||||
}
|
||||
if($ARGV[$n]=~/loc=/i){
|
||||
@backup_location = split('=', $ARGV[$n]);
|
||||
print "[db_dumper.pl] Backup Directory: " . $backup_location[1] . "\n";
|
||||
}
|
||||
if($ARGV[$n]=~/tables=/i){
|
||||
@Tables = split('=', $ARGV[$n]); @TList = split(',', $Tables[1]);
|
||||
foreach my $tables (@TList){
|
||||
$t_tables .= $tables . " ";
|
||||
$t_tables_l .= $tables . "_";
|
||||
$t_tables_p .= $tables . "\n";
|
||||
}
|
||||
print "[db_dumper.pl] Backing up tables: \n############################\n" . $t_tables_p . "############################\n";
|
||||
}
|
||||
$n++;
|
||||
}
|
||||
|
||||
#::: Check for Backup Directory existence, if doesn't exist then create...
|
||||
if (-d $backup_location[1]) {
|
||||
print "[db_dumper.pl] Directory currently exists... Adding files to it...\n";
|
||||
}
|
||||
elsif($backup_location[1] ne ""){
|
||||
print "[db_dumper.pl] Directory does NOT exist! Creating...\n";
|
||||
mkdir($backup_location[1]) or die 'Failed to create folder, maybe created the folder manually at "' . $backup_location[1]. '" ?';
|
||||
}
|
||||
else{
|
||||
print "[db_dumper.pl] No save location specified... Saving to folder script is running in...\n";
|
||||
}
|
||||
if($backup_location[1] ne ""){
|
||||
if($OS eq "Windows"){ $file_app = "\\"; }
|
||||
if($OS eq "Linux"){ $file_app = "/"; }
|
||||
}
|
||||
else {
|
||||
$file_app = "";
|
||||
}
|
||||
|
||||
if($t_tables ne ""){
|
||||
$tables_f_l = substr($t_tables_l, 0, 20) . '-';
|
||||
if($backup_name){
|
||||
$target_file = $backup_name . '_' . $date . '';
|
||||
}
|
||||
else {
|
||||
$target_file = '' . $tables_f_l . '_' . $date . '';
|
||||
}
|
||||
|
||||
print "[db_dumper.pl] Performing table based backup...\n";
|
||||
#::: Backup Database...
|
||||
print "[db_dumper.pl] Backing up Database " . $db . "... \n";
|
||||
if($no_lock == 1){
|
||||
$added_parameters .= " --skip-lock-tables ";
|
||||
}
|
||||
$cmd = 'mysqldump -u' . $user . ' --host ' . $host . ' ' . $added_parameters . ' --max_allowed_packet=512M --password="' . $pass . '" ' . $db . ' ' . $t_tables . ' > "' . $backup_location[1] . '' . $file_app . '' . $target_file . '.sql"';
|
||||
printcmd($cmd);
|
||||
system($cmd);
|
||||
}
|
||||
else{ #::: Entire DB Backup
|
||||
|
||||
if($backup_name){
|
||||
$target_file = $backup_name . '_' . $db . '_' . $date . '';
|
||||
}
|
||||
else {
|
||||
$target_file = '' . $db . '_' . $date . '';
|
||||
}
|
||||
|
||||
#::: Backup Database...
|
||||
print "[db_dumper.pl] Backing up Database " . $db . "... \n";
|
||||
if($no_lock == 1){
|
||||
$added_parameters .= " --skip-lock-tables ";
|
||||
}
|
||||
$cmd = 'mysqldump -u' . $user . ' --host ' . $host . ' ' . $added_parameters . ' --max_allowed_packet=512M --password="' . $pass . '" ' . $db . ' > "' . $backup_location[1] . '' . $file_app . '' . $target_file . '.sql"';
|
||||
printcmd($cmd);
|
||||
system($cmd);
|
||||
}
|
||||
|
||||
#::: Get File Size
|
||||
$fileloc = '' . $backup_location[1] . '' . $file_app . '' . $target_file . '.sql';
|
||||
$filesize = -s $fileloc;
|
||||
if($filesize < 1000){ print "[db_dumper.pl] " . 'Error occurred... exiting...' . "\n"; exit; }
|
||||
print "[db_dumper.pl] Backup DONE... DB Backup File Size '" . $filesize . "' (" . get_filesize_str($fileloc) . ")\n";
|
||||
|
||||
#::: WinRar Get, check compression flag
|
||||
if($Compress == 1){
|
||||
if($OS eq "Windows"){
|
||||
if(-d $localdrive . "\\Program Files\\7-Zip"){
|
||||
print "[db_dumper.pl] ::: You have 7-Zip installed as 64 Bit...\n";
|
||||
$S_ZIP = $localdrive . "\\Program Files\\7-Zip";
|
||||
}
|
||||
elsif(-d $localdrive . "\\Program Files (x86)\\7-Zip"){
|
||||
print "[db_dumper.pl] ::: You have 7-Zip installed as 32 Bit...\n";
|
||||
$S_ZIP = $localdrive . "\\Program Files (x86)\\7-Zip";
|
||||
}
|
||||
elsif(-d $localdrive . "\\Program Files (x86)\\WinRAR"){
|
||||
print "[db_dumper.pl] ::: You have WinRAR installed as 32 Bit...\n";
|
||||
$WinRar = $localdrive . "\\Program Files (x86)\\WinRAR";
|
||||
}
|
||||
elsif(-d $localdrive . "\\Program Files\\WinRAR"){
|
||||
print "[db_dumper.pl] ::: You have WinRAR installed as 64 Bit...\n";
|
||||
$WinRar = $localdrive . "\\Program Files\\WinRAR";
|
||||
}
|
||||
else{
|
||||
print "[db_dumper.pl] No WinRAR installed... Will not compress...\n";
|
||||
}
|
||||
if($S_ZIP ne ""){
|
||||
print "[db_dumper.pl] Compressing Database with 7-ZIP... \n";
|
||||
$cmd = '"' . $S_ZIP . '\\7z" a -t7z -m0=lzma -mx=9 "' . $backup_location[1] . '' . $file_app . '' . $target_file . '.7z" "' . $backup_location[1] . '' . $file_app . '' . $target_file . '.sql" ';
|
||||
printcmd($cmd);
|
||||
system($cmd);
|
||||
print "[db_dumper.pl] \nDeleting RAW .sql Dump... \n";
|
||||
$cmd = 'del "' . $backup_location[1] . '' . $file_app . '' . $target_file . '.sql" ';
|
||||
printcmd($cmd);
|
||||
system($cmd);
|
||||
$final_file = $target_file . ".7z";
|
||||
}
|
||||
elsif($WinRar ne ""){
|
||||
print "[db_dumper.pl] Compressing Database with WinRAR... \n";
|
||||
$cmd = '"' . $WinRar . '\\rar" a "' . $backup_location[1] . '' . $file_app . '' . $target_file . '.rar" "' . $backup_location[1] . '' . $file_app . '' . $target_file . '.sql" ';
|
||||
printcmd($cmd);
|
||||
system($cmd);
|
||||
print "[db_dumper.pl] \nDeleting RAW .sql Dump... \n";
|
||||
$cmd = 'del "' . $backup_location[1] . '' . $file_app . '' . $target_file . '.sql" ';
|
||||
printcmd($cmd);
|
||||
system($cmd);
|
||||
$final_file = $target_file . ".rar";
|
||||
}
|
||||
}
|
||||
if($OS eq "Linux"){
|
||||
print "[db_dumper.pl] Compressing Database with Tarball... \n";
|
||||
$cmd = 'tar -zcvf "' . $backup_location[1] . '' . $file_app . '' . $target_file . '.tar.gz" "' . $backup_location[1] . '' . $file_app . '' . $target_file . '.sql" ';
|
||||
printcmd($cmd);
|
||||
system($cmd);
|
||||
print "[db_dumper.pl] \nDeleting RAW .sql Dump... \n";
|
||||
$cmd = 'rm "' . $backup_location[1] . '' . $file_app . '' . $target_file . '.sql" ';
|
||||
printcmd($cmd);
|
||||
system($cmd);
|
||||
$final_file = $target_file . ".tar.gz";
|
||||
}
|
||||
}
|
||||
else {
|
||||
$final_file = $target_file . ".sql";
|
||||
}
|
||||
|
||||
#::: Get Final File Location for display
|
||||
if($backup_location[1] ne ""){ $final_loc = $backup_location[1] . '' . $file_app . ""; }
|
||||
else{
|
||||
if($OS eq "Windows"){
|
||||
$final_loc = `echo %cd%`;
|
||||
}
|
||||
elsif($OS eq "Linux"){
|
||||
$final_loc = `pwd`;
|
||||
}
|
||||
}
|
||||
|
||||
print "[db_dumper.pl] Final file located: " . $final_loc . "" . $final_file . "\n";
|
||||
|
||||
sub printcmd{
|
||||
print "[db_dumper.pl] Command [" . $_[0] . "]\n";
|
||||
}
|
||||
|
||||
sub get_filesize_str{
|
||||
my $file = shift();
|
||||
my $size = (stat($file))[7] || die "stat($file): $!\n";
|
||||
if ($size > 1099511627776) { return sprintf("%.2f TiB", $size / 1099511627776); }
|
||||
elsif ($size > 1073741824) { return sprintf("%.2f GiB", $size / 1073741824); }
|
||||
elsif ($size > 1048576) { return sprintf("%.2f MiB", $size / 1048576); }
|
||||
elsif ($size > 1024) { return sprintf("%.2f KiB", $size / 1024); }
|
||||
else { return "$size byte" . ($size == 1 ? "" : "s"); }
|
||||
}
|
||||
@ -48,10 +48,18 @@ if(-e "eqemu_server_skip_update.txt"){
|
||||
}
|
||||
|
||||
#::: Check for script self update
|
||||
check_xml_to_json_conversion() if $ARGV[0] eq "convert_xml";
|
||||
do_self_update_check_routine() if !$skip_self_update_check;
|
||||
get_windows_wget();
|
||||
get_perl_version();
|
||||
read_eqemu_config_xml();
|
||||
if(-e "eqemu_config.json") {
|
||||
read_eqemu_config_json();
|
||||
}
|
||||
else {
|
||||
#::: This will need to stay for servers who simply haven't updated yet
|
||||
# This script can still update without the server bins being updated
|
||||
read_eqemu_config_xml();
|
||||
}
|
||||
get_mysql_path();
|
||||
|
||||
#::: Remove old eqemu_update.pl
|
||||
@ -265,7 +273,7 @@ sub new_server {
|
||||
analytics_insertion("new_server::install", $database_name);
|
||||
|
||||
if($OS eq "Linux"){
|
||||
build_linux_source();
|
||||
build_linux_source("login");
|
||||
}
|
||||
|
||||
do_installer_routines();
|
||||
@ -281,6 +289,10 @@ sub new_server {
|
||||
|
||||
show_install_summary_info();
|
||||
|
||||
if($OS eq "Linux") {
|
||||
unlink('/home/eqemu/install_variables.txt');
|
||||
}
|
||||
|
||||
rmtree('updates_staged');
|
||||
|
||||
return;
|
||||
@ -291,6 +303,61 @@ sub new_server {
|
||||
}
|
||||
}
|
||||
|
||||
sub check_xml_to_json_conversion {
|
||||
if(-e "eqemu_config.xml" && !-e "eqemu_config.json") {
|
||||
|
||||
if($OS eq "Windows"){
|
||||
get_remote_file("https://raw.githubusercontent.com/EQEmu/Server/master/utils/xmltojson/xmltojson-windows-x86.exe", "xmltojson.exe");
|
||||
print "Converting eqemu_config.xml to eqemu_config.json\n";
|
||||
print `xmltojson eqemu_config.xml`;
|
||||
}
|
||||
if($OS eq "Linux"){
|
||||
get_remote_file("https://raw.githubusercontent.com/EQEmu/Server/master/utils/xmltojson/xmltojson-linux-x86", "xmltojson");
|
||||
print "Converting eqemu_config.xml to eqemu_config.json\n";
|
||||
print `chmod 755 xmltojson`;
|
||||
print `./xmltojson eqemu_config.xml`;
|
||||
}
|
||||
|
||||
#::: Prettify and alpha order the config
|
||||
use JSON;
|
||||
my $json = new JSON();
|
||||
|
||||
my $content;
|
||||
open(my $fh, '<', "eqemu_config.json") or die "cannot open file $filename"; {
|
||||
local $/;
|
||||
$content = <$fh>;
|
||||
}
|
||||
close($fh);
|
||||
|
||||
$result = $json->decode($content);
|
||||
$json->canonical(1);
|
||||
|
||||
print $json->pretty->indent_length(5)->utf8->encode($result),"\n";
|
||||
|
||||
open(my $fh, '>', 'eqemu_config.json');
|
||||
print $fh $json->pretty->indent_length(5)->utf8->encode($result);
|
||||
close $fh;
|
||||
|
||||
mkdir('backups');
|
||||
copy_file("eqemu_config.xml", "backups/eqemu_config.xml");
|
||||
unlink('eqemu_config.xml');
|
||||
unlink('db_dumper.pl');
|
||||
|
||||
print "[Server Maintenance] eqemu_config.xml is now DEPRECATED \n";
|
||||
print "[Server Maintenance] eqemu_config.json is now the new Server config format \n";
|
||||
print " A backup of this old config is located in the backups folder of your server directory\n";
|
||||
print " --- \n";
|
||||
print " You may have some plugins and/or applications that still require reference of this config file\n";
|
||||
print " Please update these plugins/applications to use the new configuration format if needed\n";
|
||||
print " --- \n";
|
||||
print " Thanks for your understanding\n";
|
||||
print " The EQEmulator Team\n\n";
|
||||
|
||||
exit;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
sub build_linux_source {
|
||||
|
||||
$build_options = $_[0];
|
||||
@ -330,10 +397,10 @@ sub build_linux_source {
|
||||
|
||||
print "Generating CMake build files...\n";
|
||||
if($os_flavor eq "fedora_core"){
|
||||
print `cmake $cmake_options -DEQEMU_BUILD_LUA=ON -DLUA_INCLUDE_DIR=/usr/include/lua-5.1/ -G "Unix Makefiles" ..`;
|
||||
print `cmake $cmake_options -DEQEMU_BUILD_LOGIN=ON -DEQEMU_BUILD_LUA=ON -DLUA_INCLUDE_DIR=/usr/include/lua-5.1/ -G "Unix Makefiles" ..`;
|
||||
}
|
||||
else {
|
||||
print `cmake $cmake_options -DEQEMU_BUILD_LUA=ON -G "Unix Makefiles" ..`;
|
||||
print `cmake $cmake_options -DEQEMU_BUILD_LOGIN=ON -DEQEMU_BUILD_LUA=ON -G "Unix Makefiles" ..`;
|
||||
}
|
||||
print "Building EQEmu Server code. This will take a while.";
|
||||
|
||||
@ -352,6 +419,7 @@ sub build_linux_source {
|
||||
print `ln -s -f $source_dir/Server/build/bin/ucs .`;
|
||||
print `ln -s -f $source_dir/Server/build/bin/world .`;
|
||||
print `ln -s -f $source_dir/Server/build/bin/zone .`;
|
||||
print `ln -s -f $source_dir/Server/build/bin/loginserver .`;
|
||||
}
|
||||
|
||||
sub do_installer_routines {
|
||||
@ -362,8 +430,8 @@ sub do_installer_routines {
|
||||
mkdir('updates_staged');
|
||||
mkdir('shared');
|
||||
|
||||
do_install_config_xml();
|
||||
read_eqemu_config_xml();
|
||||
do_install_config_json();
|
||||
read_eqemu_config_json();
|
||||
get_installation_variables();
|
||||
|
||||
$db_name = "peq";
|
||||
@ -579,7 +647,12 @@ sub do_self_update_check_routine {
|
||||
sub get_installation_variables{
|
||||
#::: Fetch installation variables before building the config
|
||||
if($OS eq "Linux"){
|
||||
open (INSTALL_VARS, "../install_variables.txt");
|
||||
if(-e "../install_variables.txt") {
|
||||
open (INSTALL_VARS, "../install_variables.txt");
|
||||
}
|
||||
elsif(-e "install_variables.txt") {
|
||||
open (INSTALL_VARS, "./install_variables.txt");
|
||||
}
|
||||
}
|
||||
if($OS eq "Windows"){
|
||||
open (INSTALL_VARS, "install_variables.txt");
|
||||
@ -593,73 +666,51 @@ sub get_installation_variables{
|
||||
close (INSTALL_VARS);
|
||||
}
|
||||
|
||||
sub do_install_config_xml {
|
||||
sub do_install_config_json {
|
||||
get_installation_variables();
|
||||
|
||||
#::: Fetch XML template
|
||||
get_remote_file($install_repository_request_url . "eqemu_config.xml", "eqemu_config_template.xml");
|
||||
#::: Fetch json template
|
||||
get_remote_file($install_repository_request_url . "eqemu_config.json", "eqemu_config_template.json");
|
||||
|
||||
#::: Open new config file
|
||||
open (NEW_CONFIG, '>', 'eqemu_config.xml');
|
||||
use JSON;
|
||||
my $json = new JSON();
|
||||
|
||||
my $content;
|
||||
open(my $fh, '<', "eqemu_config_template.json") or die "cannot open file $filename"; {
|
||||
local $/;
|
||||
$content = <$fh>;
|
||||
}
|
||||
close($fh);
|
||||
|
||||
$config = $json->decode($content);
|
||||
|
||||
$in_database_tag = 0;
|
||||
$long_name = "Akkas " . $OS . " PEQ Installer (" . generate_random_password(5) . ')';
|
||||
$config->{"server"}{"world"}{"longname"} = $long_name;
|
||||
$config->{"server"}{"world"}{"key"} = generate_random_password(30);
|
||||
|
||||
#::: Iterate through template and replace variables...
|
||||
open (FILE_TEMPLATE, "eqemu_config_template.xml");
|
||||
while (<FILE_TEMPLATE>){
|
||||
chomp;
|
||||
$o = $_;
|
||||
|
||||
#::: Find replace variables
|
||||
|
||||
if($o=~/\<\!--/i){
|
||||
next;
|
||||
}
|
||||
|
||||
if($o=~/database/i && $o=~/\<\//i){
|
||||
$in_database_tag = 0;
|
||||
}
|
||||
if($o=~/database/i){
|
||||
$in_database_tag = 1;
|
||||
}
|
||||
|
||||
if($o=~/key/i){
|
||||
my($replace_key) = $o =~ />(\w+)</;
|
||||
$new_key = generate_random_password(30);
|
||||
$o =~ s/$replace_key/$new_key/g;
|
||||
}
|
||||
if($o=~/\<longname\>/i){
|
||||
my($replace_name) = $o =~ /<longname>(.*)<\/longname>/;
|
||||
$append = '(' . generate_random_password(5) . ')';
|
||||
$o =~ s/$replace_name/Akkas $OS PEQ Installer $append/g;
|
||||
}
|
||||
if($o=~/\<username\>/i && $in_database_tag){
|
||||
my($replace_username) = $o =~ />(\w+)</;
|
||||
$o =~ s/$replace_username/$installation_variables{"mysql_eqemu_user"}/g;
|
||||
}
|
||||
if($o=~/\<password\>/i && $in_database_tag){
|
||||
my($replace_password) = $o =~ />(\w+)</;
|
||||
$o =~ s/$replace_password/$installation_variables{"mysql_eqemu_password"}/g;
|
||||
}
|
||||
if($o=~/\<db\>/i){
|
||||
my($replace_db_name) = $o =~ />(\w+)</;
|
||||
|
||||
#::: There is really no reason why this shouldn't be set
|
||||
if($installation_variables{"mysql_eqemu_db_name"}){
|
||||
$db_name = $installation_variables{"mysql_eqemu_db_name"};
|
||||
}
|
||||
else {
|
||||
$db_name = "peq";
|
||||
}
|
||||
|
||||
$o =~ s/$replace_db_name/$db_name/g;
|
||||
}
|
||||
print NEW_CONFIG $o . "\n";
|
||||
if($installation_variables{"mysql_eqemu_db_name"}){
|
||||
$db_name = $installation_variables{"mysql_eqemu_db_name"};
|
||||
}
|
||||
else {
|
||||
$db_name = "peq";
|
||||
}
|
||||
|
||||
close(FILE_TEMPLATE);
|
||||
close(NEW_CONFIG);
|
||||
unlink("eqemu_config_template.xml");
|
||||
$config->{"server"}{"database"}{"username"} = $installation_variables{"mysql_eqemu_user"};
|
||||
$config->{"server"}{"database"}{"password"} = $installation_variables{"mysql_eqemu_password"};
|
||||
$config->{"server"}{"database"}{"db"} = $db_name;
|
||||
|
||||
$config->{"server"}{"qsdatabase"}{"username"} = $installation_variables{"mysql_eqemu_user"};
|
||||
$config->{"server"}{"qsdatabase"}{"password"} = $installation_variables{"mysql_eqemu_password"};
|
||||
$config->{"server"}{"qsdatabase"}{"db"} = $db_name;
|
||||
|
||||
$json->canonical(1);
|
||||
$json->indent_length(5);
|
||||
|
||||
open(my $fh, '>', 'eqemu_config.json');
|
||||
print $fh $json->pretty->indent_length(5)->utf8->encode($config);
|
||||
close $fh;
|
||||
|
||||
unlink("eqemu_config_template.json");
|
||||
}
|
||||
|
||||
sub fetch_utility_scripts {
|
||||
@ -766,6 +817,7 @@ sub show_menu_prompt {
|
||||
elsif($input eq "setup_loginserver"){ do_windows_login_server_setup(); $dc = 1; }
|
||||
elsif($input eq "new_server"){ new_server(); $dc = 1; }
|
||||
elsif($input eq "setup_bots"){ setup_bots(); $dc = 1; }
|
||||
elsif($input eq "linux_login_server_setup"){ do_linux_login_server_setup(); $dc = 1; }
|
||||
elsif($input eq "exit"){
|
||||
exit;
|
||||
}
|
||||
@ -858,13 +910,13 @@ sub check_for_database_dump_script{
|
||||
return;
|
||||
}
|
||||
|
||||
#::: Check for script changes :: db_dumper.pl
|
||||
get_remote_file($eqemu_repository_request_url . "utils/scripts/db_dumper.pl", "updates_staged/db_dumper.pl", 0, 1, 1);
|
||||
#::: Check for script changes :: database_dumper.pl
|
||||
get_remote_file($eqemu_repository_request_url . "utils/scripts/database_dumper.pl", "updates_staged/database_dumper.pl", 0, 1, 1);
|
||||
|
||||
if(-e "updates_staged/db_dumper.pl") {
|
||||
if(-e "updates_staged/database_dumper.pl") {
|
||||
|
||||
my $remote_script_size = -s "updates_staged/db_dumper.pl";
|
||||
my $local_script_size = -s "db_dumper.pl";
|
||||
my $remote_script_size = -s "updates_staged/database_dumper.pl";
|
||||
my $local_script_size = -s "database_dumper.pl";
|
||||
|
||||
if($remote_script_size != $local_script_size){
|
||||
print "[Update] Script has been updated, updating...\n";
|
||||
@ -876,14 +928,14 @@ sub check_for_database_dump_script{
|
||||
$start_dir
|
||||
);
|
||||
for my $file (@files) {
|
||||
if($file=~/db_dumper/i){
|
||||
if($file=~/database_dumper/i){
|
||||
$destination_file = $file;
|
||||
$destination_file =~s/updates_staged\///g;
|
||||
print "[Install] Installing :: " . $destination_file . "\n";
|
||||
unlink($destination_file);
|
||||
copy_file($file, $destination_file);
|
||||
if($OS eq "Linux"){
|
||||
system("chmod 755 db_dumper.pl");
|
||||
system("chmod 755 database_dumper.pl");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -893,7 +945,7 @@ sub check_for_database_dump_script{
|
||||
print "[Update] No script update necessary...\n";
|
||||
}
|
||||
|
||||
unlink("updates_staged/db_dumper.pl");
|
||||
unlink("updates_staged/database_dumper.pl");
|
||||
}
|
||||
|
||||
return;
|
||||
@ -903,7 +955,7 @@ sub check_for_database_dump_script{
|
||||
sub database_dump {
|
||||
check_for_database_dump_script();
|
||||
print "[Database] Performing database backup....\n";
|
||||
print `perl db_dumper.pl database="$db" loc="backups"`;
|
||||
print `perl database_dumper.pl database="$db" loc="backups"`;
|
||||
}
|
||||
|
||||
sub database_dump_player_tables {
|
||||
@ -921,7 +973,7 @@ sub database_dump_player_tables {
|
||||
}
|
||||
$tables = substr($tables, 0, -1);
|
||||
|
||||
print `perl db_dumper.pl database="$db" loc="backups" tables="$tables" backup_name="player_tables_export" nolock`;
|
||||
print `perl database_dumper.pl database="$db" loc="backups" tables="$tables" backup_name="player_tables_export" nolock`;
|
||||
|
||||
print "[Database] Press any key to continue...\n";
|
||||
|
||||
@ -932,7 +984,7 @@ sub database_dump_player_tables {
|
||||
sub database_dump_compress {
|
||||
check_for_database_dump_script();
|
||||
print "[Database] Performing database backup....\n";
|
||||
print `perl db_dumper.pl database="$db" loc="backups" compress`;
|
||||
print `perl database_dumper.pl database="$db" loc="backups" compress`;
|
||||
}
|
||||
|
||||
sub script_exit{
|
||||
@ -1008,7 +1060,7 @@ sub get_remote_file{
|
||||
}
|
||||
|
||||
#::: wget -O db_update/db_update_manifest.txt https://raw.githubusercontent.com/EQEmu/Server/master/utils/sql/db_update_manifest.txt
|
||||
$wget = `wget -N --no-check-certificate --quiet -O $destination_file $request_url`;
|
||||
$wget = `wget -N --cache=no --no-check-certificate --quiet -O $destination_file $request_url`;
|
||||
print "[Download] Saved: (" . $destination_file . ") from " . $request_url . "\n" if !$silent_download;
|
||||
if($wget=~/unable to resolve/i){
|
||||
print "Error, no connection or failed request...\n\n";
|
||||
@ -1072,6 +1124,26 @@ sub read_eqemu_config_xml {
|
||||
close(CONFIG);
|
||||
}
|
||||
|
||||
sub read_eqemu_config_json {
|
||||
use JSON;
|
||||
my $json = new JSON();
|
||||
|
||||
my $content;
|
||||
open(my $fh, '<', "eqemu_config.json") or die "cannot open file $filename"; {
|
||||
local $/;
|
||||
$content = <$fh>;
|
||||
}
|
||||
close($fh);
|
||||
|
||||
$config = $json->decode($content);
|
||||
|
||||
$db = $config->{"server"}{"database"}{"db"};
|
||||
$host = $config->{"server"}{"database"}{"host"};
|
||||
$user = $config->{"server"}{"database"}{"username"};
|
||||
$pass = $config->{"server"}{"database"}{"password"};
|
||||
|
||||
}
|
||||
|
||||
#::: Fetch Latest PEQ AA's
|
||||
sub aa_fetch{
|
||||
if(!$db){
|
||||
@ -1277,6 +1349,8 @@ sub do_windows_login_server_setup {
|
||||
|
||||
sub do_linux_login_server_setup {
|
||||
|
||||
build_linux_source();
|
||||
|
||||
for my $file (@files) {
|
||||
$destination_file = $file;
|
||||
$destination_file =~s/updates_staged\/login_server\///g;
|
||||
@ -1297,6 +1371,8 @@ sub do_linux_login_server_setup {
|
||||
get_remote_file($install_repository_request_url . "linux/login.ini", "login_template.ini");
|
||||
get_remote_file($install_repository_request_url . "linux/login_opcodes.conf", "login_opcodes.conf");
|
||||
get_remote_file($install_repository_request_url . "linux/login_opcodes_sod.conf", "login_opcodes_sod.conf");
|
||||
get_remote_file($install_repository_request_url . "linux/server_start_with_login.sh", "server_start_with_login.sh");
|
||||
system("chmod 755 *.sh");
|
||||
|
||||
get_installation_variables();
|
||||
my $db_name = $installation_variables{"mysql_eqemu_db_name"};
|
||||
|
||||
@ -119,6 +119,7 @@ if [[ "$OS" == "Debian" ]]; then
|
||||
apt-get $apt_options install zlibc
|
||||
apt-get $apt_options install libsodium-dev
|
||||
apt-get $apt_options install libsodium18
|
||||
apt-get $apt_options install libjson-perl
|
||||
|
||||
# If libsodium18 isn't installed (Debian), let's download both that and the dev package and install them.
|
||||
if dpkg-query -s "libsodium18" 1>/dev/null 2>&1; then
|
||||
@ -159,7 +160,7 @@ EOF
|
||||
# Install prereqs
|
||||
yum -y install https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm
|
||||
yum -y install deltarpm
|
||||
yum -y install open-vm-tools vim cmake boost-* zlib-devel mariadb-server mariadb-client mariadb-devel mariadb-libs mariadb-compat perl-* lua* dos2unix php-mysql proftpd
|
||||
yum -y install open-vm-tools vim cmake boost-* zlib-devel mariadb-server mariadb-client mariadb-devel mariadb-libs mariadb-compat perl-* lua* dos2unix php-mysql proftpd libuuid-devel
|
||||
yum -y groupinstall "Development Tools" "Basic Web Server" "Compatibility Libraries"
|
||||
|
||||
elif [[ "$OS" == "fedora_core" ]]; then
|
||||
|
||||
@ -17,7 +17,6 @@ character_bind
|
||||
character_corpses
|
||||
character_corpse_items
|
||||
character_languages
|
||||
character_lookup
|
||||
character_skills
|
||||
character_spells
|
||||
character_memmed_spells
|
||||
@ -89,4 +88,4 @@ spell_globals
|
||||
timers
|
||||
trader
|
||||
trader_audit
|
||||
zone_flags
|
||||
zone_flags
|
||||
|
||||
2
utils/xmltojson/.gitignore
vendored
Normal file
2
utils/xmltojson/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
eqemu_config.xml
|
||||
eqemu_config.json
|
||||
1
utils/xmltojson/README.md
Normal file
1
utils/xmltojson/README.md
Normal file
@ -0,0 +1 @@
|
||||
Converts the old eqemu_config.xml to eqemu_config.json
|
||||
21
utils/xmltojson/build.bat
Normal file
21
utils/xmltojson/build.bat
Normal file
@ -0,0 +1,21 @@
|
||||
@echo off
|
||||
setlocal
|
||||
set name="xmltojson"
|
||||
|
||||
echo Building Linux
|
||||
set GOOS=linux
|
||||
set GOARCH=amd64
|
||||
go build -o %name%-linux-x64 main.go
|
||||
set GOARCH=386
|
||||
go build -o %name%-linux-x86 main.go
|
||||
echo Building Windows
|
||||
set GOOS=windows
|
||||
set GOARCH=amd64
|
||||
go build -o %name%-windows-x64.exe main.go
|
||||
set GOARCH=386
|
||||
go build -o %name%-windows-x86.exe main.go
|
||||
echo Building OSX
|
||||
REM set GOOS=darwin
|
||||
REM set GOARCH=amd64
|
||||
REM go build -o %name%-osx-x64 main.go
|
||||
endlocal
|
||||
11
utils/xmltojson/build.sh
Normal file
11
utils/xmltojson/build.sh
Normal file
@ -0,0 +1,11 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
export NAME="xmltojson"
|
||||
echo Building Linux
|
||||
GOOS=linux GOARCH=amd64 go build -o $NAME-linux-x64 main.go
|
||||
GOOS=linux GOARCH=386 go build -o $NAME-linux-x86 main.go
|
||||
echo Building Windows
|
||||
GOOS=windows GOARCH=amd64 go build -o $NAME-windows-x64.exe main.go
|
||||
GOOS=windows GOARCH=386 go build -o $NAME-windows-x86.exe main.go
|
||||
#echo Building OSX
|
||||
#GOOS=darwin GOARCH=amd64 go build -o $NAME-osx-x64 main.go
|
||||
85
utils/xmltojson/main.go
Normal file
85
utils/xmltojson/main.go
Normal file
@ -0,0 +1,85 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
xj "github.com/basgys/goxml2json"
|
||||
)
|
||||
|
||||
func main() {
|
||||
var err error
|
||||
var data []byte
|
||||
var sData string
|
||||
buf := &bytes.Buffer{}
|
||||
var buf2 bytes.Buffer
|
||||
|
||||
if data, err = ioutil.ReadFile("eqemu_config.xml"); err != nil {
|
||||
fmt.Println("Failed to open eqemu_config.xml:", err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
//detect malformed xml in eqemuconfig
|
||||
sData = strings.Replace(string(data), "<?xml version=\"1.0\">", "<?xml version=\"1.0\"?>", 1)
|
||||
r := strings.NewReader(sData)
|
||||
dec := xj.NewDecoder(r)
|
||||
root := &xj.Node{}
|
||||
if err = dec.DecodeWithCustomPrefixes(root, "", ""); err != nil {
|
||||
fmt.Println("Failed to decode eqemu_config.xml:", err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if root.Children["server"] == nil || len(root.Children["server"]) < 1 {
|
||||
fmt.Println("Server element not found")
|
||||
os.Exit(1)
|
||||
}
|
||||
server := root.Children["server"][0]
|
||||
|
||||
//locked: "true" is only way to trigger locked
|
||||
if server.Children["world"] != nil && len(server.Children["world"]) > 0 {
|
||||
world := server.Children["world"][0]
|
||||
|
||||
if world.Children["locked"] != nil && len(world.Children["locked"]) > 0 {
|
||||
fmt.Println("Locked!")
|
||||
world.Children["locked"][0].Data = "true"
|
||||
}
|
||||
}
|
||||
|
||||
elements := []string{
|
||||
"chatserver",
|
||||
"directories",
|
||||
"files",
|
||||
"launcher",
|
||||
"mailserver",
|
||||
"webinterface",
|
||||
"world",
|
||||
"zones",
|
||||
}
|
||||
for _, ele := range elements {
|
||||
if server.Children[ele] != nil && len(server.Children[ele]) > 0 && len(server.Children[ele][0].Children) == 0 {
|
||||
delete(server.Children, ele)
|
||||
}
|
||||
}
|
||||
|
||||
enc := xj.NewEncoder(buf)
|
||||
err = enc.EncodeWithCustomPrefixes(root, "", "")
|
||||
if err != nil {
|
||||
fmt.Println("Failed to encode eqemu_config.xml:", err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
//prettyprint
|
||||
if err = json.Indent(&buf2, buf.Bytes(), "", "\t"); err != nil {
|
||||
fmt.Println("Failed to encode json:", err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if err = ioutil.WriteFile("eqemu_config.json", buf2.Bytes(), 0744); err != nil {
|
||||
fmt.Println("Failed to write eqemu_config.json:", err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
BIN
utils/xmltojson/xmltojson-linux-x64
Normal file
BIN
utils/xmltojson/xmltojson-linux-x64
Normal file
Binary file not shown.
BIN
utils/xmltojson/xmltojson-linux-x86
Normal file
BIN
utils/xmltojson/xmltojson-linux-x86
Normal file
Binary file not shown.
BIN
utils/xmltojson/xmltojson-windows-x64
Normal file
BIN
utils/xmltojson/xmltojson-windows-x64
Normal file
Binary file not shown.
BIN
utils/xmltojson/xmltojson-windows-x64.exe
Normal file
BIN
utils/xmltojson/xmltojson-windows-x64.exe
Normal file
Binary file not shown.
BIN
utils/xmltojson/xmltojson-windows-x86
Normal file
BIN
utils/xmltojson/xmltojson-windows-x86
Normal file
Binary file not shown.
BIN
utils/xmltojson/xmltojson-windows-x86.exe
Normal file
BIN
utils/xmltojson/xmltojson-windows-x86.exe
Normal file
Binary file not shown.
@ -104,6 +104,7 @@ EQEmuLogSys LogSys;
|
||||
WebInterfaceList web_interface;
|
||||
|
||||
void CatchSignal(int sig_num);
|
||||
void CheckForServerScript(bool force_download = false);
|
||||
|
||||
inline void UpdateWindowTitle(std::string new_title) {
|
||||
#ifdef _WINDOWS
|
||||
@ -116,6 +117,17 @@ int main(int argc, char** argv) {
|
||||
LogSys.LoadLogSettingsDefaults();
|
||||
set_exception_handler();
|
||||
|
||||
/* If eqemu_config.json does not exist - create it from conversion... */
|
||||
if (!std::ifstream("eqemu_config.json")) {
|
||||
CheckForServerScript(true);
|
||||
/* Run EQEmu Server script (Checks for database updates) */
|
||||
system("perl eqemu_server.pl convert_xml");
|
||||
}
|
||||
else {
|
||||
/* Download EQEmu Server Maintenance Script if doesn't exist */
|
||||
CheckForServerScript();
|
||||
}
|
||||
|
||||
/* Database Version Check */
|
||||
uint32 Database_Version = CURRENT_BINARY_DATABASE_VERSION;
|
||||
uint32 Bots_Database_Version = CURRENT_BINARY_BOTS_DATABASE_VERSION;
|
||||
@ -173,7 +185,7 @@ int main(int argc, char** argv) {
|
||||
}
|
||||
}
|
||||
|
||||
Log(Logs::General, Logs::World_Server, "Connecting to MySQL...");
|
||||
Log(Logs::General, Logs::World_Server, "Connecting to MySQL %s@%s:%i...", Config->DatabaseUsername.c_str(), Config->DatabaseHost.c_str(), Config->DatabasePort);
|
||||
if (!database.Connect(
|
||||
Config->DatabaseHost.c_str(),
|
||||
Config->DatabaseUsername.c_str(),
|
||||
@ -577,4 +589,33 @@ int main(int argc, char** argv) {
|
||||
void CatchSignal(int sig_num) {
|
||||
Log(Logs::General, Logs::World_Server, "Caught signal %d", sig_num);
|
||||
RunLoops = false;
|
||||
}
|
||||
|
||||
void UpdateWindowTitle(char* iNewTitle) {
|
||||
#ifdef _WINDOWS
|
||||
char tmp[500];
|
||||
if (iNewTitle) {
|
||||
snprintf(tmp, sizeof(tmp), "World: %s", iNewTitle);
|
||||
}
|
||||
else {
|
||||
snprintf(tmp, sizeof(tmp), "World");
|
||||
}
|
||||
SetConsoleTitle(tmp);
|
||||
#endif
|
||||
}
|
||||
|
||||
void CheckForServerScript(bool force_download) {
|
||||
/* Fetch EQEmu Server script */
|
||||
if (!std::ifstream("eqemu_server.pl") || force_download) {
|
||||
|
||||
if(force_download)
|
||||
std::remove("eqemu_server.pl"); /* Delete local before fetch */
|
||||
|
||||
std::cout << "Pulling down EQEmu Server Maintenance Script (eqemu_server.pl)..." << std::endl;
|
||||
#ifdef _WIN32
|
||||
system("perl -MLWP::UserAgent -e \"require LWP::UserAgent; my $ua = LWP::UserAgent->new; $ua->timeout(10); $ua->env_proxy; my $response = $ua->get('https://raw.githubusercontent.com/EQEmu/Server/master/utils/scripts/eqemu_server.pl'); if ($response->is_success){ open(FILE, '> eqemu_server.pl'); print FILE $response->decoded_content; close(FILE); }\"");
|
||||
#else
|
||||
system("wget -N --no-check-certificate --quiet -O eqemu_server.pl https://raw.githubusercontent.com/EQEmu/Server/master/utils/scripts/eqemu_server.pl");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@ -52,7 +52,7 @@ public:
|
||||
_world_config=new WorldConfig;
|
||||
_config=_world_config;
|
||||
|
||||
return _config->ParseFile(EQEmuConfig::ConfigFile.c_str(),"server");
|
||||
return _config->parseFile();
|
||||
}
|
||||
|
||||
// Accessors for the static private object
|
||||
|
||||
@ -253,7 +253,7 @@ bool Mob::CheckWillAggro(Mob *mob) {
|
||||
//sometimes if a client has some lag while zoning into a dangerous place while either invis or a GM
|
||||
//they will aggro mobs even though it's supposed to be impossible, to lets make sure we've finished connecting
|
||||
if (mob->IsClient()) {
|
||||
if (!mob->CastToClient()->ClientFinishedLoading() || mob->CastToClient()->IsHoveringForRespawn() || mob->CastToClient()->zoning)
|
||||
if (!mob->CastToClient()->ClientFinishedLoading() || mob->CastToClient()->IsHoveringForRespawn() || mob->CastToClient()->bZoning)
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@ -1728,6 +1728,15 @@ bool Client::Death(Mob* killerMob, int32 damage, uint16 spell, EQEmu::skills::Sk
|
||||
if (!RuleB(Character, UseDeathExpLossMult)) {
|
||||
exploss = (int)(GetLevel() * (GetLevel() / 18.0) * 12000);
|
||||
}
|
||||
|
||||
if (RuleB(Zone, LevelBasedEXPMods)) {
|
||||
// Death in levels with xp_mod (such as hell levels) was resulting
|
||||
// in losing more that appropriate since the loss was the same but
|
||||
// getting it back would take way longer. This makes the death the
|
||||
// same amount of time to recover. Will also lose more if level is
|
||||
// granting a bonus.
|
||||
exploss *= zone->level_exp_mod[GetLevel()].ExpMod;
|
||||
}
|
||||
|
||||
if ((GetLevel() < RuleI(Character, DeathExpLossLevel)) || (GetLevel() > RuleI(Character, DeathExpLossMaxLevel)) || IsBecomeNPC())
|
||||
{
|
||||
@ -2675,7 +2684,7 @@ void Mob::AddToHateList(Mob* other, uint32 hate /*= 0*/, int32 damage /*= 0*/, b
|
||||
|
||||
hate_list.AddEntToHateList(other, hate, damage, bFrenzy, !iBuffTic);
|
||||
|
||||
if (other->IsClient() && !on_hatelist)
|
||||
if (other->IsClient() && !on_hatelist && !IsOnFeignMemory(other->CastToClient()))
|
||||
other->CastToClient()->AddAutoXTarget(this);
|
||||
|
||||
#ifdef BOTS
|
||||
|
||||
@ -215,7 +215,7 @@ Client::Client(EQStreamInterface* ieqs)
|
||||
linkdead_timer.Disable();
|
||||
zonesummon_id = 0;
|
||||
zonesummon_ignorerestrictions = 0;
|
||||
zoning = false;
|
||||
bZoning = false;
|
||||
zone_mode = ZoneUnsolicited;
|
||||
casting_spell_id = 0;
|
||||
npcflag = false;
|
||||
@ -395,7 +395,7 @@ Client::~Client() {
|
||||
GetTarget()->IsTargeted(-1);
|
||||
|
||||
//if we are in a group and we are not zoning, force leave the group
|
||||
if(isgrouped && !zoning && is_zone_loaded)
|
||||
if(isgrouped && !bZoning && is_zone_loaded)
|
||||
LeaveGroup();
|
||||
|
||||
UpdateWho(2);
|
||||
|
||||
@ -606,6 +606,10 @@ public:
|
||||
uint32 GetExperienceForKill(Mob *against);
|
||||
void AddEXP(uint32 in_add_exp, uint8 conlevel = 0xFF, bool resexp = false);
|
||||
uint32 CalcEXP(uint8 conlevel = 0xFF);
|
||||
void CalculateNormalizedAAExp(uint32 &add_aaxp, uint8 conlevel, bool resexp);
|
||||
void CalculateStandardAAExp(uint32 &add_aaxp, uint8 conlevel, bool resexp);
|
||||
void CalculateLeadershipExp(uint32 &add_exp, uint8 conlevel);
|
||||
void CalculateExp(uint32 in_add_exp, uint32 &add_exp, uint32 &add_aaxp, uint8 conlevel, bool resexp);
|
||||
void SetEXP(uint32 set_exp, uint32 set_aaxp, bool resexp=false);
|
||||
void AddLevelBasedExp(uint8 exp_percentage, uint8 max_level=0);
|
||||
void SetLeadershipEXP(uint32 group_exp, uint32 raid_exp);
|
||||
@ -651,6 +655,7 @@ public:
|
||||
void Sacrifice(Client* caster);
|
||||
void GoToDeath();
|
||||
inline const int32 GetInstanceID() const { return zone->GetInstanceID(); }
|
||||
void SetZoning(bool in) { bZoning = in; }
|
||||
|
||||
FACTION_VALUE GetReverseFactionCon(Mob* iOther);
|
||||
FACTION_VALUE GetFactionLevel(uint32 char_id, uint32 npc_id, uint32 p_race, uint32 p_class, uint32 p_deity, int32 pFaction, Mob* tnpc);
|
||||
@ -1542,7 +1547,7 @@ private:
|
||||
bool npcflag;
|
||||
uint8 npclevel;
|
||||
bool feigned;
|
||||
bool zoning;
|
||||
bool bZoning;
|
||||
bool tgb;
|
||||
bool instalog;
|
||||
int32 last_reported_mana;
|
||||
|
||||
@ -264,7 +264,7 @@ bool Client::Process() {
|
||||
if (distance <= scan_range) {
|
||||
close_mobs.insert(std::pair<Mob *, float>(mob, distance));
|
||||
}
|
||||
else if (mob->GetAggroRange() > scan_range) {
|
||||
else if ((mob->GetAggroRange() * mob->GetAggroRange()) > scan_range) {
|
||||
close_mobs.insert(std::pair<Mob *, float>(mob, distance));
|
||||
}
|
||||
}
|
||||
@ -655,17 +655,17 @@ bool Client::Process() {
|
||||
{
|
||||
//client logged out or errored out
|
||||
//ResetTrade();
|
||||
if (client_state != CLIENT_KICKED && !zoning && !instalog) {
|
||||
if (client_state != CLIENT_KICKED && !bZoning && !instalog) {
|
||||
Save();
|
||||
}
|
||||
|
||||
client_state = CLIENT_LINKDEAD;
|
||||
if (zoning || instalog || GetGM())
|
||||
if (bZoning || instalog || GetGM())
|
||||
{
|
||||
Group *mygroup = GetGroup();
|
||||
if (mygroup)
|
||||
{
|
||||
if (!zoning)
|
||||
if (!bZoning)
|
||||
{
|
||||
entity_list.MessageGroup(this, true, 15, "%s logged out.", GetName());
|
||||
LeaveGroup();
|
||||
@ -684,7 +684,7 @@ bool Client::Process() {
|
||||
Raid *myraid = entity_list.GetRaidByClient(this);
|
||||
if (myraid)
|
||||
{
|
||||
if (!zoning)
|
||||
if (!bZoning)
|
||||
{
|
||||
//entity_list.MessageGroup(this,true,15,"%s logged out.",GetName());
|
||||
myraid->MemberZoned(this);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -3257,7 +3257,7 @@ void EntityList::AddHealAggro(Mob *target, Mob *caster, uint16 hate)
|
||||
|
||||
for (auto &e : npc_list) {
|
||||
auto &npc = e.second;
|
||||
if (!npc->CheckAggro(target) || npc->IsFeared())
|
||||
if (!npc->CheckAggro(target) || npc->IsFeared() || npc->IsPet())
|
||||
continue;
|
||||
|
||||
if (zone->random.Roll(50)) // witness check -- place holder
|
||||
|
||||
437
zone/exp.cpp
437
zone/exp.cpp
@ -37,6 +37,49 @@
|
||||
|
||||
extern QueryServ* QServ;
|
||||
|
||||
static uint32 ScaleAAXPBasedOnCurrentAATotal(int earnedAA, uint32 add_aaxp)
|
||||
{
|
||||
float baseModifier = RuleR(AA, ModernAAScalingStartPercent);
|
||||
int aaMinimum = RuleI(AA, ModernAAScalingAAMinimum);
|
||||
int aaLimit = RuleI(AA, ModernAAScalingAALimit);
|
||||
|
||||
// Are we within the scaling window?
|
||||
if (earnedAA >= aaLimit || earnedAA < aaMinimum)
|
||||
{
|
||||
Log(Logs::Detail, Logs::None, "Not within AA scaling window.");
|
||||
|
||||
// At or past the limit. We're done.
|
||||
return add_aaxp;
|
||||
}
|
||||
|
||||
// We're not at the limit yet. How close are we?
|
||||
int remainingAA = aaLimit - earnedAA;
|
||||
|
||||
// We might not always be X - 0
|
||||
int scaleRange = aaLimit - aaMinimum;
|
||||
|
||||
// Normalize and get the effectiveness based on the range and the character's
|
||||
// current spent AA.
|
||||
float normalizedScale = (float)remainingAA / scaleRange;
|
||||
|
||||
// Scale.
|
||||
uint32 totalWithExpMod = add_aaxp * (baseModifier / 100) * normalizedScale;
|
||||
|
||||
// Are we so close to the scale limit that we're earning more XP without scaling? This
|
||||
// will happen when we get very close to the limit. In this case, just grant the unscaled
|
||||
// amount.
|
||||
if (totalWithExpMod < add_aaxp)
|
||||
{
|
||||
return add_aaxp;
|
||||
}
|
||||
|
||||
Log(Logs::Detail,
|
||||
Logs::None,
|
||||
"Total before the modifier %d :: NewTotal %d :: ScaleRange: %d, SpentAA: %d, RemainingAA: %d, normalizedScale: %0.3f",
|
||||
add_aaxp, totalWithExpMod, scaleRange, earnedAA, remainingAA, normalizedScale);
|
||||
|
||||
return totalWithExpMod;
|
||||
}
|
||||
|
||||
static uint32 MaxBankedGroupLeadershipPoints(int Level)
|
||||
{
|
||||
@ -174,192 +217,302 @@ uint32 Client::GetExperienceForKill(Mob *against)
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Client::AddEXP(uint32 in_add_exp, uint8 conlevel, bool resexp) {
|
||||
float static GetConLevelModifierPercent(uint8 conlevel)
|
||||
{
|
||||
switch (conlevel)
|
||||
{
|
||||
case CON_GREEN:
|
||||
return (float)RuleI(Character, GreenModifier) / 100;
|
||||
break;
|
||||
case CON_LIGHTBLUE:
|
||||
return (float)RuleI(Character, LightBlueModifier) / 100;
|
||||
break;
|
||||
case CON_BLUE:
|
||||
return (float)RuleI(Character, BlueModifier) / 100;
|
||||
break;
|
||||
case CON_WHITE:
|
||||
return (float)RuleI(Character, WhiteModifier) / 100;
|
||||
break;
|
||||
case CON_YELLOW:
|
||||
return (float)RuleI(Character, YellowModifier) / 100;
|
||||
break;
|
||||
case CON_RED:
|
||||
return (float)RuleI(Character, RedModifier) / 100;
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
this->EVENT_ITEM_ScriptStopReturn();
|
||||
|
||||
uint32 add_exp = in_add_exp;
|
||||
|
||||
if(!resexp && (XPRate != 0))
|
||||
add_exp = static_cast<uint32>(in_add_exp * (static_cast<float>(XPRate) / 100.0f));
|
||||
|
||||
if (m_epp.perAA<0 || m_epp.perAA>100)
|
||||
m_epp.perAA=0; // stop exploit with sanity check
|
||||
|
||||
uint32 add_aaxp;
|
||||
if(resexp) {
|
||||
void Client::CalculateNormalizedAAExp(uint32 &add_aaxp, uint8 conlevel, bool resexp)
|
||||
{
|
||||
// Functionally this is the same as having the case in the switch, but this is
|
||||
// cleaner to read.
|
||||
if (CON_GRAY == conlevel || resexp)
|
||||
{
|
||||
add_aaxp = 0;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
// For this, we ignore the provided value of add_aaxp because it doesn't
|
||||
// apply. XP per AA is normalized such that there are X white con kills
|
||||
// per AA.
|
||||
|
||||
uint32 whiteConKillsPerAA = RuleI(AA, NormalizedAANumberOfWhiteConPerAA);
|
||||
uint32 xpPerAA = RuleI(AA, ExpPerPoint);
|
||||
|
||||
float colorModifier = GetConLevelModifierPercent(conlevel);
|
||||
float percentToAAXp = (float)m_epp.perAA / 100;
|
||||
|
||||
// Normalize the amount of AA XP we earned for this kill.
|
||||
add_aaxp = percentToAAXp * (xpPerAA / (whiteConKillsPerAA / colorModifier));
|
||||
}
|
||||
|
||||
void Client::CalculateStandardAAExp(uint32 &add_aaxp, uint8 conlevel, bool resexp)
|
||||
{
|
||||
if (!resexp)
|
||||
{
|
||||
//if XP scaling is based on the con of a monster, do that now.
|
||||
if (RuleB(Character, UseXPConScaling))
|
||||
{
|
||||
if (conlevel != 0xFF && !resexp)
|
||||
{
|
||||
add_aaxp *= GetConLevelModifierPercent(conlevel);
|
||||
}
|
||||
}
|
||||
} //end !resexp
|
||||
|
||||
float aatotalmod = 1.0;
|
||||
if (zone->newzone_data.zone_exp_multiplier >= 0) {
|
||||
aatotalmod *= zone->newzone_data.zone_exp_multiplier;
|
||||
}
|
||||
|
||||
// Shouldn't race not affect AA XP?
|
||||
if (RuleB(Character, UseRaceClassExpBonuses))
|
||||
{
|
||||
if (GetBaseRace() == HALFLING) {
|
||||
aatotalmod *= 1.05;
|
||||
}
|
||||
|
||||
if (GetClass() == ROGUE || GetClass() == WARRIOR) {
|
||||
aatotalmod *= 1.05;
|
||||
}
|
||||
}
|
||||
|
||||
// why wasn't this here? Where should it be?
|
||||
if (zone->IsHotzone())
|
||||
{
|
||||
aatotalmod += RuleR(Zone, HotZoneBonus);
|
||||
}
|
||||
|
||||
if (RuleB(Zone, LevelBasedEXPMods)) {
|
||||
if (zone->level_exp_mod[GetLevel()].ExpMod) {
|
||||
add_aaxp *= zone->level_exp_mod[GetLevel()].AAExpMod;
|
||||
}
|
||||
}
|
||||
|
||||
add_aaxp = (uint32)(RuleR(Character, AAExpMultiplier) * add_aaxp * aatotalmod);
|
||||
}
|
||||
|
||||
void Client::CalculateLeadershipExp(uint32 &add_exp, uint8 conlevel)
|
||||
{
|
||||
if (IsLeadershipEXPOn() && (conlevel == CON_BLUE || conlevel == CON_WHITE || conlevel == CON_YELLOW || conlevel == CON_RED))
|
||||
{
|
||||
add_exp = static_cast<uint32>(static_cast<float>(add_exp) * 0.8f);
|
||||
|
||||
if (GetGroup())
|
||||
{
|
||||
if (m_pp.group_leadership_points < MaxBankedGroupLeadershipPoints(GetLevel())
|
||||
&& RuleI(Character, KillsPerGroupLeadershipAA) > 0)
|
||||
{
|
||||
uint32 exp = GROUP_EXP_PER_POINT / RuleI(Character, KillsPerGroupLeadershipAA);
|
||||
Client *mentoree = GetGroup()->GetMentoree();
|
||||
if (GetGroup()->GetMentorPercent() && mentoree &&
|
||||
mentoree->GetGroupPoints() < MaxBankedGroupLeadershipPoints(mentoree->GetLevel()))
|
||||
{
|
||||
uint32 mentor_exp = exp * (GetGroup()->GetMentorPercent() / 100.0f);
|
||||
exp -= mentor_exp;
|
||||
mentoree->AddLeadershipEXP(mentor_exp, 0); // ends up rounded down
|
||||
mentoree->Message_StringID(MT_Leadership, GAIN_GROUP_LEADERSHIP_EXP);
|
||||
}
|
||||
if (exp > 0)
|
||||
{
|
||||
// possible if you mentor 100% to the other client
|
||||
AddLeadershipEXP(exp, 0); // ends up rounded up if mentored, no idea how live actually does it
|
||||
Message_StringID(MT_Leadership, GAIN_GROUP_LEADERSHIP_EXP);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Message_StringID(MT_Leadership, MAX_GROUP_LEADERSHIP_POINTS);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Raid *raid = GetRaid();
|
||||
// Raid leaders CAN NOT gain group AA XP, other group leaders can though!
|
||||
if (raid->IsLeader(this))
|
||||
{
|
||||
if (m_pp.raid_leadership_points < MaxBankedRaidLeadershipPoints(GetLevel())
|
||||
&& RuleI(Character, KillsPerRaidLeadershipAA) > 0)
|
||||
{
|
||||
AddLeadershipEXP(0, RAID_EXP_PER_POINT / RuleI(Character, KillsPerRaidLeadershipAA));
|
||||
Message_StringID(MT_Leadership, GAIN_RAID_LEADERSHIP_EXP);
|
||||
}
|
||||
else
|
||||
{
|
||||
Message_StringID(MT_Leadership, MAX_RAID_LEADERSHIP_POINTS);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_pp.group_leadership_points < MaxBankedGroupLeadershipPoints(GetLevel())
|
||||
&& RuleI(Character, KillsPerGroupLeadershipAA) > 0)
|
||||
{
|
||||
uint32 group_id = raid->GetGroup(this);
|
||||
uint32 exp = GROUP_EXP_PER_POINT / RuleI(Character, KillsPerGroupLeadershipAA);
|
||||
Client *mentoree = raid->GetMentoree(group_id);
|
||||
if (raid->GetMentorPercent(group_id) && mentoree &&
|
||||
mentoree->GetGroupPoints() < MaxBankedGroupLeadershipPoints(mentoree->GetLevel()))
|
||||
{
|
||||
uint32 mentor_exp = exp * (raid->GetMentorPercent(group_id) / 100.0f);
|
||||
exp -= mentor_exp;
|
||||
mentoree->AddLeadershipEXP(mentor_exp, 0);
|
||||
mentoree->Message_StringID(MT_Leadership, GAIN_GROUP_LEADERSHIP_EXP);
|
||||
}
|
||||
if (exp > 0)
|
||||
{
|
||||
AddLeadershipEXP(exp, 0);
|
||||
Message_StringID(MT_Leadership, GAIN_GROUP_LEADERSHIP_EXP);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Message_StringID(MT_Leadership, MAX_GROUP_LEADERSHIP_POINTS);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Client::CalculateExp(uint32 in_add_exp, uint32 &add_exp, uint32 &add_aaxp, uint8 conlevel, bool resexp)
|
||||
{
|
||||
add_exp = in_add_exp;
|
||||
|
||||
if (!resexp && (XPRate != 0))
|
||||
{
|
||||
add_exp = static_cast<uint32>(in_add_exp * (static_cast<float>(XPRate) / 100.0f));
|
||||
}
|
||||
|
||||
// Make sure it was initialized.
|
||||
add_aaxp = 0;
|
||||
|
||||
if (!resexp)
|
||||
{
|
||||
//figure out how much of this goes to AAs
|
||||
add_aaxp = add_exp * m_epp.perAA / 100;
|
||||
|
||||
//take that amount away from regular exp
|
||||
add_exp -= add_aaxp;
|
||||
|
||||
float totalmod = 1.0;
|
||||
float zemmod = 1.0;
|
||||
|
||||
//get modifiers
|
||||
if(RuleR(Character, ExpMultiplier) >= 0){
|
||||
if (RuleR(Character, ExpMultiplier) >= 0) {
|
||||
totalmod *= RuleR(Character, ExpMultiplier);
|
||||
}
|
||||
|
||||
if(zone->newzone_data.zone_exp_multiplier >= 0){
|
||||
//add the zone exp modifier.
|
||||
if (zone->newzone_data.zone_exp_multiplier >= 0) {
|
||||
zemmod *= zone->newzone_data.zone_exp_multiplier;
|
||||
}
|
||||
|
||||
if(RuleB(Character,UseRaceClassExpBonuses))
|
||||
if (RuleB(Character, UseRaceClassExpBonuses))
|
||||
{
|
||||
if(GetBaseRace() == HALFLING){
|
||||
if (GetBaseRace() == HALFLING) {
|
||||
totalmod *= 1.05;
|
||||
}
|
||||
|
||||
if(GetClass() == ROGUE || GetClass() == WARRIOR){
|
||||
if (GetClass() == ROGUE || GetClass() == WARRIOR) {
|
||||
totalmod *= 1.05;
|
||||
}
|
||||
}
|
||||
|
||||
if(zone->IsHotzone())
|
||||
//add hotzone modifier if one has been set.
|
||||
if (zone->IsHotzone())
|
||||
{
|
||||
totalmod += RuleR(Zone, HotZoneBonus);
|
||||
}
|
||||
|
||||
add_exp = uint32(float(add_exp) * totalmod * zemmod);
|
||||
|
||||
if(RuleB(Character,UseXPConScaling))
|
||||
//if XP scaling is based on the con of a monster, do that now.
|
||||
if (RuleB(Character, UseXPConScaling))
|
||||
{
|
||||
if (conlevel != 0xFF && !resexp) {
|
||||
switch (conlevel)
|
||||
{
|
||||
case CON_GRAY:
|
||||
add_exp = 0;
|
||||
add_aaxp = 0;
|
||||
return;
|
||||
case CON_GREEN:
|
||||
add_exp = add_exp * RuleI(Character, GreenModifier) / 100;
|
||||
add_aaxp = add_aaxp * RuleI(Character, GreenModifier) / 100;
|
||||
break;
|
||||
case CON_LIGHTBLUE:
|
||||
add_exp = add_exp * RuleI(Character, LightBlueModifier)/100;
|
||||
add_aaxp = add_aaxp * RuleI(Character, LightBlueModifier)/100;
|
||||
break;
|
||||
case CON_BLUE:
|
||||
add_exp = add_exp * RuleI(Character, BlueModifier)/100;
|
||||
add_aaxp = add_aaxp * RuleI(Character, BlueModifier)/100;
|
||||
break;
|
||||
case CON_WHITE:
|
||||
add_exp = add_exp * RuleI(Character, WhiteModifier)/100;
|
||||
add_aaxp = add_aaxp * RuleI(Character, WhiteModifier)/100;
|
||||
break;
|
||||
case CON_YELLOW:
|
||||
add_exp = add_exp * RuleI(Character, YellowModifier)/100;
|
||||
add_aaxp = add_aaxp * RuleI(Character, YellowModifier)/100;
|
||||
break;
|
||||
case CON_RED:
|
||||
add_exp = add_exp * RuleI(Character, RedModifier)/100;
|
||||
add_aaxp = add_aaxp * RuleI(Character, RedModifier)/100;
|
||||
break;
|
||||
}
|
||||
if (conlevel != 0xFF && !resexp)
|
||||
{
|
||||
add_exp = add_exp * GetConLevelModifierPercent(conlevel);
|
||||
}
|
||||
}
|
||||
|
||||
if (IsLeadershipEXPOn() && (conlevel == CON_BLUE || conlevel == CON_WHITE || conlevel == CON_YELLOW || conlevel == CON_RED)) {
|
||||
add_exp = static_cast<uint32>(static_cast<float>(add_exp) * 0.8f);
|
||||
|
||||
if (GetGroup()) {
|
||||
if (m_pp.group_leadership_points < MaxBankedGroupLeadershipPoints(GetLevel())
|
||||
&& RuleI(Character, KillsPerGroupLeadershipAA) > 0) {
|
||||
uint32 exp = GROUP_EXP_PER_POINT / RuleI(Character, KillsPerGroupLeadershipAA);
|
||||
Client *mentoree = GetGroup()->GetMentoree();
|
||||
if (GetGroup()->GetMentorPercent() && mentoree &&
|
||||
mentoree->GetGroupPoints() < MaxBankedGroupLeadershipPoints(mentoree->GetLevel())) {
|
||||
uint32 mentor_exp = exp * (GetGroup()->GetMentorPercent() / 100.0f);
|
||||
exp -= mentor_exp;
|
||||
mentoree->AddLeadershipEXP(mentor_exp, 0); // ends up rounded down
|
||||
mentoree->Message_StringID(MT_Leadership, GAIN_GROUP_LEADERSHIP_EXP);
|
||||
}
|
||||
if (exp > 0) { // possible if you mentor 100% to the other client
|
||||
AddLeadershipEXP(exp, 0); // ends up rounded up if mentored, no idea how live actually does it
|
||||
Message_StringID(MT_Leadership, GAIN_GROUP_LEADERSHIP_EXP);
|
||||
}
|
||||
} else {
|
||||
Message_StringID(MT_Leadership, MAX_GROUP_LEADERSHIP_POINTS);
|
||||
}
|
||||
} else {
|
||||
Raid *raid = GetRaid();
|
||||
// Raid leaders CAN NOT gain group AA XP, other group leaders can though!
|
||||
if (raid->IsLeader(this)) {
|
||||
if (m_pp.raid_leadership_points < MaxBankedRaidLeadershipPoints(GetLevel())
|
||||
&& RuleI(Character, KillsPerRaidLeadershipAA) > 0) {
|
||||
AddLeadershipEXP(0, RAID_EXP_PER_POINT / RuleI(Character, KillsPerRaidLeadershipAA));
|
||||
Message_StringID(MT_Leadership, GAIN_RAID_LEADERSHIP_EXP);
|
||||
} else {
|
||||
Message_StringID(MT_Leadership, MAX_RAID_LEADERSHIP_POINTS);
|
||||
}
|
||||
} else {
|
||||
if (m_pp.group_leadership_points < MaxBankedGroupLeadershipPoints(GetLevel())
|
||||
&& RuleI(Character, KillsPerGroupLeadershipAA) > 0) {
|
||||
uint32 group_id = raid->GetGroup(this);
|
||||
uint32 exp = GROUP_EXP_PER_POINT / RuleI(Character, KillsPerGroupLeadershipAA);
|
||||
Client *mentoree = raid->GetMentoree(group_id);
|
||||
if (raid->GetMentorPercent(group_id) && mentoree &&
|
||||
mentoree->GetGroupPoints() < MaxBankedGroupLeadershipPoints(mentoree->GetLevel())) {
|
||||
uint32 mentor_exp = exp * (raid->GetMentorPercent(group_id) / 100.0f);
|
||||
exp -= mentor_exp;
|
||||
mentoree->AddLeadershipEXP(mentor_exp, 0);
|
||||
mentoree->Message_StringID(MT_Leadership, GAIN_GROUP_LEADERSHIP_EXP);
|
||||
}
|
||||
if (exp > 0) {
|
||||
AddLeadershipEXP(exp, 0);
|
||||
Message_StringID(MT_Leadership, GAIN_GROUP_LEADERSHIP_EXP);
|
||||
}
|
||||
} else {
|
||||
Message_StringID(MT_Leadership, MAX_GROUP_LEADERSHIP_POINTS);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Calculate any changes to leadership experience.
|
||||
CalculateLeadershipExp(add_exp, conlevel);
|
||||
} //end !resexp
|
||||
|
||||
float aatotalmod = 1.0;
|
||||
if(zone->newzone_data.zone_exp_multiplier >= 0){
|
||||
aatotalmod *= zone->newzone_data.zone_exp_multiplier;
|
||||
}
|
||||
|
||||
// Shouldn't race not affect AA XP?
|
||||
if(RuleB(Character,UseRaceClassExpBonuses))
|
||||
{
|
||||
if(GetBaseRace() == HALFLING){
|
||||
aatotalmod *= 1.05;
|
||||
}
|
||||
|
||||
if(GetClass() == ROGUE || GetClass() == WARRIOR){
|
||||
aatotalmod *= 1.05;
|
||||
}
|
||||
}
|
||||
|
||||
// why wasn't this here? Where should it be?
|
||||
if(zone->IsHotzone())
|
||||
{
|
||||
aatotalmod += RuleR(Zone, HotZoneBonus);
|
||||
}
|
||||
|
||||
if(RuleB(Zone, LevelBasedEXPMods)){
|
||||
if(zone->level_exp_mod[GetLevel()].ExpMod){
|
||||
if (RuleB(Zone, LevelBasedEXPMods)) {
|
||||
if (zone->level_exp_mod[GetLevel()].ExpMod) {
|
||||
add_exp *= zone->level_exp_mod[GetLevel()].ExpMod;
|
||||
add_aaxp *= zone->level_exp_mod[GetLevel()].AAExpMod;
|
||||
}
|
||||
}
|
||||
|
||||
uint32 exp = GetEXP() + add_exp;
|
||||
add_exp = GetEXP() + add_exp;
|
||||
}
|
||||
|
||||
uint32 aaexp = (uint32)(RuleR(Character, AAExpMultiplier) * add_aaxp * aatotalmod);
|
||||
void Client::AddEXP(uint32 in_add_exp, uint8 conlevel, bool resexp) {
|
||||
|
||||
this->EVENT_ITEM_ScriptStopReturn();
|
||||
|
||||
uint32 exp = 0;
|
||||
uint32 aaexp = 0;
|
||||
|
||||
if (m_epp.perAA<0 || m_epp.perAA>100)
|
||||
m_epp.perAA=0; // stop exploit with sanity check
|
||||
|
||||
// Calculate regular XP
|
||||
CalculateExp(in_add_exp, exp, aaexp, conlevel, resexp);
|
||||
|
||||
// Calculate regular AA XP
|
||||
if (!RuleB(AA, NormalizedAAEnabled))
|
||||
{
|
||||
CalculateStandardAAExp(aaexp, conlevel, resexp);
|
||||
}
|
||||
else
|
||||
{
|
||||
CalculateNormalizedAAExp(aaexp, conlevel, resexp);
|
||||
}
|
||||
|
||||
// Are we also doing linear AA acceleration?
|
||||
if (RuleB(AA, ModernAAScalingEnabled) && aaexp > 0)
|
||||
{
|
||||
aaexp = ScaleAAXPBasedOnCurrentAATotal(GetAAPoints(), aaexp);
|
||||
}
|
||||
|
||||
// Get current AA XP total
|
||||
uint32 had_aaexp = GetAAXP();
|
||||
aaexp += had_aaexp;
|
||||
if(aaexp < had_aaexp)
|
||||
aaexp = had_aaexp; //watch for wrap
|
||||
|
||||
// Add it to the XP we just earned.
|
||||
aaexp += had_aaexp;
|
||||
|
||||
// Make sure our new total (existing + just earned) isn't lower than the
|
||||
// existing total. If it is, we overflowed the bounds of uint32 and wrapped.
|
||||
// Reset to the existing total.
|
||||
if (aaexp < had_aaexp)
|
||||
{
|
||||
aaexp = had_aaexp; //watch for wrap
|
||||
}
|
||||
|
||||
// Now update our character's normal and AA xp
|
||||
SetEXP(exp, aaexp, resexp);
|
||||
}
|
||||
|
||||
|
||||
@ -336,7 +336,8 @@ void NPC::AddLootDrop(const EQEmu::ItemData *item2, ItemList* itemlist, int16 ch
|
||||
eslot = EQEmu::textures::weaponPrimary;
|
||||
if (item2->Damage > 0) {
|
||||
SendAddPlayerState(PlayerState::PrimaryWeaponEquipped);
|
||||
SetFacestab(true);
|
||||
if (!RuleB(Combat, ClassicNPCBackstab))
|
||||
SetFacestab(true);
|
||||
}
|
||||
if (item2->IsType2HWeapon())
|
||||
SetTwoHanderEquipped(true);
|
||||
|
||||
@ -127,7 +127,7 @@ bool NPC::AICastSpell(Mob* tar, uint8 iChance, uint32 iSpellTypes) {
|
||||
}
|
||||
case SpellType_Root: {
|
||||
Mob *rootee = GetHateRandom();
|
||||
if (rootee && !rootee->IsRooted() && zone->random.Roll(50)
|
||||
if (rootee && !rootee->IsRooted() && !rootee->IsFeared() && zone->random.Roll(50)
|
||||
&& rootee->DontRootMeBefore() < Timer::GetCurrentTime()
|
||||
&& rootee->CanBuffStack(AIspells[i].spellid, GetLevel(), true) >= 0
|
||||
) {
|
||||
@ -1391,19 +1391,33 @@ void Mob::AI_Process() {
|
||||
//if(owner->IsClient())
|
||||
// printf("Pet start pos: (%f, %f, %f)\n", GetX(), GetY(), GetZ());
|
||||
|
||||
float dist = DistanceSquared(m_Position, owner->GetPosition());
|
||||
if (dist >= 400)
|
||||
glm::vec4 ownerPos = owner->GetPosition();
|
||||
float dist = DistanceSquared(m_Position, ownerPos);
|
||||
float distz = ownerPos.z - m_Position.z;
|
||||
|
||||
if (dist >= 400 || distz > 100)
|
||||
{
|
||||
int speed = GetWalkspeed();
|
||||
if (dist >= 5625)
|
||||
speed = GetRunspeed();
|
||||
|
||||
CalculateNewPosition2(owner->GetX(), owner->GetY(), owner->GetZ(), speed);
|
||||
if (distz > 100)
|
||||
{
|
||||
m_Position = ownerPos;
|
||||
SendPositionUpdate();
|
||||
moved = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
CalculateNewPosition2(owner->GetX(),
|
||||
owner->GetY(), owner->GetZ(), speed);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(moved)
|
||||
{
|
||||
this->FixZ();
|
||||
SetCurrentSpeed(0);
|
||||
moved = false;
|
||||
}
|
||||
@ -1748,6 +1762,12 @@ void Mob::AI_Event_Engaged(Mob* attacker, bool iYellForHelp) {
|
||||
|
||||
SetAppearance(eaStanding);
|
||||
|
||||
/*
|
||||
Kick off auto cast timer
|
||||
*/
|
||||
if (this->IsNPC())
|
||||
this->CastToNPC()->AIautocastspell_timer->Start(300, false);
|
||||
|
||||
if (iYellForHelp) {
|
||||
if(IsPet()) {
|
||||
GetOwner()->AI_Event_Engaged(attacker, iYellForHelp);
|
||||
|
||||
@ -420,6 +420,8 @@ public:
|
||||
|
||||
bool IgnoreDespawn() { return ignore_despawn; }
|
||||
|
||||
std::unique_ptr<Timer> AIautocastspell_timer;
|
||||
|
||||
protected:
|
||||
|
||||
const NPCType* NPCTypedata;
|
||||
@ -453,7 +455,7 @@ protected:
|
||||
|
||||
uint32 npc_spells_id;
|
||||
uint8 casting_spell_AIindex;
|
||||
std::unique_ptr<Timer> AIautocastspell_timer;
|
||||
|
||||
uint32* pDontCastBefore_casting_spell;
|
||||
std::vector<AISpells_Struct> AIspells;
|
||||
bool HasAISpell;
|
||||
|
||||
@ -655,10 +655,15 @@ void NPC::SetPetState(SpellBuff_Struct *pet_buffs, uint32 *items) {
|
||||
continue;
|
||||
|
||||
const EQEmu::ItemData* item2 = database.GetItem(items[i]);
|
||||
if (item2 && item2->NoDrop != 0) {
|
||||
//dont bother saving item charges for now, NPCs never use them
|
||||
//and nobody should be able to get them off the corpse..?
|
||||
AddLootDrop(item2, &itemlist, 0, 1, 255, true, true);
|
||||
|
||||
if (item2) {
|
||||
bool noDrop=(item2->NoDrop == 0); // Field is reverse logic
|
||||
bool petCanHaveNoDrop = (RuleB(Pets, CanTakeNoDrop) &&
|
||||
_CLIENTPET(this) && GetPetType() <= petOther);
|
||||
|
||||
if (!noDrop || petCanHaveNoDrop) {
|
||||
AddLootDrop(item2, &itemlist, 0, 1, 255, true, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1796,8 +1796,12 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
|
||||
Message_StringID(4, CORPSE_CANT_SENSE);
|
||||
}
|
||||
}
|
||||
else if (caster)
|
||||
caster->Message_StringID(MT_SpellFailure, SPELL_LEVEL_REQ);
|
||||
else if (caster) {
|
||||
char level[4];
|
||||
ConvertArray(effect_value, level);
|
||||
caster->Message_StringID(MT_SpellFailure,
|
||||
SPELL_LEVEL_REQ, level);
|
||||
}
|
||||
}
|
||||
else {
|
||||
Message_StringID(4, TARGET_NOT_FOUND);
|
||||
|
||||
@ -523,7 +523,6 @@ void Client::FinishTrade(Mob* tradingWith, bool finalizer, void* event_entry, st
|
||||
else
|
||||
qs_audit->char1_count += detail->charges;
|
||||
|
||||
//for (uint8 sub_slot = SUB_BEGIN; ((sub_slot < inst->GetItem()->BagSlots) && (sub_slot < EmuConstants::ITEM_CONTAINER_SIZE)); ++sub_slot) {
|
||||
for (uint8 sub_slot = EQEmu::inventory::containerBegin; (sub_slot < EQEmu::inventory::ContainerCount); ++sub_slot) { // this is to catch ALL items
|
||||
const EQEmu::ItemInstance* bag_inst = inst->GetItem(sub_slot);
|
||||
|
||||
@ -743,7 +742,6 @@ void Client::FinishTrade(Mob* tradingWith, bool finalizer, void* event_entry, st
|
||||
qs_audit->char1_count += detail->charges;
|
||||
|
||||
// 'step 3' should never really see containers..but, just in case...
|
||||
//for (uint8 sub_slot = SUB_BEGIN; ((sub_slot < inst->GetItem()->BagSlots) && (sub_slot < EmuConstants::ITEM_CONTAINER_SIZE)); ++sub_slot) {
|
||||
for (uint8 sub_slot = EQEmu::inventory::containerBegin; (sub_slot < EQEmu::inventory::ContainerCount); ++sub_slot) { // this is to catch ALL items
|
||||
const EQEmu::ItemInstance* bag_inst = inst->GetItem(sub_slot);
|
||||
|
||||
@ -888,8 +886,12 @@ void Client::FinishTrade(Mob* tradingWith, bool finalizer, void* event_entry, st
|
||||
|
||||
const EQEmu::ItemData* item = inst->GetItem();
|
||||
if(item && quest_npc == false) {
|
||||
bool isPetAndCanHaveNoDrop = (RuleB(Pets, CanTakeNoDrop) &&
|
||||
_CLIENTPET(tradingWith) &&
|
||||
tradingWith->GetPetType()<=petOther);
|
||||
// if it was not a NO DROP or Attuned item (or if a GM is trading), let the NPC have it
|
||||
if(GetGM() || (item->NoDrop != 0 && inst->IsAttuned() == false)) {
|
||||
if(GetGM() || (inst->IsAttuned() == false &&
|
||||
(item->NoDrop != 0 || isPetAndCanHaveNoDrop))) {
|
||||
// pets need to look inside bags and try to equip items found there
|
||||
if (item->IsClassBag() && item->BagSlots > 0) {
|
||||
for (int16 bslot = EQEmu::inventory::containerBegin; bslot < item->BagSlots; bslot++) {
|
||||
|
||||
@ -884,7 +884,7 @@ void Mob::FixZ(int32 z_find_offset /*= 5*/)
|
||||
glm::vec3 current_loc(m_Position);
|
||||
float new_z = GetFixedZ(current_loc, z_find_offset);
|
||||
|
||||
if (new_z != m_Position.z)
|
||||
if (!IsClient() && new_z != m_Position.z)
|
||||
{
|
||||
if ((new_z > -2000) && new_z != BEST_Z_INVALID) {
|
||||
if (RuleB(Map, MobZVisualDebug))
|
||||
|
||||
@ -338,6 +338,7 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p)
|
||||
if (ztz->response <= 0) {
|
||||
zc2->success = ZONE_ERROR_NOTREADY;
|
||||
entity->CastToMob()->SetZone(ztz->current_zone_id, ztz->current_instance_id);
|
||||
entity->CastToClient()->SetZoning(false);
|
||||
}
|
||||
else {
|
||||
entity->CastToClient()->UpdateWho(1);
|
||||
|
||||
@ -1515,7 +1515,7 @@ void Zone::Repop(uint32 delay) {
|
||||
void Zone::GetTimeSync()
|
||||
{
|
||||
if (worldserver.Connected() && !zone_has_current_time) {
|
||||
auto pack = new ServerPacket(ServerOP_GetWorldTime, 0);
|
||||
auto pack = new ServerPacket(ServerOP_GetWorldTime, 1);
|
||||
worldserver.SendPacket(pack);
|
||||
safe_delete(pack);
|
||||
}
|
||||
|
||||
@ -49,7 +49,7 @@ class ZoneConfig : public EQEmuConfig {
|
||||
_zone_config=new ZoneConfig;
|
||||
_config=_zone_config;
|
||||
|
||||
return _config->ParseFile(EQEmuConfig::ConfigFile.c_str(),"server");
|
||||
return _config->parseFile();
|
||||
}
|
||||
|
||||
// Accessors for the static private object
|
||||
|
||||
@ -42,7 +42,7 @@ void Client::Handle_OP_ZoneChange(const EQApplicationPacket *app) {
|
||||
Bot::ProcessClientZoneChange(this);
|
||||
#endif
|
||||
|
||||
zoning = true;
|
||||
bZoning = true;
|
||||
if (app->size != sizeof(ZoneChange_Struct)) {
|
||||
Log(Logs::General, Logs::None, "Wrong size: OP_ZoneChange, size=%d, expected %d", app->size, sizeof(ZoneChange_Struct));
|
||||
return;
|
||||
@ -308,6 +308,8 @@ void Client::SendZoneCancel(ZoneChange_Struct *zc) {
|
||||
|
||||
//reset to unsolicited.
|
||||
zone_mode = ZoneUnsolicited;
|
||||
// reset since we're not zoning anymore
|
||||
bZoning = false;
|
||||
}
|
||||
|
||||
void Client::SendZoneError(ZoneChange_Struct *zc, int8 err)
|
||||
@ -327,6 +329,8 @@ void Client::SendZoneError(ZoneChange_Struct *zc, int8 err)
|
||||
|
||||
//reset to unsolicited.
|
||||
zone_mode = ZoneUnsolicited;
|
||||
// reset since we're not zoning anymore
|
||||
bZoning = false;
|
||||
}
|
||||
|
||||
void Client::DoZoneSuccess(ZoneChange_Struct *zc, uint16 zone_id, uint32 instance_id, float dest_x, float dest_y, float dest_z, float dest_h, int8 ignore_r) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user