summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--src/engineapi.c1
-rw-r--r--src/gameinfo.c276
-rw-r--r--src/gameinfo.h13
-rw-r--r--src/l4dmm.c8
-rw-r--r--src/os-unix.h9
-rw-r--r--src/os-win32.h7
-rw-r--r--src/sst.c115
7 files changed, 109 insertions, 320 deletions
diff --git a/src/engineapi.c b/src/engineapi.c
index 831836b..f4a54d6 100644
--- a/src/engineapi.c
+++ b/src/engineapi.c
@@ -71,7 +71,6 @@ bool engineapi_init(int pluginver) {
void *pim = factory_server("PlayerInfoManager002", 0);
if (pim) globalvars = GetGlobalVars(pim);
- inputsystem = factory_inputsystem("InputSystemVersion001", 0);
vgui = factory_engine("VEngineVGui001", 0);
void *srvdll;
diff --git a/src/gameinfo.c b/src/gameinfo.c
index 71922a6..9a6686e 100644
--- a/src/gameinfo.c
+++ b/src/gameinfo.c
@@ -14,226 +14,25 @@
* PERFORMANCE OF THIS SOFTWARE.
*/
-#ifdef _WIN32
-#include <shlwapi.h>
-#endif
-
-#include "con_.h"
#include "engineapi.h"
#include "errmsg.h"
#include "gamedata.h"
#include "gametype.h"
-#include "intdefs.h"
-#include "kv.h"
#include "os.h"
#include "vcall.h"
#ifdef _WIN32
-#define fS "S" // os string (wide string) to regular string
-#define Fs L"S" // regular string to os string (wide string)
-#define PATHSEP L"\\" // for joining. could just be / but \ is more consistent
-#else
-// everything is just a regular string already
-#define fS "s"
-#define Fs "s"
-#define PATHSEP "/"
-#endif
-
-// ~~TODO(opt): get rid of the rest of the snprintf and strcpy, some day~~
-// TODO(opt): remove almost all this parsing nonsense, it's not needed any more!
-// We can simply GetWindowText (and do a little more work on Linux...) and do
-// away with absolute paths to DLLs which won't be required with deferred init.
-
-static os_char bindir[PATH_MAX] = {0};
-#ifdef _WIN32
static os_char gamedir[PATH_MAX] = {0};
#endif
-static os_char clientlib[PATH_MAX] = {0};
-static os_char serverlib[PATH_MAX] = {0};
static char title[64] = {0};
-const os_char *gameinfo_bindir = bindir;
const os_char *gameinfo_gamedir
#ifdef _WIN32
= gamedir // on linux, the pointer gets directly set in gameinfo_init()
#endif
;
-const os_char *gameinfo_clientlib = clientlib;
-const os_char *gameinfo_serverlib = serverlib;
const char *gameinfo_title = title;
-// case insensitive substring match, expects s2 to be lowercase already!
-// note: in theory this shouldn't need to be case sensitive, but I've seen mods
-// use both lowercase and TitleCase so this is just to be as lenient as possible
-static bool matchtok(const char *s1, const char *s2, usize sz) {
- for (; sz; --sz, ++s1, ++s2) if (tolower(*s1) != *s2) return false;
- return true;
-}
-
-static void trygamelib(const os_char *path, os_char *outpath) {
- // _technically_ this is toctou, but I don't think that matters here
- if (os_access(path, F_OK) != -1) {
- os_strcpy(outpath, path);
- }
- else if (errno != ENOENT) {
- errmsg_warnstd("failed to access %" fS, path);
- }
-}
-
-// note: p and len are a non-null-terminated string
-static inline void dolibsearch(const char *p, uint len, bool isgamebin,
- const os_char *cwd) {
- // sanity check: don't do a bunch of work for no reason
- if (len >= PATH_MAX - 1 - (sizeof("client" OS_DLSUFFIX) - 1)) goto toobig;
- os_char bindir[PATH_MAX];
- os_char *outp = bindir;
- // this should really be an snprintf, meh whatever
- os_strcpy(bindir, cwd);
- outp = bindir + os_strlen(bindir);
- // quick note about windows encoding conversion: this MIGHT clobber the
- // encoding of non-ascii mod names, but it's unclear if/how source handles
- // that anyway, so we just have to assume there *are no* non-ascii mod
- // names, since they'd also be clobbered, probably. NOTE that this
- // assumption does NOT apply to the absolute base path; see further down.
- const os_char *fmt = isgamebin ?
- OS_LIT("/%.*") Fs OS_LIT("/") :
- OS_LIT("/%.*") Fs OS_LIT("/bin/");
- int spaceleft = PATH_MAX;
- if (len >= 25 && matchtok(p, "|all_source_engine_paths|", 25)) {
- // this special path doesn't seem any different to normal,
- // why is this a thing?
- p += 25; len -= 25;
- }
- else if (len >= 15 && matchtok(p, "|gameinfo_path|", 15)) {
- // search in the actual mod/game directory
- p += 15; len -= 15;
- int ret = os_snprintf(bindir, PATH_MAX, OS_LIT("%s"), gamedir);
- outp = bindir + ret;
- spaceleft -= ret;
- }
- else {
-#ifdef _WIN32
- // sigh
- char api_needs_null_term[PATH_MAX];
- memcpy(api_needs_null_term, p, len * sizeof(*p));
- api_needs_null_term[len] = L'\0';
- if (!PathIsRelativeA(api_needs_null_term))
-#else
- if (*p == '/') // so much easier :')
-#endif
- {
- // the mod path is absolute, so we're not sticking anything else in
- // front of it, so skip the leading slash in fmt and point the pointer
- // at the start of the buffer
- ++fmt;
- outp = bindir;
- }}
-
- // leave room for server/client.dll/so (note: server and client happen to
- // conveniently have the same number of letters)
- int fmtspace = spaceleft - (sizeof("client" OS_DLSUFFIX) - 1);
- int ret = os_snprintf(outp, fmtspace, fmt, len, p);
- if (ret >= fmtspace) {
-toobig: errmsg_warnx("skipping an overly long search path");
- return;
- }
- outp += ret;
- if (!*gameinfo_clientlib) {
- os_strcpy(outp, OS_LIT("client" OS_DLSUFFIX));
- trygamelib(bindir, clientlib);
- }
- if (!*gameinfo_serverlib) {
- os_strcpy(outp, OS_LIT("server" OS_DLSUFFIX));
- trygamelib(bindir, serverlib);
- }
-}
-
-// state for the callback below to keep it somewhat reentrant (except where
-// it's not because I got lazy and wrote some spaghetti)
-struct kv_parsestate {
- const os_char *cwd;
- // after parsing a key we *don't* care about, how many nested subkeys have
- // we come across?
- short dontcarelvl;
- // after parsing a key we *do* care about, which key in the matchkeys[]
- // array below are we looking for next?
- schar nestlvl;
- // what kind of key did we just match?
- schar matchtype;
-};
-
-// this is a sprawling mess. Too Bad!
-static void kv_cb(enum kv_token type, const char *p, uint len, void *_ctxt) {
- struct kv_parsestate *ctxt = _ctxt;
-
- static const struct {
- const char *s;
- uint len;
- } matchkeys[] = {
- {"gameinfo", 8},
- {"filesystem", 10},
- {"searchpaths", 11}
- };
-
- // values for ctxt->matchtype
- enum { mt_none, mt_title, mt_nest, mt_game, mt_gamebin };
-
- #define MATCH(s) (len == sizeof(s) - 1 && matchtok(p, s, sizeof(s) - 1))
- switch (type) {
- case KV_IDENT: case KV_IDENT_QUOTED:
- if (ctxt->nestlvl == 1 && MATCH("game")) {
- ctxt->matchtype = mt_title;
- }
- else if (ctxt->nestlvl == 3) {
- // for some reason there's a million different ways of
- // specifying the same type of path
- if (MATCH("mod+game") || MATCH("game+mod") || MATCH("game") ||
- MATCH("mod")) {
- ctxt->matchtype = mt_game;
- }
- else if (MATCH("gamebin")) {
- ctxt->matchtype = mt_gamebin;
- }
- }
- else if (len == matchkeys[ctxt->nestlvl].len &&
- matchtok(p, matchkeys[ctxt->nestlvl].s, len)) {
- ctxt->matchtype = mt_nest;
- }
- break;
- case KV_NEST_START:
- if (ctxt->matchtype == mt_nest) ++ctxt->nestlvl;
- else ++ctxt->dontcarelvl;
- ctxt->matchtype = mt_none;
- break;
- case KV_VAL: case KV_VAL_QUOTED:
- if (ctxt->dontcarelvl) break;
- // dumb hack: ignore Survivors title (they left it set to "Left 4
- // Dead 2" but it clearly isn't Left 4 Dead 2)
- if (ctxt->matchtype == mt_title && !GAMETYPE_MATCHES(L4DS)) {
- // title really shouldn't get this long, but truncate just to
- // avoid any trouble...
- // also note: leaving 1 byte of space for null termination (the
- // buffer is already zeroed initially)
- if (len > sizeof(title) - 1) len = sizeof(title) - 1;
- memcpy(title, p, len);
- }
- else if (ctxt->matchtype == mt_game ||
- ctxt->matchtype == mt_gamebin) {
- // if we already have everything, we can just stop!
- if (*gameinfo_clientlib && *gameinfo_serverlib) break;
- dolibsearch(p, len, ctxt->matchtype == mt_gamebin, ctxt->cwd);
- }
- ctxt->matchtype = mt_none;
- break;
- case KV_NEST_END:
- if (ctxt->dontcarelvl) --ctxt->dontcarelvl; else --ctxt->nestlvl;
- break;
- case KV_COND_PREFIX: case KV_COND_SUFFIX:
- errmsg_warnx("just ignoring conditional \"%.*s\"", len, p);
- }
- #undef MATCH
-}
-
DECL_VFUNC_DYN(const char *, GetGameDirectory)
bool gameinfo_init(void) {
@@ -242,76 +41,39 @@ bool gameinfo_init(void) {
return false;
}
- // engine always calls chdir() with its own base path on startup, so engine
- // base dir is just cwd
- os_char cwd[PATH_MAX];
- if (!os_getcwd(cwd, sizeof(cwd) / sizeof(*cwd))) {
- errmsg_errorstd("couldn't get working directory");
- return false;
- }
- int len = os_strlen(cwd);
- if (len + sizeof("/bin") > sizeof(bindir) / sizeof(*bindir)) {
- errmsg_errorx("working directory path is too long!");
- return false;
- }
- memcpy(bindir, cwd, len * sizeof(*cwd));
- memcpy(bindir + len, PATHSEP OS_LIT("bin"), 5 * sizeof(os_char));
-
#ifdef _WIN32
// Although the engine itself uses Unicode-incompatible stuff everywhere so
// supporting arbitrary paths is basically a no-go, turns out we still have
- // to respect the system code page setting, otherwise some users using e.g.
- // Cyrillic folder names and successfully loading their speedgames won't be
- // able to load SST. Thanks Windows!
+ // to respect the system legacy code page setting, otherwise some users
+ // using e.g. Cyrillic folder names and successfully loading their
+ // speedgames won't be able to load SST. Thanks Windows!
const char *lcpgamedir = GetGameDirectory(engclient);
- int gamedirlen = MultiByteToWideChar(CP_ACP, 0, lcpgamedir,
- strlen(lcpgamedir), gamedir, sizeof(gamedir) / sizeof(*gamedir));
- if (!gamedirlen) {
+ if (!MultiByteToWideChar(CP_ACP, 0, lcpgamedir, strlen(lcpgamedir), gamedir,
+ sizeof(gamedir) / sizeof(*gamedir))) {
errmsg_errorsys("couldn't convert game directory path character set");
return false;
}
#else
// no need to munge charset, use the string pointer directly
gameinfo_gamedir = GetGameDirectory(engclient);
- int gamedirlen = strlen(gameinfo_gamedir);
#endif
- os_char gameinfopath[PATH_MAX];
- if (gamedirlen + sizeof("/gameinfo.txt") > sizeof(gameinfopath) /
- sizeof(*gameinfopath)) {
- errmsg_errorx("game directory path is too long!");
- return false;
- }
- memcpy(gameinfopath, gameinfo_gamedir, gamedirlen *
- sizeof(*gameinfo_gamedir));
- memcpy(gameinfopath + gamedirlen, PATHSEP OS_LIT("gameinfo.txt"),
- 14 * sizeof(os_char));
- int fd = os_open(gameinfopath, O_RDONLY);
- if (fd == -1) {
- errmsg_errorstd("couldn't open gameinfo.txt");
- return false;
+
+ // dumb hack: ignore Survivors title (they left it set to "Left 4 Dead 2"
+ // but that game clearly isn't Left 4 Dead 2)
+ if (GAMETYPE_MATCHES(L4DS)) {
+ gameinfo_title = "Left 4 Dead: Survivors";
}
- char buf[1024];
- struct kv_parser kvp = {0};
- struct kv_parsestate ctxt = {.cwd = cwd};
- int nread;
- while (nread = read(fd, buf, sizeof(buf))) {
- if (nread == -1) {
- errmsg_errorstd("couldn't read gameinfo.txt");
- goto e;
- }
- if (!kv_parser_feed(&kvp, buf, nread, &kv_cb, &ctxt)) goto ep;
+ else {
+#ifdef _WIN32
+ // XXX: this same FindWindow call happens in ac.c - maybe factor out?
+ void *gamewin = FindWindowW(L"Valve001", 0);
+ // assuming: all games/mods use narrow chars only; this won't fail.
+ GetWindowTextA(gamewin, title, sizeof(title));
+#else
+#erorr TODO(linux): grab window handle and title from SDL (a bit involved...)
+#endif
}
- if (!kv_parser_done(&kvp)) goto ep;
- close(fd);
-
- // dumb hack pt2, see also kv callback above
- if (GAMETYPE_MATCHES(L4DS)) gameinfo_title = "Left 4 Dead: Survivors";
return true;
-
-ep: errmsg_errorx("couldn't parse gameinfo.txt (%d:%d): %s", kvp.line, kvp.col,
- kvp.errmsg);
-e: close(fd);
- return false;
}
// vi: sw=4 ts=4 noet tw=80 cc=80
diff --git a/src/gameinfo.h b/src/gameinfo.h
index 4948268..76c3c57 100644
--- a/src/gameinfo.h
+++ b/src/gameinfo.h
@@ -1,5 +1,5 @@
/*
- * Copyright © 2022 Michael Smith <mikesmiffy128@gmail.com>
+ * Copyright © 2023 Michael Smith <mikesmiffy128@gmail.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -22,18 +22,11 @@
#include "intdefs.h"
#include "os.h"
-/* These variables are only set after calling gameinfo_init(). */
-extern const os_char *gameinfo_bindir; /* Absolute path to top-level bin/ */
+/* Miscellaneous metadata variables about the currently running game */
extern const os_char *gameinfo_gamedir; /* Absolute path to game directory */
extern const char *gameinfo_title; /* Name of the game (window title) */
-extern const os_char *gameinfo_clientlib; /* Absolute path to the client lib */
-extern const os_char *gameinfo_serverlib; /* Absolute path to the server lib */
-/*
- * This function is called early in the plugin load and does a whole bunch of
- * spaghetti magic to figure out which game/engine we're in and where its
- * libraries (which we want to hook) are located.
- */
+/* Called early in plugin initialisation to set up the variables above. */
bool gameinfo_init(void);
#endif
diff --git a/src/l4dmm.c b/src/l4dmm.c
index 3c7a07c..d391584 100644
--- a/src/l4dmm.c
+++ b/src/l4dmm.c
@@ -95,13 +95,7 @@ const char *l4dmm_curcampaign(void) {
}
INIT {
- // XXX: ugh, we NEED to centralise library stuff at some point, this sucks
-#ifdef _WIN32
- void *mmlib = GetModuleHandleW(L"matchmaking.dll");
-#else
- void *mmlib = dlopen("matchmaking.so", RTLD_NOW | RTLD_NOLOAD);
- if (mmlib) dlclose(mmlib);
-#endif
+ void *mmlib = os_dlhandle(OS_LIT("matchmaking") OS_LIT(OS_DLSUFFIX));
if (mmlib) {
ifacefactory factory = (ifacefactory)os_dlsym(mmlib, "CreateInterface");
if (!factory) {
diff --git a/src/os-unix.h b/src/os-unix.h
index ec9a940..a25d8ed 100644
--- a/src/os-unix.h
+++ b/src/os-unix.h
@@ -39,10 +39,19 @@ typedef char os_char;
#define os_getenv getenv
#define os_getcwd getcwd
+#define OS_DLPREFIX "lib"
#define OS_DLSUFFIX ".so"
#define OS_MAIN main
+static inline void *os_dlopen(const char *name) {
+ return dlopen(name, RTLD_NOW);
+}
+static inline void *os_dlhandle(const char *name) {
+ void *ret = dlopen(name, RTLD_NOW | RTLD_NOLOAD);
+ if (ret) dlclose(ret);
+ return ret;
+}
#define os_dlsym dlsym
#ifdef __linux__
diff --git a/src/os-win32.h b/src/os-win32.h
index fe61f84..a006083 100644
--- a/src/os-win32.h
+++ b/src/os-win32.h
@@ -45,10 +45,17 @@ typedef unsigned short os_char;
#define os_getenv _wgetenv
#define os_getcwd _wgetcwd
+#define OS_DLPREFIX ""
#define OS_DLSUFFIX ".dll"
#define OS_MAIN wmain
+static inline void *os_dlopen(const ushort *name) {
+ return LoadLibraryW(name);
+}
+static inline void *os_dlhandle(const ushort *name) {
+ return GetModuleHandleW(name);
+}
static inline void *os_dlsym(void *m, const char *s) {
return (void *)GetProcAddress(m, s);
}
diff --git a/src/sst.c b/src/sst.c
index 9dc8829..e00451a 100644
--- a/src/sst.c
+++ b/src/sst.c
@@ -20,7 +20,6 @@
#include <shlwapi.h>
#endif
-#include "ac.h"
#include "con_.h"
#include "engineapi.h"
#include "errmsg.h"
@@ -70,22 +69,63 @@ static inline void *ownhandle(void) {
#define VDFBASENAME "SourceSpeedrunTools"
+#ifdef _WIN32
+// not a proper check, just a short-circuit check to avoid doing more work.
+static inline bool checksamedrive(const ushort *restrict path1,
+ const ushort *restrict path2) {
+ bool ret = (path1[0] | 32) == (path2[0] | 32);
+ if (!ret) errmsg_errorx("game and plugin must be on the same drive\n");
+ return ret;
+}
+#endif
+
DEF_CCMD_HERE(sst_autoload_enable, "Register SST to load on game startup", 0) {
- // note: gamedir doesn't account for if the dll is in a base mod's
- // directory, although it will yield a valid/working relative path anyway.
- const os_char *searchdir = ifacever == 3 ?
- gameinfo_gamedir : gameinfo_bindir;
os_char path[PATH_MAX];
if (os_dlfile(ownhandle(), path, sizeof(path) / sizeof(*path)) == -1) {
// hopefully by this point this won't happen, but, like, never know
errmsg_errordl("failed to get path to plugin");
return;
}
+ os_char _startdir[PATH_MAX];
+ const os_char *startdir;
+ if (ifacever == 2) {
+ startdir = _startdir;
+ os_getcwd(_startdir, PATH_MAX); // if this fails, OS devs are all fired.
+#ifdef _WIN32
+ // note: strictly speaking we *could* allow this with an absolute path
+ // since old builds allow absolute plugin_load paths but since it's less
+ // reliable if e.g. a disk is removed, and also doesn't work for all
+ // games, just rule it out entirely to keep things simple.
+ if (!checksamedrive(path, startdir)) return;
+#endif
+ int len = os_strlen(startdir);
+ if (len + sizeof("/bin") >= PATH_MAX) {
+ errmsg_errorx("path to game is too long");
+ return;
+ }
+ memcpy(_startdir + len, OS_LIT("/bin"), 5 * sizeof(os_char));
+ }
+ else /* ifacever == 3 */ {
+ // newer games load from the mod dir instead of engine bin, and search
+ // in inherited search paths too, although we don't bother with those as
+ // the actual VDF is only read from the mod itself so it's always enough
+ // to make the path relative to that (and that makes the actual plugin
+ // search fast too as it should find it in the first place it looks).
+ // we *still* refuse to autoload across different drives even if some
+ // obscure gameinfo.txt arrangement could technically allow that to work
+ startdir = gameinfo_gamedir;
+#ifdef _WIN32
+ if (!checksamedrive(path, startdir)) return;
+#endif
+ }
os_char relpath[PATH_MAX];
#ifdef _WIN32
- if (!PathRelativePathToW(relpath, searchdir, FILE_ATTRIBUTE_DIRECTORY,
+ // note: dll isn't actually in gamedir if it's in a base mod directory
+ // note: gamedir doesn't account for if the dll is in a base mod's
+ // directory, although it will yield a valid/working relative path anyway.
+ if (!PathRelativePathToW(relpath, startdir, FILE_ATTRIBUTE_DIRECTORY,
path, 0)) {
- errmsg_errorsys("couldn't compute a relative path for some reason");
+ errmsg_errorsys("couldn't compute a relative path");
return;
}
// arbitrary aesthetic judgement
@@ -113,9 +153,8 @@ DEF_CCMD_HERE(sst_autoload_enable, "Register SST to load on game startup", 0) {
errmsg_errorstd("couldn't open %" fS, path);
return;
}
- // XXX: oh, crap, we're clobbering unicode again. welp, let's hope the
- // theory that the engine is just as bad if not worse is true so that it
- // doesn't matter.
+ // XXX: oh crap, we're clobbering unicode again. welp, let's continue
+ // relying on the theory that the engine would fail to deal with it anyway.
if (fprintf(f, "Plugin { file \"%" fS "\" }\n", relpath) < 0 ||
fflush(f) == -1) {
errmsg_errorstd("couldn't write to %" fS, path);
@@ -183,6 +222,27 @@ static const char *updatenotes = "\
#include <featureinit.gen.h> // generated by build/codegen.c
static void do_featureinit(void) {
+ // load libs that might not be there early (...at least on Linux???)
+ clientlib = os_dlhandle(OS_LIT("client") OS_LIT(OS_DLSUFFIX));
+ if (!clientlib) {
+ errmsg_warndl("couldn't get the game's client library");
+ }
+ else if (!(factory_client = (ifacefactory)os_dlsym(clientlib,
+ "CreateInterface"))) {
+ errmsg_warndl("couldn't get client's CreateInterface");
+ }
+ void *inputsystemlib = os_dlhandle(OS_LIT("bin/") OS_LIT(OS_DLPREFIX)
+ OS_LIT("inputsystem") OS_LIT(OS_DLSUFFIX));
+ if (!inputsystemlib) {
+ errmsg_warndl("couldn't get the input system library");
+ }
+ else if (!(factory_inputsystem = (ifacefactory)os_dlsym(inputsystemlib,
+ "CreateInterface"))) {
+ errmsg_warndl("couldn't get input system's CreateInterface");
+ }
+ inputsystem = factory_inputsystem("InputSystemVersion001", 0);
+ if (!inputsystem) errmsg_warnx("missing input system interface");
+ // ... and now for the real magic!
initfeatures();
// if we're autoloaded and the external autoupdate script downloaded a new
@@ -254,42 +314,8 @@ static bool do_load(ifacefactory enginef, ifacefactory serverf) {
errmsg_warnsys("couldn't set up memory for function hooking");
return false;
}
-
factory_engine = enginef; factory_server = serverf;
-#ifdef _WIN32
- void *inputsystemlib = GetModuleHandleW(L"inputsystem.dll");
-#else
- // TODO(linux): assuming the above doesn't apply to this; check if it does!
- // ... actually, there's a good chance this assumption is now wrong!
- void *inputsystemlib = dlopen("bin/libinputsystem.so",
- RTLD_NOW | RLTD_NOLOAD);
- if (inputsystemlib) dlclose(inputsystemlib); // blegh
-#endif
- if (!inputsystemlib) {
- errmsg_warndl("couldn't get the input system library");
- }
- else if (!(factory_inputsystem = (ifacefactory)os_dlsym(inputsystemlib,
- "CreateInterface"))) {
- errmsg_warndl("couldn't get input system's CreateInterface");
- }
if (!engineapi_init(ifacever)) return false;
-#ifdef _WIN32
- clientlib = GetModuleHandleW(gameinfo_clientlib);
-#else
- // Apparently on Linux, the client library isn't actually loaded yet here,
- // so RTLD_NOLOAD won't actually find it. We have to just dlopen it
- // normally - and then remember to decrement the refcount again later in
- // do_unload() so nothing gets leaked!
- clientlib = dlopen(gameinfo_clientlib, RTLD_NOW);
-#endif
- if (!clientlib) {
- errmsg_warndl("couldn't get the game's client library");
- }
- else if (!(factory_client = (ifacefactory)os_dlsym(clientlib,
- "CreateInterface"))) {
- errmsg_warndl("couldn't get client's CreateInterface");
- }
-
const void **p = vtable_firstdiff;
if (GAMETYPE_MATCHES(Portal2)) *p++ = (void *)&nop_p_v; // ClientFullyConnect
*p++ = (void *)&nop_p_v; // ClientDisconnect
@@ -303,7 +329,6 @@ static bool do_load(ifacefactory enginef, ifacefactory serverf) {
*p++ = (void *)&nop_ipipp_v; // OnQueryCvarValueFinished (002+)
*p++ = (void *)&nop_p_v; // OnEdictAllocated
*p = (void *)&nop_p_v; // OnEdictFreed
-
if (!deferinit()) { do_featureinit(); fixes_apply(); }
return true;
}