From 075bff298858a216d8bcba6ec27be449f9e288df Mon Sep 17 00:00:00 2001 From: jeefo Date: Tue, 15 Dec 2020 15:28:58 +0300 Subject: [PATCH] Custom.cfg (#213) Added custom configs. These are used for replacing some hardcoded strings inside bot code, currently custom cvar for parachute detection is available, as well as custom c4 model names. Added editorconfig, and fixed CRLF for files (was a mix between LF & CRLF). Fixed use-after-free sanitizer error with chatlib. Fixed configs files loaded with memory-loader does not process last line in config files. --- .editorconfig | 10 + cfg/addons/yapb/conf/custom.cfg | 22 ++ cfg/addons/yapb/conf/lang/ru_lang.cfg | 6 - ext/crlib/cr-detour.h | 2 +- ext/crlib/cr-files.h | 24 +- ext/crlib/cr-http.h | 19 +- inc/config.h | 22 ++ inc/control.h | 1 + inc/support.h | 4 + inc/version.h | 4 +- meson.build | 333 +++++++++++----------- package.py | 395 +++++++++++++------------- src/botlib.cpp | 6 +- src/chatlib.cpp | 12 +- src/config.cpp | 43 ++- src/control.cpp | 9 + src/engine.cpp | 3 + src/graph.cpp | 3 +- src/manager.cpp | 5 +- src/navigate.cpp | 10 +- src/support.cpp | 4 + 21 files changed, 533 insertions(+), 404 deletions(-) create mode 100644 .editorconfig create mode 100644 cfg/addons/yapb/conf/custom.cfg diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..9f12464 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,10 @@ +# http://editorconfig.org +root = true + +[*] +indent_style = space +indent_size = 3 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true \ No newline at end of file diff --git a/cfg/addons/yapb/conf/custom.cfg b/cfg/addons/yapb/conf/custom.cfg new file mode 100644 index 0000000..5893135 --- /dev/null +++ b/cfg/addons/yapb/conf/custom.cfg @@ -0,0 +1,22 @@ +; +; @package: YaPB +; @version: 4.2 +; @author: YaPB Development Team +; @filename: yapb.cfg +; +; Custom configuration file, allow to change some hardcoded stuff in bot code. +; + +; +; Custom name for C4 model, for servers that replacing C4 model with it's own. +; By default it's "c4.mdl" (the models/ path is omitted), so if you need to use +; "models/mybomb/mybomb.mdl", you should specify "mybomb/mybomb.mdl". +; +C4ModelName = c4.mdl + +; +; Custon cvar name for parachute handling, there are all the different plugins that +; handles parachute (AMX Parachute, AMX Parachute Lite, etc..), you can specify needed +; cvar here. +; +AMXParachuteCvar = sv_parachute diff --git a/cfg/addons/yapb/conf/lang/ru_lang.cfg b/cfg/addons/yapb/conf/lang/ru_lang.cfg index 962e2b3..511dd66 100644 --- a/cfg/addons/yapb/conf/lang/ru_lang.cfg +++ b/cfg/addons/yapb/conf/lang/ru_lang.cfg @@ -1994,9 +1994,3 @@ You're launched standalone version of %s. Metamod is not installed or not enable [TRANSLATED] Вы запустили автономную версию %s. Metamod не установлен или не включён! - -[ORIGINAL] -EndOfFile - -[TRANSLATED] -EndOfFile diff --git a/ext/crlib/cr-detour.h b/ext/crlib/cr-detour.h index 509b509..3ba852a 100644 --- a/ext/crlib/cr-detour.h +++ b/ext/crlib/cr-detour.h @@ -1,4 +1,4 @@ -// +// // YaPB - Counter-Strike Bot based on PODBot by Markus Klinge. // Copyright © 2004-2020 YaPB Project . // diff --git a/ext/crlib/cr-files.h b/ext/crlib/cr-files.h index 88bd2a6..b8a4ffa 100644 --- a/ext/crlib/cr-files.h +++ b/ext/crlib/cr-files.h @@ -71,6 +71,8 @@ public: int ch = 0; SmallArray data (255); + line.clear (); + while ((ch = get ()) != EOF && !eof ()) { data.push (static_cast (ch)); @@ -78,9 +80,10 @@ public: break; } } - line.assign (data.data (), data.length ()); - - return !eof (); + if (!data.empty ()) { + line.assign (data.data (), data.length ()); + } + return !line.empty (); } template size_t puts (const char *fmt, Args &&...args) { @@ -282,10 +285,7 @@ public: if (!contents_ || seek_ >= length_) { return Eof; } - auto ch = contents_[seek_]; - ++seek_; - - return static_cast (ch); + return static_cast (contents_[seek_++]); } char *getString (char *buffer, size_t count) { @@ -315,16 +315,20 @@ public: char ch; SmallArray data (255); - while ((ch = get ()) != Eof) { + line.clear (); + + while ((ch = get ()) != Eof && !eof ()) { data.push (ch); if (ch == '\n') { break; } } - line.assign (data.data (), data.length ()); - return !eof (); + if (!data.empty ()) { + line.assign (data.data (), data.length ()); + } + return !line.empty (); } size_t read (void *buffer, size_t size, size_t count = 1) { diff --git a/ext/crlib/cr-http.h b/ext/crlib/cr-http.h index f0c4223..7687b4c 100644 --- a/ext/crlib/cr-http.h +++ b/ext/crlib/cr-http.h @@ -111,7 +111,6 @@ CR_DECLARE_SCOPED_ENUM (HttpClientResult, CR_NAMESPACE_BEGIN - namespace detail { // simple http uri omitting query-string and port @@ -281,12 +280,10 @@ private: String userAgent_ = "crlib"; HttpClientResult statusCode_ = HttpClientResult::Undefined; int32 chunkSize_ = 4096; + bool initialized_ = false; public: - HttpClient () { - detail::SocketInit::start (); - } - + HttpClient () = default; ~HttpClient () = default; private: @@ -333,9 +330,17 @@ private: } public: + void startup () { + detail::SocketInit::start (); + initialized_ = true; + } // simple blocked download bool downloadFile (StringRef url, StringRef localPath, int32 timeout = DefaultSocketTimeout) { + if (plat.win && !initialized_) { + plat.abort ("Sockets not initialized."); + } + if (File::exists (localPath)) { statusCode_ = HttpClientResult::LocalFileExists; return false; @@ -402,6 +407,10 @@ public: } bool uploadFile (StringRef url, StringRef localPath, const int32 timeout = DefaultSocketTimeout) { + if (plat.win && !initialized_) { + plat.abort ("Sockets not initialized."); + } + if (!File::exists (localPath)) { statusCode_ = HttpClientResult::NoLocalFile; return false; diff --git a/inc/config.h b/inc/config.h index 38914fc..5064d2e 100644 --- a/inc/config.h +++ b/inc/config.h @@ -51,6 +51,7 @@ private: HashMap > m_language; HashMap m_difficulty; + HashMap m_custom; // default tables for personality weapon preferences, overridden by weapon.cfg SmallArray m_normalWeaponPrefs = { 0, 2, 1, 4, 5, 6, 3, 12, 10, 24, 25, 13, 11, 8, 7, 22, 23, 18, 21, 17, 19, 15, 17, 9, 14, 16 }; @@ -98,6 +99,9 @@ public: // loads bots map-specific config void loadMapSpecificConfig (); + // loads custom config + void loadCustomConfig (); + // sets memfile to use engine functions void setupMemoryFiles (); @@ -119,6 +123,9 @@ public: // translates bot message into needed language const char *translate (StringRef input); + // display current custom values + void showCustomValues (); + private: bool isCommentLine (StringRef line) const { if (line.empty ()) { @@ -237,6 +244,21 @@ public: StringRef getRandomLogoName (int index) { return m_logos[index]; } + + // get custom value + StringRef fetchCustom (StringRef name) { + if (m_custom.has (name)) { + return m_custom[name]; + } + SimpleLogger::instance ().error ("Trying to fetch uknonwn custom variable: %s", name); + + return ""; + } + + // simple accessor to c4 model name + StringRef getBombModelName () { + return fetchCustom ("C4ModelName"); + } }; // explose global diff --git a/inc/control.h b/inc/control.h index 88cb00a..6f9ca1a 100644 --- a/inc/control.h +++ b/inc/control.h @@ -99,6 +99,7 @@ private: int cmdMenu (); int cmdList (); int cmdCvars (); + int cmdShowCustom (); int cmdNode (); int cmdNodeOn (); int cmdNodeOff (); diff --git a/inc/support.h b/inc/support.h index 587702d..7aeafee 100644 --- a/inc/support.h +++ b/inc/support.h @@ -108,6 +108,10 @@ public: // check if object inside frustum plane bool isObjectInsidePlane (FrustumPlane &plane, const Vector ¢er, float height, float radius); + + // checks if same model ommiting the models directory + bool isModel (const edict_t *ent, StringRef model); + public: // re-show welcome after changelevel ? diff --git a/inc/version.h b/inc/version.h index d398395..074c766 100644 --- a/inc/version.h +++ b/inc/version.h @@ -14,6 +14,6 @@ #define MODULE_BUILD_MACHINE "localhost" #define MODULE_BUILD_COMPILER "unknown" -#define MODULE_BOT_VERSION "4.1" -#define MODULE_BOT_VERSION_FILE 4,0,0,000 +#define MODULE_BOT_VERSION "4.2" +#define MODULE_BOT_VERSION_FILE 4,2,0,000 #define MODULE_BOT_BUILD_ID "0:0" diff --git a/meson.build b/meson.build index faeee08..2a9af6b 100644 --- a/meson.build +++ b/meson.build @@ -6,25 +6,25 @@ # project ( - 'yapb', - 'cpp', + 'yapb', + 'cpp', - version: '4.1', - license: 'MIT', + version: '4.2', + license: 'MIT', - default_options: [ - 'buildtype=release', - 'b_ndebug=if-release', - 'cpp_std=c++14', - 'warning_level=3', - 'werror=true', - 'backend=ninja', - 'strip=true', - 'optimization=3', - 'default_library=static', - 'cpp_eh=none' - ], - meson_version: '>=0.48.0') + default_options: [ + 'buildtype=release', + 'b_ndebug=if-release', + 'cpp_std=c++14', + 'warning_level=3', + 'werror=true', + 'backend=ninja', + 'strip=true', + 'optimization=3', + 'default_library=static', + 'cpp_eh=none' + ], + meson_version: '>=0.48.0') find_program ('ninja', required: true) find_program ('git', required: true) @@ -53,9 +53,9 @@ ccflags = [] cdata = configuration_data() if isWindows - cdata.set ('buildVersionWin', ','.join (buildVersion.split ('.'))) + cdata.set ('buildVersionWin', ','.join (buildVersion.split ('.'))) else - cdata.set ('buildVersionWin', buildVersion) + cdata.set ('buildVersionWin', buildVersion) endif cdata.set ('commitHash', run_command ('git', 'rev-parse', '--short', 'HEAD').stdout ().strip ()) @@ -71,199 +71,198 @@ configure_file (input: 'inc/version.h.in', output: 'version.build.h', configurat ccflags += '-DVERSION_GENERATED' if isCLang or isGCC or (isIntel and not isWindows) - ccflags += [ - '-m32', - '-fno-threadsafe-statics', - '-fno-exceptions', - '-fno-rtti' - ] + ccflags += [ + '-m32', + '-fno-threadsafe-statics', + '-fno-exceptions', + '-fno-rtti' + ] - if not isDarwin - ccflags += [ - '-pedantic', - ] - endif + if not isDarwin + ccflags += [ + '-pedantic', + ] + endif - if isOptimize - ccflags += '-msse3' + if isOptimize + ccflags += '-msse3' - if (isCLang or isGCC) and not isDarwin - ccflags += [ - '-flto', - '-fdata-sections', - '-ffunction-sections' - ] + if (isCLang or isGCC) and not isDarwin + ccflags += [ + '-flto', + '-fdata-sections', + '-ffunction-sections' + ] - if isGCC - ccflags += '-fgraphite-identity' - ldflags += '-flto-partition=none' - endif + if isGCC + ccflags += '-fgraphite-identity' + ldflags += '-flto-partition=none' + endif - ldflags += [ - '-flto', - '-Wl,--version-script=../version_script.lds', - '-Wl,--gc-sections' - ] - endif - endif + ldflags += [ + '-flto', + '-Wl,--version-script=../version_script.lds', + '-Wl,--gc-sections' + ] + endif + endif - if isLinux - ldflags += [ - '-m32', - '-lm', - '-ldl' - ] - endif + if isLinux + ldflags += [ + '-m32', + '-lm', + '-ldl' + ] + endif endif if isIntel and (isLinux or isDarwin) - ldflags += [ - '-static-intel', - '-no-intel-extensions' - ] + ldflags += [ + '-static-intel', + '-no-intel-extensions' + ] endif if isLinux or isDarwin - if isDarwin - ccflags += '-mmacosx-version-min=10.9' - ldflags += [ - '-dynamiclib', - '-lstdc++', - '-mmacosx-version-min=10.9' - ] - else - ldflags += '-static-libgcc' - endif + if isDarwin + ccflags += '-mmacosx-version-min=10.9' + ldflags += [ + '-dynamiclib', + '-lstdc++', + '-mmacosx-version-min=10.9' + ] + else + ldflags += '-static-libgcc' + endif - if not isOptimize - ccflags += [ - '-g3', - '-ggdb', - '-DCR_DEBUG' - ] - else - ccflags += [ - '-mtune=generic', - '-msse3', - '-mfpmath=sse', - '-fno-builtin', - '-funroll-loops', - '-fomit-frame-pointer', - '-fno-stack-protector', - '-fvisibility=hidden', - '-fvisibility-inlines-hidden' - ] + if not isOptimize + ccflags += [ + '-g3', + '-ggdb', + '-DCR_DEBUG' + ] + else + ccflags += [ + '-mtune=generic', + '-msse3', + '-mfpmath=sse', + '-fno-builtin', + '-funroll-loops', + '-fomit-frame-pointer', + '-fno-stack-protector', + '-fvisibility=hidden', + '-fvisibility-inlines-hidden' + ] - if isIntel - ccflags += [ - '-ipo', - '-wd11076', - '-wd11074' - ] + if isIntel + ccflags += [ + '-ipo', + '-wd11076', + '-wd11074' + ] - ldflags += [ - '-cxxlib-nostd', - '-Wl,--no-undefined,-z,notext,--gc-sections', - '-ipo' - ] - elif isCLang and not isDarwin - llvmLinker = find_program ('lld', required: false) + ldflags += [ + '-cxxlib-nostd', + '-Wl,--no-undefined,-z,notext,--gc-sections', + '-ipo' + ] + elif isCLang and not isDarwin + llvmLinker = find_program ('lld', required: false) if llvmLinker.found() == true - ldflags += '-fuse-ld=' + llvmLinker.path ().split ('/')[-1] + ldflags += '-fuse-ld=' + llvmLinker.path ().split ('/')[-1] endif ldflags += [ - '-nostdlib++', - '-Wunused-command-line-argument', - '-Wl,-z,notext', - '--no-undefined' + '-nostdlib++', + '-Wunused-command-line-argument', + '-Wl,-z,notext', + '--no-undefined' ] - elif isGCC and not isDarwin + elif isGCC and not isDarwin ldflags += '-Wl,--no-undefined' - endif - endif + endif + endif endif if isWindows and (isVC or isIntel) - ldflags += [ - '/MACHINE:X86', - 'user32.lib', - 'ws2_32.lib' - ] + ldflags += [ + '/MACHINE:X86', + 'user32.lib', + 'ws2_32.lib' + ] - ccflags += [ - '/TP' - ] + ccflags += [ + '/TP' + ] - if isOptimize - ccflags += [ - '/GL', - '/arch:SSE2', - '/GS-', - '/Ob2', - '/Oy', - '/Oi' - ] - - ldflags += '/LTCG' - endif + if isOptimize + ccflags += [ + '/GL', + '/arch:SSE2', + '/GS-', + '/Ob2', + '/Oy', + '/Oi' + ] + ldflags += '/LTCG' + endif elif isWindows and (isCLang or isGCC) - if isCLang - ldflags += '-Wl,/MACHINE:X86' - else - ldflags += [ - '-static-libgcc', - '-Wl,--kill-at' - ] - endif + if isCLang + ldflags += '-Wl,/MACHINE:X86' + else + ldflags += [ + '-static-libgcc', + '-Wl,--kill-at' + ] + endif - ldflags += [ - '-luser32', - '-lws2_32' - ] + ldflags += [ + '-luser32', + '-lws2_32' + ] endif add_global_arguments (ccflags, language: 'cpp') add_global_link_arguments (ldflags, language: 'cpp') sources = files ( - 'src/android.cpp', - 'src/botlib.cpp', - 'src/chatlib.cpp', - 'src/combat.cpp', - 'src/config.cpp', - 'src/control.cpp', - 'src/engine.cpp', - 'src/graph.cpp', - 'src/linkage.cpp', - 'src/manager.cpp', - 'src/module.cpp', - 'src/message.cpp', - 'src/navigate.cpp', - 'src/support.cpp' + 'src/android.cpp', + 'src/botlib.cpp', + 'src/chatlib.cpp', + 'src/combat.cpp', + 'src/config.cpp', + 'src/control.cpp', + 'src/engine.cpp', + 'src/graph.cpp', + 'src/linkage.cpp', + 'src/manager.cpp', + 'src/module.cpp', + 'src/message.cpp', + 'src/navigate.cpp', + 'src/support.cpp' ) includes = include_directories ([ - '.', 'inc', 'ext', + '.', 'inc', 'ext', ], is_system: true) if isWindows and not isCLang - sources += import('windows').compile_resources ( - 'vc/yapb.rc', - include_directories: includes, - args: ['-DVERSION_GENERATED'] - ) + sources += import('windows').compile_resources ( + 'vc/yapb.rc', + include_directories: includes, + args: ['-DVERSION_GENERATED'] + ) endif shared_library ( - meson.project_name (), - sources, - include_directories: includes, - - gnu_symbol_visibility: 'hidden', - name_prefix: '') + meson.project_name (), + sources, + include_directories: includes, + + gnu_symbol_visibility: 'hidden', + name_prefix: '') run_target ('package', command : ['python', meson.source_root() + '/package.py', '@0@.@1@'.format (buildVersion, buildCount)]) \ No newline at end of file diff --git a/package.py b/package.py index e868442..cc9d397 100644 --- a/package.py +++ b/package.py @@ -1,4 +1,5 @@ -# +# -*- coding: utf-8 -*-# + # YaPB - Counter-Strike Bot based on PODBot by Markus Klinge. # Copyright © 2004-2020 YaPB Project . # @@ -14,208 +15,208 @@ import datetime, calendar from github import Github class BotRelease (object): - def __init__(self): - print ("Initializing Packaging") - - self.workDir = os.path.join (pathlib.Path ().absolute (), 'cfg') - self.botDir = os.path.join (self.workDir, 'addons', 'yapb') - self.distDir = os.path.join (pathlib.Path ().absolute (), 'dist'); - - if len (sys.argv) < 2: - raise Exception('Missing required parameters.') + def __init__(self): + print ("Initializing Packaging") + + self.workDir = os.path.join (pathlib.Path ().absolute (), 'cfg') + self.botDir = os.path.join (self.workDir, 'addons', 'yapb') + self.distDir = os.path.join (pathlib.Path ().absolute (), 'dist'); + + if len (sys.argv) < 2: + raise Exception('Missing required parameters.') - self.version = sys.argv[1] - - os.makedirs (self.distDir, exist_ok=True) - - self.outFileWin32 = os.path.join (self.distDir, "yapb-{}-windows.zip".format (self.version)) - self.outFileLinux = os.path.join (self.distDir, "yapb-{}-linux.tar.xz".format (self.version)) - self.outFileMacOS = os.path.join (self.distDir, "yapb-{}-macos.zip".format (self.version)) - - self.outFileWin32Setup = self.outFileWin32.replace ("zip", "exe") - self.win32SetupUrl = "https://github.com/yapb/setup/releases/latest/download/botsetup.exe" - - def makeDirs (self): - dirs = [ - 'bin', - os.path.join ('data', 'pwf'), - os.path.join ('data', 'train'), - os.path.join ('data', 'graph'), - os.path.join ('data', 'logs') - ] - - for dir in dirs: - os.makedirs (os.path.join (self.botDir, dir), exist_ok=True) - - def download (self, fromWhere, toFile): - http = urllib3.PoolManager (10, headers = {"user-agent": "YaPB"}) - data = http.urlopen ('GET', fromWhere) - - with open (toFile, "wb") as file: - file.write (data.data) - - def getGraph (self, name): - file = os.path.join (self.botDir, 'data', 'graph', "{}.graph".format (name)) - url = "http://graph.yapb.ru/graph/{}.graph".format (name); - - if os.path.exists (file): - return - - self.download (url, file) + self.version = sys.argv[1] + + os.makedirs (self.distDir, exist_ok=True) + + self.outFileWin32 = os.path.join (self.distDir, "yapb-{}-windows.zip".format (self.version)) + self.outFileLinux = os.path.join (self.distDir, "yapb-{}-linux.tar.xz".format (self.version)) + self.outFileMacOS = os.path.join (self.distDir, "yapb-{}-macos.zip".format (self.version)) + + self.outFileWin32Setup = self.outFileWin32.replace ("zip", "exe") + self.win32SetupUrl = "https://github.com/yapb/setup/releases/latest/download/botsetup.exe" + + def makeDirs (self): + dirs = [ + 'bin', + os.path.join ('data', 'pwf'), + os.path.join ('data', 'train'), + os.path.join ('data', 'graph'), + os.path.join ('data', 'logs') + ] + + for dir in dirs: + os.makedirs (os.path.join (self.botDir, dir), exist_ok=True) + + def download (self, fromWhere, toFile): + http = urllib3.PoolManager (10, headers = {"user-agent": "YaPB"}) + data = http.urlopen ('GET', fromWhere) + + with open (toFile, "wb") as file: + file.write (data.data) + + def getGraph (self, name): + file = os.path.join (self.botDir, 'data', 'graph', "{}.graph".format (name)) + url = "http://graph.yapb.ru/graph/{}.graph".format (name); + + if os.path.exists (file): + return + + self.download (url, file) - def getGraphs (self): - print ("Downloading graphs: ") - - files = ['as_oilrig', 'cs_747', 'cs_estate', 'cs_assault', 'cs_office', - 'cs_italy', 'cs_havana', 'cs_siege', 'cs_backalley', 'cs_militia', - 'cs_downed_cz', 'cs_havana_cz', 'cs_italy_cz', 'cs_militia_cz', - 'cs_office_cz', 'de_airstrip_cz', 'de_aztec_cz', 'de_cbble_cz', - 'de_chateau_cz', 'de_corruption_cz', 'de_dust_cz', 'de_dust2_cz', - 'de_fastline_cz', 'de_inferno_cz', 'de_piranesi_cz', 'de_prodigy_cz', - 'de_sienna_cz', 'de_stadium_cz', 'de_tides_cz', 'de_torn_cz', - 'de_truth_cz', 'de_vostok_cz', 'de_inferno', 'de_aztec', 'de_dust', - 'de_dust2', 'de_torn', 'de_storm', 'de_airstrip', 'de_piranesi', - 'de_prodigy', 'de_chateau', 'de_cbble', 'de_nuke', 'de_survivor', - 'de_vertigo', 'de_train'] - - for file in files: - print (" " + file) - - self.getGraph (file) + def getGraphs (self): + print ("Downloading graphs: ") + + files = ['as_oilrig', 'cs_747', 'cs_estate', 'cs_assault', 'cs_office', + 'cs_italy', 'cs_havana', 'cs_siege', 'cs_backalley', 'cs_militia', + 'cs_downed_cz', 'cs_havana_cz', 'cs_italy_cz', 'cs_militia_cz', + 'cs_office_cz', 'de_airstrip_cz', 'de_aztec_cz', 'de_cbble_cz', + 'de_chateau_cz', 'de_corruption_cz', 'de_dust_cz', 'de_dust2_cz', + 'de_fastline_cz', 'de_inferno_cz', 'de_piranesi_cz', 'de_prodigy_cz', + 'de_sienna_cz', 'de_stadium_cz', 'de_tides_cz', 'de_torn_cz', + 'de_truth_cz', 'de_vostok_cz', 'de_inferno', 'de_aztec', 'de_dust', + 'de_dust2', 'de_torn', 'de_storm', 'de_airstrip', 'de_piranesi', + 'de_prodigy', 'de_chateau', 'de_cbble', 'de_nuke', 'de_survivor', + 'de_vertigo', 'de_train'] + + for file in files: + print (" " + file) + + self.getGraph (file) - def unlinkBinaries (self): - libs = ['yapb.so', 'yapb.dll', 'yapb.dylib'] - - for lib in libs: - path = os.path.join (self.botDir, 'bin', lib) - - if os.path.exists (path): - os.remove (path) - - def copyBinary (self, path): - shutil.copy (path, os.path.join (self.botDir, 'bin')) - - def zipDir (self, path, handle): - length = len (path) + 1 - emptyDirs = [] - - for root, dirs, files in os.walk (path): - emptyDirs.extend ([dir for dir in dirs if os.listdir (os.path.join (root, dir)) == []]) - - for file in files: - filePath = os.path.join (root, file) - handle.write (filePath, filePath[length:]) - - for dir in emptyDirs: - dirPath = os.path.join (root, dir) - - zif = zipfile.ZipInfo (dirPath[length:] + "/") - handle.writestr (zif, "") - - emptyDirs = [] + def unlinkBinaries (self): + libs = ['yapb.so', 'yapb.dll', 'yapb.dylib'] + + for lib in libs: + path = os.path.join (self.botDir, 'bin', lib) + + if os.path.exists (path): + os.remove (path) + + def copyBinary (self, path): + shutil.copy (path, os.path.join (self.botDir, 'bin')) + + def zipDir (self, path, handle): + length = len (path) + 1 + emptyDirs = [] + + for root, dirs, files in os.walk (path): + emptyDirs.extend ([dir for dir in dirs if os.listdir (os.path.join (root, dir)) == []]) + + for file in files: + filePath = os.path.join (root, file) + handle.write (filePath, filePath[length:]) + + for dir in emptyDirs: + dirPath = os.path.join (root, dir) + + zif = zipfile.ZipInfo (dirPath[length:] + "/") + handle.writestr (zif, "") + + emptyDirs = [] - def generateZip (self, dir): - zipFile = zipfile.ZipFile (dir, 'w', zipfile.ZIP_DEFLATED) - zipFile.comment = bytes (self.version, encoding = "ascii") - - self.zipDir (self.workDir, zipFile) - zipFile.close () - - def convertZipToTXZ (self, zip, txz): - timeshift = int ((datetime.datetime.now () - datetime.datetime.utcnow ()).total_seconds ()) - - with zipfile.ZipFile (zip) as zipf: - with tarfile.open (txz, 'w:xz') as tarf: - for zif in zipf.infolist (): - tif = tarfile.TarInfo (name = zif.filename) - tif.size = zif.file_size - tif.mtime = calendar.timegm (zif.date_time) - timeshift - - tarf.addfile (tarinfo = tif, fileobj = zipf.open (zif.filename)) - - os.remove (zip) - - def generateWin32 (self): - print ("Generating Win32 ZIP") - - binary = os.path.join ('build_x86_win32', 'yapb.dll') - - if not os.path.exists (binary): - return - - self.unlinkBinaries () - - self.copyBinary (binary) - self.generateZip (self.outFileWin32) - - self.download (self.win32SetupUrl, "botsetup.exe") - - print ("Generating Win32 EXE") + def generateZip (self, dir): + zipFile = zipfile.ZipFile (dir, 'w', zipfile.ZIP_DEFLATED) + zipFile.comment = bytes (self.version, encoding = "ascii") + + self.zipDir (self.workDir, zipFile) + zipFile.close () + + def convertZipToTXZ (self, zip, txz): + timeshift = int ((datetime.datetime.now () - datetime.datetime.utcnow ()).total_seconds ()) + + with zipfile.ZipFile (zip) as zipf: + with tarfile.open (txz, 'w:xz') as tarf: + for zif in zipf.infolist (): + tif = tarfile.TarInfo (name = zif.filename) + tif.size = zif.file_size + tif.mtime = calendar.timegm (zif.date_time) - timeshift + + tarf.addfile (tarinfo = tif, fileobj = zipf.open (zif.filename)) + + os.remove (zip) + + def generateWin32 (self): + print ("Generating Win32 ZIP") + + binary = os.path.join ('build_x86_win32', 'yapb.dll') + + if not os.path.exists (binary): + return + + self.unlinkBinaries () + + self.copyBinary (binary) + self.generateZip (self.outFileWin32) + + self.download (self.win32SetupUrl, "botsetup.exe") + + print ("Generating Win32 EXE") - with open ("botsetup.exe", "rb") as sfx, open (self.outFileWin32, "rb") as zip, open (self.outFileWin32Setup, "wb") as exe: - exe.write (sfx.read ()) - exe.write (zip.read ()) - - def generateLinux (self): - print ("Generating Linux TXZ") - - binary = os.path.join ('build_x86_linux', 'yapb.so'); - - if not os.path.exists (binary): - return - - self.unlinkBinaries () - self.copyBinary (binary) - - tmpFile = "tmp.zip" - - self.generateZip (tmpFile) - self.convertZipToTXZ (tmpFile, self.outFileLinux) - - def generateMac (self): - print ("Generating macOS ZIP") - - binary = os.path.join ('build_x86_macos', 'yapb.dylib') - - if not os.path.exists (binary): - return - - self.unlinkBinaries () - self.copyBinary (binary) - - self.generateZip (self.outFileMacOS) - - def generate (self): - self.generateWin32 () - self.generateLinux () - self.generateMac () - - def createRelease (self, repository, version): - print ("Creating Github Tag") - - releases = [self.outFileLinux, self.outFileWin32, self.outFileMacOS, self.outFileWin32Setup] - - for release in releases: - if not os.path.exists (release): - return - - releaseName = "YaPB " + version - releaseMessage = repository.get_commits()[0].commit.message - releaseSha = repository.get_commits()[0].sha; + with open ("botsetup.exe", "rb") as sfx, open (self.outFileWin32, "rb") as zip, open (self.outFileWin32Setup, "wb") as exe: + exe.write (sfx.read ()) + exe.write (zip.read ()) + + def generateLinux (self): + print ("Generating Linux TXZ") + + binary = os.path.join ('build_x86_linux', 'yapb.so'); + + if not os.path.exists (binary): + return + + self.unlinkBinaries () + self.copyBinary (binary) + + tmpFile = "tmp.zip" + + self.generateZip (tmpFile) + self.convertZipToTXZ (tmpFile, self.outFileLinux) + + def generateMac (self): + print ("Generating macOS ZIP") + + binary = os.path.join ('build_x86_macos', 'yapb.dylib') + + if not os.path.exists (binary): + return + + self.unlinkBinaries () + self.copyBinary (binary) + + self.generateZip (self.outFileMacOS) + + def generate (self): + self.generateWin32 () + self.generateLinux () + self.generateMac () + + def createRelease (self, repository, version): + print ("Creating Github Tag") + + releases = [self.outFileLinux, self.outFileWin32, self.outFileMacOS, self.outFileWin32Setup] + + for release in releases: + if not os.path.exists (release): + return + + releaseName = "YaPB " + version + releaseMessage = repository.get_commits()[0].commit.message + releaseSha = repository.get_commits()[0].sha; - ghr = repository.create_git_tag_and_release (tag = version, tag_message = version, release_name = releaseName, release_message = releaseMessage, type='commit', object = releaseSha, draft = False) - - print ("Uploading packages to Github") - - for release in releases: - ghr.upload_asset( path = release, label = os.path.basename (release)) - - def uploadGithub (self): - gh = Github (os.environ["GITHUB_TOKEN"]) - repo = gh.get_repo ("yapb/yapb") - - self.createRelease (repo, self.version) - + ghr = repository.create_git_tag_and_release (tag = version, tag_message = version, release_name = releaseName, release_message = releaseMessage, type='commit', object = releaseSha, draft = False) + + print ("Uploading packages to Github") + + for release in releases: + ghr.upload_asset( path = release, label = os.path.basename (release)) + + def uploadGithub (self): + gh = Github (os.environ["GITHUB_TOKEN"]) + repo = gh.get_repo ("yapb/yapb") + + self.createRelease (repo, self.version) + release = BotRelease () release.makeDirs () diff --git a/src/botlib.cpp b/src/botlib.cpp index 515bfe9..68ca655 100644 --- a/src/botlib.cpp +++ b/src/botlib.cpp @@ -572,7 +572,7 @@ void Bot::updatePickups () { allowPickup = true; pickupType = Pickup::DefusalKit; } - else if (strncmp ("grenade", classname, 7) == 0 && strcmp (model, "c4.mdl") == 0) { + else if (strncmp ("grenade", classname, 7) == 0 && conf.getBombModelName () == model) { allowPickup = true; pickupType = Pickup::PlantedC4; } @@ -2888,7 +2888,7 @@ void Bot::checkDarkness () { } void Bot::checkParachute () { - static auto parachute = engfuncs.pfnCVarGetPointer ("sv_parachute"); + static auto parachute = engfuncs.pfnCVarGetPointer (conf.fetchCustom ("AMXParachuteCvar").chars ()); // if no cvar or it's not enabled do not bother if (parachute && parachute->value > 0.0f) { @@ -5460,7 +5460,7 @@ edict_t *Bot::correctGrenadeVelocity (const char *model) { edict_t *result = nullptr; game.searchEntities ("classname", "grenade", [&] (edict_t *ent) { - if (ent->v.owner == this->ent () && strcmp (ent->v.model.chars (9), model) == 0) { + if (ent->v.owner == this->ent () && util.isModel (ent, model)) { result = ent; // set the correct velocity for the grenade diff --git a/src/chatlib.cpp b/src/chatlib.cpp index dd5e2f8..3768fde 100644 --- a/src/chatlib.cpp +++ b/src/chatlib.cpp @@ -144,7 +144,7 @@ void Bot::prepareChatMessage (StringRef message) { } // get the humanized name out of client - auto humanizedName = [] (int index) -> StringRef { + auto humanizedName = [] (int index) -> String { auto ent = game.playerOfIndex (index); if (!util.isPlayer (ent)) { @@ -157,7 +157,7 @@ void Bot::prepareChatMessage (StringRef message) { }; // find highfrag player - auto getHighfragPlayer = [&] () -> StringRef { + auto getHighfragPlayer = [&] () -> String { int highestFrags = -1; int index = 0; @@ -178,7 +178,7 @@ void Bot::prepareChatMessage (StringRef message) { }; // get roundtime - auto getRoundTime = [] () -> StringRef { + auto getRoundTime = [] () -> String { auto roundTimeSecs = static_cast (bots.getRoundEndTime () - game.time ()); String roundTime; @@ -188,12 +188,12 @@ void Bot::prepareChatMessage (StringRef message) { }; // get bot's victim - auto getMyVictim = [&] () -> StringRef {; + auto getMyVictim = [&] () -> String {; return humanizedName (game.indexOfPlayer (m_lastVictim)); }; // get the game name alias - auto getGameName = [] () -> StringRef { + auto getGameName = [] () -> String { String gameName; if (game.is (GameFlags::ConditionZero)) { @@ -216,7 +216,7 @@ void Bot::prepareChatMessage (StringRef message) { }; // get enemy or teammate alive - auto getPlayerAlive = [&] (bool needsEnemy) -> StringRef { + auto getPlayerAlive = [&] (bool needsEnemy) -> String { for (const auto &client : util.getClients ()) { if (!(client.flags & ClientFlags::Used) || !(client.flags & ClientFlags::Alive) || client.ent == ent ()) { continue; diff --git a/src/config.cpp b/src/config.cpp index 373f5d6..6a55b2f 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -1,6 +1,6 @@ // // YaPB - Counter-Strike Bot based on PODBot by Markus Klinge. -// Copyright 2004-2020 YaPB Project . +// Copyright © 2004-2020 YaPB Project . // // SPDX-License-Identifier: MIT // @@ -28,6 +28,7 @@ void BotConfig::loadConfigs () { loadLogosConfig (); loadAvatarsConfig (); loadDifficultyConfig (); + loadCustomConfig (); } void BotConfig::loadMainConfig (bool isFirstLoad) { @@ -614,6 +615,38 @@ void BotConfig::loadMapSpecificConfig () { } } +void BotConfig::loadCustomConfig () { + String line; + MemFile file; + + m_custom["C4ModelName"] = "c4.mdl"; + m_custom["AMXParachuteCvar"] = "sv_parachute"; + + // custom inititalization + if (util.openConfig ("custom.cfg", "Custom config file not found. Loading defaults.", &file)) { + m_custom.clear (); + + while (file.getLine (line)) { + line.trim (); + + if (isCommentLine (line)) { + continue; + } + auto values = line.split ("="); + + if (values.length () != 2) { + logger.error ("Bad configuration for custom.cfg"); + return; + } + auto kv = Twin (values[0].trim (), values[1].trim ()); + + if (!kv.first.empty () && !kv.second.empty ()) { + m_custom[kv.first] = kv.second; + } + } + } +} + void BotConfig::loadLogosConfig () { setupMemoryFiles (); @@ -750,6 +783,14 @@ const char *BotConfig::translate (StringRef input) { return input.chars (); // nothing found } +void BotConfig::showCustomValues () { + game.print ("Current values for custom config items:"); + + m_custom.foreach ([&](const String &key, const String &val) { + game.print (" %s = %s", key, val); + }); +} + uint32 BotConfig::hashLangString (StringRef str) { auto test = [] (const char ch) { return (ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z'); diff --git a/src/control.cpp b/src/control.cpp index 2148b4c..f8671f7 100644 --- a/src/control.cpp +++ b/src/control.cpp @@ -262,6 +262,14 @@ int BotControl::cmdCvars () { return BotCommandResult::Handled; } +int BotControl::cmdShowCustom () { + enum args { alias = 1 }; + + conf.showCustomValues (); + + return BotCommandResult::Handled; +} + int BotControl::cmdNode () { enum args { root, alias, cmd, cmd2 }; @@ -1895,6 +1903,7 @@ BotControl::BotControl () { m_cmds.emplace ("list/listbots", "list [noarguments]", "Lists the bots currently playing on server.", &BotControl::cmdList); m_cmds.emplace ("graph/g/wp/wpt/waypoint", "graph [help]", "Handles graph operations.", &BotControl::cmdNode); m_cmds.emplace ("cvars", "cvars [save|cvar]", "Display all the cvars with their descriptions.", &BotControl::cmdCvars); + m_cmds.emplace ("show_custom", "show_custom [noarguments]", "Show's the curent values from custom.cfg.", &BotControl::cmdShowCustom); // declare the menus createMenus (); diff --git a/src/engine.cpp b/src/engine.cpp index 2132130..2ca103e 100644 --- a/src/engine.cpp +++ b/src/engine.cpp @@ -799,6 +799,9 @@ bool Game::postload () { // set out user agent for http stuff http.setUserAgent (strings.format ("%s/%s", product.name, product.version)); + // startup the sockets + http.startup (); + // set the app name plat.setAppName (product.name.chars ()); diff --git a/src/graph.cpp b/src/graph.cpp index d78a2bc..bc26c09 100644 --- a/src/graph.cpp +++ b/src/graph.cpp @@ -2806,9 +2806,10 @@ void BotGraph::setBombOrigin (bool reset, const Vector &pos) { return; } bool wasFound = false; + auto bombModel = conf.getBombModelName (); game.searchEntities ("classname", "grenade", [&] (edict_t *ent) { - if (strcmp (ent->v.model.chars (9), "c4.mdl") == 0) { + if (util.isModel (ent, bombModel)) { m_bombOrigin = game.getEntityWorldOrigin (ent); wasFound = true; diff --git a/src/manager.cpp b/src/manager.cpp index 1d83946..8044459 100644 --- a/src/manager.cpp +++ b/src/manager.cpp @@ -1628,10 +1628,13 @@ void BotManager::updateActiveGrenade () { } m_activeGrenades.clear (); // clear previously stored grenades + // need to ignore bomb model in active grenades... + auto bombModel = conf.getBombModelName (); + // search the map for any type of grenade game.searchEntities ("classname", "grenade", [&] (edict_t *e) { // do not count c4 as a grenade - if (strcmp (e->v.model.chars (9), "c4.mdl") != 0) { + if (!util.isModel (e, bombModel)) { m_activeGrenades.push (e); } return EntitySearchResult::Continue; // continue iteration diff --git a/src/navigate.cpp b/src/navigate.cpp index 6044616..c2ac373 100644 --- a/src/navigate.cpp +++ b/src/navigate.cpp @@ -17,7 +17,7 @@ int Bot::findBestGoal () { int result = kInvalidNodeIndex; game.searchEntities ("classname", "weaponbox", [&] (edict_t *ent) { - if (strcmp (ent->v.model.chars (9), "backpack.mdl") == 0) { + if (util.isModel (ent, "backpack.mdl")) { result = graph.getNearest (game.getEntityWorldOrigin (ent)); if (graph.exists (result)) { @@ -2965,11 +2965,13 @@ int Bot::getNearestToPlantedBomb () { if (!game.mapIs (MapFlags::Demolition)) { return kInvalidNodeIndex; // don't search for bomb if the player is CT, or it's not defusing bomb } - int result = kInvalidNodeIndex; + + auto bombModel = conf.getBombModelName (); + auto result = kInvalidNodeIndex; // search the bomb on the map - game.searchEntities ("classname", "grenade", [&result] (edict_t *ent) { - if (strcmp (ent->v.model.chars (9), "c4.mdl") == 0) { + game.searchEntities ("classname", "grenade", [&result, &bombModel] (edict_t *ent) { + if (util.isModel (ent, bombModel)) { result = graph.getNearest (game.getEntityWorldOrigin (ent)); if (graph.exists (result)) { diff --git a/src/support.cpp b/src/support.cpp index 855d04a..8d3ca18 100644 --- a/src/support.cpp +++ b/src/support.cpp @@ -687,6 +687,10 @@ bool BotSupport::isObjectInsidePlane (FrustumPlane &plane, const Vector ¢er, return isPointInsidePlane (top) || isPointInsidePlane (bottom); } +bool BotSupport::isModel (const edict_t *ent, StringRef model) { + return model == ent->v.model.chars (9); +} + int32 BotSupport::sendTo (int socket, const void *message, size_t length, int flags, const sockaddr *dest, int destLength) { const auto send = [&] (const Twin &msg) -> int32 { return Socket::sendto (socket, msg.first, msg.second, flags, dest, destLength);