diff options
Diffstat (limited to 'src/l4dmm.c')
-rw-r--r-- | src/l4dmm.c | 134 |
1 files changed, 134 insertions, 0 deletions
diff --git a/src/l4dmm.c b/src/l4dmm.c new file mode 100644 index 0000000..8394038 --- /dev/null +++ b/src/l4dmm.c @@ -0,0 +1,134 @@ +/* + * 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 + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED “AS IS” AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#include <string.h> + +#include "engineapi.h" +#include "errmsg.h" +#include "feature.h" +#include "gamedata.h" +#include "gametype.h" +#include "kvsys.h" +#include "mem.h" +#include "os.h" +#include "vcall.h" + +FEATURE() +REQUIRE(kvsys) +REQUIRE_GAMEDATA(vtidx_GetMatchNetworkMsgController) +REQUIRE_GAMEDATA(vtidx_GetActiveGameServerDetails) + +DECL_VFUNC_DYN(void *, GetMatchNetworkMsgController) +DECL_VFUNC_DYN(struct KeyValues *, GetActiveGameServerDetails, + struct KeyValues *) + +// Old L4D1 uses a heavily modified version of the CMatchmaking in Source 2007. +// None of it is publicly documented or well-understood but I was able to figure +// out that this random function does something *close enough* to what we want. +struct contextval { + const char *name; + int _unknown[8]; + const char *val; + /* other stuff unknown */ +}; +DECL_VFUNC(struct contextval *, unknown_contextlookup, 67, const char *) + +static void *matchfwk; +static union { // space saving + struct { int sym_game, sym_campaign; }; // "game/campaign" KV lookup + void *oldmmiface; // old L4D1 interface +} U; +#define oldmmiface U.oldmmiface +#define sym_game U.sym_game +#define sym_campaign U.sym_campaign +static char campaignbuf[32]; + +const char *l4dmm_curcampaign(void) { +#ifdef _WIN32 + if (!matchfwk) { // we must have oldmmiface, then + struct contextval *ctxt = unknown_contextlookup(oldmmiface, + "CONTEXT_L4D_CAMPAIGN"); + if (ctxt) { + // HACK: since this context symbol stuff was the best that was found + // for this old MM interface, just map things back to their names + // manually. bit stupid, but it gets the (rather difficult) job done + if (strncmp(ctxt->val, "CONTEXT_L4D_CAMPAIGN_", 21)) return 0; + if (!strcmp(ctxt->val + 21, "APARTMENTS")) return "Hospital"; + if (!strcmp(ctxt->val + 21, "CAVES")) return "SmallTown"; + if (!strcmp(ctxt->val + 21, "GREENHOUSE")) return "Airport"; + if (!strcmp(ctxt->val + 21, "HILLTOP")) return "Farm"; + } + return 0; + } +#endif + void *ctrlr = GetMatchNetworkMsgController(matchfwk); + struct KeyValues *kv = GetActiveGameServerDetails(ctrlr, 0); + if (!kv) return 0; // not in server, probably + const char *ret = 0; + struct KeyValues *subkey = kvsys_getsubkey(kv, sym_game); + if (subkey) subkey = kvsys_getsubkey(subkey, sym_campaign); + if (subkey) ret = kvsys_getstrval(subkey); + if (ret) { + // ugh, we have to free all the memory allocated by the engine, so copy + // this glorified global state to a buffer so the caller doesn't have to + // deal with freeing. this necessitates a length cap but it's hopefully + // reasonable... + int len = strlen(ret); + if (len > sizeof(campaignbuf) - 1) ret = 0; + else ret = memcpy(campaignbuf, ret, len + 1); + } + kvsys_free(kv); + return ret; +} + +INIT { + // ugh, we NEED to centralise the 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 + if (mmlib) { + ifacefactory factory = (ifacefactory)os_dlsym(mmlib, "CreateInterface"); + if (!factory) { + errmsg_errordl("couldn't get matchmaking interface factory"); + return false; + } + matchfwk = factory("MATCHFRAMEWORK_001", 0); + if (!matchfwk) { + errmsg_errorx("couldn't get IMatchFramework interface"); + return false; + } + sym_game = kvsys_strtosym("game"); + sym_campaign = kvsys_strtosym("campaign"); + } + else { +#ifdef _WIN32 + oldmmiface = factory_engine("VENGINE_MATCHMAKING_VERSION001", 0); + if (!oldmmiface) { + errmsg_errorx("couldn't get IMatchmaking interface"); + return false; + } +#else // Linux L4D1 has always used the separate matchmaking library + errmsg_errordl("couldn't get matchmaking library"); + return false; +#endif + } + return true; +} + +// vi: sw=4 ts=4 noet tw=80 cc=80 |