From de615ef1f75520ed072acf3e80755564884a7717 Mon Sep 17 00:00:00 2001 From: BotoX Date: Wed, 25 Sep 2019 11:36:02 +0200 Subject: [PATCH] initial commit --- Makefile | 209 ++++++++++++++++++++++++++++++++++++++ README.txt | 4 + cassandra.cpp | 254 ++++++++++++++++++++++++++++++++++++++++++++++ cassandra.h | 67 ++++++++++++ cassandra.vdf | 5 + engine_wrappers.h | 97 ++++++++++++++++++ 6 files changed, 636 insertions(+) create mode 100644 Makefile create mode 100644 README.txt create mode 100644 cassandra.cpp create mode 100644 cassandra.h create mode 100644 cassandra.vdf create mode 100644 engine_wrappers.h diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..54be01c --- /dev/null +++ b/Makefile @@ -0,0 +1,209 @@ +# (C)2004-2010 Metamod:Source Development Team +# Makefile written by David "BAILOPAN" Anderson + +########################################### +### EDIT THESE PATHS FOR YOUR OWN SETUP ### +########################################### + +HL2SDK_ORIG = ../../hl2sdk +HL2SDK_OB = ../../hl2sdk-ob +HL2SDK_CSS = ../../hl2sdk-css +HL2SDK_OB_VALVE = ../../hl2sdk-ob-valve +HL2SDK_L4D = ../../hl2sdk-l4d +HL2SDK_L4D2 = ../../hl2sdk-l4d2 +HL2SDK_CSGO = ../../hl2sdk-csgo +MMSOURCE19 = .. + +##################################### +### EDIT BELOW FOR OTHER PROJECTS ### +##################################### + +PROJECT = cassandra +OBJECTS = cassandra.cpp + +############################################## +### CONFIGURE ANY OTHER FLAGS/OPTIONS HERE ### +############################################## + +OPT_FLAGS = -O3 -funroll-loops -pipe +GCC4_FLAGS = -fvisibility=hidden -fvisibility-inlines-hidden +DEBUG_FLAGS = -g -ggdb3 -D_DEBUG +CPP = gcc +CPP_OSX = clang + +########################## +### SDK CONFIGURATIONS ### +########################## + +override ENGSET = false + +# Check for valid list of engines +ifneq (,$(filter original orangebox orangeboxvalve css left4dead left4dead2 csgo,$(ENGINE))) + override ENGSET = true +endif + +ifeq "$(ENGINE)" "original" + HL2SDK = $(HL2SDK_ORIG) + CFLAGS += -DSOURCE_ENGINE=1 +endif +ifeq "$(ENGINE)" "orangebox" + HL2SDK = $(HL2SDK_OB) + CFLAGS += -DSOURCE_ENGINE=3 +endif +ifeq "$(ENGINE)" "css" + HL2SDK = $(HL2SDK_CSS) + CFLAGS += -DSOURCE_ENGINE=6 +endif +ifeq "$(ENGINE)" "orangeboxvalve" + HL2SDK = $(HL2SDK_OB_VALVE) + CFLAGS += -DSOURCE_ENGINE=7 +endif +ifeq "$(ENGINE)" "left4dead" + HL2SDK = $(HL2SDK_L4D) + CFLAGS += -DSOURCE_ENGINE=8 +endif +ifeq "$(ENGINE)" "left4dead2" + HL2SDK = $(HL2SDK_L4D2) + CFLAGS += -DSOURCE_ENGINE=9 +endif +ifeq "$(ENGINE)" "csgo" + HL2SDK = $(HL2SDK_CSGO) + CFLAGS += -DSOURCE_ENGINE=12 +endif + +HL2PUB = $(HL2SDK)/public + +ifeq "$(ENGINE)" "original" + INCLUDE += -I$(HL2SDK)/public/dlls + METAMOD = $(MMSOURCE19)/core-legacy +else + INCLUDE += -I$(HL2SDK)/public/game/server + METAMOD = $(MMSOURCE19)/core +endif + +OS := $(shell uname -s) + +ifeq "$(OS)" "Darwin" + LIB_EXT = dylib + HL2LIB = $(HL2SDK)/lib/mac +else + LIB_EXT = so + ifeq "$(ENGINE)" "original" + HL2LIB = $(HL2SDK)/linux_sdk + else + HL2LIB = $(HL2SDK)/lib/linux + endif +endif + +# if ENGINE is original or OB +ifneq (,$(filter original orangebox,$(ENGINE))) + LIB_SUFFIX = _i486.$(LIB_EXT) +else + LIB_PREFIX = lib + ifneq (,$(filter orangeboxvalve css left4dead2,$(ENGINE))) + ifneq "$(OS)" "Darwin" + LIB_SUFFIX = _srv.$(LIB_EXT) + else + LIB_SUFFIX = .$(LIB_EXT) + endif + else + LIB_SUFFIX = .$(LIB_EXT) + endif +endif + +CFLAGS += -DSE_EPISODEONE=1 -DSE_DARKMESSIAH=2 -DSE_ORANGEBOX=3 -DSE_BLOODYGOODTIME=4 -DSE_EYE=5 \ + -DSE_CSS=6 -DSE_ORANGEBOXVALVE=7 -DSE_LEFT4DEAD=8 -DSE_LEFT4DEAD2=9 -DSE_ALIENSWARM=10 \ + -DSE_PORTAL2=11 -DSE_CSGO=12 + +LINK += $(HL2LIB)/tier1_i486.a $(LIB_PREFIX)vstdlib$(LIB_SUFFIX) $(LIB_PREFIX)tier0$(LIB_SUFFIX) + +ifeq "$(ENGINE)" "csgo" + LINK += $(HL2LIB)/interfaces_i486.a +endif + +INCLUDE += -I. -I.. -I$(HL2PUB) -I$(HL2PUB)/engine -I$(HL2PUB)/mathlib -I$(HL2PUB)/vstdlib \ + -I$(HL2PUB)/tier0 -I$(HL2PUB)/tier1 -I. -I$(METAMOD) -I$(METAMOD)/sourcehook -I$(HL2SDK)/game/server + +################################################ +### DO NOT EDIT BELOW HERE FOR MOST PROJECTS ### +################################################ + +BINARY = $(PROJECT).$(LIB_EXT) + +ifeq "$(DEBUG)" "true" + BIN_DIR = Debug.$(ENGINE) + CFLAGS += $(DEBUG_FLAGS) +else + BIN_DIR = Release.$(ENGINE) + CFLAGS += $(OPT_FLAGS) +endif + + +ifeq "$(OS)" "Darwin" + CPP = $(CPP_OSX) + LIB_EXT = dylib + CFLAGS += -DOSX -D_OSX + LINK += -dynamiclib -lstdc++ -mmacosx-version-min=10.5 +else + LIB_EXT = so + CFLAGS += -D_LINUX + LINK += -shared +endif + +IS_CLANG := $(shell $(CPP) --version | head -1 | grep clang > /dev/null && echo "1" || echo "0") + +ifeq "$(IS_CLANG)" "1" + CPP_MAJOR := $(shell $(CPP) --version | grep clang | sed "s/.*version \([0-9]\)*\.[0-9]*.*/\1/") + CPP_MINOR := $(shell $(CPP) --version | grep clang | sed "s/.*version [0-9]*\.\([0-9]\)*.*/\1/") +else + CPP_MAJOR := $(shell $(CPP) -dumpversion >&1 | cut -b1) + CPP_MINOR := $(shell $(CPP) -dumpversion >&1 | cut -b3) +endif + +CFLAGS += -DPOSIX -Dstricmp=strcasecmp -D_stricmp=strcasecmp -D_strnicmp=strncasecmp \ + -Dstrnicmp=strncasecmp -D_snprintf=snprintf -D_vsnprintf=vsnprintf -D_alloca=alloca \ + -Dstrcmpi=strcasecmp -DCOMPILER_GCC -Wall -Wno-non-virtual-dtor -Wno-overloaded-virtual \ + -fPIC -fno-exceptions -fno-rtti -msse -m32 -fno-strict-aliasing + +# Clang || GCC >= 4 +ifeq "$(shell expr $(IS_CLANG) \| $(CPP_MAJOR) \>= 4)" "1" + CFLAGS += $(GCC4_FLAGS) +endif + +# Clang >= 3 || GCC >= 4.7 +ifeq "$(shell expr $(IS_CLANG) \& $(CPP_MAJOR) \>= 3 \| $(CPP_MAJOR) \>= 4 \& $(CPP_MINOR) \>= 7)" "1" + CFLAGS += -Wno-delete-non-virtual-dtor +endif + +# OS is Linux and not using clang +ifeq "$(shell expr $(OS) \= Linux \& $(IS_CLANG) \= 0)" "1" + LINK += -static-libgcc +endif + +OBJ_BIN := $(OBJECTS:%.cpp=$(BIN_DIR)/%.o) + +$(BIN_DIR)/%.o: %.cpp + $(CPP) $(INCLUDE) $(CFLAGS) -o $@ -c $< + +all: check + mkdir -p $(BIN_DIR) + ln -sf $(HL2LIB)/$(LIB_PREFIX)vstdlib$(LIB_SUFFIX) + ln -sf $(HL2LIB)/$(LIB_PREFIX)tier0$(LIB_SUFFIX) + $(MAKE) -f Makefile cassandra + +check: + if [ "$(ENGSET)" = "false" ]; then \ + echo "You must supply one of the following values for ENGINE:"; \ + echo "csgo, left4dead2, left4dead, css, orangeboxvalve, orangebox, or original"; \ + exit 1; \ + fi + +cassandra: check $(OBJ_BIN) + $(CPP) $(INCLUDE) -m32 $(OBJ_BIN) $(LINK) -ldl -lm -o $(BIN_DIR)/$(BINARY) + +default: all + +clean: check + rm -rf $(BIN_DIR)/*.o + rm -rf $(BIN_DIR)/$(BINARY) + diff --git a/README.txt b/README.txt new file mode 100644 index 0000000..2a002c1 --- /dev/null +++ b/README.txt @@ -0,0 +1,4 @@ +For more information on compiling and reading the plugin's source code, see: + + http://wiki.alliedmods.net/Category:Metamod:Source_Development + diff --git a/cassandra.cpp b/cassandra.cpp new file mode 100644 index 0000000..dcac908 --- /dev/null +++ b/cassandra.cpp @@ -0,0 +1,254 @@ +/** + * vim: set ts=4 sw=4 tw=99 noet : + * ====================================================== + * Metamod:Source Stub Plugin + * Written by AlliedModders LLC. + * ====================================================== + * + * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * This stub plugin is public domain. + */ + +#include "cassandra.h" + +using namespace SourceHook; + +Cassandra g_Cassandra; +IVEngineServer *engine = NULL; +IServerGameDLL *gamedll = NULL; +ICvar *icvar = NULL; +IPlayerInfoManager *playerinfo = NULL; +CGlobalVars *gpGlobals = NULL; + +PLUGIN_EXPOSE(Cassandra, g_Cassandra); + +SH_DECL_HOOK6(IServerGameDLL, LevelInit, SH_NOATTRIB, false, bool, const char *, const char *, const char *, const char *, bool, bool); +SH_DECL_HOOK1_void(IServerGameDLL, GameFrame, SH_NOATTRIB, false, bool); + +static const int installedsignals[] = { SIGALRM, SIGABRT, SIGSEGV, SIGBUS }; +static const size_t installedlen = (sizeof(installedsignals) / sizeof(int)); +static struct sigaction savedsig[installedlen]; +static int signalmap[63]; /* Fuck you, SIGRTMAX. */ + +ConVar *g_pPacketThread = NULL; + +bool Cassandra::Load(PluginId id, ISmmAPI *ismm, char *error, size_t maxlen, bool late) +{ + signalmap[SIGALRM] = 0; + signalmap[SIGABRT] = 1; + signalmap[SIGSEGV] = 2; + signalmap[SIGBUS] = 3; + + PLUGIN_SAVEVARS(); + + GET_V_IFACE_CURRENT(GetEngineFactory, engine, IVEngineServer, INTERFACEVERSION_VENGINESERVER); + GET_V_IFACE_ANY(GetServerFactory, gamedll, IServerGameDLL, INTERFACEVERSION_SERVERGAMEDLL); + GET_V_IFACE_CURRENT(GetEngineFactory, icvar, ICvar, CVAR_INTERFACE_VERSION); + GET_V_IFACE_CURRENT(GetServerFactory, playerinfo, IPlayerInfoManager, INTERFACEVERSION_PLAYERINFOMANAGER); + + this->m_iGameFrameHook = SH_ADD_VPHOOK(IServerGameDLL, GameFrame, gamedll, SH_MEMBER(this, &Cassandra::OnGameFrame), false); + this->m_iLevelInitHook = SH_ADD_HOOK(IServerGameDLL, LevelInit, gamedll, SH_MEMBER(this, &Cassandra::LevelInit), false); + gpGlobals = ismm->GetCGlobals(); + + g_pPacketThread = icvar->FindVar("net_queued_packet_thread"); + return true; +} + +bool Cassandra::Unload(char *error, size_t maxlen) +{ + SH_REMOVE_HOOK_ID(this->m_iGameFrameHook); + SH_REMOVE_HOOK_ID(this->m_iLevelInitHook); + + this->RemoveSignalHandler(); + alarm(0); + return true; +} + +bool Cassandra::LevelInit(char const *pMapName, char const *pMapEntities, char const *pOldLevel, char const *pLandmarkName, bool loadGame, bool background) +{ + alarm(12); /* Level Change, alarm later in-case something happens. */ + RETURN_META_VALUE(MRES_IGNORED, true); +} + +void Cassandra::OnGameFrame(bool simulating) +{ + alarm(7); /* Reset our Alarm here. We're still ticking. */ + this->InstallSignalHandler(); + RETURN_META(MRES_IGNORED); +} + +void Cassandra::InstallSignalHandler(void) +{ + static const int ignoredsignals[] = { SIGINT, SIGQUIT, SIGALRM, SIGABRT }; + + struct sigaction sig; + unsigned iter; + + for (iter = 0; iter < installedlen; ++iter) + { + sigaction(installedsignals[iter], NULL, &sig); + if (sig.sa_sigaction != &(SignalAction)) + { + break; + } + } + + if (iter == installedlen) + { + return; + } + + sigset_t sigset; + sigemptyset(&sigset); + for (iter = 0; iter < installedlen; ++iter) + { + sigaddset(&sigset, ignoredsignals[iter]); + } + + sig.sa_mask = sigset; + sig.sa_handler = NULL; + sig.sa_sigaction = &(SignalAction); /* We don't really care who gets installed. This is a union on some platforms so we prefer this. */ + sig.sa_flags = SA_ONSTACK|SA_SIGINFO; + + for (iter = 0; iter < installedlen; ++iter) + { + sigaction(installedsignals[iter], &sig, &savedsig[iter]); + } +} + +void Cassandra::RemoveSignalHandler(void) +{ + for (unsigned iter = 0; iter < installedlen; ++iter) + { + sigaction(installedsignals[iter], &savedsig[iter], NULL); + } +} + +void Cassandra::Hooped(void) +{ + if (g_pPacketThread != NULL) + { + g_pPacketThread->SetValue(0); + } + + VCRHook_recvfrom = &RecvFrom; + for (int counter = 1; counter <= gpGlobals->maxClients; ++counter) + { + edict_t *pEdict = PEntityOfEntIndex(counter); + if (pEdict == NULL || pEdict->IsFree()) + { + continue; + } + + IPlayerInfo *pInfo = playerinfo->GetPlayerInfo(pEdict); + INetChannel *netchan = static_cast(engine->GetPlayerNetInfo(counter)); + if (pInfo == NULL || !(pInfo->IsConnected()) || pInfo->IsFakeClient() || netchan == NULL) + { + continue; + } + + engine->ClientPrintf(pEdict, "\x05[Cassandra]\x04 Server Crashed. Recovering...\n"); + for (unsigned char count = 4; count != 0; --count) + { + engine->ClientCommand(pEdict, "retry"); + netchan->Transmit(); + } + } +} + +void Cassandra::TakeStackTrace(const char *pInput) +{ +/* + backward::Printer p; + p.snippet = true; + p.object = true; + p.address = true; + + Stacktrace st; + st.load_here(32); + + FILE *pFile = fopen(pInput, "abw"); + + if (pFile != NULL) + { + p.color = false; + p.print(&st, pFile); + fclose(pFile); + } + else + { + p.color = true; + p.Print(&st); + } +*/ +} + +int RecvFrom(int s, char *buf, int len, int flags, struct sockaddr *from, int *fromlen) +{ + /* This should take all packets, then do nothing with them. We're dead. */ + return -ECONNREFUSED; +} + +void SignalAction(int sig, siginfo_t *pInfo, void *pData) +{ + static bool used = false; + if (used == true) + { + signal(sig, SIG_DFL); + return; + } + + used = true; + alarm(12); + g_Cassandra.Hooped(); + g_Cassandra.TakeStackTrace("Cassandra.txt"); + g_Cassandra.RemoveSignalHandler(); + signal(sig, savedsig[signalmap[sig]].sa_handler); + + /* Exit for good measure. */ + exit(EXIT_FAILURE); +} + + +const char *Cassandra::GetLicense() +{ + return "GPL"; +} + +const char *Cassandra::GetVersion() +{ + return "1.0"; +} + +const char *Cassandra::GetDate() +{ + return __DATE__; +} + +const char *Cassandra::GetLogTag() +{ + return "Cassandra"; +} + +const char *Cassandra::GetAuthor() +{ + return "Kyle (KyleS) Sanderson"; +} + +const char *Cassandra::GetDescription() +{ + return "Reconnect players on crash."; +} + +const char *Cassandra::GetName() +{ + return "Cassandra"; +} + +const char *Cassandra::GetURL() +{ + return "http://www.AlliedMods.net"; +} diff --git a/cassandra.h b/cassandra.h new file mode 100644 index 0000000..d91f092 --- /dev/null +++ b/cassandra.h @@ -0,0 +1,67 @@ +/** + * vim: set ts=4 sw=4 tw=99 noet : + * ====================================================== + * Metamod:Source Stub Plugin + * Written by AlliedModders LLC. + * ====================================================== + * + * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * This stub plugin is public domain. + */ + +#ifndef _INCLUDE_METAMOD_SOURCE_CASSANDRA_PLUGIN_H_ +#define _INCLUDE_METAMOD_SOURCE_CASSANDRA_PLUGIN_H_ + +#include +#include "engine_wrappers.h" +#include + +#include +#include + +#include +#include + +#if defined WIN32 && !defined snprintf +#define snprintf _snprintf +#endif + +class Cassandra : public ISmmPlugin +{ +public: + bool Load(PluginId id, ISmmAPI *ismm, char *error, size_t maxlen, bool late); + bool Unload(char *error, size_t maxlen); +public: + const char *GetAuthor(); + const char *GetName(); + const char *GetDescription(); + const char *GetURL(); + const char *GetLicense(); + const char *GetVersion(); + const char *GetDate(); + const char *GetLogTag(); +public: + bool LevelInit(char const *pMapName, char const *pMapEntities, char const *pOldLevel, char const *pLandmarkName, bool loadGame, bool background); + void OnGameFrame(bool simulating); +public: + void InstallSignalHandler(void); + void RemoveSignalHandler(void); + void Hooped(void); + void TakeStackTrace(const char *pInput); +private: + int m_iGameFrameHook; + int m_iLevelInitHook; + unsigned m_iCounter; +}; + +extern Cassandra g_Cassandra; + +PLUGIN_GLOBALVARS(); + +void SignalAction(int sig, siginfo_t *pInfo, void *pData); +int RecvFrom(int s, char *buf, int len, int flags, struct sockaddr *from, int *fromlen); + +#endif //_INCLUDE_METAMOD_SOURCE_CASSANDRA_PLUGIN_H_ diff --git a/cassandra.vdf b/cassandra.vdf new file mode 100644 index 0000000..a23817a --- /dev/null +++ b/cassandra.vdf @@ -0,0 +1,5 @@ +"Metamod Plugin" +{ + "alias" "cassandra" + "file" "addons/cassandra.so" +} diff --git a/engine_wrappers.h b/engine_wrappers.h new file mode 100644 index 0000000..0eebbe7 --- /dev/null +++ b/engine_wrappers.h @@ -0,0 +1,97 @@ +/** + * vim: set ts=4 sw=4 tw=99 noet : + * ====================================================== + * Metamod:Source Sample Plugin + * Written by AlliedModders LLC. + * ====================================================== + * + * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * This sample plugin is public domain. + */ + +#ifndef _INCLUDE_SOURCE_ENGINE_WRAPPERS_ +#define _INCLUDE_SOURCE_ENGINE_WRAPPERS_ + +#include + +extern IVEngineServer *engine; +extern CGlobalVars *gpGlobals; + +#if SOURCE_ENGINE == SE_EPISODEONE && defined METAMOD_PLAPI_VERSION +#error "Metamod:Source 1.6 API is not supported on the old engine." +#endif + +#define ENGINE_CALL(func) SH_CALL(engine, &IVEngineServer::func) + +/** + * Wrap some API calls for legacy MM:S. + */ +#if !defined METAMOD_PLAPI_VERSION +#define GetEngineFactory engineFactory +#define GetServerFactory serverFactory +#define MM_Format snprintf +#define GetCGlobals pGlobals +#else +#define MM_Format g_SMAPI->Format +#endif + +#if SOURCE_ENGINE <= SE_DARKMESSIAH +/** + * Wrap the CCommand class so our code looks the same on all engines. + */ +class CCommand +{ +public: + const char *ArgS() + { + return engine->Cmd_Args(); + } + int ArgC() + { + return engine->Cmd_Argc(); + } + + const char *Arg(int index) + { + return engine->Cmd_Argv(index); + } +}; + +#define CVAR_INTERFACE_VERSION VENGINE_CVAR_INTERFACE_VERSION +#endif + +/** + * Left 4 Dead engine removed these from IVEngineServer. + */ +#if SOURCE_ENGINE >= SE_LEFT4DEAD + +inline int IndexOfEdict(const edict_t *pEdict) +{ + return (int)(pEdict - gpGlobals->pEdicts); +} +inline edict_t *PEntityOfEntIndex(int iEntIndex) +{ + if (iEntIndex >= 0 && iEntIndex < gpGlobals->maxEntities) + { + return (edict_t *)(gpGlobals->pEdicts + iEntIndex); + } + return NULL; +} + +#else + +inline int IndexOfEdict(const edict_t *pEdict) +{ + return engine->IndexOfEdict(pEdict); +} +inline edict_t *PEntityOfEntIndex(int iEntIndex) +{ + return engine->PEntityOfEntIndex(iEntIndex); +} + +#endif + +#endif //_INCLUDE_SOURCE_ENGINE_WRAPPERS_