Merge branch 'master' into lsid

This commit is contained in:
Akkadius 2018-01-17 22:04:46 -06:00
commit e0391dfcb8
50 changed files with 1915 additions and 1095 deletions

View File

@ -17,10 +17,7 @@
|:---:|:---:|:---:|
|**Install Count**|![Windows Install Count](http://analytics.akkadius.com/?install_count&windows_count)|![Linux Install Count](http://analytics.akkadius.com/?install_count&linux_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)

View File

@ -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");

View File

@ -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;
}

View File

@ -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;

View File

@ -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();
}

View File

@ -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();
}

View File

@ -164,7 +164,7 @@ namespace RoF2
ItemPacket11 = 111,
ItemPacket12 = 112,
ItemPacketRecovery = 113,
ItemPacket14 = 115
ItemPacket14 = 115 // Parcel? adds to merchant window too
};
} /*item*/

View File

@ -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)

View File

@ -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) {

View File

@ -46,7 +46,7 @@ public:
_chat_config=new queryservconfig;
_config=_chat_config;
return _config->ParseFile(EQEmuConfig::ConfigFile.c_str(),"server");
return _config->parseFile();
}
};

View File

@ -46,7 +46,7 @@ public:
_chat_config=new ucsconfig;
_config=_chat_config;
return _config->ParseFile(EQEmuConfig::ConfigFile.c_str(),"server");
return _config->parseFile();
}
};

View File

@ -0,0 +1,8 @@
{
"server": {
"world": {
"shortname": "setme",
"longname": "I Forgot To Edit My Config"
}
}
}

View 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"
}
}
}

View 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",
}

View 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"); }
}

View File

@ -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"};

View File

@ -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

View File

@ -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
View File

@ -0,0 +1,2 @@
eqemu_config.xml
eqemu_config.json

View File

@ -0,0 +1 @@
Converts the old eqemu_config.xml to eqemu_config.json

21
utils/xmltojson/build.bat Normal file
View 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
View 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
View 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)
}
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -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
}
}

View File

@ -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

View File

@ -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;
}

View File

@ -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

View File

@ -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);

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -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);
}

View File

@ -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);

View File

@ -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);

View File

@ -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;

View File

@ -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);
}
}
}
}

View File

@ -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);

View File

@ -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++) {

View File

@ -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))

View File

@ -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);

View File

@ -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);
}

View File

@ -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

View File

@ -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) {