diff --git a/CMakeLists.txt b/CMakeLists.txt index 64d53cc6e..dedce5c3f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -18,6 +18,8 @@ #EQEMU_BUILD_CLIENT_FILES #EQEMU_USE_MAP_MMFS #EQEMU_MAP_DIR +#EQEMU_ARCH +#EQEMU_ARCH_ALT CMAKE_MINIMUM_REQUIRED(VERSION 2.8) IF(POLICY CMP0074) @@ -49,33 +51,37 @@ ENDIF(MSVC OR MINGW) IF(MSVC) IF(CMAKE_CL_64) - SET(ZLIB_ROOT "${CMAKE_CURRENT_SOURCE_DIR}/dependencies/zlib_x64") - SET(MYSQL_ROOT "${CMAKE_CURRENT_SOURCE_DIR}/dependencies/mysql_x64") - SET(LUA_ROOT "${CMAKE_CURRENT_SOURCE_DIR}/dependencies/luaj_x64") - SET(OPENSSL_ROOT_DIR "${CMAKE_CURRENT_SOURCE_DIR}/dependencies/openssl_x64") - SET(SODIUM_INCLUDE_HINTS "${CMAKE_CURRENT_SOURCE_DIR}/dependencies/libsodium/include") - IF(MSVC_VERSION GREATER 1800) - SET(SODIUM_LIBRARY_HINTS "${CMAKE_CURRENT_SOURCE_DIR}/dependencies/libsodium/x64/Release/v140/dynamic") - ELSEIF(MSVC_VERSION EQUAL 1800) - SET(SODIUM_LIBRARY_HINTS "${CMAKE_CURRENT_SOURCE_DIR}/dependencies/libsodium/x64/Release/v120/dynamic") - ELSE() - SET(SODIUM_LIBRARY_HINTS "${CMAKE_CURRENT_SOURCE_DIR}/dependencies/libsodium/x64/Release/v110/dynamic") - ENDIF() + SET(EQEMU_ARCH "x64") + SET(EQEMU_ARCH_ALT "x64") ELSE(CMAKE_CL_64) - SET(ZLIB_ROOT "${CMAKE_CURRENT_SOURCE_DIR}/dependencies/zlib_x86") - SET(MYSQL_ROOT "${CMAKE_CURRENT_SOURCE_DIR}/dependencies/mysql_x86") - SET(LUA_ROOT "${CMAKE_CURRENT_SOURCE_DIR}/dependencies/luaj_x86") - SET(SODIUM_INCLUDE_HINTS "${CMAKE_CURRENT_SOURCE_DIR}/dependencies/libsodium/include") - SET(OPENSSL_ROOT_DIR "${CMAKE_CURRENT_SOURCE_DIR}/dependencies/openssl_x86") - IF(MSVC_VERSION GREATER 1800) - SET(SODIUM_LIBRARY_HINTS "${CMAKE_CURRENT_SOURCE_DIR}/dependencies/libsodium/Win32/Release/v140/dynamic") - ELSEIF(MSVC_VERSION EQUAL 1800) - SET(SODIUM_LIBRARY_HINTS "${CMAKE_CURRENT_SOURCE_DIR}/dependencies/libsodium/Win32/Release/v120/dynamic") - ELSE() - SET(SODIUM_LIBRARY_HINTS "${CMAKE_CURRENT_SOURCE_DIR}/dependencies/libsodium/Win32/Release/v110/dynamic") - ENDIF() + SET(EQEMU_ARCH "x86") + SET(EQEMU_ARCH_ALT "Win32") ENDIF(CMAKE_CL_64) - + + SET(MYSQL_ROOT "${CMAKE_CURRENT_SOURCE_DIR}/dependencies/mysql_${EQEMU_ARCH}") + + IF(VCPKG_TOOLCHAIN) + IF(NOT MSVC_VERSION GREATER 1800) + SET(SODIUM_INCLUDE_HINTS "${CMAKE_CURRENT_SOURCE_DIR}/dependencies/libsodium/include") + ENDIF() + ELSE(VCPKG_TOOLCHAIN) + SET(ZLIB_ROOT "${CMAKE_CURRENT_SOURCE_DIR}/dependencies/zlib_${EQEMU_ARCH}") + SET(LUA_ROOT "${CMAKE_CURRENT_SOURCE_DIR}/dependencies/luaj_${EQEMU_ARCH}") + SET(OPENSSL_ROOT_DIR "${CMAKE_CURRENT_SOURCE_DIR}/dependencies/openssl_${EQEMU_ARCH}") + + SET(SODIUM_INCLUDE_HINTS "${CMAKE_CURRENT_SOURCE_DIR}/dependencies/libsodium/include") + ENDIF(VCPKG_TOOLCHAIN) + + IF(SODIUM_INCLUDE_HINTS) + IF(MSVC_VERSION GREATER 1800) + SET(SODIUM_LIBRARY_HINTS "${CMAKE_CURRENT_SOURCE_DIR}/dependencies/libsodium/${EQEMU_ARCH_ALT}/Release/v140/dynamic") + ELSEIF(MSVC_VERSION EQUAL 1800) + SET(SODIUM_LIBRARY_HINTS "${CMAKE_CURRENT_SOURCE_DIR}/dependencies/libsodium/${EQEMU_ARCH_ALT}/Release/v120/dynamic") + ELSE() + SET(SODIUM_LIBRARY_HINTS "${CMAKE_CURRENT_SOURCE_DIR}/dependencies/libsodium/${EQEMU_ARCH_ALT}/Release/v110/dynamic") + ENDIF() + ENDIF(SODIUM_INCLUDE_HINTS) + #disable CRT warnings on windows cause they're annoying as shit and we use C functions everywhere OPTION(EQEMU_DISABLE_CRT_SECURE_WARNINGS "Disable Secure CRT Warnings" ON) IF(EQEMU_DISABLE_CRT_SECURE_WARNINGS) @@ -243,6 +249,8 @@ IF(ZLIB_FOUND) SET(SERVER_LIBS ${SERVER_LIBS} ${ZLIB_LIBRARY}) ENDIF() ELSE() + MESSAGE(STATUS "Could NOT find ZLIB - using ZLIBSTATIC package.") + SET(EQEMU_BUILD_ZLIB ON) INCLUDE_DIRECTORIES(BEFORE SYSTEM "${CMAKE_CURRENT_BINARY_DIR}/libs/zlibng" "${CMAKE_CURRENT_SOURCE_DIR}/libs/zlibng") SET(SERVER_LIBS ${SERVER_LIBS} "zlibstatic") ENDIF() diff --git a/README.md b/README.md index e1126241c..5e4f919fe 100644 --- a/README.md +++ b/README.md @@ -70,3 +70,9 @@ forum, although pull requests will be much quicker and easier on all parties. * GPL Perl - GPL / ActiveState (under the assumption that this is a free project) * CPPUnit - GLP StringUtilities - Apache * LUA - MIT + +## Contributors + + + + diff --git a/changelog.txt b/changelog.txt index dc9d0f16c..3f69bf570 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,11 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- +== 8/6/2019 == +Akkadius: Optimizations to movement updates to eliminate ghosting possibilities in larger zones + +== 7/22/2019 == +Uleat: Added script 'vcxproj_dependencies.py' - a script to help determine conflicting project dependencies (alpha-stage) + == 7/10/2019 == Akkadius: Add #npcedit flymode [0 = ground, 1 = flying, 2 = levitate, 3 = water, 4 = floating] diff --git a/common/emu_constants.h b/common/emu_constants.h index 384aa3768..c6491b3b3 100644 --- a/common/emu_constants.h +++ b/common/emu_constants.h @@ -77,6 +77,10 @@ namespace EQEmu } // namespace invtype + namespace DevTools { + const int32 GM_ACCOUNT_STATUS_LEVEL = 150; + } + namespace popupresponse { const int32 SERVER_INTERNAL_USE_BASE = 2000000000; const int32 MOB_INFO_DISMISS = 2000000001; diff --git a/common/patches/sof.cpp b/common/patches/sof.cpp index 7569b4c5d..bb310a76c 100644 --- a/common/patches/sof.cpp +++ b/common/patches/sof.cpp @@ -1116,7 +1116,10 @@ namespace SoF } OUT(deity); OUT(intoxication); + OUT_array(spellSlotRefresh, spells::SPELL_GEM_COUNT); + eq->spellSlotRefresh[9] = 0; // 10th slot is not valid in this release + OUT(abilitySlotRefresh); OUT(points); // Relocation Test // OUT(unknown0166[4]); @@ -1177,7 +1180,10 @@ namespace SoF } // OUT(unknown4184[128]); + OUT_array(mem_spells, spells::SPELL_GEM_COUNT); + eq->mem_spells[9] = 0xFFFFFFFFU; // 10th slot is not valid in this release + // OUT(unknown04396[32]); OUT(platinum); OUT(gold); diff --git a/common/patches/sof_limits.h b/common/patches/sof_limits.h index 399ff8c1a..a03676f2d 100644 --- a/common/patches/sof_limits.h +++ b/common/patches/sof_limits.h @@ -328,7 +328,11 @@ namespace SoF const int SPELL_ID_MAX = 15999; const int SPELLBOOK_SIZE = 480; + // Be careful not to confuse these two..SoF disc release has a special requirement... + // - The number of available spell gems HAS NOT increased from 9 at this point + // - The profile allocation HAS increased to 10 at this point const int SPELL_GEM_COUNT = static_cast(CastingSlot::MaxGems); + const int SPELL_GEM_PROFILE_SIZE = 10; // special case declaration const int LONG_BUFFS = 25; const int SHORT_BUFFS = 15; diff --git a/common/patches/sof_structs.h b/common/patches/sof_structs.h index 93b185e23..4d4a591e1 100644 --- a/common/patches/sof_structs.h +++ b/common/patches/sof_structs.h @@ -885,7 +885,7 @@ struct PlayerProfile_Struct //23576 Octets /*00060*/ BindStruct binds[5]; // Bind points (primary is first) /*00160*/ uint32 deity; // deity /*00164*/ uint32 intoxication; // Alcohol level (in ticks till sober?) -/*00168*/ uint32 spellSlotRefresh[spells::SPELL_GEM_COUNT]; // Refresh time (millis) - 4 Octets Each +/*00168*/ uint32 spellSlotRefresh[spells::SPELL_GEM_PROFILE_SIZE]; // Refresh time (millis) - 4 Octets Each /*00208*/ uint32 abilitySlotRefresh; /*00212*/ uint8 haircolor; // Player hair color /*00213*/ uint8 beardcolor; // Player beard color @@ -912,7 +912,7 @@ struct PlayerProfile_Struct //23576 Octets /*04173*/ uint8 unknown02264[147]; // was [139] /*04312*/ uint32 spell_book[spells::SPELLBOOK_SIZE]; // List of the Spells in spellbook 480 = 60 pages /*06232*/ uint8 unknown4184[128]; // was [136] -/*06396*/ uint32 mem_spells[spells::SPELL_GEM_COUNT]; // List of spells memorized +/*06396*/ uint32 mem_spells[spells::SPELL_GEM_PROFILE_SIZE]; // List of spells memorized /*06436*/ uint8 unknown04396[28]; //#### uint8 unknown04396[32]; in Titanium ####[28] /*06464*/ uint32 platinum; // Platinum Pieces on player /*06468*/ uint32 gold; // Gold Pieces on player @@ -3768,7 +3768,7 @@ struct AnnoyingZoneUnknown_Struct { }; struct LoadSpellSet_Struct { - uint32 spell[spells::SPELL_GEM_COUNT]; + uint32 spell[spells::SPELL_GEM_PROFILE_SIZE]; uint32 unknown; }; diff --git a/common/ruletypes.h b/common/ruletypes.h index 821f36c0d..16f29fc17 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -240,7 +240,7 @@ RULE_BOOL (World, IPLimitDisconnectAll, false) RULE_BOOL(World, MaxClientsSimplifiedLogic, false) // New logic that only uses ExemptMaxClientsStatus and MaxClientsPerIP. Done on the loginserver. This mimics the P99-style special IP rules. RULE_INT (World, TellQueueSize, 20) RULE_BOOL(World, StartZoneSameAsBindOnCreation, true) //Should the start zone ALWAYS be the same location as your bind? -RULE_BOOL(World, DisallowDuplicateAccountLogins, true) +RULE_BOOL(World, EnforceCharacterLimitAtLogin, false) RULE_CATEGORY_END() RULE_CATEGORY(Zone) diff --git a/common/servertalk.h b/common/servertalk.h index 1b34d3918..73014e11a 100644 --- a/common/servertalk.h +++ b/common/servertalk.h @@ -80,6 +80,7 @@ #define ServerOP_GroupJoin 0x003e //for joining ooz folks #define ServerOP_UpdateSpawn 0x003f #define ServerOP_SpawnStatusChange 0x0040 +#define ServerOP_DropClient 0x0041 // DropClient #define ServerOP_ReloadTasks 0x0060 #define ServerOP_DepopAllPlayersCorpses 0x0061 #define ServerOP_ReloadTitles 0x0062 @@ -320,11 +321,17 @@ struct ServerZoneIncomingClient_Struct { uint32 accid; int16 admin; uint32 charid; + uint32 lsid; bool tellsoff; char charname[64]; char lskey[30]; }; +struct ServerZoneDropClient_Struct +{ + uint32 lsid; +}; + struct ServerChangeWID_Struct { uint32 charid; uint32 newwid; diff --git a/queryserv/database.cpp b/queryserv/database.cpp index 02d985a8b..a1261e3a9 100644 --- a/queryserv/database.cpp +++ b/queryserv/database.cpp @@ -379,6 +379,7 @@ void Database::LogMerchantTransaction(QSMerchantLogTransaction_Struct* QS, uint3 } +// this function does not delete the ServerPacket, so it must be handled at call site void Database::GeneralQueryReceive(ServerPacket *pack) { /* These are general queries passed from anywhere in zone instead of packing structures and breaking them down again and again @@ -393,7 +394,6 @@ void Database::GeneralQueryReceive(ServerPacket *pack) { Log(Logs::Detail, Logs::QS_Server, "%s", query.c_str()); } - safe_delete(pack); safe_delete_array(queryBuffer); } diff --git a/ucs/clientlist.cpp b/ucs/clientlist.cpp index b7d1f01b3..c5d9c0927 100644 --- a/ucs/clientlist.cpp +++ b/ucs/clientlist.cpp @@ -470,7 +470,7 @@ static void ProcessCommandIgnore(Client *c, std::string Ignoree) { Clientlist::Clientlist(int ChatPort) { EQStreamManagerInterfaceOptions chat_opts(ChatPort, false, false); chat_opts.opcode_size = 1; - chat_opts.daybreak_options.stale_connection_ms = 300000; + chat_opts.daybreak_options.stale_connection_ms = 600000; chat_opts.daybreak_options.resend_delay_ms = RuleI(Network, ResendDelayBaseMS); chat_opts.daybreak_options.resend_delay_factor = RuleR(Network, ResendDelayFactor); chat_opts.daybreak_options.resend_delay_min = RuleI(Network, ResendDelayMinMS); diff --git a/utils/scripts/.gitignore b/utils/scripts/.gitignore index eba9f59cb..76f70485c 100644 --- a/utils/scripts/.gitignore +++ b/utils/scripts/.gitignore @@ -1 +1,2 @@ -opcode_handlers_output \ No newline at end of file +opcode_handlers_output +vcxproj_dependencies_output diff --git a/utils/scripts/eqemu_server.pl b/utils/scripts/eqemu_server.pl index f05c2c266..b2a53b864 100644 --- a/utils/scripts/eqemu_server.pl +++ b/utils/scripts/eqemu_server.pl @@ -1587,22 +1587,24 @@ sub add_login_server_firewall_rules { print "If firewall rules don't add you must run this script (eqemu_server.pl) as administrator\n"; print "\n"; print "[Install] Instructions \n"; - print "[Install] In order to connect your server to the loginserver you must point your eqemu_config.xml to your local server similar to the following:\n"; + print "[Install] In order to connect your server to the loginserver you must point your eqemu_config.json to your local server similar to the following:\n"; print " - - login.eqemulator.net - 5998 - - - - - 127.0.0.1 - 5998 - - - + \"loginserver1\" : { + \"account\" : \"\", + \"host\" : \"login.eqemulator.net\", + \"password\" : \"\", + \"port\" : \"5998\", + \"legacy\": \"1\" + }, + \"loginserver2\" : { + \"account\" : \"\", + \"host\" : \"192.168.197.129\", + \"password\" : \"\", + \"port\" : \"5998\" + }, + \"localaddress\" : \"192.168.197.129\", "; - print "[Install] When done, make sure your EverQuest client points to your loginserver's IP (In this case it would be 127.0.0.1) in the eqhosts.txt file\n"; + print "[Install] When done, make sure your EverQuest client points to your loginserver's IP (In this case it would be 192.168.197.129) in the eqhosts.txt file\n"; } } diff --git a/utils/scripts/vcxproj_dependencies.py b/utils/scripts/vcxproj_dependencies.py new file mode 100644 index 000000000..f50c9aec4 --- /dev/null +++ b/utils/scripts/vcxproj_dependencies.py @@ -0,0 +1,793 @@ +#! /usr/bin/env python +# + +""" +'VCXProj-Dependencies' for EQEmulator + +This script locates external dependency paths and generates lists for each +project. In addition, it will cross-check these lists to determine if any +discrepancies exist for any dependencies globally and across all projects. + +""" + + +import sys +import os +import fnmatch + +try: + import xml.etree.cElementTree as ElementTree +except ImportError: + import xml.etree.ElementTree as ElementTree + +from time import time, ctime + + +QUIET_REPORT = True + +include_projects = [] +exclude_projects = ['VCTargetsPath', 'CompilerIdC', 'CompilerIdCXX'] # these three should be left in by default + +base_path = os.getcwd()[:-14] # '/utils/scripts' +base_path = base_path.replace('\\', '/') + +file_extensions = ['vcxproj'] +project_paths = [] +master_dependencies = [] +# {[project]:{[build]:{[resource]:{[reference]:[paths]}}}} +project_dependencies = {} + +out_files = {} + +col1 = '{0}'.format(' ' * 0) +col2 = '{0}'.format(' ' * 2) +col3 = '{0}'.format(' ' * 4) +col4 = '{0}'.format(' ' * 6) +col5 = '{0}'.format(' ' * 8) + + +def main(): + """ main """ + + if not create_output_directory(): + exit() + + if not open_output_files(): + exit() + + print 'Locating project paths...' + locate_project_paths() + print '..project count: {0}'.format(len(project_paths)) + print 'Parsing project files...' + parse_project_files() + print 'Building master dependencies...' + build_master_dependencies() + print '..dependency count: {0}'.format(len(master_dependencies)) + print 'Checking for version discrepancies...' + check_for_version_discrepancies() + close_output_files() + print '\n__fin__' + + return + + +def create_output_directory(): + """ Check for output directory - create if does not exist """ + + try: + output_path = '{0}/utils/scripts/vcxproj_dependencies_output'.format(base_path) + if not os.path.exists(output_path): + os.mkdir(output_path) + + return True + + except IOError: + print('(Exception Error: {0}) create_output_directory()'.format(sys.exc_info()[0])) + + return False + + +def open_output_files(): + """ Open all output files """ + + try: + file_name = '{0}/utils/scripts/vcxproj_dependencies_output/ProjectPaths.txt'.format(base_path) + out_files['ProjectPaths'] = open(file_name, 'w') + file_name = '{0}/utils/scripts/vcxproj_dependencies_output/MasterDependencies.txt'.format(base_path) + out_files['MasterDependencies'] = open(file_name, 'w') + file_name = '{0}/utils/scripts/vcxproj_dependencies_output/ProjectDependencies.txt'.format(base_path) + out_files['ProjectDependencies'] = open(file_name, 'w') + file_name = '{0}/utils/scripts/vcxproj_dependencies_output/ContextTree.txt'.format(base_path) + out_files['ContextTree'] = open(file_name, 'w') + file_name = '{0}/utils/scripts/vcxproj_dependencies_output/DiscrepancyReport.txt'.format(base_path) + out_files['DiscrepancyReport'] = open(file_name, 'w') + for file in out_files: + out_files[file].write('>> \'VCXProj-Dependencies\' {0} file\n'.format(file)) + out_files[file].write('>> file generated @ {0}\n\n'.format(ctime(time()))) + + return True + + except IOError: + print('(Exception Error: {0}) open_output_files()'.format(sys.exc_info()[0])) + close_output_files() + + return False + + +def locate_project_paths(): + """ Locate vcxproj files in the build folder """ + + for root, dirs, files in os.walk('{0}/build'.format(base_path)): + for name in files: + project = name.split('.')[0] + if not len(include_projects) == 0 and project not in include_projects: + continue + if not len(exclude_projects) == 0 and project in exclude_projects: + continue + for extension in file_extensions: + if fnmatch.fnmatch(name, '*.{0}'.format(extension)): + project_paths.append(os.path.join(root, name).replace('\\', '/').lower()) + for path in project_paths: + out_files['ProjectPaths'].write('{0};\n'.format(path)) + + return + + +def fixup_path(project_path, dependency_path): + """ Fix-up malformed dependency paths """ + + trailing = dependency_path.replace('\\', '/') + if '../' in trailing: + if trailing[:3] == '../': # windows + leading = project_path[:project_path.rfind('/')] + while trailing[:3] == '../': + leading = leading[:leading.rfind('/')] + trailing = trailing[3:] + trailing = trailing.lower() + trailing = '{0}/{1}'.format(leading, trailing) + else: # unix + print '..processing unix-style path fix-up' + while '../' in trailing: + backout = trailing.find('../') + backdir = trailing.rfind('/', 0, backout - 1) + trailing = trailing.replace(trailing[backdir:backout + 2], '', 1) + trailing = trailing.lower() + else: + trailing = trailing.lower() + + return trailing + + +def parse_project_files(): + """ Parse each vcxproj file's xml data """ + + for key1 in project_paths: + with open(key1, 'r') as vcxproj_file: + project_dependencies[key1] = {} + xml_tree = ElementTree.ElementTree(file=vcxproj_file) + for element1 in xml_tree.getroot(): + if not element1.tag[-19:] == 'ItemDefinitionGroup': + continue + # add '.split('|')[0]' to remove the '|Win##' attribute + key2 = element1.attrib['Condition'].split('==')[1][1:-1] + project_dependencies[key1][key2] = {} + for element2 in element1.getiterator(): + if element2.tag[-9:] == 'ClCompile': + key3 = element2.tag[-9:] + project_dependencies[key1][key2][key3] = {} + for element3 in element2.getiterator(): + if element3.tag[-28:] == 'AdditionalIncludeDirectories': + key4 = element3.tag[-28:] + project_dependencies[key1][key2][key3][key4] = [] + paths = element3.text.split(';') + for path in paths: + project_dependencies[key1][key2][key3][key4].append(fixup_path(key1, path)) + elif element2.tag[-15:] == 'ResourceCompile': + key3 = element2.tag[-15:] + project_dependencies[key1][key2][key3] = {} + for element3 in element2.getiterator(): + if element3.tag[-28:] == 'AdditionalIncludeDirectories': + key4 = element3.tag[-28:] + project_dependencies[key1][key2][key3][key4] = [] + paths = element3.text.split(';') + for path in paths: + project_dependencies[key1][key2][key3][key4].append(fixup_path(key1, path)) + elif element2.tag[-4:] == 'Midl': + key3 = element2.tag[-4:] + project_dependencies[key1][key2][key3] = {} + for element3 in element2.getiterator(): + if element3.tag[-28:] == 'AdditionalIncludeDirectories': + key4 = element3.tag[-28:] + project_dependencies[key1][key2][key3][key4] = [] + paths = element3.text.split(';') + for path in paths: + project_dependencies[key1][key2][key3][key4].append(fixup_path(key1, path)) + elif element2.tag[-4:] == 'Link': + key3 = element2.tag[-4:] + project_dependencies[key1][key2][key3] = {} + for element3 in element2.getiterator(): + if element3.tag[-22:] == 'AdditionalDependencies': + key4 = element3.tag[-22:] + project_dependencies[key1][key2][key3][key4] = [] + paths = element3.text.split(';') + for path in paths: + project_dependencies[key1][key2][key3][key4].append(fixup_path(key1, path)) + if element3.tag[-28:] == 'AdditionalLibraryDirectories': + key4 = element3.tag[-28:] + project_dependencies[key1][key2][key3][key4] = [] + paths = element3.text.split(';') + for path in paths: + project_dependencies[key1][key2][key3][key4].append(fixup_path(key1, path)) + vcxproj_file.close() + + return + + +def build_master_dependencies(): + """ Build master dependencies list """ + + def write(message): + """ internal 'ProjectDependencies' write method - performed here so processing takes place after fix-up """ + + out_files['ProjectDependencies'].write('{0}\n'.format(message)) + + return + + for key1 in project_dependencies: + write('{0}'.format(col1, key1)) + for key2 in project_dependencies[key1]: + write('{0}'.format(col2, key2)) + for key3 in project_dependencies[key1][key2]: + write('{0}'.format(col3, key3)) + for key4 in project_dependencies[key1][key2][key3]: + write('{0}'.format(col4, key4)) + for path in project_dependencies[key1][key2][key3][key4]: + write('{0}{1}'.format(col4, path)) + if path not in master_dependencies: + master_dependencies.append(path) + write('{0}'.format(col4)) + write('{0}'.format(col3)) + write('{0}'.format(col2)) + write('{0}'.format(col1)) + master_dependencies.sort() + for path in master_dependencies: + out_files['MasterDependencies'].write('{0}\n'.format(path)) + + return + + +def check_for_version_discrepancies(): + """ Check for dependency version discrepancies """ + + def twrite(message): + """ internal 'ContextTree' write method """ + + out_files['ContextTree'].write('{0}\n'.format(message)) + + return + + def rwrite(message): + """ internal 'DiscrepancyReport' write method """ + + out_files['DiscrepancyReport'].write('{0}\n'.format(message)) + + return + + libraries = [ + 'mysql', + 'zlib', + 'perl', + 'lua', + 'boost', + 'sodium', + 'openssl' + ] + references = [ + 'include', + 'source', + 'library' + ] + priorities = { + 0: 'NOT FOUND', + 1: 'install', + 2: 'dependencies', + 3: 'libs', + 4: 'vcpkg', + 5: 'static', + 6: 'submodule' + } + # use all lowercase for path description + # use forward slash ('/') for directory name separators + # use '|' token for multiple hints ('my_file_path_1|my_file_path_2') + # use '!!' token for explicit argument ('/perl/core!!' will find '../perl/core' but not '../perl/core/perl512.lib') + # use '##' token for joined hints ('my_file_##_1') + # use '&&', '^' and '@' tokens for multiple argument hints ('my_file_&&path_1^path_2^path_3@') + # (i.e., 'my_file_path_1|my_file_##_2|my_##_##&&_3^_4!!@') + # {[library]:{[reference]:[[priority]:hint]}} + hints = { + # Notes: + 'mysql': { + 'include': [ + '', # 'NOT FOUND' + '', # 'install' + '/dependencies/mysql_##/include', # 'dependencies' + '', # 'libs' + '', # 'vcpkg' + '', # 'static' + '' # 'submodule' + ], + 'source': [ + '', # 'NOT FOUND' + '', # 'install' + '', # 'dependencies' + '', # 'libs' + '', # 'vcpkg' + '', # 'static' + '' # 'submodule' + ], + 'library': [ + '', # 'NOT FOUND' + '', # 'install' + 'dependencies/mysql_##/lib', # 'dependencies' + '', # 'libs' + '', # 'vcpkg' + '', # 'static' + '' # 'submodule' + ] + }, + 'zlib': { + 'include': [ + '', # 'NOT FOUND' + '', # 'install' + '/server/dependencies/zlib_x##/include', # 'dependencies' + # not sure if this should be '/libs/zlibng' or '/build/libs/zlibng' based on cmake behavior + '/server/build/libs/zlibng!!', # 'libs' + '/server/vcpkg/vcpkg-export-##/installed/x##-windows/include', # 'vcpkg' + '/server/build/libs/zlibng!!', # 'static' + '' # 'submodule' + ], + 'source': [ + '', # 'NOT FOUND' + '', # 'install' + '', # 'dependencies' + '/server/libs/zlibng!!', # 'libs' + '', # 'vcpkg' + '', # 'static' + '' # 'submodule' + ], + 'library': [ + '', # 'NOT FOUND' + '', # 'install' + '/server/dependencies/zlib_x##/lib/zdll.lib', # 'dependencies' + '', # 'libs' + '/server/vcpkg/vcpkg-export-##/installed/x##-windows/&&lib/zlib.lib!!' + '^debug/lib/zlibd.lib!!@', # 'vcpkg' + '/server/build/libs/zlibng/&&debug/zlibstaticd.lib!!^minsizerel/zlibstatic.lib!!' + '^release/zlibstatic.lib!!^relwithdebinfo/zlibstatic.lib!!@', # 'static' + '' # 'submodule' + ] + }, + 'perl': { + 'include': [ + '', # 'NOT FOUND' + '/perl/lib/core!!', # 'install' + '', # 'dependencies' + '', # 'libs' + '', # 'vcpkg' + '', # 'static' + '' # 'submodule' + ], + 'source': [ + '', # 'NOT FOUND' + '', # 'install' + '', # 'dependencies' + '', # 'libs' + '', # 'vcpkg' + '', # 'static' + '' # 'submodule' + ], + 'library': [ + '', # 'NOT FOUND' + '/perl/lib/core/perl51##.lib', # 'install' + '', # 'dependencies' + '', # 'libs' + '', # 'vcpkg' + '', # 'static' + '' # 'submodule' + ] + }, + 'lua': { + 'include': [ + '', # 'NOT FOUND' + '', # 'install' + '/server/dependencies/luaj_x##/src', # 'dependencies' + '', # 'libs' + '/server/vcpkg/vcpkg-export-##/installed/x##-windows/include', # 'vcpkg' + '', # 'static' + '' # 'submodule' + ], + 'source': [ + '', # 'NOT FOUND' + '', # 'install' + '/server/dependencies/luaj_x##/src', # 'dependencies' + '', # 'libs' + '', # 'vcpkg' + '', # 'static' + '' # 'submodule' + ], + 'library': [ + '', # 'NOT FOUND' + '', # 'install' + '/server/dependencies/luaj_x##/bin/lua51.lib', # 'dependencies' + '', # 'libs' + # debug lua package likely incorrect..should be 'lua51d.lib' - or whatever debug version is + '/server/vcpkg/vcpkg-export-##/installed/x##-windows/&&lib/lua51.lib!!' + '^debug/lib/lua51.lib!!@', # 'vcpkg' + '', # 'static' + '' # 'submodule' + ] + }, + 'boost': { + 'include': [ + '', # 'NOT FOUND' + '', # 'install' + '/server/dependencies/boost', # 'dependencies' + '', # 'libs' + '/server/vcpkg/vcpkg-export-##/installed/x##-windows/include', # 'vcpkg' + '', # 'static' + '' # 'submodule' + ], + 'source': [ + '', # 'NOT FOUND' + '', # 'install' + '', # 'dependencies' + '', # 'libs' + '/server/vcpkg/vcpkg-export-##/installed/x##-windows/include', # 'vcpkg' + '', # 'static' + '' # 'submodule' + ], + 'library': [ + '', # 'NOT FOUND' + '', # 'install' + '/server/dependencies/boost', # 'dependencies' + '', # 'libs' + '/server/vcpkg/vcpkg-export-##/installed/x##-windows/lib!!', # 'vcpkg' + '', # 'static' + '' # 'submodule' + ] + }, + 'sodium': { + 'include': [ + '', # 'NOT FOUND' + '', # 'install' + '/server/dependencies/libsodium/include', # 'dependencies' + '', # 'libs' + '/server/vcpkg/vcpkg-export-##/installed/x##-windows/include', # 'vcpkg' + '', # 'static' + '' # 'submodule' + ], + 'source': [ + '', # 'NOT FOUND' + '', # 'install' + '', # 'dependencies' + '', # 'libs' + '', # 'vcpkg' + '', # 'static' + '' # 'submodule' + ], + 'library': [ + '', # 'NOT FOUND' + '', # 'install' + '/server/dependencies/libsodium/##/dynamic/libsodium.lib', # 'dependencies' + '', # 'libs' + # debug libsodium package likely incorrect..should be 'libsodiumd.lib' - or whatever debug version is + '/server/vcpkg/vcpkg-export-##/installed/x##-windows/&&lib/libsodium.lib!!^' + 'debug/lib/libsodium.lib!!@', # 'vcpkg' + '', # 'static' + '' # 'submodule' + ] + }, + 'openssl': { + 'include': [ + '', # 'NOT FOUND' + '', # 'install' + '/server/dependencies/openssl_x##/include', # 'dependencies' + '', # 'libs' + '/server/vcpkg/vcpkg-export-##/installed/x##-windows/include', # 'vcpkg' + '', # 'static' + '' # 'submodule' + ], + 'source': [ + '', # 'NOT FOUND' + '', # 'install' + '', # 'dependencies' + '', # 'libs' + '', # 'vcpkg' + '', # 'static' + '' # 'submodule' + ], + 'library': [ + '', # 'NOT FOUND' + '', # 'install' + '/server/dependencies/openssl_x##/lib/VC/&&libeay32MD.lib!!^libeay32MDd.lib!!^' + 'ssleay32MD.lib!!^ssleay32MDd.lib!!@', # 'dependencies' + '', # 'libs' + # debug openssl package likely incorrect..should be + # 'libeay32d.lib' and 'ssleay32d.lib' - or whatever debug versions are + '/server/vcpkg/vcpkg-export-##/installed/x##-windows/&&lib/libeay32.lib!!^' + 'lib/ssleay32.lib!!^debug/lib/libeay32.lib!!^debug/lib/ssleay32.lib!!@', # 'vcpkg' + '', # 'static' + '' # 'submodule' + ] + } + } + # {[project]:{[build]:{[resource]:{[library]:{[reference]:priority}}}}} + context_tree = {} + # {[library]:priority} + global_priorities = {} + # {[build]:{[library]:priority}} + build_priorities = {} + # loop for discovering first occurence dependency sources (assumes same search precedence as compiler includes) + for project in project_dependencies: + if project not in context_tree.keys(): + context_tree[project] = {} + for build in project_dependencies[project]: + if build not in context_tree[project].keys(): + context_tree[project][build] = {} + if build not in build_priorities.keys(): + build_priorities[build] = {} + for resource in project_dependencies[project][build]: + if resource not in context_tree[project][build].keys(): + context_tree[project][build][resource] = {} + for reference_project in project_dependencies[project][build][resource]: + for path in project_dependencies[project][build][resource][reference_project]: + for library in libraries: + if library not in context_tree[project][build][resource].keys(): + context_tree[project][build][resource][library] = {} + if library not in build_priorities[build].keys(): + build_priorities[build][library] = 0 + if library not in global_priorities.keys(): + global_priorities[library] = 0 + for reference in references: + if reference not in context_tree[project][build][resource][library].keys(): + context_tree[project][build][resource][library][reference] = 0 + elif not context_tree[project][build][resource][library][reference] == 0: + continue + for priority in priorities: + if hints[library][reference][priority] == '': + continue + hint_found = False + for hint in hints[library][reference][priority].split('|'): + if not find_hint_in_path(hint, path) == -1: + context_tree[project][build][resource][library][reference] = priority + if context_tree[project][build][resource][library][reference] >\ + build_priorities[build][library]: + build_priorities[build][library] =\ + context_tree[project][build][resource][library][reference] + if context_tree[project][build][resource][library][reference] >\ + global_priorities[library]: + global_priorities[library] =\ + context_tree[project][build][resource][library][reference] + hint_found = True + break + if hint_found is True: + break + # loop for hack to fix odd behavior caused by 'FindZLIB.cmake' - ref: '../server/build/libs/zlibng/zconf.h' + # this does not change anything in the build files..only silences a false discrepancy due to mixing priority types + if global_priorities['zlib'] == 5: + for project in context_tree: + for build in context_tree[project]: + for resource in context_tree[project][build]: + if context_tree[project][build][resource]['zlib']['source'] == 3: + context_tree[project][build][resource]['zlib']['source'] = 5 + if context_tree[project][build][resource]['zlib']['include'] == 3: + context_tree[project][build][resource]['zlib']['include'] = 5 + # loop for dumping 'global_priorities' + twrite('{0}'.format(col1)) + for library in libraries: + twrite('{0}{2}'.format(col2, library, global_priorities[library])) + twrite('{0}'.format(col1)) + twrite('') + # loop for dumping 'build_priorities' + for build in build_priorities: + twrite('{0}'.format(col1, build)) + for library in libraries: + twrite('{0}{2}'.format(col2, library, build_priorities[build][library])) + twrite('{0}'.format(col1)) + twrite('') + # loop for dumping 'context_tree' + for project in context_tree: + twrite('{0}'.format(col1, project)) + for build in context_tree[project]: + twrite('{0}'.format(col2, build)) + for resource in context_tree[project][build]: + twrite('{0}'.format(col3, resource)) + for library in context_tree[project][build][resource]: + twrite('{0}'.format(col4, library)) + for reference in context_tree[project][build][resource][library]: + twrite( + '{0}{2}'.format( + col5, + reference, + context_tree[project][build][resource][library][reference] + ) + ) + twrite('{0}'.format(col4)) + twrite('{0}'.format(col3)) + twrite('{0}'.format(col2)) + twrite('{0}'.format(col1)) + if QUIET_REPORT is False: + for library in libraries: + rwrite( + '> Global Library \'{0}\' status: \'{1}\' ({2})'.format( + library, + priorities[global_priorities[library]], + global_priorities[library] + ) + ) + # loop for identifying dependency discrepancies + for project in context_tree: + for build in context_tree[project]: + for resource in context_tree[project][build]: + for library in context_tree[project][build][resource]: + if global_priorities[library] == 0: + if QUIET_REPORT is False: + rwrite( + '> No Global Library \'{0}\' .. skipping Project:Build:Resource' + ' "{1}":"{2}":"{3}"'.format( + library, + project, + build, + resource + ) + ) + continue + if build_priorities[build][library] == 0: + if QUIET_REPORT is False: + rwrite( + '> No Build Library \'{0}\' .. skipping Project:Build:Resource' + ' "{1}":"{2}":"{3}"'.format( + library, + project, + build, + resource + ) + ) + continue + for reference in context_tree[project][build][resource][library]: + if context_tree[project][build][resource][library][reference] == 0: + continue + if not global_priorities[library] == context_tree[project][build][resource][library][reference]: + rwrite( + '> Global-Project Library \'{0}\' mis-match \'{1}!={2}\'' + ' ({3}!={4}) Project:Build:Resource "{5}":"{6}":"{7}"'.format( + library, + priorities[global_priorities[library]], + priorities[context_tree[project][build][resource][library][reference]], + global_priorities[library], + context_tree[project][build][resource][library][reference], + project, + build, + resource + ) + ) + # 'builds' are allowed to have different dependencies..so, we'll start crossing at 'resource' + for cross_resource in context_tree[project][build]: + for cross_reference in context_tree[project][build][cross_resource][library]: + if cross_resource == resource and cross_reference == reference: + continue + if context_tree[project][build][cross_resource][library][cross_reference] == 0: + continue + if QUIET_REPORT is False and\ + not context_tree[project][build][cross_resource][library][cross_reference] ==\ + context_tree[project][build][resource][library][reference]: + rwrite( + '> Project Library \'{0}\' mis-match \'{1}:{2}:{3}!={4}:{5}:{6}\'' + ' ({7}!={8}) Project:Build "{9}":"{10}"'.format( + library, + resource, + reference, + priorities[context_tree[project][build][resource][library][reference]], + cross_resource, + cross_reference, + priorities[context_tree[project][build][cross_resource][library] + [cross_reference]], + context_tree[project][build][resource][library][reference], + context_tree[project][build][cross_resource][library][cross_reference], + project, + build + ) + ) + + return + + +def find_hint_in_path(hint, path): + """ + Helper function for parsing and checking for hints in paths + + Hints strings should be split ('|') and passed as a singular hint into this function + + """ + + if hint == '' or path == '': + return -1 + + joined_index = hint.find('##') + pretext_index = hint.find('&&') + if joined_index == -1 and pretext_index == -1: + if '^' in hint or '@' in hint: + print '..malformed or improper handling of hint: \'{0}\' path: \'{1}\''.format(hint, path) + + return -1 + + explicit_index = hint.find('!!') + if explicit_index == -1: + return path.find(hint) + + else: + explicit_hint = hint[:explicit_index] + found_index = path.find(explicit_hint) + if (len(explicit_hint) + found_index) == len(path): + return found_index + + else: + return -1 + + elif (not joined_index == -1 and pretext_index == -1) or\ + (not joined_index == -1 and not pretext_index == -1 and joined_index < pretext_index): + start_index = 0 + for partial_hint in hint.split('##', 1): + if partial_hint == '': + continue + found_index = find_hint_in_path(partial_hint, path[start_index:]) + if found_index == -1: + return found_index + + start_index = found_index + len(partial_hint) + + return start_index + + elif (joined_index == -1 and not pretext_index == -1) or\ + (not joined_index == -1 and not pretext_index == -1 and joined_index > pretext_index): + pretext_hints = hint.split('&&', 1) + found_index = 0 + if not pretext_hints[0] == '': + found_index = find_hint_in_path(pretext_hints[0], path) + if found_index == -1: + return found_index + + start_index = found_index + len(pretext_hints[0]) + partial_hints = pretext_hints[1].split('@', 1) + for partial_hint in partial_hints: + if partial_hint == '': + continue + for alt_hint in partial_hint.split('^'): + if alt_hint == '': + continue + found_index = find_hint_in_path(alt_hint, path[start_index:]) + if found_index == 0: + if not partial_hints[1] == '': + print '..unhandled hint method: \'{0}\''.format(partial_hints[1]) + else: + return found_index + + return -1 + + else: + return -1 + + +def close_output_files(): + """ Close all output files """ + + while not len(out_files) == 0: + key = out_files.keys()[0] + out_files[key].close() + del out_files[key] + + return + + +if __name__ == '__main__': + main() diff --git a/utils/sql/db_update_manifest.txt b/utils/sql/db_update_manifest.txt index 92bcfd260..4addfe6ce 100644 --- a/utils/sql/db_update_manifest.txt +++ b/utils/sql/db_update_manifest.txt @@ -393,7 +393,7 @@ 9137|2018_12_12_client_faction_tables.sql|SHOW TABLES LIKE 'faction_base_data'|empty| 9138|2018_12_12_convert_to_client_functions.sql|SELECT `id` FROM `faction_list` WHERE `id` > 4999|empty| 9139|2019_03_25_optional_npc_model.sql|SHOW COLUMNS FROM `npc_types` LIKE 'model'|empty| -9140|2019_07_03_update_range.sql|SHOW COLUMNS FROM `npc_types` LIKE 'max_movement_update_range'|empty| +9140|2019_07_03_update_range.sql|SHOW COLUMNS FROM `zone` LIKE 'max_movement_update_range'|empty| 9141|2019_07_10_npc_flymode.sql|SHOW COLUMNS FROM `npc_types` LIKE 'flymode'|empty| # Upgrade conditions: diff --git a/world/adventure.cpp b/world/adventure.cpp index c984fa831..af3cfe0e2 100644 --- a/world/adventure.cpp +++ b/world/adventure.cpp @@ -287,7 +287,7 @@ void Adventure::Finished(AdventureWinStatus ws) ClientListEntry *current = client_list.FindCharacter((*iter).c_str()); if(current) { - if(current->Online() == CLE_Status_InZone) + if(current->Online() == CLE_Status::InZone) { //We can send our packets only. auto pack = diff --git a/world/client.cpp b/world/client.cpp index 53ea40036..aa69e3ac7 100644 --- a/world/client.cpp +++ b/world/client.cpp @@ -88,7 +88,6 @@ extern volatile bool UCSServerAvailable_; Client::Client(EQStreamInterface* ieqs) : autobootup_timeout(RuleI(World, ZoneAutobootTimeoutMS)), - CLE_keepalive_timer(RuleI(World, ClientKeepaliveTimeoutMS)), connect(1000), eqs(ieqs) { @@ -105,6 +104,7 @@ Client::Client(EQStreamInterface* ieqs) char_name[0] = 0; charid = 0; zone_waiting_for_bootup = 0; + enter_world_triggered = false; StartInTutorial = false; m_ClientVersion = eqs->ClientVersion(); @@ -115,7 +115,7 @@ Client::Client(EQStreamInterface* ieqs) Client::~Client() { if (RunLoops && cle && zone_id == 0) - cle->SetOnline(CLE_Status_Offline); + cle->SetOnline(CLE_Status::Offline); numclients--; @@ -185,7 +185,7 @@ void Client::SendExpansionInfo() { void Client::SendCharInfo() { if (cle) { - cle->SetOnline(CLE_Status_CharSelect); + cle->SetOnline(CLE_Status::CharSelect); } if (m_ClientVersionBit & EQEmu::versions::maskRoFAndLater) { @@ -428,7 +428,7 @@ bool Client::HandleSendLoginInfoPacket(const EQApplicationPacket *app) // Track who is in and who is out of the game char *inout= (char *) ""; - if (cle->GetOnline() == CLE_Status_Never) { + if (cle->GetOnline() == CLE_Status::Never){ // Desktop -> Char Select inout = (char *) "In"; } @@ -441,7 +441,7 @@ bool Client::HandleSendLoginInfoPacket(const EQApplicationPacket *app) // Either from a fresh client launch or coming back from the game. // Exiting the game entirely does not come through here. // Could use a Logging Out Completely message somewhere. - cle->SetOnline(CLE_Status_CharSelect); + cle->SetOnline(CLE_Status::CharSelect); Log(Logs::General, Logs::World_Server, "Account (%s) Logging(%s) to character select :: LSID: %d ", @@ -1038,7 +1038,7 @@ bool Client::HandlePacket(const EQApplicationPacket *app) { { // I don't see this getting executed on logout eqs->Close(); - cle->SetOnline(CLE_Status_Offline); //allows this player to log in again without an ip restriction. + cle->SetOnline(CLE_Status::Offline); //allows this player to log in again without an ip restriction. return false; } case OP_ZoneChange: @@ -1081,15 +1081,15 @@ bool Client::Process() { Log(Logs::General, Logs::World_Server, "Zone bootup timer expired, bootup failed or too slow."); TellClientZoneUnavailable(); } + if(connect.Check()){ SendGuildList();// Send OPCode: OP_GuildsList SendApproveWorld(); connect.Disable(); } - if (CLE_keepalive_timer.Check()) { - if (cle) - cle->KeepAlive(); - } + + if (cle) + cle->KeepAlive(); /************ Get all packets from packet manager out queue and process them ************/ EQApplicationPacket *app = 0; @@ -1153,11 +1153,18 @@ void Client::EnterWorld(bool TryBootup) { else zone_server = zoneserver_list.FindByZoneID(zone_id); - const char *zone_name = database.GetZoneName(zone_id, true); if (zone_server) { - // warn the world we're comming, so it knows not to shutdown - zone_server->IncomingClient(this); + if (false == enter_world_triggered) { + //Drop any clients we own in other zones. + zoneserver_list.DropClient(GetLSID(), zone_server); + + // warn the zone we're coming + zone_server->IncomingClient(this); + + //tell the server not to trigger this multiple times before we get a zone unavailable + enter_world_triggered = true; + } } else { if (TryBootup) { @@ -1176,9 +1183,17 @@ void Client::EnterWorld(bool TryBootup) { return; } } + zone_waiting_for_bootup = 0; - if(!cle) { + if (GetAdmin() < 80 && zoneserver_list.IsZoneLocked(zone_id)) { + Log(Logs::General, Logs::World_Server, "Enter world failed. Zone is locked."); + TellClientZoneUnavailable(); + return; + } + + if (!cle) { + TellClientZoneUnavailable(); return; } @@ -1195,12 +1210,6 @@ void Client::EnterWorld(bool TryBootup) { ); if (seen_character_select) { - if (GetAdmin() < 80 && zoneserver_list.IsZoneLocked(zone_id)) { - Log(Logs::General, Logs::World_Server, "Enter world failed. Zone is locked."); - TellClientZoneUnavailable(); - return; - } - auto pack = new ServerPacket; pack->opcode = ServerOP_AcceptWorldEntrance; pack->size = sizeof(WorldToZone_Struct); @@ -1306,7 +1315,7 @@ void Client::Clearance(int8 response) safe_delete(outapp); if (cle) - cle->SetOnline(CLE_Status_Zoning); + cle->SetOnline(CLE_Status::Zoning); } void Client::TellClientZoneUnavailable() { @@ -1320,6 +1329,7 @@ void Client::TellClientZoneUnavailable() { zone_id = 0; zone_waiting_for_bootup = 0; + enter_world_triggered = false; autobootup_timeout.Disable(); } diff --git a/world/client.h b/world/client.h index d36dc692d..4dbad85c5 100644 --- a/world/client.h +++ b/world/client.h @@ -82,6 +82,7 @@ private: bool is_player_zoning; Timer autobootup_timeout; uint32 zone_waiting_for_bootup; + bool enter_world_triggered; bool StartInTutorial; EQEmu::versions::ClientVersion m_ClientVersion; @@ -94,7 +95,6 @@ private: void SetClassLanguages(PlayerProfile_Struct *pp); ClientListEntry* cle; - Timer CLE_keepalive_timer; Timer connect; bool firstlogin; bool seen_character_select; diff --git a/world/cliententry.cpp b/world/cliententry.cpp index 2ce17c53e..df245a930 100644 --- a/world/cliententry.cpp +++ b/world/cliententry.cpp @@ -83,8 +83,8 @@ ClientListEntry::ClientListEntry( memset(pLFGComments, 0, 64); } -ClientListEntry::ClientListEntry(uint32 in_id, ZoneServer *iZS, ServerClientList_Struct *scl, int8 iOnline) - : id(in_id) +ClientListEntry::ClientListEntry(uint32 in_id, ZoneServer* iZS, ServerClientList_Struct* scl, CLE_Status iOnline) +: id(in_id) { ClearVars(true); @@ -104,7 +104,7 @@ ClientListEntry::ClientListEntry(uint32 in_id, ZoneServer *iZS, ServerClientList pLFGMatchFilter = false; memset(pLFGComments, 0, 64); - if (iOnline >= CLE_Status_Zoning) { + if (iOnline >= CLE_Status::Zoning) { Update(iZS, scl, iOnline); } else { @@ -129,31 +129,39 @@ void ClientListEntry::SetChar(uint32 iCharID, const char *iCharName) strn0cpy(pname, iCharName, sizeof(pname)); } -void ClientListEntry::SetOnline(ZoneServer *iZS, int8 iOnline) -{ +void ClientListEntry::SetOnline(ZoneServer* iZS, CLE_Status iOnline) { if (iZS == this->Server()) { SetOnline(iOnline); } } -void ClientListEntry::SetOnline(int8 iOnline) +void ClientListEntry::SetOnline(CLE_Status iOnline) { - if (iOnline >= CLE_Status_Online && pOnline < CLE_Status_Online) { + Log(Logs::General, + Logs::World_Server, + "ClientListEntry::SetOnline for %s(%i) = %i", + AccountName(), + AccountID(), + iOnline); + + if (iOnline >= CLE_Status::Online && pOnline < CLE_Status::Online) { numplayers++; } - else if (iOnline < CLE_Status_Online && pOnline >= CLE_Status_Online) { + else if (iOnline < CLE_Status::Online && pOnline >= CLE_Status::Online) { numplayers--; } - if (iOnline != CLE_Status_Online || pOnline < CLE_Status_Online) { + if (iOnline != CLE_Status::Online || pOnline < CLE_Status::Online) { pOnline = iOnline; } - if (iOnline < CLE_Status_Zoning) { + if (iOnline < CLE_Status::Zoning) { Camp(); } - if (pOnline >= CLE_Status_Online) { + if (pOnline >= CLE_Status::Online) { stale = 0; } + } + void ClientListEntry::LSUpdate(ZoneServer *iZS) { if (WorldConfig::get()->UpdateStats) { @@ -184,8 +192,8 @@ void ClientListEntry::LSZoneChange(ZoneToZone_Struct *ztz) safe_delete(pack); } } -void ClientListEntry::Update(ZoneServer *iZS, ServerClientList_Struct *scl, int8 iOnline) -{ + +void ClientListEntry::Update(ZoneServer* iZS, ServerClientList_Struct* scl, CLE_Status iOnline) { if (pzoneserver != iZS) { if (pzoneserver) { pzoneserver->RemovePlayer(); @@ -232,7 +240,7 @@ void ClientListEntry::Update(ZoneServer *iZS, ServerClientList_Struct *scl, int8 SetOnline(iOnline); } -void ClientListEntry::LeavingZone(ZoneServer *iZS, int8 iOnline) +void ClientListEntry::LeavingZone(ZoneServer *iZS, CLE_Status iOnline) { if (iZS != 0 && iZS != pzoneserver) { return; @@ -250,8 +258,8 @@ void ClientListEntry::LeavingZone(ZoneServer *iZS, int8 iOnline) void ClientListEntry::ClearVars(bool iAll) { if (iAll) { - pOnline = CLE_Status_Never; - stale = 0; + pOnline = CLE_Status::Never; + stale = 0; pLSID = 0; memset(loginserver_account_name, 0, sizeof(loginserver_account_name)); @@ -299,12 +307,10 @@ bool ClientListEntry::CheckStale() { stale++; if (stale > 20) { - if (pOnline > CLE_Status_Offline) { - SetOnline(CLE_Status_Offline); - } - else { - return true; - } + if (pOnline > CLE_Status::Offline) + SetOnline(CLE_Status::Offline); + + return true; } return false; } diff --git a/world/cliententry.h b/world/cliententry.h index 30ba002d5..8cd21928c 100644 --- a/world/cliententry.h +++ b/world/cliententry.h @@ -8,13 +8,15 @@ #include "../common/rulesys.h" #include - -#define CLE_Status_Never -1 -#define CLE_Status_Offline 0 -#define CLE_Status_Online 1 // Will not overwrite more specific online status -#define CLE_Status_CharSelect 2 -#define CLE_Status_Zoning 3 -#define CLE_Status_InZone 4 +typedef enum +{ + Never, + Offline, + Online, + CharSelect, + Zoning, + InZone +} CLE_Status; class ZoneServer; struct ServerClientList_Struct; @@ -49,23 +51,26 @@ public: * @param scl * @param iOnline */ - ClientListEntry(uint32 id, ZoneServer *iZS, ServerClientList_Struct *scl, int8 iOnline); + ClientListEntry(uint32 id, uint32 iAccID, const char* iAccName, MD5& iMD5Pass, int16 iAdmin = 0); + ClientListEntry(uint32 id, ZoneServer* iZS, ServerClientList_Struct* scl, CLE_Status iOnline); ~ClientListEntry(); bool CheckStale(); - void Update(ZoneServer* zoneserver, ServerClientList_Struct* scl, int8 iOnline = CLE_Status_InZone); + void Update(ZoneServer* zoneserver, ServerClientList_Struct* scl, CLE_Status iOnline = CLE_Status::InZone); void LSUpdate(ZoneServer* zoneserver); void LSZoneChange(ZoneToZone_Struct* ztz); bool CheckAuth(uint32 loginserver_account_id, const char* key_password); - void SetOnline(ZoneServer* iZS, int8 iOnline); - void SetOnline(int8 iOnline = CLE_Status_Online); + bool CheckAuth(const char* iName, MD5& iMD5Password); + bool CheckAuth(uint32 id, const char* key, uint32 ip); + void SetOnline(ZoneServer* iZS, CLE_Status iOnline); + void SetOnline(CLE_Status iOnline = CLE_Status::Online); void SetChar(uint32 iCharID, const char* iCharName); - inline int8 Online() { return pOnline; } + inline CLE_Status Online() { return pOnline; } inline const uint32 GetID() const { return id; } inline const uint32 GetIP() const { return pIP; } inline void SetIP(const uint32& iIP) { pIP = iIP; } inline void KeepAlive() { stale = 0; } inline uint8 GetStaleCounter() const { return stale; } - void LeavingZone(ZoneServer* iZS = 0, int8 iOnline = CLE_Status_Offline); + void LeavingZone(ZoneServer* iZS = 0, CLE_Status iOnline = CLE_Status::Offline); void Camp(ZoneServer* iZS = 0); // Login Server stuff @@ -75,7 +80,7 @@ public: inline const char* LSName() const { return loginserver_account_name; } inline int16 WorldAdmin() const { return pworldadmin; } inline const char* GetLSKey() const { return plskey; } - inline const int8 GetOnline() const { return pOnline; } + inline const CLE_Status GetOnline() const { return pOnline; } // Account stuff inline uint32 AccountID() const { return paccountid; } @@ -118,8 +123,8 @@ private: const uint32 id; uint32 pIP; - int8 pOnline{}; - uint8 stale{}; + CLE_Status pOnline; + uint8 stale; // Login Server stuff char source_loginserver[64]{}; //Loginserver we came from. diff --git a/world/clientlist.cpp b/world/clientlist.cpp index 2614d1d71..4ac74e3b3 100644 --- a/world/clientlist.cpp +++ b/world/clientlist.cpp @@ -41,7 +41,7 @@ extern ZSList zoneserver_list; uint32 numplayers = 0; //this really wants to be a member variable of ClientList... ClientList::ClientList() -: CLStale_timer(45000) +: CLStale_timer(10000) { NextCLEID = 1; @@ -64,8 +64,6 @@ void ClientList::Process() { struct in_addr in; in.s_addr = iterator.GetData()->GetIP(); Log(Logs::Detail, Logs::World_Server,"Removing client from %s:%d", inet_ntoa(in), iterator.GetData()->GetPort()); -//the client destructor should take care of this. -// iterator.GetData()->Free(); iterator.RemoveCurrent(); } else @@ -122,7 +120,7 @@ void ClientList::GetCLEIP(uint32 iIP) { return; } else { Log(Logs::General, Logs::Client_Login, "Disconnect: Account %s on IP %s.", countCLEIPs->LSName(), long2ip(countCLEIPs->GetIP()).c_str()); - countCLEIPs->SetOnline(CLE_Status_Offline); + countCLEIPs->SetOnline(CLE_Status::Offline); iterator.RemoveCurrent(); continue; } @@ -138,7 +136,7 @@ void ClientList::GetCLEIP(uint32 iIP) { return; } else { Log(Logs::General, Logs::Client_Login, "Disconnect: Account %s on IP %s.", countCLEIPs->LSName(), long2ip(countCLEIPs->GetIP()).c_str()); - countCLEIPs->SetOnline(CLE_Status_Offline); // Remove the connection + countCLEIPs->SetOnline(CLE_Status::Offline); // Remove the connection iterator.RemoveCurrent(); continue; } @@ -150,7 +148,7 @@ void ClientList::GetCLEIP(uint32 iIP) { return; } else { Log(Logs::General, Logs::Client_Login, "Disconnect: Account %s on IP %s.", countCLEIPs->LSName(), long2ip(countCLEIPs->GetIP()).c_str()); - countCLEIPs->SetOnline(CLE_Status_Offline); // Remove the connection + countCLEIPs->SetOnline(CLE_Status::Offline); // Remove the connection iterator.RemoveCurrent(); continue; } @@ -161,7 +159,7 @@ void ClientList::GetCLEIP(uint32 iIP) { return; } else { Log(Logs::General, Logs::Client_Login, "Disconnect: Account %s on IP %s.", countCLEIPs->LSName(), long2ip(countCLEIPs->GetIP()).c_str()); - countCLEIPs->SetOnline(CLE_Status_Offline); // Remove the connection + countCLEIPs->SetOnline(CLE_Status::Offline); // Remove the connection iterator.RemoveCurrent(); continue; } @@ -182,7 +180,7 @@ uint32 ClientList::GetCLEIPCount(uint32 iIP) { while (iterator.MoreElements()) { countCLEIPs = iterator.GetData(); - if ((countCLEIPs->GetIP() == iIP) && ((countCLEIPs->Admin() < (RuleI(World, ExemptMaxClientsStatus))) || (RuleI(World, ExemptMaxClientsStatus) < 0)) && countCLEIPs->Online() >= CLE_Status_Online) { // If the IP matches, and the connection admin status is below the exempt status, or exempt status is less than 0 (no-one is exempt) + if ((countCLEIPs->GetIP() == iIP) && ((countCLEIPs->Admin() < (RuleI(World, ExemptMaxClientsStatus))) || (RuleI(World, ExemptMaxClientsStatus) < 0)) && countCLEIPs->Online() >= CLE_Status::Online) { // If the IP matches, and the connection admin status is below the exempt status, or exempt status is less than 0 (no-one is exempt) IPInstances++; // Increment the occurences of this IP address } iterator.Advance(); @@ -208,7 +206,7 @@ void ClientList::DisconnectByIP(uint32 iIP) { zoneserver_list.SendPacket(pack); safe_delete(pack); } - countCLEIPs->SetOnline(CLE_Status_Offline); + countCLEIPs->SetOnline(CLE_Status::Offline); iterator.RemoveCurrent(); } iterator.Advance(); @@ -255,19 +253,6 @@ ClientListEntry* ClientList::FindCLEByCharacterID(uint32 iCharID) { return nullptr; } -ClientListEntry* ClientList::FindCLEByLSID(uint32 iLSID) { - LinkedListIterator iterator(clientlist); - - iterator.Reset(); - while (iterator.MoreElements()) { - if (iterator.GetData()->LSID() == iLSID) { - return iterator.GetData(); - } - iterator.Advance(); - } - return nullptr; -} - void ClientList::SendCLEList(const int16& admin, const char* to, WorldTCPConnection* connection, const char* iName) { LinkedListIterator iterator(clientlist); char* output = 0; @@ -343,10 +328,10 @@ void ClientList::ClientUpdate(ZoneServer* zoneserver, ServerClientList_Struct* s if (iterator.GetData()->GetID() == scl->wid) { cle = iterator.GetData(); if (scl->remove == 2){ - cle->LeavingZone(zoneserver, CLE_Status_Offline); + cle->LeavingZone(zoneserver, CLE_Status::Offline); } else if (scl->remove == 1) - cle->LeavingZone(zoneserver, CLE_Status_Zoning); + cle->LeavingZone(zoneserver, CLE_Status::Zoning); else cle->Update(zoneserver, scl); return; @@ -354,11 +339,11 @@ void ClientList::ClientUpdate(ZoneServer* zoneserver, ServerClientList_Struct* s iterator.Advance(); } if (scl->remove == 2) - cle = new ClientListEntry(GetNextCLEID(), zoneserver, scl, CLE_Status_Online); + cle = new ClientListEntry(GetNextCLEID(), zoneserver, scl, CLE_Status::Online); else if (scl->remove == 1) - cle = new ClientListEntry(GetNextCLEID(), zoneserver, scl, CLE_Status_Zoning); + cle = new ClientListEntry(GetNextCLEID(), zoneserver, scl, CLE_Status::Zoning); else - cle = new ClientListEntry(GetNextCLEID(), zoneserver, scl, CLE_Status_InZone); + cle = new ClientListEntry(GetNextCLEID(), zoneserver, scl, CLE_Status::InZone); clientlist.Insert(cle); zoneserver->ChangeWID(scl->charid, cle->GetID()); } @@ -504,7 +489,7 @@ void ClientList::SendWhoAll(uint32 fromid,const char* to, int16 admin, Who_All_S countcle = countclients.GetData(); const char* tmpZone = database.GetZoneName(countcle->zone()); if ( - (countcle->Online() >= CLE_Status_Zoning) && + (countcle->Online() >= CLE_Status::Zoning) && (!countcle->GetGM() || countcle->Anon() != 1 || admin >= countcle->Admin()) && (whom == 0 || ( ((countcle->Admin() >= 80 && countcle->GetGM()) || whom->gmlookup == 0xFFFF) && @@ -584,7 +569,7 @@ void ClientList::SendWhoAll(uint32 fromid,const char* to, int16 admin, Who_All_S const char* tmpZone = database.GetZoneName(cle->zone()); if ( - (cle->Online() >= CLE_Status_Zoning) && + (cle->Online() >= CLE_Status::Zoning) && (!cle->GetGM() || cle->Anon() != 1 || admin >= cle->Admin()) && (whom == 0 || ( ((cle->Admin() >= 80 && cle->GetGM()) || whom->gmlookup == 0xFFFF) && @@ -757,7 +742,7 @@ void ClientList::SendFriendsWho(ServerFriendsWho_Struct *FriendsWho, WorldTCPCon Friend_[Seperator - FriendsPointer] = 0; ClientListEntry* CLE = FindCharacter(Friend_); - if(CLE && CLE->name() && (CLE->Online() >= CLE_Status_Zoning) && !(CLE->GetGM() && CLE->Anon())) { + if(CLE && CLE->name() && (CLE->Online() >= CLE_Status::Zoning) && !(CLE->GetGM() && CLE->Anon())) { FriendsCLEs.push_back(CLE); TotalLength += strlen(CLE->name()); int GuildNameLength = strlen(guild_mgr.GetGuildName(CLE->GuildID())); @@ -969,7 +954,7 @@ void ClientList::ConsoleSendWhoAll(const char* to, int16 admin, Who_All_Struct* cle = iterator.GetData(); const char* tmpZone = database.GetZoneName(cle->zone()); if ( - (cle->Online() >= CLE_Status_Zoning) + (cle->Online() >= CLE_Status::Zoning) && (whom == 0 || ( ((cle->Admin() >= 80 && cle->GetGM()) || whom->gmlookup == 0xFFFF) && (whom->lvllow == 0xFFFF || (cle->level() >= whom->lvllow && cle->level() <= whom->lvlhigh)) && @@ -1258,6 +1243,19 @@ void ClientList::RemoveCLEByLSID(uint32 iLSID) } } +bool ClientList::IsAccountInGame(uint32 iLSID) { + LinkedListIterator iterator(clientlist); + + while (iterator.MoreElements()) { + if (iterator.GetData()->LSID() == iLSID && iterator.GetData()->Online() == CLE_Status::InZone) { + return true; + } + iterator.Advance(); + } + + return false; +} + int ClientList::GetClientCount() { return(numplayers); } diff --git a/world/clientlist.h b/world/clientlist.h index d8b542b02..16259e2e0 100644 --- a/world/clientlist.h +++ b/world/clientlist.h @@ -55,7 +55,6 @@ public: ClientListEntry* FindCharacter(const char* name); ClientListEntry* FindCLEByAccountID(uint32 iAccID); ClientListEntry* FindCLEByCharacterID(uint32 iCharID); - ClientListEntry* FindCLEByLSID(uint32 iLSID); ClientListEntry* GetCLE(uint32 iID); void GetCLEIP(uint32 iIP); uint32 GetCLEIPCount(uint32 iLSAccountID); @@ -65,6 +64,7 @@ public: void CLEAdd(uint32 iLSID, const char* iLoginServerName, const char* iLoginName, const char* iLoginKey, int16 iWorldAdmin = 0, uint32 ip = 0, uint8 local=0); void UpdateClientGuild(uint32 char_id, uint32 guild_id); void RemoveCLEByLSID(uint32 iLSID); + bool IsAccountInGame(uint32 iLSID); int GetClientCount(); void GetClients(const char *zone_name, std::vector &into); diff --git a/world/login_server.cpp b/world/login_server.cpp index 78c736a0c..112a7c92f 100644 --- a/world/login_server.cpp +++ b/world/login_server.cpp @@ -108,19 +108,11 @@ void LoginServer::ProcessUsertoWorldReqLeg(uint16_t opcode, EQ::Net::Packet &p) return; } - if (RuleB(World, DisallowDuplicateAccountLogins)) { - auto cle = client_list.FindCLEByLSID(utwr->lsaccountid); - if (cle != nullptr) { - auto status = cle->GetOnline(); - if (CLE_Status_Zoning == status || CLE_Status_InZone == status) { - utwrs->response = UserToWorldStatusAlreadyOnline; - SendPacket(&outpack); - return; - } - else { - //our existing cle is in a state we can login to, mark the old as stale and remove it. - client_list.RemoveCLEByLSID(utwr->lsaccountid); - } + if (RuleB(World, EnforceCharacterLimitAtLogin)) { + if (client_list.IsAccountInGame(utwr->lsaccountid)) { + utwrs->response = UserToWorldStatusAlreadyOnline; + SendPacket(&outpack); + return; } } diff --git a/world/zonelist.cpp b/world/zonelist.cpp index 84cd87842..77782ee9b 100644 --- a/world/zonelist.cpp +++ b/world/zonelist.cpp @@ -708,6 +708,18 @@ void ZSList::WorldShutDown(uint32 time, uint32 interval) } } +void ZSList::DropClient(uint32 lsid, ZoneServer *ignore_zoneserver) { + ServerPacket packet(ServerOP_DropClient, sizeof(ServerZoneDropClient_Struct)); + auto drop = (ServerZoneDropClient_Struct*)packet.pBuffer; + drop->lsid = lsid; + + for (auto &zs : zone_server_list) { + if (zs.get() != ignore_zoneserver) { + zs->SendPacket(&packet); + } + } +} + void ZSList::OnTick(EQ::Timer *t) { if (!EventSubscriptionWatcher::Get()->IsSubscribed("EQW::ZoneUpdate")) { diff --git a/world/zonelist.h b/world/zonelist.h index 947f75a2b..8b8525f57 100644 --- a/world/zonelist.h +++ b/world/zonelist.h @@ -57,6 +57,7 @@ public: void SOPZoneBootup(const char *adminname, uint32 ZoneServerID, const char *zonename, bool iMakeStatic = false); void UpdateUCSServerAvailable(bool ucss_available = true); void WorldShutDown(uint32 time, uint32 interval); + void DropClient(uint32 lsid, ZoneServer *ignore_zoneserver); ZoneServer* FindByPort(uint16 port); ZoneServer* FindByID(uint32 ZoneID); diff --git a/world/zoneserver.cpp b/world/zoneserver.cpp index 0d0ba2f5d..21c5d8e65 100644 --- a/world/zoneserver.cpp +++ b/world/zoneserver.cpp @@ -437,7 +437,7 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) { } ClientListEntry* cle = client_list.FindCharacter(scm->deliverto); - if (cle == 0 || cle->Online() < CLE_Status_Zoning || + if (cle == 0 || cle->Online() < CLE_Status::Zoning || (cle->TellsOff() && ((cle->Anon() == 1 && scm->fromadmin < cle->Admin()) || scm->fromadmin < 80))) { if (!scm->noreply) { ClientListEntry* sender = client_list.FindCharacter(scm->from); @@ -450,7 +450,7 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) { sender->Server()->SendPacket(pack); } } - else if (cle->Online() == CLE_Status_Zoning) { + else if (cle->Online() == CLE_Status::Zoning) { if (!scm->noreply) { ClientListEntry* sender = client_list.FindCharacter(scm->from); if (cle->TellQueueFull()) { @@ -518,7 +518,7 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) { ClientListEntry* cle = client_list.FindCharacter(svm->To); - if (!cle || (cle->Online() < CLE_Status_Zoning) || !cle->Server()) { + if (!cle || (cle->Online() < CLE_Status::Zoning) || !cle->Server()) { zoneserver_list.SendEmoteMessage(svm->From, 0, 0, 0, "'%s is not online at this time'", svm->To); @@ -1457,6 +1457,7 @@ void ZoneServer::IncomingClient(Client* client) { s->accid = client->GetAccountID(); s->admin = client->GetAdmin(); s->charid = client->GetCharID(); + s->lsid = client->GetLSID(); if (client->GetCLE()) s->tellsoff = client->GetCLE()->TellsOff(); strn0cpy(s->charname, client->GetCharName(), sizeof(s->charname)); diff --git a/zone/aa.cpp b/zone/aa.cpp index 48ee715f7..4d9854d86 100644 --- a/zone/aa.cpp +++ b/zone/aa.cpp @@ -489,7 +489,7 @@ void Client::ResetAA() { database.DeleteCharacterLeadershipAAs(CharacterID()); // undefined for these clients if (ClientVersionBit() & EQEmu::versions::maskTitaniumAndEarlier) - Kick(); + Kick("AA Reset on client that doesn't support it"); } void Client::SendClearAA() diff --git a/zone/client.cpp b/zone/client.cpp index e90dafa8a..7e6baf180 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -136,6 +136,7 @@ Client::Client(EQStreamInterface* ieqs) forget_timer(0), autosave_timer(RuleI(Character, AutosaveIntervalS) * 1000), client_scan_npc_aggro_timer(RuleI(Aggro, ClientAggroCheckInterval) * 1000), + client_zone_wide_full_position_update_timer(5 * 60 * 1000), tribute_timer(Tribute_duration), proximity_timer(ClientProximity_interval), TaskPeriodic_Timer(RuleI(TaskSystem, PeriodicCheckTimer) * 1000), @@ -446,7 +447,7 @@ Client::~Client() { numclients--; UpdateWindowTitle(); if(zone) - zone->RemoveAuth(GetName()); + zone->RemoveAuth(GetName(), lskey); //let the stream factory know were done with this stream eqs->Close(); @@ -845,7 +846,7 @@ void Client::ChannelMessageReceived(uint8 chan_num, uint8 language, uint8 lang_s { if(AttemptedMessages > RuleI(Chat, MaxMessagesBeforeKick)) { - Kick(); + Kick("Sent too many chat messages at once."); return; } if(GlobalChatLimiterTimer) @@ -2586,13 +2587,19 @@ void Client::SetPVP(bool toggle, bool message) { Save(); } +void Client::Kick(const std::string &reason) { + client_state = CLIENT_KICKED; + + Log(Logs::General, Logs::Client_Login, "Client [%s] kicked, reason [%s]", GetCleanName(), reason.c_str()); +} + void Client::WorldKick() { auto outapp = new EQApplicationPacket(OP_GMKick, sizeof(GMKick_Struct)); GMKick_Struct* gmk = (GMKick_Struct *)outapp->pBuffer; strcpy(gmk->name,GetName()); QueuePacket(outapp); safe_delete(outapp); - Kick(); + Kick("World kick issued"); } void Client::GMKill() { @@ -9115,3 +9122,16 @@ bool Client::GotoPlayer(std::string player_name) return false; } + +glm::vec4 &Client::GetLastPositionBeforeBulkUpdate() +{ + return last_position_before_bulk_update; +} + +/** + * @param in_last_position_before_bulk_update + */ +void Client::SetLastPositionBeforeBulkUpdate(glm::vec4 in_last_position_before_bulk_update) +{ + Client::last_position_before_bulk_update = in_last_position_before_bulk_update; +} \ No newline at end of file diff --git a/zone/client.h b/zone/client.h index c12302b03..1928ff99d 100644 --- a/zone/client.h +++ b/zone/client.h @@ -371,9 +371,9 @@ public: inline bool ClientDataLoaded() const { return client_data_loaded; } inline bool Connected() const { return (client_state == CLIENT_CONNECTED); } inline bool InZone() const { return (client_state == CLIENT_CONNECTED || client_state == CLIENT_LINKDEAD); } - inline void Kick() { client_state = CLIENT_KICKED; } inline void Disconnect() { eqs->Close(); client_state = DISCONNECTED; } inline bool IsLD() const { return (bool) (client_state == CLIENT_LINKDEAD); } + void Kick(const std::string &reason); void WorldKick(); inline uint8 GetAnon() const { return m_pp.anon; } inline PlayerProfile_Struct& GetPP() { return m_pp; } @@ -1309,6 +1309,9 @@ public: uint32 trapid; //ID of trap player has triggered. This is cleared when the player leaves the trap's radius, or it despawns. + void SetLastPositionBeforeBulkUpdate(glm::vec4 in_last_position_before_bulk_update); + glm::vec4 &GetLastPositionBeforeBulkUpdate(); + protected: friend class Mob; void CalcItemBonuses(StatBonuses* newbon); @@ -1518,6 +1521,7 @@ private: Timer forget_timer; // our 2 min everybody forgets you timer Timer autosave_timer; Timer client_scan_npc_aggro_timer; + Timer client_zone_wide_full_position_update_timer; Timer tribute_timer; Timer proximity_timer; @@ -1540,6 +1544,7 @@ private: Timer position_update_timer; /* Timer used when client hasn't updated within a 10 second window */ glm::vec3 m_Proximity; + glm::vec4 last_position_before_bulk_update; void BulkSendInventoryItems(); diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index b1d4b14c5..38838d279 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -60,6 +60,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #include "water_map.h" #include "worldserver.h" #include "zone.h" +#include "mob_movement_manager.h" #ifdef BOTS #include "bot.h" @@ -805,6 +806,8 @@ void Client::CompleteConnect() parse->EventPlayer(EVENT_ENTER_ZONE, this, "", 0); + SetLastPositionBeforeBulkUpdate(GetPosition()); + /* This sub event is for if a player logs in for the first time since entering world. */ if (firstlogon == 1) { parse->EventPlayer(EVENT_CONNECT, this, "", 0); @@ -1166,13 +1169,11 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app) */ Client* client = entity_list.GetClientByName(cze->char_name); if (!zone->GetAuth(ip, cze->char_name, &WID, &account_id, &character_id, &admin, lskey, &tellsoff)) { - Log(Logs::General, Logs::Error, "GetAuth() returned false kicking client"); - if (client != 0) { + Log(Logs::General, Logs::Client_Login, "%s failed zone auth check.", cze->char_name); + if (nullptr != client) { client->Save(); - client->Kick(); + client->Kick("Failed auth check"); } - //ret = false; // TODO: Can we tell the client to get lost in a good way - client_state = CLIENT_KICKED; return; } @@ -1682,7 +1683,7 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app) /** * DevTools Load Settings */ - if (Admin() >= 200) { + if (Admin() >= EQEmu::DevTools::GM_ACCOUNT_STATUS_LEVEL) { std::string dev_tools_window_key = StringFormat("%i-dev-tools-window-disabled", AccountID()); if (DataBucket::GetData(dev_tools_window_key) == "true") { dev_tools_window_enabled = false; @@ -4467,16 +4468,16 @@ void Client::Handle_OP_ClientUpdate(const EQApplicationPacket *app) { /* Handle client aggro scanning timers NPCs */ is_client_moving = (ppu->y_pos == m_Position.y && ppu->x_pos == m_Position.x) ? false : true; - + if (is_client_moving) { Log(Logs::Detail, Logs::Normal, "ClientUpdate: Client is moving - scan timer is: %u", client_scan_npc_aggro_timer.GetDuration()); if (client_scan_npc_aggro_timer.GetDuration() > 1000) { client_scan_npc_aggro_timer.Disable(); client_scan_npc_aggro_timer.Start(500); - } - } else { + } + else { Log(Logs::Detail, Logs::Normal, "ClientUpdate: Client is NOT moving - scan timer is: %u", client_scan_npc_aggro_timer.GetDuration()); if (client_scan_npc_aggro_timer.GetDuration() < 1000) { @@ -4484,7 +4485,51 @@ void Client::Handle_OP_ClientUpdate(const EQApplicationPacket *app) { client_scan_npc_aggro_timer.Start(3000); } } - + + /** + * On a normal basis we limit mob movement updates based on distance + * This ensures we send a periodic full zone update to a client that has started moving after 5 or so minutes + * + * For very large zones we will also force a full update based on distance + * + * We ignore a small distance around us so that we don't interrupt already pathing deltas as those npcs will appear + * to full stop when they are actually still pathing + */ + + float distance_moved = DistanceNoZ(GetLastPositionBeforeBulkUpdate(), GetPosition()); + bool moved_far_enough_before_bulk_update = distance_moved >= zone->GetNpcPositionUpdateDistance(); + bool is_ready_to_update = ( + client_zone_wide_full_position_update_timer.Check() || moved_far_enough_before_bulk_update + ); + + if (is_client_moving && is_ready_to_update) { + Log(Logs::Detail, Logs::Normal, "[%s] Client Zone Wide Position Update NPCs", GetCleanName()); + + auto &mob_movement_manager = MobMovementManager::Get(); + auto &mob_list = entity_list.GetMobList(); + + for (auto &it : mob_list) { + Mob *entity = it.second; + if (!entity->IsNPC()) { + continue; + } + + int animation_speed = 0; + if (entity->IsMoving()) { + if (entity->IsRunning()) { + animation_speed = (entity->IsFeared() ? entity->GetFearSpeed() : entity->GetRunspeed()); + } + else { + animation_speed = entity->GetWalkspeed(); + } + } + + mob_movement_manager.SendCommandToClients(entity, 0.0, 0.0, 0.0, 0.0, animation_speed, ClientRangeAny, this); + } + + SetLastPositionBeforeBulkUpdate(GetPosition()); + } + float new_heading = EQ12toFloat(ppu->heading); int32 new_animation = ppu->animation; @@ -9757,7 +9802,7 @@ void Client::Handle_OP_MoveItem(const EQApplicationPacket *app) casting_spell_id); database.SetMQDetectionFlag(AccountName(), GetName(), detect, zone->GetShortName()); safe_delete_array(detect); - Kick(); // Kick client to prevent client and server from getting out-of-sync inventory slots + Kick("Inventory desync"); // Kick client to prevent client and server from getting out-of-sync inventory slots return; } } @@ -9801,7 +9846,7 @@ void Client::Handle_OP_MoveItem(const EQApplicationPacket *app) void Client::Handle_OP_MoveMultipleItems(const EQApplicationPacket *app) { - Kick(); // TODO: lets not desync though + Kick("Unimplemented move multiple items"); // TODO: lets not desync though } void Client::Handle_OP_OpenContainer(const EQApplicationPacket *app) diff --git a/zone/client_process.cpp b/zone/client_process.cpp index 493864fea..87655e8bc 100644 --- a/zone/client_process.cpp +++ b/zone/client_process.cpp @@ -591,8 +591,8 @@ bool Client::Process() { // only if client is not feigned if (zone->CanDoCombat() && ret && !GetFeigned() && client_scan_npc_aggro_timer.Check()) { int npc_scan_count = 0; - for (auto it = close_mobs.begin(); it != close_mobs.end(); ++it) { - Mob *mob = it->first; + for (auto & close_mob : close_mobs) { + Mob *mob = close_mob.first; if (!mob) continue; @@ -603,6 +603,7 @@ bool Client::Process() { if (mob->CheckWillAggro(this) && !mob->CheckAggro(this)) { mob->AddToHateList(this, 25); } + npc_scan_count++; } Log(Logs::General, Logs::Aggro, "Checking Reverse Aggro (client->npc) scanned_npcs (%i)", npc_scan_count); diff --git a/zone/command.cpp b/zone/command.cpp index 673935246..da9ef3bb5 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -1912,7 +1912,7 @@ void command_permaclass(Client *c, const Seperator *sep) Log(Logs::General, Logs::Normal, "Class change request from %s for %s, requested class:%i", c->GetName(), t->GetName(), atoi(sep->arg[1]) ); t->SetBaseClass(atoi(sep->arg[1])); t->Save(); - t->Kick(); + t->Kick("Class was changed."); } } @@ -3904,7 +3904,7 @@ void command_kick(Client *c, const Seperator *sep) client->Message(0, "You have been kicked by %s", c->GetName()); auto outapp = new EQApplicationPacket(OP_GMKick, 0); client->QueuePacket(outapp); - client->Kick(); + client->Kick("Ordered kicked by command"); c->Message(0, "Kick: local: kicking %s", sep->arg[1]); } } @@ -4499,7 +4499,7 @@ void command_gmzone(Client *c, const Seperator *sep) identifier = sep->arg[3]; } - std::string bucket_key = StringFormat("%s-%s-instance", zone_short_name, identifier.c_str()); + std::string bucket_key = StringFormat("%s-%s-%u-instance", zone_short_name, identifier.c_str(), zone_version); std::string existing_zone_instance = DataBucket::GetData(bucket_key); if (existing_zone_instance.length() > 0) { @@ -5196,7 +5196,7 @@ void command_name(Client *c, const Seperator *sep) c->Message(0, "Successfully renamed %s to %s", oldname, sep->arg[1]); // until we get the name packet working right this will work c->Message(0, "Sending player to char select."); - target->Kick(); + target->Kick("Name was changed"); } else c->Message(13, "ERROR: Unable to rename %s. Check that the new name '%s' isn't already taken.", oldname, sep->arg[2]); diff --git a/zone/entity.cpp b/zone/entity.cpp index f7fea532b..1c8de0780 100644 --- a/zone/entity.cpp +++ b/zone/entity.cpp @@ -1713,6 +1713,18 @@ Client *EntityList::GetClientByWID(uint32 iWID) return nullptr; } +Client *EntityList::GetClientByLSID(uint32 iLSID) +{ + auto it = client_list.begin(); + while (it != client_list.end()) { + if (it->second->LSAccountID() == iLSID) { + return it->second; + } + ++it; + } + return nullptr; +} + Client *EntityList::GetRandomClient(const glm::vec3& location, float Distance, Client *ExcludeClient) { std::vector ClientsInRange; diff --git a/zone/entity.h b/zone/entity.h index c50575b5b..a02a8be2e 100644 --- a/zone/entity.h +++ b/zone/entity.h @@ -176,6 +176,7 @@ public: } Client *GetClientByCharID(uint32 iCharID); Client *GetClientByWID(uint32 iWID); + Client *GetClientByLSID(uint32 iLSID); Client *GetClient(uint32 ip, uint16 port); Client *GetRandomClient(const glm::vec3& location, float Distance, Client *ExcludeClient = nullptr); Group *GetGroupByMob(Mob* mob); diff --git a/zone/inventory.cpp b/zone/inventory.cpp index ec4f095b0..96f0fe15b 100644 --- a/zone/inventory.cpp +++ b/zone/inventory.cpp @@ -1608,7 +1608,7 @@ bool Client::SwapItem(MoveItem_Struct* move_in) { banker ? banker->GetName() : "UNKNOWN NPC", distance); database.SetMQDetectionFlag(AccountName(), GetName(), hacked_string, zone->GetShortName()); safe_delete_array(hacked_string); - Kick(); // Kicking player to avoid item loss do to client and server inventories not being sync'd + Kick("Inventory desync"); // Kicking player to avoid item loss do to client and server inventories not being sync'd return false; } } @@ -1822,7 +1822,7 @@ bool Client::SwapItem(MoveItem_Struct* move_in) { // Step 4: Check for entity trade if (dst_slot_id >= EQEmu::invslot::TRADE_BEGIN && dst_slot_id <= EQEmu::invslot::TRADE_END) { if (src_slot_id != EQEmu::invslot::slotCursor) { - Kick(); + Kick("Trade with non-cursor item"); return false; } if (with) { diff --git a/zone/lua_client.cpp b/zone/lua_client.cpp index 9447c48af..c50f6b2f3 100644 --- a/zone/lua_client.cpp +++ b/zone/lua_client.cpp @@ -47,7 +47,7 @@ bool Lua_Client::InZone() { void Lua_Client::Kick() { Lua_Safe_Call_Void(); - self->Kick(); + self->Kick("Lua Quest"); } void Lua_Client::Disconnect() { diff --git a/zone/mob.cpp b/zone/mob.cpp index ca844d459..3a307bf3f 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -1128,7 +1128,10 @@ void Mob::FillSpawnStruct(NewSpawn_Struct* ns, Mob* ForWho) UpdateActiveLight(); ns->spawn.light = m_Light.Type[EQEmu::lightsource::LightActive]; - ns->spawn.showhelm = (helmtexture && helmtexture != 0xFF) ? 1 : 0; + if (IsNPC() && race == ERUDITE) + ns->spawn.showhelm = 1; + else + ns->spawn.showhelm = (helmtexture && helmtexture != 0xFF) ? 1 : 0; ns->spawn.invis = (invisible || hidden) ? 1 : 0; // TODO: load this before spawning players ns->spawn.NPC = IsClient() ? 0 : 1; diff --git a/zone/mob_movement_manager.cpp b/zone/mob_movement_manager.cpp index 1fcc19187..4bf1663d3 100644 --- a/zone/mob_movement_manager.cpp +++ b/zone/mob_movement_manager.cpp @@ -14,40 +14,41 @@ #include extern double frame_time; -extern Zone *zone; +extern Zone *zone; -class IMovementCommand -{ +class IMovementCommand { public: IMovementCommand() = default; virtual ~IMovementCommand() = default; - virtual bool Process(MobMovementManager *mgr, Mob *m) = 0; + virtual bool Process(MobMovementManager *mob_movement_manager, Mob *mob) = 0; virtual bool Started() const = 0; }; -class RotateToCommand : public IMovementCommand -{ +class RotateToCommand : public IMovementCommand { public: - RotateToCommand(double rotate_to, double dir, MobMovementMode mode) { - m_rotate_to = rotate_to; - m_rotate_to_dir = dir; - m_rotate_to_mode = mode; - m_started = false; + RotateToCommand(double rotate_to, double dir, MobMovementMode mob_movement_mode) + { + m_rotate_to = rotate_to; + m_rotate_to_dir = dir; + m_rotate_to_mode = mob_movement_mode; + m_started = false; } - virtual ~RotateToCommand() { + virtual ~RotateToCommand() + { } - virtual bool Process(MobMovementManager *mgr, Mob *m) { - if (!m->IsAIControlled()) { + virtual bool Process(MobMovementManager *mob_movement_manager, Mob *mob) + { + if (!mob->IsAIControlled()) { return true; } - auto rotate_to_speed = m_rotate_to_mode == MovementRunning ? 200.0 : 16.0; //todo: get this from mob + auto rotate_to_speed = m_rotate_to_mode == MovementRunning ? 200.0 : 16.0; //todo: get this from mob - auto from = FixHeading(m->GetHeading()); - auto to = FixHeading(m_rotate_to); + auto from = FixHeading(mob->GetHeading()); + auto to = FixHeading(m_rotate_to); auto diff = to - from; while (diff < -256.0) { @@ -62,265 +63,282 @@ public: if (!m_started) { m_started = true; - m->SetMoving(true); - + mob->SetMoving(true); + if (dist > 15.0f && rotate_to_speed > 0.0 && rotate_to_speed <= 25.0) { //send basic rotation - mgr->SendCommandToClients(m, 0.0, 0.0, 0.0, m_rotate_to_dir * rotate_to_speed, 0, ClientRangeClose); + mob_movement_manager->SendCommandToClients( + mob, + 0.0, + 0.0, + 0.0, + m_rotate_to_dir * rotate_to_speed, + 0, + ClientRangeClose + ); } } - + auto td = rotate_to_speed * 19.0 * frame_time; - + if (td >= dist) { - m->SetHeading(to); - m->SetMoving(false); - mgr->SendCommandToClients(m, 0.0, 0.0, 0.0, 0.0, 0, ClientRangeCloseMedium); + mob->SetHeading(to); + mob->SetMoving(false); + mob_movement_manager->SendCommandToClients(mob, 0.0, 0.0, 0.0, 0.0, 0, ClientRangeCloseMedium); return true; } - + from += td * m_rotate_to_dir; - m->SetHeading(FixHeading(from)); + mob->SetHeading(FixHeading(from)); return false; } - virtual bool Started() const { + virtual bool Started() const + { return m_started; } private: - double m_rotate_to; - double m_rotate_to_dir; + double m_rotate_to; + double m_rotate_to_dir; MobMovementMode m_rotate_to_mode; - bool m_started; + bool m_started; }; -class MoveToCommand : public IMovementCommand -{ +class MoveToCommand : public IMovementCommand { public: - MoveToCommand(float x, float y, float z, MobMovementMode mode) { + MoveToCommand(float x, float y, float z, MobMovementMode mob_movement_mode) + { m_distance_moved_since_correction = 0.0; - m_move_to_x = x; - m_move_to_y = y; - m_move_to_z = z; - m_move_to_mode = mode; - m_last_sent_time = 0.0; - m_last_sent_speed = 0; - m_started = false; - m_total_h_dist = 0.0; - m_total_v_dist = 0.0; + m_move_to_x = x; + m_move_to_y = y; + m_move_to_z = z; + m_move_to_mode = mob_movement_mode; + m_last_sent_time = 0.0; + m_last_sent_speed = 0; + m_started = false; + m_total_h_dist = 0.0; + m_total_v_dist = 0.0; } - virtual ~MoveToCommand() { + virtual ~MoveToCommand() + { } - virtual bool Process(MobMovementManager *mgr, Mob *m) { - if (!m->IsAIControlled()) { + /** + * @param mob_movement_manager + * @param mob + * @return + */ + virtual bool Process(MobMovementManager *mob_movement_manager, Mob *mob) + { + if (!mob->IsAIControlled()) { return true; } //Send a movement packet when you start moving - double current_time = static_cast(Timer::GetCurrentTime()) / 1000.0; - int current_speed = 0; + double current_time = static_cast(Timer::GetCurrentTime()) / 1000.0; + int current_speed = 0; if (m_move_to_mode == MovementRunning) { - if (m->IsFeared()) { - current_speed = m->GetFearSpeed(); + if (mob->IsFeared()) { + current_speed = mob->GetFearSpeed(); } else { - current_speed = m->GetRunspeed(); + current_speed = mob->GetRunspeed(); } } else { - current_speed = m->GetWalkspeed(); + current_speed = mob->GetWalkspeed(); } if (!m_started) { m_started = true; //rotate to the point - m->SetMoving(true); - m->SetHeading(m->CalculateHeadingToTarget(m_move_to_x, m_move_to_y)); + mob->SetMoving(true); + mob->SetHeading(mob->CalculateHeadingToTarget(m_move_to_x, m_move_to_y)); m_last_sent_speed = current_speed; - m_last_sent_time = current_time; - m_total_h_dist = DistanceNoZ(m->GetPosition(), glm::vec4(m_move_to_x, m_move_to_y, 0.0f, 0.0f)); - m_total_v_dist = m_move_to_z - m->GetZ(); - mgr->SendCommandToClients(m, 0.0, 0.0, 0.0, 0.0, current_speed, ClientRangeCloseMedium); + m_last_sent_time = current_time; + m_total_h_dist = DistanceNoZ(mob->GetPosition(), glm::vec4(m_move_to_x, m_move_to_y, 0.0f, 0.0f)); + m_total_v_dist = m_move_to_z - mob->GetZ(); + mob_movement_manager->SendCommandToClients(mob, 0.0, 0.0, 0.0, 0.0, current_speed, ClientRangeCloseMedium); } //When speed changes if (current_speed != m_last_sent_speed) { if (RuleB(Map, FixZWhenPathing)) { - m->FixZ(); + mob->FixZ(); } m_distance_moved_since_correction = 0.0; m_last_sent_speed = current_speed; - m_last_sent_time = current_time; - mgr->SendCommandToClients(m, 0.0, 0.0, 0.0, 0.0, current_speed, ClientRangeCloseMedium); + m_last_sent_time = current_time; + mob_movement_manager->SendCommandToClients(mob, 0.0, 0.0, 0.0, 0.0, current_speed, ClientRangeCloseMedium); } //If x seconds have passed without sending an update. if (current_time - m_last_sent_time >= 5.0) { if (RuleB(Map, FixZWhenPathing)) { - m->FixZ(); + mob->FixZ(); } m_distance_moved_since_correction = 0.0; m_last_sent_speed = current_speed; - m_last_sent_time = current_time; - mgr->SendCommandToClients(m, 0.0, 0.0, 0.0, 0.0, current_speed, ClientRangeCloseMedium); + m_last_sent_time = current_time; + mob_movement_manager->SendCommandToClients(mob, 0.0, 0.0, 0.0, 0.0, current_speed, ClientRangeCloseMedium); } - auto &p = m->GetPosition(); + auto &p = mob->GetPosition(); glm::vec2 tar(m_move_to_x, m_move_to_y); glm::vec2 pos(p.x, p.y); - double len = glm::distance(pos, tar); + double len = glm::distance(pos, tar); if (len == 0) { return true; } - m->SetMoved(true); + mob->SetMoved(true); - glm::vec2 dir = tar - pos; - glm::vec2 ndir = glm::normalize(dir); - double distance_moved = frame_time * current_speed * 0.4f * 1.45f; + glm::vec2 dir = tar - pos; + glm::vec2 ndir = glm::normalize(dir); + double distance_moved = frame_time * current_speed * 0.4f * 1.45f; - if (distance_moved > len) { - if (m->IsNPC()) { - entity_list.ProcessMove(m->CastToNPC(), m_move_to_x, m_move_to_y, m_move_to_z); + if (distance_moved > len) { + if (mob->IsNPC()) { + entity_list.ProcessMove(mob->CastToNPC(), m_move_to_x, m_move_to_y, m_move_to_z); } - m->SetPosition(m_move_to_x, m_move_to_y, m_move_to_z); - + mob->SetPosition(m_move_to_x, m_move_to_y, m_move_to_z); + if (RuleB(Map, FixZWhenPathing)) { - m->FixZ(); + mob->FixZ(); } return true; } else { glm::vec2 npos = pos + (ndir * static_cast(distance_moved)); - + len -= distance_moved; double total_distance_traveled = m_total_h_dist - len; - double start_z = m_move_to_z - m_total_v_dist; - double z_at_pos = start_z + (m_total_v_dist * (total_distance_traveled / m_total_h_dist)); + double start_z = m_move_to_z - m_total_v_dist; + double z_at_pos = start_z + (m_total_v_dist * (total_distance_traveled / m_total_h_dist)); - if (m->IsNPC()) { - entity_list.ProcessMove(m->CastToNPC(), npos.x, npos.y, z_at_pos); + if (mob->IsNPC()) { + entity_list.ProcessMove(mob->CastToNPC(), npos.x, npos.y, z_at_pos); } - m->SetPosition(npos.x, npos.y, z_at_pos); + mob->SetPosition(npos.x, npos.y, z_at_pos); if (RuleB(Map, FixZWhenPathing)) { m_distance_moved_since_correction += distance_moved; if (m_distance_moved_since_correction > RuleR(Map, DistanceCanTravelBeforeAdjustment)) { m_distance_moved_since_correction = 0.0; - m->FixZ(); + mob->FixZ(); } } } - + return false; } - virtual bool Started() const { + virtual bool Started() const + { return m_started; } protected: - double m_distance_moved_since_correction; - double m_move_to_x; - double m_move_to_y; - double m_move_to_z; + double m_distance_moved_since_correction; + double m_move_to_x; + double m_move_to_y; + double m_move_to_z; MobMovementMode m_move_to_mode; - bool m_started; + bool m_started; double m_last_sent_time; - int m_last_sent_speed; + int m_last_sent_speed; double m_total_h_dist; double m_total_v_dist; }; -class SwimToCommand : public MoveToCommand -{ +class SwimToCommand : public MoveToCommand { public: - SwimToCommand(float x, float y, float z, MobMovementMode mode) : MoveToCommand(x, y, z, mode) { + SwimToCommand(float x, float y, float z, MobMovementMode mob_movement_mode) : MoveToCommand(x, y, z, mob_movement_mode) + { } - virtual bool Process(MobMovementManager *mgr, Mob *m) + virtual bool Process(MobMovementManager *mob_movement_manager, Mob *mob) { - if (!m->IsAIControlled()) { + if (!mob->IsAIControlled()) { return true; } - //Send a movement packet when you start moving - double current_time = static_cast(Timer::GetCurrentTime()) / 1000.0; - int current_speed = 0; + //Send a movement packet when you start moving + double current_time = static_cast(Timer::GetCurrentTime()) / 1000.0; + int current_speed = 0; if (m_move_to_mode == MovementRunning) { - if (m->IsFeared()) { - current_speed = m->GetFearSpeed(); + if (mob->IsFeared()) { + current_speed = mob->GetFearSpeed(); } else { - current_speed = m->GetRunspeed(); + current_speed = mob->GetRunspeed(); } } else { - current_speed = m->GetWalkspeed(); + current_speed = mob->GetWalkspeed(); } if (!m_started) { m_started = true; //rotate to the point - m->SetMoving(true); - m->SetHeading(m->CalculateHeadingToTarget(m_move_to_x, m_move_to_y)); + mob->SetMoving(true); + mob->SetHeading(mob->CalculateHeadingToTarget(m_move_to_x, m_move_to_y)); m_last_sent_speed = current_speed; - m_last_sent_time = current_time; - m_total_h_dist = DistanceNoZ(m->GetPosition(), glm::vec4(m_move_to_x, m_move_to_y, 0.0f, 0.0f)); - m_total_v_dist = m_move_to_z - m->GetZ(); - mgr->SendCommandToClients(m, 0.0, 0.0, 0.0, 0.0, current_speed, ClientRangeCloseMedium); + m_last_sent_time = current_time; + m_total_h_dist = DistanceNoZ(mob->GetPosition(), glm::vec4(m_move_to_x, m_move_to_y, 0.0f, 0.0f)); + m_total_v_dist = m_move_to_z - mob->GetZ(); + mob_movement_manager->SendCommandToClients(mob, 0.0, 0.0, 0.0, 0.0, current_speed, ClientRangeCloseMedium); } //When speed changes if (current_speed != m_last_sent_speed) { m_last_sent_speed = current_speed; - m_last_sent_time = current_time; - mgr->SendCommandToClients(m, 0.0, 0.0, 0.0, 0.0, current_speed, ClientRangeCloseMedium); + m_last_sent_time = current_time; + mob_movement_manager->SendCommandToClients(mob, 0.0, 0.0, 0.0, 0.0, current_speed, ClientRangeCloseMedium); } //If x seconds have passed without sending an update. if (current_time - m_last_sent_time >= 1.5) { m_last_sent_speed = current_speed; - m_last_sent_time = current_time; - mgr->SendCommandToClients(m, 0.0, 0.0, 0.0, 0.0, current_speed, ClientRangeCloseMedium); + m_last_sent_time = current_time; + mob_movement_manager->SendCommandToClients(mob, 0.0, 0.0, 0.0, 0.0, current_speed, ClientRangeCloseMedium); } - auto &p = m->GetPosition(); + auto &p = mob->GetPosition(); glm::vec2 tar(m_move_to_x, m_move_to_y); glm::vec2 pos(p.x, p.y); - double len = glm::distance(pos, tar); + double len = glm::distance(pos, tar); if (len == 0) { return true; } - m->SetMoved(true); + mob->SetMoved(true); - glm::vec2 dir = tar - pos; - glm::vec2 ndir = glm::normalize(dir); - double distance_moved = frame_time * current_speed * 0.4f * 1.45f; + glm::vec2 dir = tar - pos; + glm::vec2 ndir = glm::normalize(dir); + double distance_moved = frame_time * current_speed * 0.4f * 1.45f; if (distance_moved > len) { - if (m->IsNPC()) { - entity_list.ProcessMove(m->CastToNPC(), m_move_to_x, m_move_to_y, m_move_to_z); + if (mob->IsNPC()) { + entity_list.ProcessMove(mob->CastToNPC(), m_move_to_x, m_move_to_y, m_move_to_z); } - m->SetPosition(m_move_to_x, m_move_to_y, m_move_to_z); + mob->SetPosition(m_move_to_x, m_move_to_y, m_move_to_z); return true; } else { @@ -328,51 +346,54 @@ public: len -= distance_moved; double total_distance_traveled = m_total_h_dist - len; - double start_z = m_move_to_z - m_total_v_dist; - double z_at_pos = start_z + (m_total_v_dist * (total_distance_traveled / m_total_h_dist)); + double start_z = m_move_to_z - m_total_v_dist; + double z_at_pos = start_z + (m_total_v_dist * (total_distance_traveled / m_total_h_dist)); - if (m->IsNPC()) { - entity_list.ProcessMove(m->CastToNPC(), npos.x, npos.y, z_at_pos); + if (mob->IsNPC()) { + entity_list.ProcessMove(mob->CastToNPC(), npos.x, npos.y, z_at_pos); } - m->SetPosition(npos.x, npos.y, z_at_pos); + mob->SetPosition(npos.x, npos.y, z_at_pos); } return false; } }; -class TeleportToCommand : public IMovementCommand -{ +class TeleportToCommand : public IMovementCommand { public: - TeleportToCommand(float x, float y, float z, float heading) { - m_teleport_to_x = x; - m_teleport_to_y = y; - m_teleport_to_z = z; + TeleportToCommand(float x, float y, float z, float heading) + { + m_teleport_to_x = x; + m_teleport_to_y = y; + m_teleport_to_z = z; m_teleport_to_heading = heading; } - virtual ~TeleportToCommand() { + virtual ~TeleportToCommand() + { } - virtual bool Process(MobMovementManager *mgr, Mob *m) { - if (!m->IsAIControlled()) { + virtual bool Process(MobMovementManager *mob_movement_manager, Mob *mob) + { + if (!mob->IsAIControlled()) { return true; } - if (m->IsNPC()) { - entity_list.ProcessMove(m->CastToNPC(), m_teleport_to_x, m_teleport_to_y, m_teleport_to_z); + if (mob->IsNPC()) { + entity_list.ProcessMove(mob->CastToNPC(), m_teleport_to_x, m_teleport_to_y, m_teleport_to_z); } - m->SetPosition(m_teleport_to_x, m_teleport_to_y, m_teleport_to_z); - m->SetHeading(mgr->FixHeading(m_teleport_to_heading)); - mgr->SendCommandToClients(m, 0.0, 0.0, 0.0, 0.0, 0, ClientRangeAny); + mob->SetPosition(m_teleport_to_x, m_teleport_to_y, m_teleport_to_z); + mob->SetHeading(mob_movement_manager->FixHeading(m_teleport_to_heading)); + mob_movement_manager->SendCommandToClients(mob, 0.0, 0.0, 0.0, 0.0, 0, ClientRangeAny); return true; } - virtual bool Started() const { + virtual bool Started() const + { return false; } @@ -384,93 +405,99 @@ private: double m_teleport_to_heading; }; -class StopMovingCommand : public IMovementCommand -{ +class StopMovingCommand : public IMovementCommand { public: - StopMovingCommand() { + StopMovingCommand() + { } - virtual ~StopMovingCommand() { + virtual ~StopMovingCommand() + { } - virtual bool Process(MobMovementManager *mgr, Mob *m) { - if (!m->IsAIControlled()) { + virtual bool Process(MobMovementManager *mob_movement_manager, Mob *mob) + { + if (!mob->IsAIControlled()) { return true; } - if (m->IsMoving()) { - m->SetMoving(false); + if (mob->IsMoving()) { + mob->SetMoving(false); if (RuleB(Map, FixZWhenPathing)) { - m->FixZ(); + mob->FixZ(); } - mgr->SendCommandToClients(m, 0.0, 0.0, 0.0, 0.0, 0, ClientRangeCloseMedium); + mob_movement_manager->SendCommandToClients(mob, 0.0, 0.0, 0.0, 0.0, 0, ClientRangeCloseMedium); } return true; } - virtual bool Started() const { + virtual bool Started() const + { return false; } }; -class EvadeCombatCommand : public IMovementCommand -{ +class EvadeCombatCommand : public IMovementCommand { public: - EvadeCombatCommand() { + EvadeCombatCommand() + { } - virtual ~EvadeCombatCommand() { + virtual ~EvadeCombatCommand() + { } - virtual bool Process(MobMovementManager *mgr, Mob *m) { - if (!m->IsAIControlled()) { + virtual bool Process(MobMovementManager *mob_movement_manager, Mob *mob) + { + if (!mob->IsAIControlled()) { return true; } - if (m->IsMoving()) { - m->SetMoving(false); - mgr->SendCommandToClients(m, 0.0, 0.0, 0.0, 0.0, 0, ClientRangeCloseMedium); + if (mob->IsMoving()) { + mob->SetMoving(false); + mob_movement_manager->SendCommandToClients(mob, 0.0, 0.0, 0.0, 0.0, 0, ClientRangeCloseMedium); } - m->BuffFadeAll(); - m->WipeHateList(); - m->Heal(); + mob->BuffFadeAll(); + mob->WipeHateList(); + mob->Heal(); return true; } - virtual bool Started() const { + virtual bool Started() const + { return false; } }; -struct MovementStats -{ - MovementStats() { - LastResetTime = static_cast(Timer::GetCurrentTime()) / 1000.0; - TotalSent = 0ULL; +struct MovementStats { + MovementStats() + { + LastResetTime = static_cast(Timer::GetCurrentTime()) / 1000.0; + TotalSent = 0ULL; TotalSentMovement = 0ULL; TotalSentPosition = 0ULL; - TotalSentHeading = 0ULL; + TotalSentHeading = 0ULL; } - double LastResetTime; + double LastResetTime; uint64_t TotalSent; uint64_t TotalSentMovement; uint64_t TotalSentPosition; uint64_t TotalSentHeading; }; -struct NavigateTo -{ - NavigateTo() { - navigate_to_x = 0.0; - navigate_to_y = 0.0; - navigate_to_z = 0.0; +struct NavigateTo { + NavigateTo() + { + navigate_to_x = 0.0; + navigate_to_y = 0.0; + navigate_to_z = 0.0; navigate_to_heading = 0.0; - last_set_time = 0.0; + last_set_time = 0.0; } double navigate_to_x; @@ -480,13 +507,13 @@ struct NavigateTo double last_set_time; }; -struct MobMovementEntry -{ +struct MobMovementEntry { std::deque> Commands; - NavigateTo NavTo; + NavigateTo NavTo; }; -void AdjustRoute(std::list &nodes, Mob *who) { +void AdjustRoute(std::list &nodes, Mob *who) +{ if (!zone->HasMap() || !zone->HasWaterMap()) { return; } @@ -494,7 +521,7 @@ void AdjustRoute(std::list &nodes, Mob *who) { auto offset = who->GetZOffset(); for (auto &node : nodes) { - if(!zone->watermap->InLiquid(node.pos)) { + if (!zone->watermap->InLiquid(node.pos)) { auto best_z = zone->zonemap->FindBestZ(node.pos, nullptr); if (best_z != BEST_Z_INVALID) { node.pos.z = best_z + offset; @@ -503,11 +530,10 @@ void AdjustRoute(std::list &nodes, Mob *who) { } } -struct MobMovementManager::Implementation -{ - std::map Entries; - std::vector Clients; - MovementStats Stats; +struct MobMovementManager::Implementation { + std::map Entries; + std::vector Clients; + MovementStats Stats; }; MobMovementManager::MobMovementManager() @@ -522,12 +548,12 @@ MobMovementManager::~MobMovementManager() void MobMovementManager::Process() { for (auto &iter : _impl->Entries) { - auto &ent = iter.second; + auto &ent = iter.second; auto &commands = ent.Commands; while (true != commands.empty()) { auto &cmd = commands.front(); - auto r = cmd->Process(this, iter.first); + auto r = cmd->Process(this, iter.first); if (true != r) { break; @@ -538,35 +564,52 @@ void MobMovementManager::Process() } } -void MobMovementManager::AddMob(Mob *m) +/** + * @param mob + */ +void MobMovementManager::AddMob(Mob *mob) { - _impl->Entries.insert(std::make_pair(m, MobMovementEntry())); + _impl->Entries.insert(std::make_pair(mob, MobMovementEntry())); } -void MobMovementManager::RemoveMob(Mob *m) +/** + * @param mob + */ +void MobMovementManager::RemoveMob(Mob *mob) { - _impl->Entries.erase(m); + _impl->Entries.erase(mob); } -void MobMovementManager::AddClient(Client *c) +/** + * @param client + */ +void MobMovementManager::AddClient(Client *client) { - _impl->Clients.push_back(c); + _impl->Clients.push_back(client); } -void MobMovementManager::RemoveClient(Client *c) +/** + * @param client + */ +void MobMovementManager::RemoveClient(Client *client) { auto iter = _impl->Clients.begin(); while (iter != _impl->Clients.end()) { - if (c == *iter) { + if (client == *iter) { _impl->Clients.erase(iter); return; } - + ++iter; } } -void MobMovementManager::RotateTo(Mob *who, float to, MobMovementMode mode) +/** + * @param who + * @param to + * @param mob_movement_mode + */ +void MobMovementManager::RotateTo(Mob *who, float to, MobMovementMode mob_movement_mode) { auto iter = _impl->Entries.find(who); auto &ent = (*iter); @@ -574,20 +617,34 @@ void MobMovementManager::RotateTo(Mob *who, float to, MobMovementMode mode) if (true != ent.second.Commands.empty()) { return; } - - PushRotateTo(ent.second, who, to, mode); + + PushRotateTo(ent.second, who, to, mob_movement_mode); } +/** + * @param who + * @param x + * @param y + * @param z + * @param heading + */ void MobMovementManager::Teleport(Mob *who, float x, float y, float z, float heading) { auto iter = _impl->Entries.find(who); auto &ent = (*iter); - + ent.second.Commands.clear(); PushTeleportTo(ent.second, x, y, z, heading); } +/** + * @param who + * @param x + * @param y + * @param z + * @param mode + */ void MobMovementManager::NavigateTo(Mob *who, float x, float y, float z, MobMovementMode mode) { if (IsPositionEqualWithinCertainZ(glm::vec3(x, y, z), glm::vec3(who->GetX(), who->GetY(), who->GetZ()), 6.0f)) { @@ -601,8 +658,13 @@ void MobMovementManager::NavigateTo(Mob *who, float x, float y, float z, MobMove double current_time = static_cast(Timer::GetCurrentTime()) / 1000.0; if ((current_time - nav.last_set_time) > 0.5) { //Can potentially recalc - - auto within = IsPositionWithinSimpleCylinder(glm::vec3(x, y, z), glm::vec3(nav.navigate_to_x, nav.navigate_to_y, nav.navigate_to_z), 1.5f, 6.0f); + + auto within = IsPositionWithinSimpleCylinder( + glm::vec3(x, y, z), + glm::vec3(nav.navigate_to_x, nav.navigate_to_y, nav.navigate_to_z), + 1.5f, + 6.0f + ); auto heading_match = IsHeadingEqual(0.0, nav.navigate_to_heading); if (false == within || false == heading_match || ent.second.Commands.size() == 0) { @@ -610,23 +672,27 @@ void MobMovementManager::NavigateTo(Mob *who, float x, float y, float z, MobMove //Path is no longer valid, calculate a new path UpdatePath(who, x, y, z, mode); - nav.navigate_to_x = x; - nav.navigate_to_y = y; - nav.navigate_to_z = z; + nav.navigate_to_x = x; + nav.navigate_to_y = y; + nav.navigate_to_z = z; nav.navigate_to_heading = 0.0; - nav.last_set_time = current_time; + nav.last_set_time = current_time; } } } -void MobMovementManager::StopNavigation(Mob *who) { +/** + * @param who + */ +void MobMovementManager::StopNavigation(Mob *who) +{ auto iter = _impl->Entries.find(who); auto &ent = (*iter); auto &nav = ent.second.NavTo; - nav.navigate_to_x = 0.0; - nav.navigate_to_y = 0.0; - nav.navigate_to_z = 0.0; + nav.navigate_to_x = 0.0; + nav.navigate_to_y = 0.0; + nav.navigate_to_z = 0.0; nav.navigate_to_heading = 0.0; if (true == ent.second.Commands.empty()) { @@ -643,24 +709,48 @@ void MobMovementManager::StopNavigation(Mob *who) { PushStopMoving(ent.second); } -void MobMovementManager::SendCommandToClients(Mob *m, float dx, float dy, float dz, float dh, int anim, ClientRange range) +/** + * @param mob + * @param delta_x + * @param delta_y + * @param delta_z + * @param delta_heading + * @param anim + * @param range + * @param single_client + */ +void MobMovementManager::SendCommandToClients( + Mob *mob, + float delta_x, + float delta_y, + float delta_z, + float delta_heading, + int anim, + ClientRange range, + Client* single_client +) { if (range == ClientRangeNone) { return; } EQApplicationPacket outapp(OP_ClientUpdate, sizeof(PlayerPositionUpdateServer_Struct)); - PlayerPositionUpdateServer_Struct *spu = (PlayerPositionUpdateServer_Struct*)outapp.pBuffer; - FillCommandStruct(spu, m, dx, dy, dz, dh, anim); + auto *spu = (PlayerPositionUpdateServer_Struct *) outapp.pBuffer; + + FillCommandStruct(spu, mob, delta_x, delta_y, delta_z, delta_heading, anim); if (range == ClientRangeAny) { for (auto &c : _impl->Clients) { + if (single_client && c != single_client) { + continue; + } + _impl->Stats.TotalSent++; if (anim != 0) { _impl->Stats.TotalSentMovement++; } - else if (dh != 0) { + else if (delta_heading != 0) { _impl->Stats.TotalSentHeading++; } else { @@ -672,26 +762,30 @@ void MobMovementManager::SendCommandToClients(Mob *m, float dx, float dy, float } else { float short_range = RuleR(Pathing, ShortMovementUpdateRange); - float long_range = zone->GetMaxMovementUpdateRange(); + float long_range = zone->GetNpcPositionUpdateDistance(); for (auto &c : _impl->Clients) { - float dist = c->CalculateDistance(m->GetX(), m->GetY(), m->GetZ()); + if (single_client && c != single_client) { + continue; + } + + float distance = c->CalculateDistance(mob->GetX(), mob->GetY(), mob->GetZ()); bool match = false; if (range & ClientRangeClose) { - if (dist < short_range) { + if (distance < short_range) { match = true; } } if (!match && range & ClientRangeMedium) { - if (dist >= short_range && dist < long_range) { + if (distance >= short_range && distance < long_range) { match = true; } } if (!match && range & ClientRangeLong) { - if (dist >= long_range) { + if (distance >= long_range) { match = true; } } @@ -702,7 +796,7 @@ void MobMovementManager::SendCommandToClients(Mob *m, float dx, float dy, float if (anim != 0) { _impl->Stats.TotalSentMovement++; } - else if (dh != 0) { + else if (delta_heading != 0) { _impl->Stats.TotalSentHeading++; } else { @@ -715,6 +809,10 @@ void MobMovementManager::SendCommandToClients(Mob *m, float dx, float dy, float } } +/** + * @param in + * @return + */ float MobMovementManager::FixHeading(float in) { auto h = in; @@ -729,83 +827,139 @@ float MobMovementManager::FixHeading(float in) return h; } -void MobMovementManager::DumpStats(Client *to) +/** + * @param client + */ +void MobMovementManager::DumpStats(Client *client) { auto current_time = static_cast(Timer::GetCurrentTime()) / 1000.0; - auto total_time = current_time - _impl->Stats.LastResetTime; + auto total_time = current_time - _impl->Stats.LastResetTime; - to->Message(MT_System, "Dumping Movement Stats:"); - to->Message(MT_System, "Total Sent: %u (%.2f / sec)", _impl->Stats.TotalSent, static_cast(_impl->Stats.TotalSent) / total_time); - to->Message(MT_System, "Total Heading: %u (%.2f / sec)", _impl->Stats.TotalSentHeading, static_cast(_impl->Stats.TotalSentHeading) / total_time); - to->Message(MT_System, "Total Movement: %u (%.2f / sec)", _impl->Stats.TotalSentMovement, static_cast(_impl->Stats.TotalSentMovement) / total_time); - to->Message(MT_System, "Total Position: %u (%.2f / sec)", _impl->Stats.TotalSentPosition, static_cast(_impl->Stats.TotalSentPosition) / total_time); + client->Message(MT_System, "Dumping Movement Stats:"); + client->Message( + MT_System, + "Total Sent: %u (%.2f / sec)", + _impl->Stats.TotalSent, + static_cast(_impl->Stats.TotalSent) / total_time + ); + client->Message( + MT_System, + "Total Heading: %u (%.2f / sec)", + _impl->Stats.TotalSentHeading, + static_cast(_impl->Stats.TotalSentHeading) / total_time + ); + client->Message( + MT_System, + "Total Movement: %u (%.2f / sec)", + _impl->Stats.TotalSentMovement, + static_cast(_impl->Stats.TotalSentMovement) / total_time + ); + client->Message( + MT_System, + "Total Position: %u (%.2f / sec)", + _impl->Stats.TotalSentPosition, + static_cast(_impl->Stats.TotalSentPosition) / total_time + ); } void MobMovementManager::ClearStats() { - _impl->Stats.LastResetTime = static_cast(Timer::GetCurrentTime()) / 1000.0; - _impl->Stats.TotalSent = 0; - _impl->Stats.TotalSentHeading = 0; + _impl->Stats.LastResetTime = static_cast(Timer::GetCurrentTime()) / 1000.0; + _impl->Stats.TotalSent = 0; + _impl->Stats.TotalSentHeading = 0; _impl->Stats.TotalSentMovement = 0; _impl->Stats.TotalSentPosition = 0; } -void MobMovementManager::FillCommandStruct(PlayerPositionUpdateServer_Struct *spu, Mob *m, float dx, float dy, float dz, float dh, int anim) +/** + * @param position_update + * @param mob + * @param delta_x + * @param delta_y + * @param delta_z + * @param delta_heading + * @param anim + */ +void MobMovementManager::FillCommandStruct( + PlayerPositionUpdateServer_Struct *position_update, + Mob *mob, + float delta_x, + float delta_y, + float delta_z, + float delta_heading, + int anim +) { - memset(spu, 0x00, sizeof(PlayerPositionUpdateServer_Struct)); - spu->spawn_id = m->GetID(); - spu->x_pos = FloatToEQ19(m->GetX()); - spu->y_pos = FloatToEQ19(m->GetY()); - spu->z_pos = FloatToEQ19(m->GetZ()); - spu->heading = FloatToEQ12(m->GetHeading()); - spu->delta_x = FloatToEQ13(dx); - spu->delta_y = FloatToEQ13(dy); - spu->delta_z = FloatToEQ13(dz); - spu->delta_heading = FloatToEQ10(dh); - spu->animation = (m->IsBot() ? (int)((float)anim / 1.785714f) : anim); + memset(position_update, 0x00, sizeof(PlayerPositionUpdateServer_Struct)); + position_update->spawn_id = mob->GetID(); + position_update->x_pos = FloatToEQ19(mob->GetX()); + position_update->y_pos = FloatToEQ19(mob->GetY()); + position_update->z_pos = FloatToEQ19(mob->GetZ()); + position_update->heading = FloatToEQ12(mob->GetHeading()); + position_update->delta_x = FloatToEQ13(delta_x); + position_update->delta_y = FloatToEQ13(delta_y); + position_update->delta_z = FloatToEQ13(delta_z); + position_update->delta_heading = FloatToEQ10(delta_heading); + position_update->animation = (mob->IsBot() ? (int) ((float) anim / 1.785714f) : anim); } -void MobMovementManager::UpdatePath(Mob *who, float x, float y, float z, MobMovementMode mode) +/** + * @param who + * @param x + * @param y + * @param z + * @param mob_movement_mode + */ +void MobMovementManager::UpdatePath(Mob *who, float x, float y, float z, MobMovementMode mob_movement_mode) { if (!zone->HasMap() || !zone->HasWaterMap()) { auto iter = _impl->Entries.find(who); auto &ent = (*iter); - - PushMoveTo(ent.second, x, y, z, mode); + + PushMoveTo(ent.second, x, y, z, mob_movement_mode); PushStopMoving(ent.second); return; } - + if (who->IsBoat()) { - UpdatePathBoat(who, x, y, z, mode); - } else if (who->IsUnderwaterOnly()) { - UpdatePathUnderwater(who, x, y, z, mode); + UpdatePathBoat(who, x, y, z, mob_movement_mode); + } + else if (who->IsUnderwaterOnly()) { + UpdatePathUnderwater(who, x, y, z, mob_movement_mode); } else { - UpdatePathGround(who, x, y, z, mode); + UpdatePathGround(who, x, y, z, mob_movement_mode); } } -void MobMovementManager::UpdatePathGround(Mob * who, float x, float y, float z, MobMovementMode mode) +/** + * @param who + * @param x + * @param y + * @param z + * @param mode + */ +void MobMovementManager::UpdatePathGround(Mob *who, float x, float y, float z, MobMovementMode mode) { PathfinderOptions opts; opts.smooth_path = true; - opts.step_size = RuleR(Pathing, NavmeshStepSize); - opts.offset = who->GetZOffset(); - opts.flags = PathingNotDisabled ^ PathingZoneLine; + opts.step_size = RuleR(Pathing, NavmeshStepSize); + opts.offset = who->GetZOffset(); + opts.flags = PathingNotDisabled ^ PathingZoneLine; //This is probably pointless since the nav mesh tool currently sets zonelines to disabled anyway auto partial = false; - auto stuck = false; - auto route = zone->pathing->FindPath( + auto stuck = false; + auto route = zone->pathing->FindPath( glm::vec3(who->GetX(), who->GetY(), who->GetZ()), glm::vec3(x, y, z), partial, stuck, - opts); + opts + ); auto eiter = _impl->Entries.find(who); - auto &ent = (*eiter); + auto &ent = (*eiter); if (route.size() == 0) { HandleStuckBehavior(who, x, y, z, mode); @@ -813,31 +967,26 @@ void MobMovementManager::UpdatePathGround(Mob * who, float x, float y, float z, } AdjustRoute(route, who); - - - + //avoid doing any processing if the mob is stuck to allow normal stuck code to work. - if (!stuck) - { + if (!stuck) { //there are times when the routes returned are no differen than where the mob is currently standing. What basically happens - //is a mob will get 'stuck' in such a way that it should be moving but the 'moving' place is the exact same spot it is at. - //this is a problem and creates an area of ground that if a mob gets to, will stay there forever. If socal this creates a - //"Ball of Death" (tm). This code tries to prevent this by simply warping the mob to the requested x/y. Better to have a warp than + //is a mob will get 'stuck' in such a way that it should be moving but the 'moving' place is the exact same spot it is at. + //this is a problem and creates an area of ground that if a mob gets to, will stay there forever. If socal this creates a + //"Ball of Death" (tm). This code tries to prevent this by simply warping the mob to the requested x/y. Better to have a warp than //have stuck mobs. - auto routeNode = route.begin(); + auto routeNode = route.begin(); bool noValidPath = true; while (routeNode != route.end() && noValidPath == true) { auto ¤tNode = (*routeNode); - if (routeNode == route.end()) - { + if (routeNode == route.end()) { continue; } - if (!(currentNode.pos.x == who->GetX() && currentNode.pos.y == who->GetY())) - { + if (!(currentNode.pos.x == who->GetX() && currentNode.pos.y == who->GetY())) { //if one of the nodes to move to, is not our current node, pass it. noValidPath = false; break; @@ -847,47 +996,62 @@ void MobMovementManager::UpdatePathGround(Mob * who, float x, float y, float z, } - if (noValidPath) - { + if (noValidPath) { //we are 'stuck' in a path, lets just get out of this by 'teleporting' to the next position. - PushTeleportTo(ent.second, x, y, z, - CalculateHeadingAngleBetweenPositions(who->GetX(), who->GetY(), x, y)); + PushTeleportTo( + ent.second, + x, + y, + z, + CalculateHeadingAngleBetweenPositions(who->GetX(), who->GetY(), x, y) + ); + return; } } - + auto iter = route.begin(); + glm::vec3 previous_pos(who->GetX(), who->GetY(), who->GetZ()); + bool first_node = true; - - while (iter != route.end()) { auto ¤t_node = (*iter); - + iter++; - + if (iter == route.end()) { continue; } - + previous_pos = current_node.pos; auto &next_node = (*iter); - + if (first_node) { - + if (mode == MovementWalking) { auto h = who->CalculateHeadingToTarget(next_node.pos.x, next_node.pos.y); PushRotateTo(ent.second, who, h, mode); } - + first_node = false; } - + //move to / teleport to node + 1 if (next_node.teleport && next_node.pos.x != 0.0f && next_node.pos.y != 0.0f) { - PushTeleportTo(ent.second, next_node.pos.x, next_node.pos.y, next_node.pos.z, - CalculateHeadingAngleBetweenPositions(current_node.pos.x, current_node.pos.y, next_node.pos.x, next_node.pos.y)); + PushTeleportTo( + ent.second, + next_node.pos.x, + next_node.pos.y, + next_node.pos.z, + CalculateHeadingAngleBetweenPositions( + current_node.pos.x, + current_node.pos.y, + next_node.pos.x, + next_node.pos.y + ) + ); } else { if (zone->watermap->InLiquid(previous_pos)) { @@ -907,41 +1071,50 @@ void MobMovementManager::UpdatePathGround(Mob * who, float x, float y, float z, } } -void MobMovementManager::UpdatePathUnderwater(Mob *who, float x, float y, float z, MobMovementMode mode) +/** + * @param who + * @param x + * @param y + * @param z + * @param movement_mode + */ +void MobMovementManager::UpdatePathUnderwater(Mob *who, float x, float y, float z, MobMovementMode movement_mode) { auto eiter = _impl->Entries.find(who); - auto &ent = (*eiter); - if (zone->watermap->InLiquid(who->GetPosition()) && zone->watermap->InLiquid(glm::vec3(x, y, z)) && zone->zonemap->CheckLoS(who->GetPosition(), glm::vec3(x, y, z))) { - PushSwimTo(ent.second, x, y, z, mode); + auto &ent = (*eiter); + if (zone->watermap->InLiquid(who->GetPosition()) && zone->watermap->InLiquid(glm::vec3(x, y, z)) && + zone->zonemap->CheckLoS(who->GetPosition(), glm::vec3(x, y, z))) { + PushSwimTo(ent.second, x, y, z, movement_mode); PushStopMoving(ent.second); return; } PathfinderOptions opts; opts.smooth_path = true; - opts.step_size = RuleR(Pathing, NavmeshStepSize); - opts.offset = who->GetZOffset(); - opts.flags = PathingNotDisabled ^ PathingZoneLine; + opts.step_size = RuleR(Pathing, NavmeshStepSize); + opts.offset = who->GetZOffset(); + opts.flags = PathingNotDisabled ^ PathingZoneLine; auto partial = false; - auto stuck = false; - auto route = zone->pathing->FindPath( + auto stuck = false; + auto route = zone->pathing->FindPath( glm::vec3(who->GetX(), who->GetY(), who->GetZ()), glm::vec3(x, y, z), partial, stuck, - opts); + opts + ); if (route.size() == 0) { - HandleStuckBehavior(who, x, y, z, mode); + HandleStuckBehavior(who, x, y, z, movement_mode); return; } AdjustRoute(route, who); - auto iter = route.begin(); + auto iter = route.begin(); glm::vec3 previous_pos(who->GetX(), who->GetY(), who->GetZ()); - bool first_node = true; + bool first_node = true; while (iter != route.end()) { auto ¤t_node = (*iter); @@ -961,7 +1134,7 @@ void MobMovementManager::UpdatePathUnderwater(Mob *who, float x, float y, float } if (route.size() == 0) { - HandleStuckBehavior(who, x, y, z, mode); + HandleStuckBehavior(who, x, y, z, movement_mode); return; } @@ -981,9 +1154,9 @@ void MobMovementManager::UpdatePathUnderwater(Mob *who, float x, float y, float if (first_node) { - if (mode == MovementWalking) { + if (movement_mode == MovementWalking) { auto h = who->CalculateHeadingToTarget(next_node.pos.x, next_node.pos.y); - PushRotateTo(ent.second, who, h, mode); + PushRotateTo(ent.second, who, h, movement_mode); } first_node = false; @@ -991,47 +1164,87 @@ void MobMovementManager::UpdatePathUnderwater(Mob *who, float x, float y, float //move to / teleport to node + 1 if (next_node.teleport && next_node.pos.x != 0.0f && next_node.pos.y != 0.0f) { - PushTeleportTo(ent.second, next_node.pos.x, next_node.pos.y, next_node.pos.z, - CalculateHeadingAngleBetweenPositions(current_node.pos.x, current_node.pos.y, next_node.pos.x, next_node.pos.y)); + PushTeleportTo( + ent.second, next_node.pos.x, next_node.pos.y, next_node.pos.z, + CalculateHeadingAngleBetweenPositions( + current_node.pos.x, + current_node.pos.y, + next_node.pos.x, + next_node.pos.y + )); } else { - PushSwimTo(ent.second, next_node.pos.x, next_node.pos.y, next_node.pos.z, mode); + PushSwimTo(ent.second, next_node.pos.x, next_node.pos.y, next_node.pos.z, movement_mode); } } if (stuck) { - HandleStuckBehavior(who, x, y, z, mode); + HandleStuckBehavior(who, x, y, z, movement_mode); } else { PushStopMoving(ent.second); } } +/** + * @param who + * @param x + * @param y + * @param z + * @param mode + */ void MobMovementManager::UpdatePathBoat(Mob *who, float x, float y, float z, MobMovementMode mode) { auto eiter = _impl->Entries.find(who); - auto &ent = (*eiter); + auto &ent = (*eiter); PushSwimTo(ent.second, x, y, z, mode); PushStopMoving(ent.second); } +/** + * @param ent + * @param x + * @param y + * @param z + * @param heading + */ void MobMovementManager::PushTeleportTo(MobMovementEntry &ent, float x, float y, float z, float heading) { ent.Commands.push_back(std::unique_ptr(new TeleportToCommand(x, y, z, heading))); } -void MobMovementManager::PushMoveTo(MobMovementEntry &ent, float x, float y, float z, MobMovementMode mode) +/** + * @param ent + * @param x + * @param y + * @param z + * @param mob_movement_mode + */ +void MobMovementManager::PushMoveTo(MobMovementEntry &ent, float x, float y, float z, MobMovementMode mob_movement_mode) { - ent.Commands.push_back(std::unique_ptr(new MoveToCommand(x, y, z, mode))); + ent.Commands.push_back(std::unique_ptr(new MoveToCommand(x, y, z, mob_movement_mode))); } -void MobMovementManager::PushSwimTo(MobMovementEntry &ent, float x, float y, float z, MobMovementMode mode) +/** + * @param ent + * @param x + * @param y + * @param z + * @param mob_movement_mode + */ +void MobMovementManager::PushSwimTo(MobMovementEntry &ent, float x, float y, float z, MobMovementMode mob_movement_mode) { - ent.Commands.push_back(std::unique_ptr(new SwimToCommand(x, y, z, mode))); + ent.Commands.push_back(std::unique_ptr(new SwimToCommand(x, y, z, mob_movement_mode))); } -void MobMovementManager::PushRotateTo(MobMovementEntry &ent, Mob *who, float to, MobMovementMode mode) +/** + * @param ent + * @param who + * @param to + * @param mob_movement_mode + */ +void MobMovementManager::PushRotateTo(MobMovementEntry &ent, Mob *who, float to, MobMovementMode mob_movement_mode) { auto from = FixHeading(who->GetHeading()); to = FixHeading(to); @@ -1050,46 +1263,59 @@ void MobMovementManager::PushRotateTo(MobMovementEntry &ent, Mob *who, float to, diff -= 512.0; } - ent.Commands.push_back(std::unique_ptr(new RotateToCommand(to, diff > 0 ? 1.0 : -1.0, mode))); + ent.Commands.push_back(std::unique_ptr(new RotateToCommand(to, diff > 0 ? 1.0 : -1.0, mob_movement_mode))); } -void MobMovementManager::PushStopMoving(MobMovementEntry &ent) +/** + * @param mob_movement_entry + */ +void MobMovementManager::PushStopMoving(MobMovementEntry &mob_movement_entry) { - ent.Commands.push_back(std::unique_ptr(new StopMovingCommand())); + mob_movement_entry.Commands.push_back(std::unique_ptr(new StopMovingCommand())); } -void MobMovementManager::PushEvadeCombat(MobMovementEntry &ent) +/** + * @param mob_movement_entry + */ +void MobMovementManager::PushEvadeCombat(MobMovementEntry &mob_movement_entry) { - ent.Commands.push_back(std::unique_ptr(new EvadeCombatCommand())); + mob_movement_entry.Commands.push_back(std::unique_ptr(new EvadeCombatCommand())); } -void MobMovementManager::HandleStuckBehavior(Mob *who, float x, float y, float z, MobMovementMode mode) +/** + * @param who + * @param x + * @param y + * @param z + * @param mob_movement_mode + */ +void MobMovementManager::HandleStuckBehavior(Mob *who, float x, float y, float z, MobMovementMode mob_movement_mode) { - auto sb = who->GetStuckBehavior(); + auto sb = who->GetStuckBehavior(); MobStuckBehavior behavior = RunToTarget; if (sb >= 0 && sb < MaxStuckBehavior) { - behavior = (MobStuckBehavior)sb; + behavior = (MobStuckBehavior) sb; } auto eiter = _impl->Entries.find(who); - auto &ent = (*eiter); + auto &ent = (*eiter); switch (sb) { - case RunToTarget: - PushMoveTo(ent.second, x, y, z, mode); - PushStopMoving(ent.second); - break; - case WarpToTarget: - PushTeleportTo(ent.second, x, y, z, 0.0f); - PushStopMoving(ent.second); - break; - case TakeNoAction: - PushStopMoving(ent.second); - break; - case EvadeCombat: - //PushEvadeCombat(ent.second); - PushStopMoving(ent.second); - break; + case RunToTarget: + PushMoveTo(ent.second, x, y, z, mob_movement_mode); + PushStopMoving(ent.second); + break; + case WarpToTarget: + PushTeleportTo(ent.second, x, y, z, 0.0f); + PushStopMoving(ent.second); + break; + case TakeNoAction: + PushStopMoving(ent.second); + break; + case EvadeCombat: + //PushEvadeCombat(ent.second); + PushStopMoving(ent.second); + break; } } diff --git a/zone/mob_movement_manager.h b/zone/mob_movement_manager.h index d9b2e7845..3c616cc5a 100644 --- a/zone/mob_movement_manager.h +++ b/zone/mob_movement_manager.h @@ -41,18 +41,29 @@ class MobMovementManager public: ~MobMovementManager(); void Process(); - void AddMob(Mob *m); - void RemoveMob(Mob *m); - void AddClient(Client *c); - void RemoveClient(Client *c); + void AddMob(Mob *mob); + void RemoveMob(Mob *mob); + void AddClient(Client *client); + void RemoveClient(Client *client); - void RotateTo(Mob *who, float to, MobMovementMode mode = MovementRunning); + void RotateTo(Mob *who, float to, MobMovementMode mob_movement_mode = MovementRunning); void Teleport(Mob *who, float x, float y, float z, float heading); void NavigateTo(Mob *who, float x, float y, float z, MobMovementMode mode = MovementRunning); void StopNavigation(Mob *who); - void SendCommandToClients(Mob *m, float dx, float dy, float dz, float dh, int anim, ClientRange range); + + void SendCommandToClients( + Mob *mob, + float delta_x, + float delta_y, + float delta_z, + float delta_heading, + int anim, + ClientRange range, + Client* single_client = nullptr + ); + float FixHeading(float in); - void DumpStats(Client *to); + void DumpStats(Client *client); void ClearStats(); static MobMovementManager &Get() { @@ -65,18 +76,18 @@ private: MobMovementManager(const MobMovementManager&); MobMovementManager& operator=(const MobMovementManager&); - void FillCommandStruct(PlayerPositionUpdateServer_Struct *spu, Mob *m, float dx, float dy, float dz, float dh, int anim); - void UpdatePath(Mob *who, float x, float y, float z, MobMovementMode mode); + void FillCommandStruct(PlayerPositionUpdateServer_Struct *position_update, Mob *mob, float delta_x, float delta_y, float delta_z, float delta_heading, int anim); + void UpdatePath(Mob *who, float x, float y, float z, MobMovementMode mob_movement_mode); void UpdatePathGround(Mob *who, float x, float y, float z, MobMovementMode mode); - void UpdatePathUnderwater(Mob *who, float x, float y, float z, MobMovementMode mode); + void UpdatePathUnderwater(Mob *who, float x, float y, float z, MobMovementMode movement_mode); void UpdatePathBoat(Mob *who, float x, float y, float z, MobMovementMode mode); void PushTeleportTo(MobMovementEntry &ent, float x, float y, float z, float heading); - void PushMoveTo(MobMovementEntry &ent, float x, float y, float z, MobMovementMode mode); - void PushSwimTo(MobMovementEntry &ent, float x, float y, float z, MobMovementMode mode); - void PushRotateTo(MobMovementEntry &ent, Mob *who, float to, MobMovementMode mode); - void PushStopMoving(MobMovementEntry &ent); - void PushEvadeCombat(MobMovementEntry &ent); - void HandleStuckBehavior(Mob *who, float x, float y, float z, MobMovementMode mode); + void PushMoveTo(MobMovementEntry &ent, float x, float y, float z, MobMovementMode mob_movement_mode); + void PushSwimTo(MobMovementEntry &ent, float x, float y, float z, MobMovementMode mob_movement_mode); + void PushRotateTo(MobMovementEntry &ent, Mob *who, float to, MobMovementMode mob_movement_mode); + void PushStopMoving(MobMovementEntry &mob_movement_entry); + void PushEvadeCombat(MobMovementEntry &mob_movement_entry); + void HandleStuckBehavior(Mob *who, float x, float y, float z, MobMovementMode mob_movement_mode); struct Implementation; std::unique_ptr _impl; diff --git a/zone/perl_client.cpp b/zone/perl_client.cpp index be1bf2a8e..f86228feb 100644 --- a/zone/perl_client.cpp +++ b/zone/perl_client.cpp @@ -174,7 +174,7 @@ XS(XS_Client_Kick) { if (THIS == nullptr) Perl_croak(aTHX_ "THIS is nullptr, avoiding crash."); - THIS->Kick(); + THIS->Kick("Perl Quest"); } XSRETURN_EMPTY; } diff --git a/zone/questmgr.cpp b/zone/questmgr.cpp index 79f5af8a5..dafc3f1e6 100644 --- a/zone/questmgr.cpp +++ b/zone/questmgr.cpp @@ -809,7 +809,7 @@ void QuestManager::changedeity(int diety_id) { initiator->SetDeity(diety_id); initiator->Message(15,"Your Deity has been changed/set to: %i", diety_id); initiator->Save(1); - initiator->Kick(); + initiator->Kick("Deity change by QuestManager"); } else { @@ -943,7 +943,7 @@ void QuestManager::permaclass(int class_id) { //Makes the client the class specified initiator->SetBaseClass(class_id); initiator->Save(2); - initiator->Kick(); + initiator->Kick("Base class change by QuestManager"); } void QuestManager::permarace(int race_id) { @@ -951,7 +951,7 @@ void QuestManager::permarace(int race_id) { //Makes the client the race specified initiator->SetBaseRace(race_id); initiator->Save(2); - initiator->Kick(); + initiator->Kick("Base race change by QuestManager"); } void QuestManager::permagender(int gender_id) { @@ -959,7 +959,7 @@ void QuestManager::permagender(int gender_id) { //Makes the client the gender specified initiator->SetBaseGender(gender_id); initiator->Save(2); - initiator->Kick(); + initiator->Kick("Base gender change by QuestManager"); } uint16 QuestManager::scribespells(uint8 max_level, uint8 min_level) { diff --git a/zone/spells.cpp b/zone/spells.cpp index 8b1a08d19..fa09727a2 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -298,7 +298,8 @@ bool Mob::CastSpell(uint16 spell_id, uint16 target_id, CastingSlot slot, if(IsClient()) { char temp[64]; sprintf(temp, "%d", spell_id); - parse->EventPlayer(EVENT_CAST_BEGIN, CastToClient(), temp, 0); + if (parse->EventPlayer(EVENT_CAST_BEGIN, CastToClient(), temp, 0) != 0) + return false; } else if(IsNPC()) { char temp[64]; sprintf(temp, "%d", spell_id); diff --git a/zone/trading.cpp b/zone/trading.cpp index 154fdf148..fe9a72daa 100644 --- a/zone/trading.cpp +++ b/zone/trading.cpp @@ -112,7 +112,7 @@ void Trade::AddEntity(uint16 trade_slot_id, uint32 stack_size) { // (it just didn't handle partial stack move actions) if (stack_size > 0) { if (!inst->IsStackable() || !inst2 || !inst2->GetItem() || (inst->GetID() != inst2->GetID()) || (stack_size > inst->GetCharges())) { - client->Kick(); + client->Kick("Error stacking item in trade"); return; } @@ -138,7 +138,7 @@ void Trade::AddEntity(uint16 trade_slot_id, uint32 stack_size) { } else { if (inst2 && inst2->GetID()) { - client->Kick(); + client->Kick("Attempting to add null item to trade"); return; } diff --git a/zone/worldserver.cpp b/zone/worldserver.cpp index 94be0eb14..ffe777808 100644 --- a/zone/worldserver.cpp +++ b/zone/worldserver.cpp @@ -534,7 +534,15 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) ServerZoneIncomingClient_Struct* szic = (ServerZoneIncomingClient_Struct*)pack->pBuffer; if (is_zone_loaded) { SetZoneData(zone->GetZoneID(), zone->GetInstanceID()); + if (szic->zoneid == zone->GetZoneID()) { + auto client = entity_list.GetClientByLSID(szic->lsid); + if (client) { + client->Kick("Dropped by world CLE subsystem"); + client->Save(); + } + + zone->RemoveAuth(szic->lsid); zone->AddAuth(szic); // This packet also doubles as "incoming client" notification, lets not shut down before they get here zone->StartShutdownTimer(AUTHENTICATION_TIMEOUT * 1000); @@ -547,6 +555,23 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) } break; } + case ServerOP_DropClient: { + if (pack->size != sizeof(ServerZoneDropClient_Struct)) { + break; + } + + ServerZoneDropClient_Struct* drop = (ServerZoneDropClient_Struct*)pack->pBuffer; + if (zone) { + zone->RemoveAuth(drop->lsid); + + auto client = entity_list.GetClientByLSID(drop->lsid); + if (client) { + client->Kick("Dropped by world CLE subsystem"); + client->Save(); + } + } + break; + } case ServerOP_ZonePlayer: { ServerZonePlayer_Struct* szp = (ServerZonePlayer_Struct*)pack->pBuffer; Client* client = entity_list.GetClientByName(szp->name); diff --git a/zone/zone.cpp b/zone/zone.cpp index 88b0253a2..d20b2934e 100755 --- a/zone/zone.cpp +++ b/zone/zone.cpp @@ -55,6 +55,7 @@ #include "zone_config.h" #include "mob_movement_manager.h" #include "npc_scale_manager.h" +#include "../common/data_verification.h" #include #include @@ -864,6 +865,8 @@ Zone::Zone(uint32 in_zoneid, uint32 in_instanceid, const char* in_short_name) m_last_ucss_update = 0; mMovementManager = &MobMovementManager::Get(); + + SetNpcPositionUpdateDistance(0); } Zone::~Zone() { @@ -1106,6 +1109,7 @@ void Zone::AddAuth(ServerZoneIncomingClient_Struct* szic) { zca->accid = szic->accid; zca->admin = szic->admin; zca->charid = szic->charid; + zca->lsid = szic->lsid; zca->tellsoff = szic->tellsoff; strn0cpy(zca->charname, szic->charname, sizeof(zca->charname)); strn0cpy(zca->lskey, szic->lskey, sizeof(zca->lskey)); @@ -1113,16 +1117,31 @@ void Zone::AddAuth(ServerZoneIncomingClient_Struct* szic) { client_auth_list.Insert(zca); } -void Zone::RemoveAuth(const char* iCharName) +void Zone::RemoveAuth(const char* iCharName, const char* iLSKey) { LinkedListIterator iterator(client_auth_list); iterator.Reset(); while (iterator.MoreElements()) { ZoneClientAuth_Struct* zca = iterator.GetData(); - if (strcasecmp(zca->charname, iCharName) == 0) { - iterator.RemoveCurrent(); - return; + if (strcasecmp(zca->charname, iCharName) == 0 && strcasecmp(zca->lskey, iLSKey) == 0) { + iterator.RemoveCurrent(); + return; + } + iterator.Advance(); + } +} + +void Zone::RemoveAuth(uint32 lsid) +{ + LinkedListIterator iterator(client_auth_list); + + iterator.Reset(); + while (iterator.MoreElements()) { + ZoneClientAuth_Struct* zca = iterator.GetData(); + if (zca->lsid == lsid) { + iterator.RemoveCurrent(); + continue; } iterator.Advance(); } @@ -1178,9 +1197,9 @@ uint32 Zone::CountAuth() { bool Zone::Process() { spawn_conditions.Process(); - if(spawn2_timer.Check()) { + if (spawn2_timer.Check()) { - LinkedListIterator iterator(spawn2_list); + LinkedListIterator iterator(spawn2_list); EQEmu::InventoryProfile::CleanDirty(); @@ -1196,10 +1215,15 @@ bool Zone::Process() { } } - if(adv_data && !did_adventure_actions) + if (adv_data && !did_adventure_actions) { DoAdventureActions(); + } + if (GetNpcPositionUpdateDistance() == 0) { + CalculateNpcUpdateDistanceSpread(); + } } + if(initgrids_timer.Check()) { //delayed grid loading stuff. initgrids_timer.Disable(); @@ -2349,3 +2373,59 @@ void Zone::SetUCSServerAvailable(bool ucss_available, uint32 update_timestamp) { if (m_last_ucss_update < update_timestamp) m_ucss_available = ucss_available; } + +int Zone::GetNpcPositionUpdateDistance() const +{ + return npc_position_update_distance; +} + +void Zone::SetNpcPositionUpdateDistance(int in_npc_position_update_distance) +{ + Zone::npc_position_update_distance = in_npc_position_update_distance; +} + +void Zone::CalculateNpcUpdateDistanceSpread() +{ + float max_x = 0; + float max_y = 0; + float min_x = 0; + float min_y = 0; + + auto &mob_list = entity_list.GetMobList(); + + for (auto &it : mob_list) { + Mob *entity = it.second; + if (!entity->IsNPC()) { + continue; + } + + if (entity->GetX() <= min_x) { + min_x = entity->GetX(); + } + + if (entity->GetY() <= min_y) { + min_y = entity->GetY(); + } + + if (entity->GetX() >= max_x) { + max_x = entity->GetX(); + } + + if (entity->GetY() >= max_y) { + max_y = entity->GetY(); + } + } + + int x_spread = int(abs(max_x - min_x)); + int y_spread = int(abs(max_y - min_y)); + int combined_spread = int(abs((x_spread + y_spread) / 2)); + int update_distance = EQEmu::ClampLower(int(combined_spread / 4), int(zone->GetMaxMovementUpdateRange())); + + SetNpcPositionUpdateDistance(update_distance); + + Log(Logs::General, Logs::Debug, + "NPC update spread distance set to [%i] combined_spread [%i]", + update_distance, + combined_spread + ); +} \ No newline at end of file diff --git a/zone/zone.h b/zone/zone.h index e86fdc339..176fe0cb3 100755 --- a/zone/zone.h +++ b/zone/zone.h @@ -52,6 +52,7 @@ struct ZoneClientAuth_Struct { uint32 accid; int16 admin; uint32 charid; + uint32 lsid; bool tellsoff; char charname[64]; char lskey[30]; @@ -125,6 +126,9 @@ public: bool Process(); bool SaveZoneCFG(); + int GetNpcPositionUpdateDistance() const; + void SetNpcPositionUpdateDistance(int in_npc_position_update_distance); + char *adv_data; const char *GetSpellBlockedMessage(uint32 spell_id, const glm::vec3 &location); @@ -218,6 +222,7 @@ public: void ChangeWeather(); void ClearBlockedSpells(); void ClearNPCTypeCache(int id); + void CalculateNpcUpdateDistanceSpread(); void DelAggroMob() { aggroedmobs--; } void DeleteQGlobal(std::string name, uint32 npcID, uint32 charID, uint32 zoneID); void Despawn(uint32 spawngroupID); @@ -243,7 +248,8 @@ public: void LoadZoneDoors(const char *zone, int16 version); void ReloadStaticData(); void ReloadWorld(uint32 Option); - void RemoveAuth(const char *iCharName); + void RemoveAuth(const char *iCharName, const char *iLSKey); + void RemoveAuth(uint32 lsid); void Repop(uint32 delay = 0); void RepopClose(const glm::vec4 &client_position, uint32 repop_distance); void RequestUCSServerStatus(); @@ -288,7 +294,7 @@ public: */ find_replace(message, std::string("%"), std::string(".")); - if (message.find("\n") != std::string::npos) { + if (message.find('\n') != std::string::npos) { auto message_split = SplitString(message, '\n'); entity_list.MessageStatus( 0, @@ -343,6 +349,7 @@ private: glm::vec4 m_Graveyard; int default_ruleset; int totalBS; + int npc_position_update_distance; int32 aggroedmobs; uint8 zone_type; uint16 instanceversion;