diff options
| -rw-r--r-- | gamedata/engine.kv | 3 | ||||
| -rw-r--r-- | src/build/mkentprops.c | 3 | ||||
| -rw-r--r-- | src/fixes.c | 8 | ||||
| -rw-r--r-- | src/fixes.h | 7 | ||||
| -rw-r--r-- | src/sst.c | 129 | 
5 files changed, 92 insertions, 58 deletions
| diff --git a/gamedata/engine.kv b/gamedata/engine.kv index 840f9a1..022ccbd 100644 --- a/gamedata/engine.kv +++ b/gamedata/engine.kv @@ -79,4 +79,7 @@ off_SP_offset {  	//2013 72 // TODO(compat): not sure about 2013/009 yet pt3  } +// IEngineVGuiInternal/CEngineVGui +vtidx_VGuiConnect "3 + NVDTOR" // note: real name is Connect, way too generic +  // vi: sw=4 ts=4 noet tw=80 cc=80 ft=plain diff --git a/src/build/mkentprops.c b/src/build/mkentprops.c index 321b954..9c366e5 100644 --- a/src/build/mkentprops.c +++ b/src/build/mkentprops.c @@ -163,6 +163,9 @@ F( "					has_%s = true;", (*pp)->varname)  			// > can detect that and set the SPROP_IS_VECTOR_ELEM flag.  			// apparently if we're loaded via VDF, it hasn't been flipped back  			// yet. just calling abs() on everything as an easy solution. +			// TODO(opt): if we moved this into deferred init we wouldn't need +			// to bother with this, but that might involve untangling more +			// engineapi stuff  F( "					%s = abs(*(int *)mem_offset(p, off_SP_offset));",  		(*pp)->varname)  _( "					if (!--needprops) break;") diff --git a/src/fixes.c b/src/fixes.c index a90d878..d055ba8 100644 --- a/src/fixes.c +++ b/src/fixes.c @@ -161,12 +161,4 @@ void fixes_apply(void) {  	else if (GAMETYPE_MATCHES(L4D2x)) l4d2specific();  } -void fixes_tryagainlater(void) { -	if (GAMETYPE_MATCHES(L4D1)) { -		// whatever dll this is in seems to load late (mm_l4d_debug is fine) -		struct con_var *v = con_findvar("ui_l4d_debug"); -		if (v) con_setvari(v, 0); -	} -} -  // vi: sw=4 ts=4 noet tw=80 cc=80 diff --git a/src/fixes.h b/src/fixes.h index 54d8d06..ae863a8 100644 --- a/src/fixes.h +++ b/src/fixes.h @@ -20,11 +20,4 @@   */  void fixes_apply(void); -/* - * Applies specific fixes *again*, in cases where they wouldn't work very early - * in game startup (like if the plugin is loaded via VDF). Isn't guaranteed to - * be called if the plugin is loaded later, so must do things redundantly. - */ -void fixes_tryagainlater(void); -  // vi: sw=4 ts=4 noet tw=80 cc=80 @@ -160,15 +160,6 @@ static const char *VCALLCONV GetStringForSymbol_hook(void *this, int s) {  	return ret;  } -// by hooking stuffcmds, we get a callback after the last of the startup -// commands have run (the command line ones) -static struct con_cmd *cmd_stuffcmds = 0; -static con_cmdcb orig_stuffcmds_cb; -static void hook_stuffcmds_cb(const struct con_cmdargs *args) { -	orig_stuffcmds_cb(args); -	fixes_tryagainlater(); -} -  // vstdlib symbol, only currently used in l4d2 but exists everywhere so oh well  IMPORT void *KeyValuesSystem(void); @@ -207,6 +198,75 @@ static bool already_loaded = false, skip_unload = false;  #define RGBA(r, g, b, a) (&(struct con_colour){(r), (g), (b), (a)}) +static void do_featureinit(void) { +	has_autojump = autojump_init(); +	has_demorec = demorec_init(); +	// not enabling demorec_custom yet - kind of incomplete and currently unused +	//if (has_demorec) demorec_custom_init(); +	bool has_ent = ent_init(); +	has_fov = fov_init(has_ent); +	if (has_ent) l4dwarp_init(); +	has_nosleep = nosleep_init(); +#ifdef _WIN32 +	has_rinput = rinput_init(); +#endif +	fixes_apply(); + +	con_colourmsg(RGBA(64, 255, 64, 255), +			LONGNAME " v" VERSION " successfully loaded"); +	con_colourmsg(RGBA(255, 255, 255, 255), " for game "); +	con_colourmsg(RGBA(0, 255, 255, 255), "%s\n", gameinfo_title); +} + +static void *vgui; +typedef void (*VCALLCONV VGuiConnect_func)(void); +static VGuiConnect_func orig_VGuiConnect; +static void VCALLCONV hook_VGuiConnect(void) { +	orig_VGuiConnect(); +	do_featureinit(); +	unhook_vtable(*(void ***)vgui, vtidx_VGuiConnect, (void *)orig_VGuiConnect); +} + +// --- Magical deferred load order hack nonsense! --- +// The engine loads VDF plugins basically right after server.dll, but long +// before most other stuff, which makes hooking certain other stuff a pain. We +// still want to be able to load via VDF as it's the only reasonable way to get +// in before config.cfg, which is needed for any kind of configuration to work +// correctly. +// +// So here, we hook CEngineVGui::Connect() which is pretty much the last thing +// that gets called on init, and defer feature init till afterwards. That allows +// us to touch pretty much any engine stuff without worrying about load order +// nonsense. +// +// In do_load() below, we check to see whether we're loading early by checking +// whether gameui.dll is loaded yet; this is one of several possible arbitrary +// checks. If it's loaded already, we assume we're getting loaded late via the +// console and just init everything immediately. +// +// Route credit to Bill for helping figure a lot of this out - mike +static void deferinit(void) { +	vgui = factory_engine("VEngineVGui001", 0); +	if (!vgui) { +		con_warn("sst: warning: couldn't get VEngineVGui for deferred " +				"feature setup\n"); +		goto e; +	} +	if (!os_mprot(*(void ***)vgui + vtidx_VGuiConnect, sizeof(void *), +			PAGE_READWRITE)) { +		con_warn("sst: warning: couldn't unprotect CEngineVGui vtable for " +				"deferred feature setup\n"); +		goto e; +	} +	orig_VGuiConnect = (VGuiConnect_func)hook_vtable(*(void ***)vgui, +			vtidx_VGuiConnect, (void *)&hook_VGuiConnect); +	return; + +e:	con_warn("!!! SOME FEATURES MAY BE BROKEN !!!\n"); +	// I think this is the lesser of two evils! Unlikely to happen anyway. +	do_featureinit(); +} +  static bool do_load(ifacefactory enginef, ifacefactory serverf) {  	factory_engine = enginef; factory_server = serverf;  	if (!engineapi_init(ifacever)) return false; @@ -226,14 +286,12 @@ static bool do_load(ifacefactory enginef, ifacefactory serverf) {  	*p   = (void *)&nop_p_v;		  // OnEdictFreed  #ifdef _WIN32 -	//serverlib = GetModuleHandleW(gameinfo_serverlib);  	void *clientlib = GetModuleHandleW(gameinfo_clientlib);  #else -	// Linux Source load order seems to be different to the point where if we -	// +plugin_load or use a vdf then RTLD_NOLOAD won't actually find these, so -	// we have to just dlopen them normally - and then remember to decrement the -	// refcount again later in do_unload() so nothing gets leaked -	//serverlib = dlopen(gameinfo_serverlib, RTLD_NOW); +	// 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) { @@ -247,6 +305,7 @@ static bool do_load(ifacefactory enginef, ifacefactory serverf) {  	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 @@ -259,19 +318,6 @@ static bool do_load(ifacefactory enginef, ifacefactory serverf) {  		con_warn("sst: warning: couldn't get input system's CreateInterface\n");  	} -	has_autojump = autojump_init(); -	has_demorec = demorec_init(); -	// not enabling demorec_custom yet - kind of incomplete and currently unused -	//if (has_demorec) demorec_custom_init(); -	bool has_ent = ent_init(); -	has_fov = fov_init(has_ent); -	if (has_ent) l4dwarp_init(); -	has_nosleep = nosleep_init(); -#ifdef _WIN32 -	has_rinput = rinput_init(); -#endif -	fixes_apply(); -  	// NOTE: this is technically redundant for early versions but I CBA writing  	// a version check; it's easier to just do this unilaterally.  	if (GAMETYPE_MATCHES(L4D2x)) { @@ -280,22 +326,21 @@ static bool do_load(ifacefactory enginef, ifacefactory serverf) {  		if (!os_mprot(kvsvt + 4, sizeof(void *), PAGE_READWRITE)) {  			con_warn("sst: warning: couldn't unprotect KeyValuesSystem "  					"vtable; won't be able to prevent nag message\n"); -			goto e;  		} -		orig_GetStringForSymbol = (GetStringForSymbol_func)hook_vtable(kvsvt, 4, -				(void *)GetStringForSymbol_hook); -	} - -	cmd_stuffcmds = con_findcmd("stuffcmds"); -	if (cmd_stuffcmds) { -		orig_stuffcmds_cb = cmd_stuffcmds->cb; -		cmd_stuffcmds->cb = hook_stuffcmds_cb; +		else { +			orig_GetStringForSymbol = (GetStringForSymbol_func)hook_vtable( +					kvsvt, 4, (void *)GetStringForSymbol_hook); +		}  	} -e:	con_colourmsg(RGBA(64, 255, 64, 255), -			LONGNAME " v" VERSION " successfully loaded"); -	con_colourmsg(RGBA(255, 255, 255, 255), " for game "); -	con_colourmsg(RGBA(0, 255, 255, 255), "%s\n", gameinfo_title); +#ifdef _WIN32 +	bool isvdf = !GetModuleHandleW(L"gameui.dll"); +#else +	void *gameuilib = dlopen("bin/libgameui.so", RTLD_NOW | RLTD_NOLOAD); +	bool isvdf = !gameuilib; +	if (gameuilib) dlclose(gameuilib); +#endif +	if (isvdf) deferinit(); else do_featureinit();  	return true;  } @@ -345,7 +390,6 @@ static void do_unload(void) {  #endif  #ifdef __linux__ -	//if (serverlib) dlclose(serverlib);  	if (clientlib) dlclose(clientlib);  #endif  	con_disconnect(); @@ -353,7 +397,6 @@ static void do_unload(void) {  	if (orig_GetStringForSymbol) {  		unhook_vtable(kvsvt, 4, (void *)orig_GetStringForSymbol);  	} -	if (cmd_stuffcmds) cmd_stuffcmds->cb = orig_stuffcmds_cb;  }  static bool VCALLCONV Load(void *this, ifacefactory enginef, | 
