From c5af41d9830cc5c243eccb7f79e83d8a24c75e00 Mon Sep 17 00:00:00 2001 From: David Anderson Date: Wed, 26 Mar 2008 03:52:37 +0000 Subject: [PATCH] merged branch-1.1.0 back into trunk --HG-- extra : convert_revision : svn%3A39bc706e-5318-0410-9160-8a85361fbb7c/trunk%401960 --- extensions/regex/CRegEx.cpp | 124 +++++ extensions/regex/CRegEx.h | 28 ++ extensions/regex/Makefile.ep1 | 89 ++++ extensions/regex/Makefile.ep2 | 91 ++++ extensions/regex/Makefile.orig | 90 ++++ extensions/regex/extension.cpp | 188 +++++++ extensions/regex/extension.h | 133 +++++ extensions/regex/lib_linux/libpcre.a | Bin 0 -> 184250 bytes extensions/regex/lib_win/pcre.lib | Bin 0 -> 172300 bytes extensions/regex/msvc8/sdk.sln | 26 + extensions/regex/msvc8/sdk.vcproj | 422 ++++++++++++++++ extensions/regex/pcre.h | 305 +++++++++++ extensions/regex/sdk/smsdk_config.h | 79 +++ extensions/regex/sdk/smsdk_ext.cpp | 455 +++++++++++++++++ extensions/regex/sdk/smsdk_ext.h | 327 ++++++++++++ extensions/tf2/Makefile.ep1 | 88 ++++ extensions/tf2/Makefile.ep2 | 88 ++++ extensions/tf2/Makefile.orig | 88 ++++ extensions/tf2/RegNatives.cpp | 50 ++ extensions/tf2/RegNatives.h | 48 ++ extensions/tf2/extension.cpp | 229 +++++++++ extensions/tf2/extension.h | 130 +++++ extensions/tf2/msvc8/tf2.sln | 32 ++ extensions/tf2/msvc8/tf2.vcproj | 727 +++++++++++++++++++++++++++ extensions/tf2/natives.cpp | 159 ++++++ extensions/tf2/sdk/smsdk_config.h | 76 +++ extensions/tf2/sdk/smsdk_ext.cpp | 422 ++++++++++++++++ extensions/tf2/sdk/smsdk_ext.h | 310 ++++++++++++ extensions/tf2/sm-tf2.games.txt | 21 + extensions/tf2/svn_version.h | 42 ++ extensions/tf2/svn_version.tpl | 42 ++ extensions/tf2/tf2-test.sp | 55 ++ extensions/tf2/tf2.inc | 24 + extensions/tf2/version.rc | 104 ++++ plugins/adminmenu.sp | 23 +- plugins/adminmenu/dynamicmenu.sp | 520 +++++++++++++++++++ plugins/include/regex.inc | 141 ++++++ plugins/mapmanager.sp | 362 +++++++++++++ plugins/mapmanager/commands.sp | 398 +++++++++++++++ plugins/mapmanager/events.sp | 166 ++++++ plugins/mapmanager/functions.sp | 335 ++++++++++++ plugins/mapmanager/functions_menu.sp | 115 +++++ plugins/mapmanager/globals.sp | 104 ++++ plugins/mapmanager/menus.sp | 263 ++++++++++ plugins/mapmanager/timers.sp | 166 ++++++ plugins/mapmanager/votes.sp | 318 ++++++++++++ plugins/testsuite/goto_test.sp | 28 ++ sourcepawn/compiler/sc4.c | 15 +- sourcepawn/compiler/sc6.c | 1 + sourcepawn/jit/x86/jit_x86.cpp | 22 + sourcepawn/jit/x86/opcode_helpers.h | 27 +- sourcepawn/jit/x86/opcode_switch.inc | 5 + 52 files changed, 8062 insertions(+), 39 deletions(-) create mode 100644 extensions/regex/CRegEx.cpp create mode 100644 extensions/regex/CRegEx.h create mode 100644 extensions/regex/Makefile.ep1 create mode 100644 extensions/regex/Makefile.ep2 create mode 100644 extensions/regex/Makefile.orig create mode 100644 extensions/regex/extension.cpp create mode 100644 extensions/regex/extension.h create mode 100644 extensions/regex/lib_linux/libpcre.a create mode 100644 extensions/regex/lib_win/pcre.lib create mode 100644 extensions/regex/msvc8/sdk.sln create mode 100644 extensions/regex/msvc8/sdk.vcproj create mode 100644 extensions/regex/pcre.h create mode 100644 extensions/regex/sdk/smsdk_config.h create mode 100644 extensions/regex/sdk/smsdk_ext.cpp create mode 100644 extensions/regex/sdk/smsdk_ext.h create mode 100644 extensions/tf2/Makefile.ep1 create mode 100644 extensions/tf2/Makefile.ep2 create mode 100644 extensions/tf2/Makefile.orig create mode 100644 extensions/tf2/RegNatives.cpp create mode 100644 extensions/tf2/RegNatives.h create mode 100644 extensions/tf2/extension.cpp create mode 100644 extensions/tf2/extension.h create mode 100644 extensions/tf2/msvc8/tf2.sln create mode 100644 extensions/tf2/msvc8/tf2.vcproj create mode 100644 extensions/tf2/natives.cpp create mode 100644 extensions/tf2/sdk/smsdk_config.h create mode 100644 extensions/tf2/sdk/smsdk_ext.cpp create mode 100644 extensions/tf2/sdk/smsdk_ext.h create mode 100644 extensions/tf2/sm-tf2.games.txt create mode 100644 extensions/tf2/svn_version.h create mode 100644 extensions/tf2/svn_version.tpl create mode 100644 extensions/tf2/tf2-test.sp create mode 100644 extensions/tf2/tf2.inc create mode 100644 extensions/tf2/version.rc create mode 100644 plugins/adminmenu/dynamicmenu.sp create mode 100644 plugins/include/regex.inc create mode 100644 plugins/mapmanager.sp create mode 100644 plugins/mapmanager/commands.sp create mode 100644 plugins/mapmanager/events.sp create mode 100644 plugins/mapmanager/functions.sp create mode 100644 plugins/mapmanager/functions_menu.sp create mode 100644 plugins/mapmanager/globals.sp create mode 100644 plugins/mapmanager/menus.sp create mode 100644 plugins/mapmanager/timers.sp create mode 100644 plugins/mapmanager/votes.sp create mode 100644 plugins/testsuite/goto_test.sp diff --git a/extensions/regex/CRegEx.cpp b/extensions/regex/CRegEx.cpp new file mode 100644 index 00000000..6405a3a0 --- /dev/null +++ b/extensions/regex/CRegEx.cpp @@ -0,0 +1,124 @@ +#include "pcre.h" +#include "CRegEx.h" +#include +#include "extension.h" + +RegEx::RegEx() +{ + mErrorOffset = 0; + mError = NULL; + re = NULL; + mFree = true; + subject = NULL; + mSubStrings = 0; +} + +void RegEx::Clear () +{ + mErrorOffset = 0; + mError = NULL; + if (re) + pcre_free(re); + re = NULL; + mFree = true; + if (subject) + delete [] subject; + subject = NULL; + mSubStrings = 0; +} + +RegEx::~RegEx() +{ + Clear(); +} + +bool RegEx::isFree(bool set, bool val) +{ + if (set) + { + mFree = val; + return true; + } else { + return mFree; + } +} + +int RegEx::Compile(const char *pattern, int iFlags) +{ + if (!mFree) + Clear(); + + re = pcre_compile(pattern, iFlags, &mError, &mErrorOffset, NULL); + + if (re == NULL) + { + return 0; + } + + mFree = false; + + return 1; +} + +int RegEx::Match(const char *str) +{ + int rc = 0; + + if (mFree || re == NULL) + return -1; + + this->ClearMatch(); + + //save str + subject = new char[strlen(str)+1]; + strcpy(subject, str); + + rc = pcre_exec(re, NULL, subject, (int)strlen(subject), 0, 0, ovector, 30); + + if (rc < 0) + { + if (rc == PCRE_ERROR_NOMATCH) + { + return 0; + } else { + mErrorOffset = rc; + return -1; + } + } + + mSubStrings = rc; + + return 1; +} +void RegEx::ClearMatch() +{ + // Clears match results + mErrorOffset = 0; + mError = NULL; + if (subject) + delete [] subject; + subject = NULL; + mSubStrings = 0; +} + +const char *RegEx::GetSubstring(int s, char buffer[], int max) +{ + int i = 0; + if (s >= mSubStrings || s < 0) + return NULL; + + char *substr_a = subject + ovector[2*s]; + int substr_l = ovector[2*s+1] - ovector[2*s]; + + for (i = 0; i= max) + break; + buffer[i] = substr_a[i]; + } + + buffer[i] = '\0'; + + return buffer; +} + diff --git a/extensions/regex/CRegEx.h b/extensions/regex/CRegEx.h new file mode 100644 index 00000000..183a3039 --- /dev/null +++ b/extensions/regex/CRegEx.h @@ -0,0 +1,28 @@ +#ifndef _INCLUDE_CREGEX_H +#define _INCLUDE_CREGEX_H + +class RegEx +{ +public: + RegEx(); + ~RegEx(); + bool isFree(bool set=false, bool val=false); + void Clear(); + + int Compile(const char *pattern, int iFlags); + int Match(const char *str); + void ClearMatch(); + const char *GetSubstring(int s, char buffer[], int max); +public: + int mErrorOffset; + const char *mError; + int mSubStrings; +private: + pcre *re; + bool mFree; + int ovector[30]; + char *subject; +}; + +#endif //_INCLUDE_CREGEX_H + diff --git a/extensions/regex/Makefile.ep1 b/extensions/regex/Makefile.ep1 new file mode 100644 index 00000000..989ee7d4 --- /dev/null +++ b/extensions/regex/Makefile.ep1 @@ -0,0 +1,89 @@ +#(C)2004-2007 AlliedModders LLC +# +# This sample Makefile is designed for SourceMod extensions that +# are built against Metamod:Source 1.4.2, Episode 1 of the HL2SDK. +# + +SMSDK = ../.. +SRCDS = ~/srcds +SOURCEMM = ../../../sourcemm +HL2SDK = ../../../hl2sdk + +##################################### +### EDIT BELOW FOR OTHER PROJECTS ### +##################################### + +PROJECT = regex + +#Uncomment for SourceMM-enabled extensions +LINK_HL2 = $(HL2LIB)/tier1_i486.a vstdlib_i486.so tier0_i486.so + +OBJECTS = sdk/smsdk_ext.cpp extension.cpp CRegEx.cpp + +############################################## +### CONFIGURE ANY OTHER FLAGS/OPTIONS HERE ### +############################################## + +C_OPT_FLAGS = -O3 -funroll-loops -s -pipe -fno-strict-aliasing +C_DEBUG_FLAGS = -g -ggdb3 +CPP_GCC4_FLAGS = -fvisibility=hidden -fvisibility-inlines-hidden +CPP = gcc-4.1 + +HL2PUB = $(HL2SDK)/public +HL2LIB = $(HL2SDK)/linux_sdk + +LINK = $(LINK_HL2) -static-libgcc + +INCLUDE = -I. -I.. -Isdk -I$(HL2PUB) -I$(HL2PUB)/dlls -I$(HL2PUB)/engine -I$(HL2PUB)/tier0 -I$(HL2PUB)/tier1 \ + -I$(HL2PUB)/vstdlib -I$(HL2SDK)/tier1 -I$(SOURCEMM) -I$(SOURCEMM)/sourcehook -I$(SOURCEMM)/sourcemm \ + -I$(SMSDK)/public -I$(SMSDK)/public/sourcepawn -I$(SMSDK)/public/extensions \ + +CFLAGS = -D_LINUX -DNDEBUG -Dstricmp=strcasecmp -D_stricmp=strcasecmp -D_strnicmp=strncasecmp -Dstrnicmp=strncasecmp -D_snprintf=snprintf -D_vsnprintf=vsnprintf -D_alloca=alloca -Dstrcmpi=strcasecmp -Wall -Werror -fPIC -mfpmath=sse -msse -DSOURCEMOD_BUILD -DHAVE_STDINT_H -Wno-uninitialized +CPPFLAGS = -Wno-non-virtual-dtor -fno-exceptions -fno-rtti + +################################################ +### DO NOT EDIT BELOW HERE FOR MOST PROJECTS ### +################################################ + +ifeq "$(DEBUG)" "true" + BIN_DIR = Debug + CFLAGS += $(C_DEBUG_FLAGS) +else + BIN_DIR = Release + CFLAGS += $(C_OPT_FLAGS) +endif + + +GCC_VERSION := $(shell $(CPP) -dumpversion >&1 | cut -b1) +ifeq "$(GCC_VERSION)" "4" + CPPFLAGS += $(CPP_GCC4_FLAGS) +endif + +BINARY = $(PROJECT).ext.so + +OBJ_LINUX := $(OBJECTS:%.cpp=$(BIN_DIR)/%.o) + +$(BIN_DIR)/%.o: %.cpp + $(CPP) $(INCLUDE) $(CFLAGS) $(CPPFLAGS) -o $@ -c $< + +all: + mkdir -p $(BIN_DIR)/sdk + ln -sf $(SRCDS)/bin/vstdlib_i486.so vstdlib_i486.so + ln -sf $(SRCDS)/bin/tier0_i486.so tier0_i486.so + $(MAKE) -f Makefile.ep1 extension + +extension: $(OBJ_LINUX) + $(CPP) $(INCLUDE) $(CFLAGS) $(CPPFLAGS) $(OBJ_LINUX) $(LINK) -shared -ldl -lm -o$(BIN_DIR)/$(BINARY) + +debug: + $(MAKE) -f Makefile.ep1 all DEBUG=true + +default: all + +clean: + rm -rf Release/*.o + rm -rf Release/sdk/*.o + rm -rf Release/$(BINARY) + rm -rf Debug/*.o + rm -rf Debug/sdk/*.o + rm -rf Debug/$(BINARY) diff --git a/extensions/regex/Makefile.ep2 b/extensions/regex/Makefile.ep2 new file mode 100644 index 00000000..3582838c --- /dev/null +++ b/extensions/regex/Makefile.ep2 @@ -0,0 +1,91 @@ +#(C)2004-2007 AlliedModders LLC +# +# This sample Makefile is designed for SourceMod extensions that +# are built against Metamod:Source 1.6.0, Episode 2 of the HL2SDK. +# + +SMSDK = ../.. +SRCDS = ~/srcds +SOURCEMM = ../../../sourcemm +HL2SDK = ../../../hl2sdk-ob + +##################################### +### EDIT BELOW FOR OTHER PROJECTS ### +##################################### + +PROJECT = regex + +#Uncomment for SourceMM-enabled extensions +LINK_HL2 = $(HL2LIB)/tier1_i486.a vstdlib_i486.so tier0_i486.so + +OBJECTS = sdk/smsdk_ext.cpp extension.cpp CRegEx.cpp + +############################################## +### CONFIGURE ANY OTHER FLAGS/OPTIONS HERE ### +############################################## + +C_OPT_FLAGS = -O3 -funroll-loops -s -pipe -fno-strict-aliasing +C_DEBUG_FLAGS = -g -ggdb3 +CPP_GCC4_FLAGS = -fvisibility=hidden -fvisibility-inlines-hidden +CPP = gcc-4.1 + +HL2PUB = $(HL2SDK)/public +HL2LIB = $(HL2SDK)/linux_sdk + +LINK = $(LINK_HL2) -static-libgcc + +INCLUDE = -I. -I.. -Isdk -I$(HL2PUB) -I$(HL2PUB)/dlls -I$(HL2PUB)/engine -I$(HL2PUB)/tier0 -I$(HL2PUB)/tier1 \ + -I$(HL2PUB)/vstdlib -I$(HL2SDK)/tier1 -I$(SOURCEMM) -I$(SOURCEMM)/sourcehook -I$(SOURCEMM)/sourcemm \ + -I$(SMSDK)/public -I$(SMSDK)/public/sourcepawn -I$(SMSDK)/public/extensions \ + +CFLAGS = -D_LINUX -DNDEBUG -Dstricmp=strcasecmp -D_stricmp=strcasecmp -D_strnicmp=strncasecmp -Dstrnicmp=strncasecmp \ + -D_snprintf=snprintf -D_vsnprintf=vsnprintf -D_alloca=alloca -Dstrcmpi=strcasecmp -Wall -Werror -fPIC -mfpmath=sse \ + -msse -DSOURCEMOD_BUILD -DHAVE_STDINT_H -Wno-uninitialized -DORANGEBOX_BUILD +CPPFLAGS = -Wno-non-virtual-dtor -fno-exceptions -fno-rtti + +################################################ +### DO NOT EDIT BELOW HERE FOR MOST PROJECTS ### +################################################ + +ifeq "$(DEBUG)" "true" + BIN_DIR = Debug + CFLAGS += $(C_DEBUG_FLAGS) +else + BIN_DIR = Release + CFLAGS += $(C_OPT_FLAGS) +endif + + +GCC_VERSION := $(shell $(CPP) -dumpversion >&1 | cut -b1) +ifeq "$(GCC_VERSION)" "4" + CPPFLAGS += $(CPP_GCC4_FLAGS) +endif + +BINARY = $(PROJECT).ext.so + +OBJ_LINUX := $(OBJECTS:%.cpp=$(BIN_DIR)/%.o) + +$(BIN_DIR)/%.o: %.cpp + $(CPP) $(INCLUDE) $(CFLAGS) $(CPPFLAGS) -o $@ -c $< + +all: + mkdir -p $(BIN_DIR)/sdk + ln -sf $(SRCDS)/orangebox/bin/vstdlib_i486.so vstdlib_i486.so + ln -sf $(SRCDS)/orangebox/bin/tier0_i486.so tier0_i486.so + $(MAKE) -f Makefile.ep1 extension + +extension: $(OBJ_LINUX) + $(CPP) $(INCLUDE) $(CFLAGS) $(CPPFLAGS) $(OBJ_LINUX) $(LINK) -shared -ldl -lm -o$(BIN_DIR)/$(BINARY) + +debug: + $(MAKE) -f Makefile.ep1 all DEBUG=true + +default: all + +clean: + rm -rf Release/*.o + rm -rf Release/sdk/*.o + rm -rf Release/$(BINARY) + rm -rf Debug/*.o + rm -rf Debug/sdk/*.o + rm -rf Debug/$(BINARY) diff --git a/extensions/regex/Makefile.orig b/extensions/regex/Makefile.orig new file mode 100644 index 00000000..3dc19ef8 --- /dev/null +++ b/extensions/regex/Makefile.orig @@ -0,0 +1,90 @@ +#(C)2004-2007 AlliedModders LLC +# +# This sample Makefile is designed for SourceMod extensions that +# are built against Metamod:Source 1.6.0, Episode 1 of the HL2SDK. + +SMSDK = ../.. +SRCDS = ~/srcds +SOURCEMM = ../../../sourcemm-1.4.2 +HL2SDK = ../../../hl2sdk + +##################################### +### EDIT BELOW FOR OTHER PROJECTS ### +##################################### + +PROJECT = regex + +#Uncomment for SourceMM-enabled extensions +LINK_HL2 = $(HL2LIB)/tier1_i486.a vstdlib_i486.so tier0_i486.so + +OBJECTS = sdk/smsdk_ext.cpp extension.cpp CRegEx.cpp + +############################################## +### CONFIGURE ANY OTHER FLAGS/OPTIONS HERE ### +############################################## + +C_OPT_FLAGS = -O3 -funroll-loops -s -pipe -fno-strict-aliasing +C_DEBUG_FLAGS = -g -ggdb3 +CPP_GCC4_FLAGS = -fvisibility=hidden -fvisibility-inlines-hidden +CPP = gcc-4.1 + +HL2PUB = $(HL2SDK)/public +HL2LIB = $(HL2SDK)/linux_sdk + +LINK = $(LINK_HL2) -static-libgcc + +INCLUDE = -I. -I.. -Isdk -I$(HL2PUB) -I$(HL2PUB)/dlls -I$(HL2PUB)/engine -I$(HL2PUB)/tier0 -I$(HL2PUB)/tier1 \ + -I$(HL2PUB)/vstdlib -I$(HL2SDK)/tier1 -I$(SOURCEMM) -I$(SOURCEMM)/sourcehook -I$(SOURCEMM)/sourcemm \ + -I$(SMSDK)/public -I$(SMSDK)/public/sourcepawn -I$(SMSDK)/public/extensions \ + +CFLAGS = -D_LINUX -DNDEBUG -Dstricmp=strcasecmp -D_stricmp=strcasecmp -D_strnicmp=strncasecmp -Dstrnicmp=strncasecmp \ + -D_snprintf=snprintf -D_vsnprintf=vsnprintf -D_alloca=alloca -Dstrcmpi=strcasecmp -Wall -Werror -fPIC -mfpmath=sse \ + -msse -DSOURCEMOD_BUILD -DHAVE_STDINT_H -Wno-uninitialized +CPPFLAGS = -Wno-non-virtual-dtor -fno-exceptions -fno-rtti + +################################################ +### DO NOT EDIT BELOW HERE FOR MOST PROJECTS ### +################################################ + +ifeq "$(DEBUG)" "true" + BIN_DIR = Debug + CFLAGS += $(C_DEBUG_FLAGS) +else + BIN_DIR = Release + CFLAGS += $(C_OPT_FLAGS) +endif + + +GCC_VERSION := $(shell $(CPP) -dumpversion >&1 | cut -b1) +ifeq "$(GCC_VERSION)" "4" + CPPFLAGS += $(CPP_GCC4_FLAGS) +endif + +BINARY = $(PROJECT).ext.so + +OBJ_LINUX := $(OBJECTS:%.cpp=$(BIN_DIR)/%.o) + +$(BIN_DIR)/%.o: %.cpp + $(CPP) $(INCLUDE) $(CFLAGS) $(CPPFLAGS) -o $@ -c $< + +all: + mkdir -p $(BIN_DIR)/sdk + ln -sf $(SRCDS)/bin/vstdlib_i486.so vstdlib_i486.so + ln -sf $(SRCDS)/bin/tier0_i486.so tier0_i486.so + $(MAKE) -f Makefile.orig extension + +extension: $(OBJ_LINUX) + $(CPP) $(INCLUDE) $(CFLAGS) $(CPPFLAGS) $(OBJ_LINUX) $(LINK) -shared -ldl -lm -o$(BIN_DIR)/$(BINARY) + +debug: + $(MAKE) -f Makefile.orig all DEBUG=true + +default: all + +clean: + rm -rf Release/*.o + rm -rf Release/sdk/*.o + rm -rf Release/$(BINARY) + rm -rf Debug/*.o + rm -rf Debug/sdk/*.o + rm -rf Debug/$(BINARY) diff --git a/extensions/regex/extension.cpp b/extensions/regex/extension.cpp new file mode 100644 index 00000000..e3519172 --- /dev/null +++ b/extensions/regex/extension.cpp @@ -0,0 +1,188 @@ +/** + * vim: set ts=4 : + * ============================================================================= + * SourceMod Sample Extension + * Copyright (C) 2004-2007 AlliedModders LLC. All rights reserved. + * ============================================================================= + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id$ + */ + +#include "extension.h" + +#include +#include "pcre.h" +#include "CRegEx.h" +using namespace SourceHook; + +/** + * @file extension.cpp + * @brief Implement extension code here. + */ + +RegexExtension g_RegexExtension; /**< Global singleton for extension's main interface */ + +SMEXT_LINK(&g_RegexExtension); + +RegexHandler g_RegexHandler; +HandleType_t g_RegexHandle=0; + + + +bool RegexExtension::SDK_OnLoad(char *error, size_t err_max, bool late) +{ + g_pShareSys->AddNatives(myself,regex_natives); + g_RegexHandle = g_pHandleSys->CreateType("Regex", &g_RegexHandler, 0, NULL, NULL, myself->GetIdentity(), NULL); + return true; +} + +void RegexExtension::SDK_OnUnload() +{ + g_pHandleSys->RemoveType(g_RegexHandle, myself->GetIdentity()); + +} + +static cell_t CompileRegex(IPluginContext *pCtx, const cell_t *params) +{ + char *regex; + pCtx->LocalToString(params[1], ®ex); + + RegEx *x = new RegEx(); + + if (x->Compile(regex, params[2]) == 0) + { + cell_t *eOff; + pCtx->LocalToPhysAddr(params[5], &eOff); + const char *err = x->mError; + *eOff = x->mErrorOffset; + pCtx->StringToLocal(params[3], params[4], err ? err:"unknown"); + return 0; + } + + return g_pHandleSys->CreateHandle(g_RegexHandle, (void*)x, pCtx->GetIdentity(), myself->GetIdentity(), NULL); +} + + +static cell_t MatchRegex(IPluginContext *pCtx, const cell_t *params) +{ + Handle_t hndl = static_cast(params[1]); + HandleError err; + HandleSecurity sec; + sec.pOwner = NULL; + sec.pIdentity = myself->GetIdentity(); + + RegEx *x; + + if ((err=g_pHandleSys->ReadHandle(hndl, g_RegexHandle, &sec, (void **)&x)) != HandleError_None) + { + return pCtx->ThrowNativeError("Invalid file handle %x (error %d)", hndl, err); + } + + if (!x) + { + pCtx->ThrowNativeError("Regex data not found\n"); + + return 0; + } + + char *str; + pCtx->LocalToString(params[2], &str); + + int e = x->Match(str); + + if (e == -1) + { + /* there was a match error. move on. */ + cell_t *res; + pCtx->LocalToPhysAddr(params[3], &res); + *res = x->mErrorOffset; + /* only clear the match results, since the regex object + may still be referenced later */ + x->ClearMatch(); + + return -1; + } + else if (e == 0) + { + /* only clear the match results, since the regex object + may still be referenced later */ + x->ClearMatch(); + + return 0; + } + else + { + return x->mSubStrings; + } +} + +static cell_t GetRegexSubString(IPluginContext *pCtx, const cell_t *params) +{ + Handle_t hndl=static_cast(params[1]); + HandleError err; + HandleSecurity sec; + sec.pOwner=NULL; + sec.pIdentity=myself->GetIdentity(); + + RegEx *x; + + if ((err=g_pHandleSys->ReadHandle(hndl, g_RegexHandle, &sec, (void **)&x)) != HandleError_None) + { + return pCtx->ThrowNativeError("Invalid file handle %x (error %d)", hndl, err); + } + + if (!x) + { + pCtx->ThrowNativeError("Regex data not found\n"); + return 0; + } + + static char buffer[4096]; + const char *ret=x->GetSubstring(params[2], buffer, sizeof(buffer)); + + if(!ret) + { + return 0; + } + + pCtx->StringToLocalUTF8(params[3], params[4], ret, NULL); + + return 1; +} + +void RegexHandler::OnHandleDestroy(HandleType_t type, void *object) +{ + RegEx *x = (RegEx *)object; + + x->Clear(); + delete x; +} + +const sp_nativeinfo_t regex_natives[] = +{ + {"GetRegexSubString", GetRegexSubString}, + {"MatchRegex", MatchRegex}, + {"CompileRegex", CompileRegex}, + {NULL, NULL}, +}; diff --git a/extensions/regex/extension.h b/extensions/regex/extension.h new file mode 100644 index 00000000..0c4beff2 --- /dev/null +++ b/extensions/regex/extension.h @@ -0,0 +1,133 @@ +/** + * vim: set ts=4 : + * ============================================================================= + * SourceMod Sample Extension + * Copyright (C) 2004-2007 AlliedModders LLC. All rights reserved. + * ============================================================================= + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id$ + */ + +#ifndef _INCLUDE_SOURCEMOD_EXTENSION_PROPER_H_ +#define _INCLUDE_SOURCEMOD_EXTENSION_PROPER_H_ + +/** + * @file extension.h + * @brief Sample extension code header. + */ + +#include "smsdk_ext.h" + + +/** + * @brief Sample implementation of the SDK Extension. + * Note: Uncomment one of the pre-defined virtual functions in order to use it. + */ +class RegexExtension : public SDKExtension +{ +public: + /** + * @brief This is called after the initial loading sequence has been processed. + * + * @param error Error message buffer. + * @param maxlength Size of error message buffer. + * @param late Whether or not the module was loaded after map load. + * @return True to succeed loading, false to fail. + */ + virtual bool SDK_OnLoad(char *error, size_t maxlength, bool late); + + /** + * @brief This is called right before the extension is unloaded. + */ + virtual void SDK_OnUnload(); + + /** + * @brief This is called once all known extensions have been loaded. + * Note: It is is a good idea to add natives here, if any are provided. + */ + //virtual void SDK_OnAllLoaded(); + + /** + * @brief Called when the pause state is changed. + */ + //virtual void SDK_OnPauseChange(bool paused); + + /** + * @brief this is called when Core wants to know if your extension is working. + * + * @param error Error message buffer. + * @param maxlength Size of error message buffer. + * @return True if working, false otherwise. + */ + //virtual bool QueryRunning(char *error, size_t maxlength); +public: +#if defined SMEXT_CONF_METAMOD + /** + * @brief Called when Metamod is attached, before the extension version is called. + * + * @param error Error buffer. + * @param maxlength Maximum size of error buffer. + * @param late Whether or not Metamod considers this a late load. + * @return True to succeed, false to fail. + */ + //virtual bool SDK_OnMetamodLoad(ISmmAPI *ismm, char *error, size_t maxlength, bool late); + + /** + * @brief Called when Metamod is detaching, after the extension version is called. + * NOTE: By default this is blocked unless sent from SourceMod. + * + * @param error Error buffer. + * @param maxlength Maximum size of error buffer. + * @return True to succeed, false to fail. + */ + //virtual bool SDK_OnMetamodUnload(char *error, size_t maxlength); + + /** + * @brief Called when Metamod's pause state is changing. + * NOTE: By default this is blocked unless sent from SourceMod. + * + * @param paused Pause state being set. + * @param error Error buffer. + * @param maxlength Maximum size of error buffer. + * @return True to succeed, false to fail. + */ + //virtual bool SDK_OnMetamodPauseChange(bool paused, char *error, size_t maxlength); +#endif +}; + + +class RegexHandler: public IHandleTypeDispatch +{ +public: + void OnHandleDestroy(HandleType_t type, void *object); +}; + +extern RegexHandler g_RegexHandler; +extern HandleType_t g_RegexHandle; + + +// Natives +extern const sp_nativeinfo_t regex_natives[]; + +#endif // _INCLUDE_SOURCEMOD_EXTENSION_PROPER_H_ diff --git a/extensions/regex/lib_linux/libpcre.a b/extensions/regex/lib_linux/libpcre.a new file mode 100644 index 0000000000000000000000000000000000000000..e8f6fbdefa0da9a25a9f12394c19c3fd43057330 GIT binary patch literal 184250 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 b=(rid9dC2{+aC0Fuo^dON{K!0&vgC=cwHgD literal 0 HcmV?d00001 diff --git a/extensions/regex/lib_win/pcre.lib b/extensions/regex/lib_win/pcre.lib new file mode 100644 index 0000000000000000000000000000000000000000..0fb8eeaa1cdc8147916540d438b7686bebf90e01 GIT binary patch literal 172300 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

IiOr1R3bq6 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

b)ifPgMt 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 literal 0 HcmV?d00001 diff --git a/extensions/regex/msvc8/sdk.sln b/extensions/regex/msvc8/sdk.sln new file mode 100644 index 00000000..90618048 --- /dev/null +++ b/extensions/regex/msvc8/sdk.sln @@ -0,0 +1,26 @@ + +Microsoft Visual Studio Solution File, Format Version 9.00 +# Visual C++ Express 2005 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "regex", "sdk.vcproj", "{B3E797CF-4E77-4C9D-B8A8-7589B6902206}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug - Episode 1|Win32 = Debug - Episode 1|Win32 + Debug - Orange Box|Win32 = Debug - Orange Box|Win32 + Release - Episode 1|Win32 = Release - Episode 1|Win32 + Release - Orange Box|Win32 = Release - Orange Box|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {B3E797CF-4E77-4C9D-B8A8-7589B6902206}.Debug - Episode 1|Win32.ActiveCfg = Debug|Win32 + {B3E797CF-4E77-4C9D-B8A8-7589B6902206}.Debug - Episode 1|Win32.Build.0 = Debug|Win32 + {B3E797CF-4E77-4C9D-B8A8-7589B6902206}.Debug - Orange Box|Win32.ActiveCfg = Debug - Orange Box|Win32 + {B3E797CF-4E77-4C9D-B8A8-7589B6902206}.Debug - Orange Box|Win32.Build.0 = Debug - Orange Box|Win32 + {B3E797CF-4E77-4C9D-B8A8-7589B6902206}.Release - Episode 1|Win32.ActiveCfg = Release|Win32 + {B3E797CF-4E77-4C9D-B8A8-7589B6902206}.Release - Episode 1|Win32.Build.0 = Release|Win32 + {B3E797CF-4E77-4C9D-B8A8-7589B6902206}.Release - Orange Box|Win32.ActiveCfg = Release - Orange Box|Win32 + {B3E797CF-4E77-4C9D-B8A8-7589B6902206}.Release - Orange Box|Win32.Build.0 = Release - Orange Box|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/extensions/regex/msvc8/sdk.vcproj b/extensions/regex/msvc8/sdk.vcproj new file mode 100644 index 00000000..ec06e64e --- /dev/null +++ b/extensions/regex/msvc8/sdk.vcproj @@ -0,0 +1,422 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/extensions/regex/pcre.h b/extensions/regex/pcre.h new file mode 100644 index 00000000..77720e1c --- /dev/null +++ b/extensions/regex/pcre.h @@ -0,0 +1,305 @@ +/************************************************* +* Perl-Compatible Regular Expressions * +*************************************************/ + +/* This is the public header file for the PCRE library, to be #included by +applications that call the PCRE functions. + + Copyright (c) 1997-2007 University of Cambridge + +----------------------------------------------------------------------------- +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + * Neither the name of the University of Cambridge nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +----------------------------------------------------------------------------- +*/ + +#ifndef _PCRE_H +#define _PCRE_H + +/* The current PCRE version information. */ + +#define PCRE_MAJOR 7 +#define PCRE_MINOR 4 +#define PCRE_PRERELEASE +#define PCRE_DATE 2007-09-21 + +/* When an application links to a PCRE DLL in Windows, the symbols that are +imported have to be identified as such. When building PCRE, the appropriate +export setting is defined in pcre_internal.h, which includes this file. So we +don't change existing definitions of PCRE_EXP_DECL and PCRECPP_EXP_DECL. */ + +#define PCRE_STATIC + +#if defined(_WIN32) && !defined(PCRE_STATIC) +# ifndef PCRE_EXP_DECL +# define PCRE_EXP_DECL extern __declspec(dllimport) +# endif +# ifdef __cplusplus +# ifndef PCRECPP_EXP_DECL +# define PCRECPP_EXP_DECL extern __declspec(dllimport) +# endif +# ifndef PCRECPP_EXP_DEFN +# define PCRECPP_EXP_DEFN __declspec(dllimport) +# endif +# endif +#endif + +/* By default, we use the standard "extern" declarations. */ + +#ifndef PCRE_EXP_DECL +# ifdef __cplusplus +# define PCRE_EXP_DECL extern "C" +# else +# define PCRE_EXP_DECL extern +# endif +#endif + +#ifdef __cplusplus +# ifndef PCRECPP_EXP_DECL +# define PCRECPP_EXP_DECL extern +# endif +# ifndef PCRECPP_EXP_DEFN +# define PCRECPP_EXP_DEFN +# endif +#endif + +/* Have to include stdlib.h in order to ensure that size_t is defined; +it is needed here for malloc. */ + +#include + +/* Allow for C++ users */ + +#ifdef __cplusplus +extern "C" { +#endif + +/* Options */ + +#define PCRE_CASELESS 0x00000001 +#define PCRE_MULTILINE 0x00000002 +#define PCRE_DOTALL 0x00000004 +#define PCRE_EXTENDED 0x00000008 +#define PCRE_ANCHORED 0x00000010 +#define PCRE_DOLLAR_ENDONLY 0x00000020 +#define PCRE_EXTRA 0x00000040 +#define PCRE_NOTBOL 0x00000080 +#define PCRE_NOTEOL 0x00000100 +#define PCRE_UNGREEDY 0x00000200 +#define PCRE_NOTEMPTY 0x00000400 +#define PCRE_UTF8 0x00000800 +#define PCRE_NO_AUTO_CAPTURE 0x00001000 +#define PCRE_NO_UTF8_CHECK 0x00002000 +#define PCRE_AUTO_CALLOUT 0x00004000 +#define PCRE_PARTIAL 0x00008000 +#define PCRE_DFA_SHORTEST 0x00010000 +#define PCRE_DFA_RESTART 0x00020000 +#define PCRE_FIRSTLINE 0x00040000 +#define PCRE_DUPNAMES 0x00080000 +#define PCRE_NEWLINE_CR 0x00100000 +#define PCRE_NEWLINE_LF 0x00200000 +#define PCRE_NEWLINE_CRLF 0x00300000 +#define PCRE_NEWLINE_ANY 0x00400000 +#define PCRE_NEWLINE_ANYCRLF 0x00500000 +#define PCRE_BSR_ANYCRLF 0x00800000 +#define PCRE_BSR_UNICODE 0x01000000 + +/* Exec-time and get/set-time error codes */ + +#define PCRE_ERROR_NOMATCH (-1) +#define PCRE_ERROR_NULL (-2) +#define PCRE_ERROR_BADOPTION (-3) +#define PCRE_ERROR_BADMAGIC (-4) +#define PCRE_ERROR_UNKNOWN_OPCODE (-5) +#define PCRE_ERROR_UNKNOWN_NODE (-5) /* For backward compatibility */ +#define PCRE_ERROR_NOMEMORY (-6) +#define PCRE_ERROR_NOSUBSTRING (-7) +#define PCRE_ERROR_MATCHLIMIT (-8) +#define PCRE_ERROR_CALLOUT (-9) /* Never used by PCRE itself */ +#define PCRE_ERROR_BADUTF8 (-10) +#define PCRE_ERROR_BADUTF8_OFFSET (-11) +#define PCRE_ERROR_PARTIAL (-12) +#define PCRE_ERROR_BADPARTIAL (-13) +#define PCRE_ERROR_INTERNAL (-14) +#define PCRE_ERROR_BADCOUNT (-15) +#define PCRE_ERROR_DFA_UITEM (-16) +#define PCRE_ERROR_DFA_UCOND (-17) +#define PCRE_ERROR_DFA_UMLIMIT (-18) +#define PCRE_ERROR_DFA_WSSIZE (-19) +#define PCRE_ERROR_DFA_RECURSE (-20) +#define PCRE_ERROR_RECURSIONLIMIT (-21) +#define PCRE_ERROR_NULLWSLIMIT (-22) /* No longer actually used */ +#define PCRE_ERROR_BADNEWLINE (-23) + +/* Request types for pcre_fullinfo() */ + +#define PCRE_INFO_OPTIONS 0 +#define PCRE_INFO_SIZE 1 +#define PCRE_INFO_CAPTURECOUNT 2 +#define PCRE_INFO_BACKREFMAX 3 +#define PCRE_INFO_FIRSTBYTE 4 +#define PCRE_INFO_FIRSTCHAR 4 /* For backwards compatibility */ +#define PCRE_INFO_FIRSTTABLE 5 +#define PCRE_INFO_LASTLITERAL 6 +#define PCRE_INFO_NAMEENTRYSIZE 7 +#define PCRE_INFO_NAMECOUNT 8 +#define PCRE_INFO_NAMETABLE 9 +#define PCRE_INFO_STUDYSIZE 10 +#define PCRE_INFO_DEFAULT_TABLES 11 +#define PCRE_INFO_OKPARTIAL 12 +#define PCRE_INFO_JCHANGED 13 +#define PCRE_INFO_HASCRORLF 14 + +/* Request types for pcre_config(). Do not re-arrange, in order to remain +compatible. */ + +#define PCRE_CONFIG_UTF8 0 +#define PCRE_CONFIG_NEWLINE 1 +#define PCRE_CONFIG_LINK_SIZE 2 +#define PCRE_CONFIG_POSIX_MALLOC_THRESHOLD 3 +#define PCRE_CONFIG_MATCH_LIMIT 4 +#define PCRE_CONFIG_STACKRECURSE 5 +#define PCRE_CONFIG_UNICODE_PROPERTIES 6 +#define PCRE_CONFIG_MATCH_LIMIT_RECURSION 7 +#define PCRE_CONFIG_BSR 8 + +/* Bit flags for the pcre_extra structure. Do not re-arrange or redefine +these bits, just add new ones on the end, in order to remain compatible. */ + +#define PCRE_EXTRA_STUDY_DATA 0x0001 +#define PCRE_EXTRA_MATCH_LIMIT 0x0002 +#define PCRE_EXTRA_CALLOUT_DATA 0x0004 +#define PCRE_EXTRA_TABLES 0x0008 +#define PCRE_EXTRA_MATCH_LIMIT_RECURSION 0x0010 + +/* Types */ + +struct real_pcre; /* declaration; the definition is private */ +typedef struct real_pcre pcre; + +/* When PCRE is compiled as a C++ library, the subject pointer type can be +replaced with a custom type. For conventional use, the public interface is a +const char *. */ + +#ifndef PCRE_SPTR +#define PCRE_SPTR const char * +#endif + +/* The structure for passing additional data to pcre_exec(). This is defined in +such as way as to be extensible. Always add new fields at the end, in order to +remain compatible. */ + +typedef struct pcre_extra { + unsigned long int flags; /* Bits for which fields are set */ + void *study_data; /* Opaque data from pcre_study() */ + unsigned long int match_limit; /* Maximum number of calls to match() */ + void *callout_data; /* Data passed back in callouts */ + const unsigned char *tables; /* Pointer to character tables */ + unsigned long int match_limit_recursion; /* Max recursive calls to match() */ +} pcre_extra; + +/* The structure for passing out data via the pcre_callout_function. We use a +structure so that new fields can be added on the end in future versions, +without changing the API of the function, thereby allowing old clients to work +without modification. */ + +typedef struct pcre_callout_block { + int version; /* Identifies version of block */ + /* ------------------------ Version 0 ------------------------------- */ + int callout_number; /* Number compiled into pattern */ + int *offset_vector; /* The offset vector */ + PCRE_SPTR subject; /* The subject being matched */ + int subject_length; /* The length of the subject */ + int start_match; /* Offset to start of this match attempt */ + int current_position; /* Where we currently are in the subject */ + int capture_top; /* Max current capture */ + int capture_last; /* Most recently closed capture */ + void *callout_data; /* Data passed in with the call */ + /* ------------------- Added for Version 1 -------------------------- */ + int pattern_position; /* Offset to next item in the pattern */ + int next_item_length; /* Length of next item in the pattern */ + /* ------------------------------------------------------------------ */ +} pcre_callout_block; + +/* Indirection for store get and free functions. These can be set to +alternative malloc/free functions if required. Special ones are used in the +non-recursive case for "frames". There is also an optional callout function +that is triggered by the (?) regex item. For Virtual Pascal, these definitions +have to take another form. */ + +#ifndef VPCOMPAT +PCRE_EXP_DECL void *(*pcre_malloc)(size_t); +PCRE_EXP_DECL void (*pcre_free)(void *); +PCRE_EXP_DECL void *(*pcre_stack_malloc)(size_t); +PCRE_EXP_DECL void (*pcre_stack_free)(void *); +PCRE_EXP_DECL int (*pcre_callout)(pcre_callout_block *); +#else /* VPCOMPAT */ +PCRE_EXP_DECL void *pcre_malloc(size_t); +PCRE_EXP_DECL void pcre_free(void *); +PCRE_EXP_DECL void *pcre_stack_malloc(size_t); +PCRE_EXP_DECL void pcre_stack_free(void *); +PCRE_EXP_DECL int pcre_callout(pcre_callout_block *); +#endif /* VPCOMPAT */ + +/* Exported PCRE functions */ + +PCRE_EXP_DECL pcre *pcre_compile(const char *, int, const char **, int *, + const unsigned char *); +PCRE_EXP_DECL pcre *pcre_compile2(const char *, int, int *, const char **, + int *, const unsigned char *); +PCRE_EXP_DECL int pcre_config(int, void *); +PCRE_EXP_DECL int pcre_copy_named_substring(const pcre *, const char *, + int *, int, const char *, char *, int); +PCRE_EXP_DECL int pcre_copy_substring(const char *, int *, int, int, char *, + int); +PCRE_EXP_DECL int pcre_dfa_exec(const pcre *, const pcre_extra *, + const char *, int, int, int, int *, int , int *, int); +PCRE_EXP_DECL int pcre_exec(const pcre *, const pcre_extra *, PCRE_SPTR, + int, int, int, int *, int); +PCRE_EXP_DECL void pcre_free_substring(const char *); +PCRE_EXP_DECL void pcre_free_substring_list(const char **); +PCRE_EXP_DECL int pcre_fullinfo(const pcre *, const pcre_extra *, int, + void *); +PCRE_EXP_DECL int pcre_get_named_substring(const pcre *, const char *, + int *, int, const char *, const char **); +PCRE_EXP_DECL int pcre_get_stringnumber(const pcre *, const char *); +PCRE_EXP_DECL int pcre_get_stringtable_entries(const pcre *, const char *, + char **, char **); +PCRE_EXP_DECL int pcre_get_substring(const char *, int *, int, int, + const char **); +PCRE_EXP_DECL int pcre_get_substring_list(const char *, int *, int, + const char ***); +PCRE_EXP_DECL int pcre_info(const pcre *, int *, int *); +PCRE_EXP_DECL const unsigned char *pcre_maketables(void); +PCRE_EXP_DECL int pcre_refcount(pcre *, int); +PCRE_EXP_DECL pcre_extra *pcre_study(const pcre *, int, const char **); +PCRE_EXP_DECL const char *pcre_version(void); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* End of pcre.h */ diff --git a/extensions/regex/sdk/smsdk_config.h b/extensions/regex/sdk/smsdk_config.h new file mode 100644 index 00000000..0889a8f5 --- /dev/null +++ b/extensions/regex/sdk/smsdk_config.h @@ -0,0 +1,79 @@ +/** + * vim: set ts=4 : + * ============================================================================= + * SourceMod Sample Extension + * Copyright (C) 2004-2007 AlliedModders LLC. All rights reserved. + * ============================================================================= + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id$ + */ + +#ifndef _INCLUDE_SOURCEMOD_EXTENSION_CONFIG_H_ +#define _INCLUDE_SOURCEMOD_EXTENSION_CONFIG_H_ + +/** + * @file smsdk_config.h + * @brief Contains macros for configuring basic extension information. + */ + +/* Basic information exposed publicly */ +#define SMEXT_CONF_NAME "Regex" +#define SMEXT_CONF_DESCRIPTION "Provides regex natives for plugins" +#define SMEXT_CONF_VERSION "SVN_FULL_VERSION" +#define SMEXT_CONF_AUTHOR "AlliedModders LLC" +#define SMEXT_CONF_URL "http://www.sourcemod.net/" +#define SMEXT_CONF_LOGTAG "REGEX" +#define SMEXT_CONF_LICENSE "GPL" +#define SMEXT_CONF_DATESTRING __DATE__ + +/** + * @brief Exposes plugin's main interface. + */ +#define SMEXT_LINK(name) SDKExtension *g_pExtensionIface = name; + +/** + * @brief Sets whether or not this plugin required Metamod. + * NOTE: Uncomment to enable, comment to disable. + */ +//#define SMEXT_CONF_METAMOD + +/** Enable interfaces you want to use here by uncommenting lines */ +//#define SMEXT_ENABLE_FORWARDSYS +#define SMEXT_ENABLE_HANDLESYS +//#define SMEXT_ENABLE_PLAYERHELPERS +//#define SMEXT_ENABLE_DBMANAGER +//#define SMEXT_ENABLE_GAMECONF +//#define SMEXT_ENABLE_MEMUTILS +//#define SMEXT_ENABLE_GAMEHELPERS +//#define SMEXT_ENABLE_TIMERSYS +//#define SMEXT_ENABLE_THREADER +//#define SMEXT_ENABLE_LIBSYS +//#define SMEXT_ENABLE_MENUS +//#define SMEXT_ENABLE_ADTFACTORY +//#define SMEXT_ENABLE_PLUGINSYS +//#define SMEXT_ENABLE_ADMINSYS +//#define SMEXT_ENABLE_TEXTPARSERS +#define SMEXT_ENABLE_USERMSGS + +#endif // _INCLUDE_SOURCEMOD_EXTENSION_CONFIG_H_ diff --git a/extensions/regex/sdk/smsdk_ext.cpp b/extensions/regex/sdk/smsdk_ext.cpp new file mode 100644 index 00000000..0c728f00 --- /dev/null +++ b/extensions/regex/sdk/smsdk_ext.cpp @@ -0,0 +1,455 @@ +/** + * vim: set ts=4 : + * ============================================================================= + * SourceMod Base Extension Code + * Copyright (C) 2004-2007 AlliedModders LLC. All rights reserved. + * ============================================================================= + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id$ + */ + +#include +#include +#include "smsdk_ext.h" + +/** + * @file smsdk_ext.cpp + * @brief Contains wrappers for making Extensions easier to write. + */ + +IExtension *myself = NULL; /**< Ourself */ +IShareSys *g_pShareSys = NULL; /**< Share system */ +IShareSys *sharesys = NULL; /**< Share system */ +ISourceMod *g_pSM = NULL; /**< SourceMod helpers */ +ISourceMod *smutils = NULL; /**< SourceMod helpers */ + +#if defined SMEXT_ENABLE_FORWARDSYS +IForwardManager *g_pForwards = NULL; /**< Forward system */ +IForwardManager *forwards = NULL; /**< Forward system */ +#endif +#if defined SMEXT_ENABLE_HANDLESYS +IHandleSys *g_pHandleSys = NULL; /**< Handle system */ +IHandleSys *handlesys = NULL; /**< Handle system */ +#endif +#if defined SMEXT_ENABLE_PLAYERHELPERS +IPlayerManager *playerhelpers = NULL; /**< Player helpers */ +#endif //SMEXT_ENABLE_PLAYERHELPERS +#if defined SMEXT_ENABLE_DBMANAGER +IDBManager *dbi = NULL; /**< DB Manager */ +#endif //SMEXT_ENABLE_DBMANAGER +#if defined SMEXT_ENABLE_GAMECONF +IGameConfigManager *gameconfs = NULL; /**< Game config manager */ +#endif //SMEXT_ENABLE_DBMANAGER +#if defined SMEXT_ENABLE_MEMUTILS +IMemoryUtils *memutils = NULL; +#endif //SMEXT_ENABLE_DBMANAGER +#if defined SMEXT_ENABLE_GAMEHELPERS +IGameHelpers *gamehelpers = NULL; +#endif +#if defined SMEXT_ENABLE_TIMERSYS +ITimerSystem *timersys = NULL; +#endif +#if defined SMEXT_ENABLE_ADTFACTORY +IADTFactory *adtfactory = NULL; +#endif +#if defined SMEXT_ENABLE_THREADER +IThreader *threader = NULL; +#endif +#if defined SMEXT_ENABLE_LIBSYS +ILibrarySys *libsys = NULL; +#endif +#if defined SMEXT_ENABLE_PLUGINSYS +SourceMod::IPluginManager *plsys; +#endif +#if defined SMEXT_ENABLE_MENUS +IMenuManager *menus = NULL; +#endif +#if defined SMEXT_ENABLE_ADMINSYS +IAdminSystem *adminsys = NULL; +#endif +#if defined SMEXT_ENABLE_TEXTPARSERS +ITextParsers *textparsers = NULL; +#endif +#if defined SMEXT_ENABLE_USERMSGS +IUserMessages *usermsgs = NULL; +#endif + +/** Exports the main interface */ +PLATFORM_EXTERN_C IExtensionInterface *GetSMExtAPI() +{ + return g_pExtensionIface; +} + +SDKExtension::SDKExtension() +{ +#if defined SMEXT_CONF_METAMOD + m_SourceMMLoaded = false; + m_WeAreUnloaded = false; + m_WeGotPauseChange = false; +#endif +} + +bool SDKExtension::OnExtensionLoad(IExtension *me, IShareSys *sys, char *error, size_t maxlength, bool late) +{ + g_pShareSys = sharesys = sys; + myself = me; + +#if defined SMEXT_CONF_METAMOD + m_WeAreUnloaded = true; + + if (!m_SourceMMLoaded) + { + if (error) + { + snprintf(error, maxlength, "Metamod attach failed"); + } + return false; + } +#endif + SM_GET_IFACE(SOURCEMOD, g_pSM); + smutils = g_pSM; +#if defined SMEXT_ENABLE_HANDLESYS + SM_GET_IFACE(HANDLESYSTEM, g_pHandleSys); + handlesys = g_pHandleSys; +#endif +#if defined SMEXT_ENABLE_FORWARDSYS + SM_GET_IFACE(FORWARDMANAGER, g_pForwards); + forwards = g_pForwards; +#endif +#if defined SMEXT_ENABLE_PLAYERHELPERS + SM_GET_IFACE(PLAYERMANAGER, playerhelpers); +#endif +#if defined SMEXT_ENABLE_DBMANAGER + SM_GET_IFACE(DBI, dbi); +#endif +#if defined SMEXT_ENABLE_GAMECONF + SM_GET_IFACE(GAMECONFIG, gameconfs); +#endif +#if defined SMEXT_ENABLE_MEMUTILS + SM_GET_IFACE(MEMORYUTILS, memutils); +#endif +#if defined SMEXT_ENABLE_GAMEHELPERS + SM_GET_IFACE(GAMEHELPERS, gamehelpers); +#endif +#if defined SMEXT_ENABLE_TIMERSYS + SM_GET_IFACE(TIMERSYS, timersys); +#endif +#if defined SMEXT_ENABLE_ADTFACTORY + SM_GET_IFACE(ADTFACTORY, adtfactory); +#endif +#if defined SMEXT_ENABLE_THREADER + SM_GET_IFACE(THREADER, threader); +#endif +#if defined SMEXT_ENABLE_LIBSYS + SM_GET_IFACE(LIBRARYSYS, libsys); +#endif +#if defined SMEXT_ENABLE_PLUGINSYS + SM_GET_IFACE(PLUGINSYSTEM, plsys); +#endif +#if defined SMEXT_ENABLE_MENUS + SM_GET_IFACE(MENUMANAGER, menus); +#endif +#if defined SMEXT_ENABLE_ADMINSYS + SM_GET_IFACE(ADMINSYS, adminsys); +#endif +#if defined SMEXT_ENABLE_TEXTPARSERS + SM_GET_IFACE(TEXTPARSERS, textparsers); +#endif +#if defined SMEXT_ENABLE_USERMSGS + SM_GET_IFACE(USERMSGS, usermsgs); +#endif + + if (SDK_OnLoad(error, maxlength, late)) + { +#if defined SMEXT_CONF_METAMOD + m_WeAreUnloaded = true; +#endif + return true; + } + + return false; +} + +bool SDKExtension::IsMetamodExtension() +{ +#if defined SMEXT_CONF_METAMOD + return true; +#else + return false; +#endif +} + +void SDKExtension::OnExtensionPauseChange(bool state) +{ +#if defined SMEXT_CONF_METAMOD + m_WeGotPauseChange = true; +#endif + SDK_OnPauseChange(state); +} + +void SDKExtension::OnExtensionsAllLoaded() +{ + SDK_OnAllLoaded(); +} + +void SDKExtension::OnExtensionUnload() +{ +#if defined SMEXT_CONF_METAMOD + m_WeAreUnloaded = true; +#endif + SDK_OnUnload(); +} + +const char *SDKExtension::GetExtensionAuthor() +{ + return SMEXT_CONF_AUTHOR; +} + +const char *SDKExtension::GetExtensionDateString() +{ + return SMEXT_CONF_DATESTRING; +} + +const char *SDKExtension::GetExtensionDescription() +{ + return SMEXT_CONF_DESCRIPTION; +} + +const char *SDKExtension::GetExtensionVerString() +{ + return SMEXT_CONF_VERSION; +} + +const char *SDKExtension::GetExtensionName() +{ + return SMEXT_CONF_NAME; +} + +const char *SDKExtension::GetExtensionTag() +{ + return SMEXT_CONF_LOGTAG; +} + +const char *SDKExtension::GetExtensionURL() +{ + return SMEXT_CONF_URL; +} + +bool SDKExtension::SDK_OnLoad(char *error, size_t maxlength, bool late) +{ + return true; +} + +void SDKExtension::SDK_OnUnload() +{ +} + +void SDKExtension::SDK_OnPauseChange(bool paused) +{ +} + +void SDKExtension::SDK_OnAllLoaded() +{ +} + +#if defined SMEXT_CONF_METAMOD + +PluginId g_PLID = 0; /**< Metamod plugin ID */ +ISmmPlugin *g_PLAPI = NULL; /**< Metamod plugin API */ +SourceHook::ISourceHook *g_SHPtr = NULL; /**< SourceHook pointer */ +ISmmAPI *g_SMAPI = NULL; /**< SourceMM API pointer */ + +IVEngineServer *engine = NULL; /**< IVEngineServer pointer */ +IServerGameDLL *gamedll = NULL; /**< IServerGameDLL pointer */ + +/** Exposes the extension to Metamod */ +SMM_API void *PL_EXPOSURE(const char *name, int *code) +{ +#if defined METAMOD_PLAPI_VERSION + if (name && !strcmp(name, METAMOD_PLAPI_NAME)) +#else + if (name && !strcmp(name, PLAPI_NAME)) +#endif + { + if (code) + { + *code = IFACE_OK; + } + return static_cast(g_pExtensionIface); + } + + if (code) + { + *code = IFACE_FAILED; + } + + return NULL; +} + +bool SDKExtension::Load(PluginId id, ISmmAPI *ismm, char *error, size_t maxlen, bool late) +{ + PLUGIN_SAVEVARS(); + +#if defined METAMOD_PLAPI_VERSION + GET_V_IFACE_ANY(serverFactory, gamedll, IServerGameDLL, INTERFACEVERSION_SERVERGAMEDLL); + GET_V_IFACE_CURRENT(engineFactory, engine, IVEngineServer, INTERFACEVERSION_VENGINESERVER); +#else + GET_V_IFACE_ANY(GetServerFactory, gamedll, IServerGameDLL, INTERFACEVERSION_SERVERGAMEDLL); + GET_V_IFACE_CURRENT(GetEngineFactory, engine, IVEngineServer, INTERFACEVERSION_VENGINESERVER); +#endif + + m_SourceMMLoaded = true; + + return SDK_OnMetamodLoad(ismm, error, maxlen, late); +} + +bool SDKExtension::Unload(char *error, size_t maxlen) +{ + if (!m_WeAreUnloaded) + { + if (error) + { + snprintf(error, maxlen, "This extension must be unloaded by SourceMod."); + } + return false; + } + + return SDK_OnMetamodUnload(error, maxlen); +} + +bool SDKExtension::Pause(char *error, size_t maxlen) +{ + if (!m_WeGotPauseChange) + { + if (error) + { + snprintf(error, maxlen, "This extension must be paused by SourceMod."); + } + return false; + } + + m_WeGotPauseChange = false; + + return SDK_OnMetamodPauseChange(true, error, maxlen); +} + +bool SDKExtension::Unpause(char *error, size_t maxlen) +{ + if (!m_WeGotPauseChange) + { + if (error) + { + snprintf(error, maxlen, "This extension must be unpaused by SourceMod."); + } + return false; + } + + m_WeGotPauseChange = false; + + return SDK_OnMetamodPauseChange(false, error, maxlen); +} + +const char *SDKExtension::GetAuthor() +{ + return GetExtensionAuthor(); +} + +const char *SDKExtension::GetDate() +{ + return GetExtensionDateString(); +} + +const char *SDKExtension::GetDescription() +{ + return GetExtensionDescription(); +} + +const char *SDKExtension::GetLicense() +{ + return SMEXT_CONF_LICENSE; +} + +const char *SDKExtension::GetLogTag() +{ + return GetExtensionTag(); +} + +const char *SDKExtension::GetName() +{ + return GetExtensionName(); +} + +const char *SDKExtension::GetURL() +{ + return GetExtensionURL(); +} + +const char *SDKExtension::GetVersion() +{ + return GetExtensionVerString(); +} + +bool SDKExtension::SDK_OnMetamodLoad(ISmmAPI *ismm, char *error, size_t maxlength, bool late) +{ + return true; +} + +bool SDKExtension::SDK_OnMetamodUnload(char *error, size_t maxlength) +{ + return true; +} + +bool SDKExtension::SDK_OnMetamodPauseChange(bool paused, char *error, size_t maxlength) +{ + return true; +} + +#endif + +/* Overload a few things to prevent libstdc++ linking */ +#if defined __linux__ +extern "C" void __cxa_pure_virtual(void) +{ +} + +void *operator new(size_t size) +{ + return malloc(size); +} + +void *operator new[](size_t size) +{ + return malloc(size); +} + +void operator delete(void *ptr) +{ + free(ptr); +} + +void operator delete[](void * ptr) +{ + free(ptr); +} +#endif diff --git a/extensions/regex/sdk/smsdk_ext.h b/extensions/regex/sdk/smsdk_ext.h new file mode 100644 index 00000000..4702f2a2 --- /dev/null +++ b/extensions/regex/sdk/smsdk_ext.h @@ -0,0 +1,327 @@ +/** + * vim: set ts=4 : + * ============================================================================= + * SourceMod Base Extension Code + * Copyright (C) 2004-2007 AlliedModders LLC. All rights reserved. + * ============================================================================= + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id$ + */ + +#ifndef _INCLUDE_SOURCEMOD_EXTENSION_BASESDK_H_ +#define _INCLUDE_SOURCEMOD_EXTENSION_BASESDK_H_ + +/** + * @file smsdk_ext.h + * @brief Contains wrappers for making Extensions easier to write. + */ + +#include "smsdk_config.h" +#include +#include +#include +#include +#include +#if defined SMEXT_ENABLE_FORWARDSYS +#include +#endif //SMEXT_ENABLE_FORWARDSYS +#if defined SMEXT_ENABLE_PLAYERHELPERS +#include +#endif //SMEXT_ENABLE_PlAYERHELPERS +#if defined SMEXT_ENABLE_DBMANAGER +#include +#endif //SMEXT_ENABLE_DBMANAGER +#if defined SMEXT_ENABLE_GAMECONF +#include +#endif +#if defined SMEXT_ENABLE_MEMUTILS +#include +#endif +#if defined SMEXT_ENABLE_GAMEHELPERS +#include +#endif +#if defined SMEXT_ENABLE_TIMERSYS +#include +#endif +#if defined SMEXT_ENABLE_ADTFACTORY +#include +#endif +#if defined SMEXT_ENABLE_THREADER +#include +#endif +#if defined SMEXT_ENABLE_LIBSYS +#include +#endif +#if defined SMEXT_ENABLE_PLUGINSYS +#include +#endif +#if defined SMEXT_ENABLE_MENUS +#include +#endif +#if defined SMEXT_ENABLE_ADMINSYS +#include +#endif +#if defined SMEXT_ENABLE_TEXTPARSERS +#include +#endif +#if defined SMEXT_ENABLE_USERMSGS +#include +#endif + +#if defined SMEXT_CONF_METAMOD +#include +#include +#endif + +using namespace SourceMod; +using namespace SourcePawn; + +class SDKExtension : +#if defined SMEXT_CONF_METAMOD + public ISmmPlugin, +#endif + public IExtensionInterface +{ +public: + /** Constructor */ + SDKExtension(); +public: + /** + * @brief This is called after the initial loading sequence has been processed. + * + * @param error Error message buffer. + * @param maxlength Size of error message buffer. + * @param late Whether or not the module was loaded after map load. + * @return True to succeed loading, false to fail. + */ + virtual bool SDK_OnLoad(char *error, size_t maxlength, bool late); + + /** + * @brief This is called right before the extension is unloaded. + */ + virtual void SDK_OnUnload(); + + /** + * @brief This is called once all known extensions have been loaded. + */ + virtual void SDK_OnAllLoaded(); + + /** + * @brief Called when the pause state is changed. + */ + virtual void SDK_OnPauseChange(bool paused); + +#if defined SMEXT_CONF_METAMOD + /** + * @brief Called when Metamod is attached, before the extension version is called. + * + * @param error Error buffer. + * @param maxlength Maximum size of error buffer. + * @param late Whether or not Metamod considers this a late load. + * @return True to succeed, false to fail. + */ + virtual bool SDK_OnMetamodLoad(ISmmAPI *ismm, char *error, size_t maxlength, bool late); + + /** + * @brief Called when Metamod is detaching, after the extension version is called. + * NOTE: By default this is blocked unless sent from SourceMod. + * + * @param error Error buffer. + * @param maxlength Maximum size of error buffer. + * @return True to succeed, false to fail. + */ + virtual bool SDK_OnMetamodUnload(char *error, size_t maxlength); + + /** + * @brief Called when Metamod's pause state is changing. + * NOTE: By default this is blocked unless sent from SourceMod. + * + * @param paused Pause state being set. + * @param error Error buffer. + * @param maxlength Maximum size of error buffer. + * @return True to succeed, false to fail. + */ + virtual bool SDK_OnMetamodPauseChange(bool paused, char *error, size_t maxlength); +#endif + +public: //IExtensionInterface + virtual bool OnExtensionLoad(IExtension *me, IShareSys *sys, char *error, size_t maxlength, bool late); + virtual void OnExtensionUnload(); + virtual void OnExtensionsAllLoaded(); + + /** Returns whether or not this is a Metamod-based extension */ + virtual bool IsMetamodExtension(); + + /** + * @brief Called when the pause state changes. + * + * @param state True if being paused, false if being unpaused. + */ + virtual void OnExtensionPauseChange(bool state); + + /** Returns name */ + virtual const char *GetExtensionName(); + /** Returns URL */ + virtual const char *GetExtensionURL(); + /** Returns log tag */ + virtual const char *GetExtensionTag(); + /** Returns author */ + virtual const char *GetExtensionAuthor(); + /** Returns version string */ + virtual const char *GetExtensionVerString(); + /** Returns description string */ + virtual const char *GetExtensionDescription(); + /** Returns date string */ + virtual const char *GetExtensionDateString(); +#if defined SMEXT_CONF_METAMOD +public: //ISmmPlugin + /** Called when the extension is attached to Metamod. */ + virtual bool Load(PluginId id, ISmmAPI *ismm, char *error, size_t maxlength, bool late); + /** Returns the author to MM */ + virtual const char *GetAuthor(); + /** Returns the name to MM */ + virtual const char *GetName(); + /** Returns the description to MM */ + virtual const char *GetDescription(); + /** Returns the URL to MM */ + virtual const char *GetURL(); + /** Returns the license to MM */ + virtual const char *GetLicense(); + /** Returns the version string to MM */ + virtual const char *GetVersion(); + /** Returns the date string to MM */ + virtual const char *GetDate(); + /** Returns the logtag to MM */ + virtual const char *GetLogTag(); + /** Called on unload */ + virtual bool Unload(char *error, size_t maxlength); + /** Called on pause */ + virtual bool Pause(char *error, size_t maxlength); + /** Called on unpause */ + virtual bool Unpause(char *error, size_t maxlength); +private: + bool m_SourceMMLoaded; + bool m_WeAreUnloaded; + bool m_WeGotPauseChange; +#endif +}; + +extern SDKExtension *g_pExtensionIface; +extern IExtension *myself; + +extern IShareSys *g_pShareSys; +extern IShareSys *sharesys; /* Note: Newer name */ +extern ISourceMod *g_pSM; +extern ISourceMod *smutils; /* Note: Newer name */ + +/* Optional interfaces are below */ +#if defined SMEXT_ENABLE_FORWARDSYS +extern IForwardManager *g_pForwards; +extern IForwardManager *forwards; /* Note: Newer name */ +#endif //SMEXT_ENABLE_FORWARDSYS +#if defined SMEXT_ENABLE_HANDLESYS +extern IHandleSys *g_pHandleSys; +extern IHandleSys *handlesys; /* Note: Newer name */ +#endif //SMEXT_ENABLE_HANDLESYS +#if defined SMEXT_ENABLE_PLAYERHELPERS +extern IPlayerManager *playerhelpers; +#endif //SMEXT_ENABLE_PLAYERHELPERS +#if defined SMEXT_ENABLE_DBMANAGER +extern IDBManager *dbi; +#endif //SMEXT_ENABLE_DBMANAGER +#if defined SMEXT_ENABLE_GAMECONF +extern IGameConfigManager *gameconfs; +#endif //SMEXT_ENABLE_DBMANAGER +#if defined SMEXT_ENABLE_MEMUTILS +extern IMemoryUtils *memutils; +#endif +#if defined SMEXT_ENABLE_GAMEHELPERS +extern IGameHelpers *gamehelpers; +#endif +#if defined SMEXT_ENABLE_TIMERSYS +extern ITimerSystem *timersys; +#endif +#if defined SMEXT_ENABLE_ADTFACTORY +extern IADTFactory *adtfactory; +#endif +#if defined SMEXT_ENABLE_THREADER +extern IThreader *threader; +#endif +#if defined SMEXT_ENABLE_LIBSYS +extern ILibrarySys *libsys; +#endif +#if defined SMEXT_ENABLE_PLUGINSYS +extern SourceMod::IPluginManager *plsys; +#endif +#if defined SMEXT_ENABLE_MENUS +extern IMenuManager *menus; +#endif +#if defined SMEXT_ENABLE_ADMINSYS +extern IAdminSystem *adminsys; +#endif +#if defined SMEXT_ENABLE_USERMSGS +extern IUserMessages *usermsgs; +#endif + +#if defined SMEXT_CONF_METAMOD +PLUGIN_GLOBALVARS(); +extern IVEngineServer *engine; +extern IServerGameDLL *gamedll; +#endif + +/** Creates a SourceMod interface macro pair */ +#define SM_MKIFACE(name) SMINTERFACE_##name##_NAME, SMINTERFACE_##name##_VERSION +/** Automates retrieving SourceMod interfaces */ +#define SM_GET_IFACE(prefix, addr) \ + if (!g_pShareSys->RequestInterface(SM_MKIFACE(prefix), myself, (SMInterface **)&addr)) \ + { \ + if (error != NULL && maxlength) \ + { \ + size_t len = snprintf(error, maxlength, "Could not find interface: %s", SMINTERFACE_##prefix##_NAME); \ + if (len >= maxlength) \ + { \ + error[maxlength - 1] = '\0'; \ + } \ + } \ + return false; \ + } +/** Automates retrieving SourceMod interfaces when needed outside of SDK_OnLoad() */ +#define SM_GET_LATE_IFACE(prefix, addr) \ + g_pShareSys->RequestInterface(SM_MKIFACE(prefix), myself, (SMInterface **)&addr) +/** Validates a SourceMod interface pointer */ +#define SM_CHECK_IFACE(prefix, addr) \ + if (!addr) \ + { \ + if (error != NULL && maxlength) \ + { \ + size_t len = snprintf(error, maxlength, "Could not find interface: %s", SMINTERFACE_##prefix##_NAME); \ + if (len >= maxlength) \ + { \ + error[maxlength - 1] = '\0'; \ + } \ + } \ + return false; \ + } + +#endif // _INCLUDE_SOURCEMOD_EXTENSION_BASESDK_H_ diff --git a/extensions/tf2/Makefile.ep1 b/extensions/tf2/Makefile.ep1 new file mode 100644 index 00000000..87f0423f --- /dev/null +++ b/extensions/tf2/Makefile.ep1 @@ -0,0 +1,88 @@ +#(C)2004-2006 SourceMM Development Team +# Makefile written by David "BAILOPAN" Anderson + +SMSDK = ../smsdk +SRCDS = ~/srcds_l/steaminstall +SOURCEMM = ../sourcemm +HL2SDK = ../hl2sdk + +##################################### +### EDIT BELOW FOR OTHER PROJECTS ### +##################################### + +PROJECT = game.tf2 + +#Uncomment for SourceMM-enabled extensions +LINK_HL2 = $(HL2LIB)/tier1_i486.a vstdlib_i486.so tier0_i486.so + +OBJECTS = sdk/smsdk_ext.cpp extension.cpp natives.cpp RegNatives.cpp + +############################################## +### CONFIGURE ANY OTHER FLAGS/OPTIONS HERE ### +############################################## + +C_OPT_FLAGS = -O3 -funroll-loops -s -pipe -fno-strict-aliasing +C_DEBUG_FLAGS = -g -ggdb3 +CPP_GCC4_FLAGS = -fvisibility=hidden -fvisibility-inlines-hidden +CPP = gcc-4.1 + +HL2PUB = $(HL2SDK)/public +HL2LIB = $(HL2SDK)/linux_sdk + +LINK = $(LINK_HL2) -static-libgcc + +INCLUDE = -I. -I.. -Isdk -I$(HL2PUB) -I$(HL2PUB)/dlls -I$(HL2PUB)/engine -I$(HL2PUB)/tier0 -I$(HL2PUB)/tier1 \ + -I$(HL2PUB)/vstdlib -I$(HL2SDK)/tier1 -I$(SOURCEMM) -I$(SOURCEMM)/sourcehook -I$(SOURCEMM)/sourcemm \ + -I$(SMSDK)/public -I$(SMSDK)/public/sourcepawn -I$(SMSDK)/public/extensions \ + +CFLAGS = -D_LINUX -DNDEBUG -Dstricmp=strcasecmp -D_stricmp=strcasecmp -D_strnicmp=strncasecmp \ + -Dstrnicmp=strncasecmp -D_snprintf=snprintf -D_vsnprintf=vsnprintf -D_alloca=alloca -Dstrcmpi=strcasecmp \ + -Wall -Werror -fPIC -mfpmath=sse -msse -DSOURCEMOD_BUILD -DHAVE_STDINT_H +CPPFLAGS = -Wno-non-virtual-dtor -fno-exceptions -fno-rtti + +################################################ +### DO NOT EDIT BELOW HERE FOR MOST PROJECTS ### +################################################ + +ifeq "$(DEBUG)" "true" + BIN_DIR = Debug + CFLAGS += $(C_DEBUG_FLAGS) +else + BIN_DIR = Release + CFLAGS += $(C_OPT_FLAGS) +endif + + +GCC_VERSION := $(shell $(CPP) -dumpversion >&1 | cut -b1) +ifeq "$(GCC_VERSION)" "4" + CPPFLAGS += $(CPP_GCC4_FLAGS) +endif + +BINARY = $(PROJECT).ext.so + +OBJ_LINUX := $(OBJECTS:%.cpp=$(BIN_DIR)/%.o) + +$(BIN_DIR)/%.o: %.cpp + $(CPP) $(INCLUDE) $(CFLAGS) $(CPPFLAGS) -o $@ -c $< + +all: + mkdir -p $(BIN_DIR)/sdk + ln -sf $(SRCDS)/bin/vstdlib_i486.so vstdlib_i486.so + ln -sf $(SRCDS)/bin/tier0_i486.so tier0_i486.so + $(MAKE) -f Makefile.ep1 extension + +extension: $(OBJ_LINUX) + $(CPP) $(INCLUDE) $(CFLAGS) $(CPPFLAGS) $(OBJ_LINUX) $(LINK) -shared -ldl -lm -o$(BIN_DIR)/$(BINARY) + +debug: + $(MAKE) -f Makefile.ep1 all DEBUG=true + +default: all + +clean: + rm -rf Release/*.o + rm -rf Release/sdk/*.o + rm -rf Release/$(BINARY) + rm -rf Debug/*.o + rm -rf Debug/sdk/*.o + rm -rf Debug/$(BINARY) diff --git a/extensions/tf2/Makefile.ep2 b/extensions/tf2/Makefile.ep2 new file mode 100644 index 00000000..ff407f70 --- /dev/null +++ b/extensions/tf2/Makefile.ep2 @@ -0,0 +1,88 @@ +#(C)2004-2006 SourceMM Development Team +# Makefile written by David "BAILOPAN" Anderson + +SMSDK = ../smsdk +SRCDS = ~/srcds_l/steaminstall +SOURCEMM = ../sourcemm +HL2SDK = ../hl2sdk-ob + +##################################### +### EDIT BELOW FOR OTHER PROJECTS ### +##################################### + +PROJECT = game.tf2 + +#Uncomment for SourceMM-enabled extensions +LINK_HL2 = $(HL2LIB)/tier1_i486.a vstdlib_i486.so tier0_i486.so + +OBJECTS = sdk/smsdk_ext.cpp extension.cpp natives.cpp RegNatives.cpp + +############################################## +### CONFIGURE ANY OTHER FLAGS/OPTIONS HERE ### +############################################## + +C_OPT_FLAGS = -O3 -funroll-loops -s -pipe -fno-strict-aliasing +C_DEBUG_FLAGS = -g -ggdb3 +CPP_GCC4_FLAGS = -fvisibility=hidden -fvisibility-inlines-hidden +CPP = gcc-4.1 + +HL2PUB = $(HL2SDK)/public +HL2LIB = $(HL2SDK)/linux_sdk + +LINK = $(LINK_HL2) -static-libgcc + +INCLUDE = -I. -I.. -Isdk -I$(HL2PUB) -I$(HL2PUB)/dlls -I$(HL2PUB)/engine -I$(HL2PUB)/tier0 -I$(HL2PUB)/tier1 \ + -I$(HL2PUB)/vstdlib -I$(HL2SDK)/tier1 -I$(SOURCEMM) -I$(SOURCEMM)/sourcehook -I$(SOURCEMM)/sourcemm \ + -I$(SMSDK)/public -I$(SMSDK)/public/sourcepawn -I$(SMSDK)/public/extensions \ + +CFLAGS = -D_LINUX -DNDEBUG -Dstricmp=strcasecmp -D_stricmp=strcasecmp -D_strnicmp=strncasecmp \ + -Dstrnicmp=strncasecmp -D_snprintf=snprintf -D_vsnprintf=vsnprintf -D_alloca=alloca -Dstrcmpi=strcasecmp \ + -Wall -Werror -fPIC -mfpmath=sse -msse -DSOURCEMOD_BUILD -DHAVE_STDINT_H -DORANGEBOX_BUILD +CPPFLAGS = -Wno-non-virtual-dtor -fno-exceptions -fno-rtti + +################################################ +### DO NOT EDIT BELOW HERE FOR MOST PROJECTS ### +################################################ + +ifeq "$(DEBUG)" "true" + BIN_DIR = Debug + CFLAGS += $(C_DEBUG_FLAGS) +else + BIN_DIR = Release + CFLAGS += $(C_OPT_FLAGS) +endif + + +GCC_VERSION := $(shell $(CPP) -dumpversion >&1 | cut -b1) +ifeq "$(GCC_VERSION)" "4" + CPPFLAGS += $(CPP_GCC4_FLAGS) +endif + +BINARY = $(PROJECT).ext.so + +OBJ_LINUX := $(OBJECTS:%.cpp=$(BIN_DIR)/%.o) + +$(BIN_DIR)/%.o: %.cpp + $(CPP) $(INCLUDE) $(CFLAGS) $(CPPFLAGS) -o $@ -c $< + +all: + mkdir -p $(BIN_DIR)/sdk + ln -sf $(SRCDS)/orangebox/bin/vstdlib_i486.so vstdlib_i486.so + ln -sf $(SRCDS)/orangebox/bin/tier0_i486.so tier0_i486.so + $(MAKE) -f Makefile.ep2 extension + +extension: $(OBJ_LINUX) + $(CPP) $(INCLUDE) $(CFLAGS) $(CPPFLAGS) $(OBJ_LINUX) $(LINK) -shared -ldl -lm -o$(BIN_DIR)/$(BINARY) + +debug: + $(MAKE) -f Makefile.ep2 all DEBUG=true + +default: all + +clean: + rm -rf Release/*.o + rm -rf Release/sdk/*.o + rm -rf Release/$(BINARY) + rm -rf Debug/*.o + rm -rf Debug/sdk/*.o + rm -rf Debug/$(BINARY) diff --git a/extensions/tf2/Makefile.orig b/extensions/tf2/Makefile.orig new file mode 100644 index 00000000..f31cf6f2 --- /dev/null +++ b/extensions/tf2/Makefile.orig @@ -0,0 +1,88 @@ +#(C)2004-2006 SourceMM Development Team +# Makefile written by David "BAILOPAN" Anderson + +SMSDK = ../smsdk +SRCDS = ~/srcds_l/steaminstall +SOURCEMM = ../sourcemm +HL2SDK = ../hl2sdk + +##################################### +### EDIT BELOW FOR OTHER PROJECTS ### +##################################### + +PROJECT = game.tf2 + +#Uncomment for SourceMM-enabled extensions +LINK_HL2 = $(HL2LIB)/tier1_i486.a vstdlib_i486.so tier0_i486.so + +OBJECTS = sdk/smsdk_ext.cpp extension.cpp natives.cpp RegNatives.cpp + +############################################## +### CONFIGURE ANY OTHER FLAGS/OPTIONS HERE ### +############################################## + +C_OPT_FLAGS = -O3 -funroll-loops -s -pipe -fno-strict-aliasing +C_DEBUG_FLAGS = -g -ggdb3 +CPP_GCC4_FLAGS = -fvisibility=hidden -fvisibility-inlines-hidden +CPP = gcc-4.1 + +HL2PUB = $(HL2SDK)/public +HL2LIB = $(HL2SDK)/linux_sdk + +LINK = $(LINK_HL2) -static-libgcc + +INCLUDE = -I. -I.. -Isdk -I$(HL2PUB) -I$(HL2PUB)/dlls -I$(HL2PUB)/engine -I$(HL2PUB)/tier0 -I$(HL2PUB)/tier1 \ + -I$(HL2PUB)/vstdlib -I$(HL2SDK)/tier1 -I$(SOURCEMM) -I$(SOURCEMM)/sourcehook -I$(SOURCEMM)/sourcemm \ + -I$(SMSDK)/public -I$(SMSDK)/public/sourcepawn -I$(SMSDK)/public/extensions \ + +CFLAGS = -D_LINUX -DNDEBUG -Dstricmp=strcasecmp -D_stricmp=strcasecmp -D_strnicmp=strncasecmp \ + -Dstrnicmp=strncasecmp -D_snprintf=snprintf -D_vsnprintf=vsnprintf -D_alloca=alloca -Dstrcmpi=strcasecmp \ + -Wall -Werror -fPIC -mfpmath=sse -msse -DSOURCEMOD_BUILD -DHAVE_STDINT_H +CPPFLAGS = -Wno-non-virtual-dtor -fno-exceptions -fno-rtti + +################################################ +### DO NOT EDIT BELOW HERE FOR MOST PROJECTS ### +################################################ + +ifeq "$(DEBUG)" "true" + BIN_DIR = Debug + CFLAGS += $(C_DEBUG_FLAGS) +else + BIN_DIR = Release + CFLAGS += $(C_OPT_FLAGS) +endif + + +GCC_VERSION := $(shell $(CPP) -dumpversion >&1 | cut -b1) +ifeq "$(GCC_VERSION)" "4" + CPPFLAGS += $(CPP_GCC4_FLAGS) +endif + +BINARY = $(PROJECT).ext.so + +OBJ_LINUX := $(OBJECTS:%.cpp=$(BIN_DIR)/%.o) + +$(BIN_DIR)/%.o: %.cpp + $(CPP) $(INCLUDE) $(CFLAGS) $(CPPFLAGS) -o $@ -c $< + +all: + mkdir -p $(BIN_DIR)/sdk + ln -sf $(SRCDS)/bin/vstdlib_i486.so vstdlib_i486.so + ln -sf $(SRCDS)/bin/tier0_i486.so tier0_i486.so + $(MAKE) -f Makefile.orig extension + +extension: $(OBJ_LINUX) + $(CPP) $(INCLUDE) $(CFLAGS) $(CPPFLAGS) $(OBJ_LINUX) $(LINK) -shared -ldl -lm -o$(BIN_DIR)/$(BINARY) + +debug: + $(MAKE) -f Makefile.orig all DEBUG=true + +default: all + +clean: + rm -rf Release/*.o + rm -rf Release/sdk/*.o + rm -rf Release/$(BINARY) + rm -rf Debug/*.o + rm -rf Debug/sdk/*.o + rm -rf Debug/$(BINARY) diff --git a/extensions/tf2/RegNatives.cpp b/extensions/tf2/RegNatives.cpp new file mode 100644 index 00000000..2f001b78 --- /dev/null +++ b/extensions/tf2/RegNatives.cpp @@ -0,0 +1,50 @@ +/** + * vim: set ts=4 : + * ============================================================================= + * SourceMod Counter-Strike:Source Extension + * Copyright (C) 2004-2007 AlliedModders LLC. All rights reserved. + * ============================================================================= + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id$ + */ + +#include "extension.h" +#include "RegNatives.h" + +RegNatives g_RegNatives; + +void RegNatives::Register(ICallWrapper *pWrapper) +{ + m_List.push_back(pWrapper); +} + +void RegNatives::UnregisterAll() +{ + SourceHook::List::iterator iter; + + for (iter=m_List.begin(); iter!=m_List.end(); iter++) + { + (*iter)->Destroy(); + } +} diff --git a/extensions/tf2/RegNatives.h b/extensions/tf2/RegNatives.h new file mode 100644 index 00000000..1913530f --- /dev/null +++ b/extensions/tf2/RegNatives.h @@ -0,0 +1,48 @@ +/** + * vim: set ts=4 : + * ============================================================================= + * SourceMod Counter-Strike:Source Extension + * Copyright (C) 2004-2007 AlliedModders LLC. All rights reserved. + * ============================================================================= + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id$ + */ + +#ifndef _INCLUDE_TF2TOOLS_REGNATIVES_H_ +#define _INCLUDE_TF2TOOLS_REGNATIVES_H_ + +#include + +class RegNatives +{ +public: + void Register(ICallWrapper *pWrapper); + void UnregisterAll(); +private: + SourceHook::List m_List; +}; + +extern RegNatives g_RegNatives; + +#endif //_INCLUDE_TF2TOOLS_REGNATIVES_H_ diff --git a/extensions/tf2/extension.cpp b/extensions/tf2/extension.cpp new file mode 100644 index 00000000..ae67d077 --- /dev/null +++ b/extensions/tf2/extension.cpp @@ -0,0 +1,229 @@ +/** + * vim: set ts=4 : + * ============================================================================= + * SourceMod Counter-Strike:Source Extension + * Copyright (C) 2004-2007 AlliedModders LLC. All rights reserved. + * ============================================================================= + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id$ + */ + +#include "extension.h" +#include "RegNatives.h" +#include "iplayerinfo.h" + +/** + * @file extension.cpp + * @brief Implement extension code here. + */ + + +TF2Tools g_TF2Tools; +IGameConfig *g_pGameConf = NULL; + +IBinTools *g_pBinTools = NULL; + +SMEXT_LINK(&g_TF2Tools); + +SendProp *playerSharedOffset; + +extern sp_nativeinfo_t g_TFNatives[]; + +bool TF2Tools::SDK_OnLoad(char *error, size_t maxlength, bool late) +{ + sharesys->AddDependency(myself, "bintools.ext", true, true); + + char conf_error[255]; + if (!gameconfs->LoadGameConfigFile("sm-tf2.games", &g_pGameConf, conf_error, sizeof(conf_error))) + { + if (error) + { + snprintf(error, maxlength, "Could not read sm-tf2.games.txt: %s", conf_error); + } + return false; + } + + sharesys->AddNatives(myself, g_TFNatives); + sharesys->RegisterLibrary(myself, "tf2"); + + playerSharedOffset = gamehelpers->FindInSendTable("CTFPlayer", "DT_TFPlayerShared"); + + playerhelpers->RegisterCommandTargetProcessor(this); + + return true; +} + +bool TF2Tools::SDK_OnMetamodLoad(ISmmAPI *ismm, char *error, size_t maxlen, bool late) +{ + GET_V_IFACE_CURRENT(GetEngineFactory, engine, IVEngineServer, INTERFACEVERSION_VENGINESERVER); + + return true; +} + +void TF2Tools::SDK_OnUnload() +{ + g_RegNatives.UnregisterAll(); + gameconfs->CloseGameConfigFile(g_pGameConf); +} + +void TF2Tools::SDK_OnAllLoaded() +{ + SM_GET_LATE_IFACE(BINTOOLS, g_pBinTools); +} + +bool TF2Tools::QueryRunning(char *error, size_t maxlength) +{ + SM_CHECK_IFACE(BINTOOLS, g_pBinTools); + + return true; +} + +bool TF2Tools::QueryInterfaceDrop(SMInterface *pInterface) +{ + if (pInterface == g_pBinTools) + { + return false; + } + + return IExtensionInterface::QueryInterfaceDrop(pInterface); +} + +void TF2Tools::NotifyInterfaceDrop(SMInterface *pInterface) +{ + g_RegNatives.UnregisterAll(); +} + +size_t UTIL_Format(char *buffer, size_t maxlength, const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + size_t len = vsnprintf(buffer, maxlength, fmt, ap); + va_end(ap); + + if (len >= maxlength) + { + buffer[maxlength - 1] = '\0'; + return (maxlength - 1); + } + else + { + return len; + } +} + +bool TF2Tools::ProcessCommandTarget(cmd_target_info_t *info) +{ + int max_clients; + IPlayerInfo *pInfo; + unsigned int team_index = 0; + IGamePlayer *pPlayer, *pAdmin; + + if ((info->flags & COMMAND_FILTER_NO_MULTI) == COMMAND_FILTER_NO_MULTI) + { + return false; + } + + if (info->admin) + { + if ((pAdmin = playerhelpers->GetGamePlayer(info->admin)) == NULL) + { + return false; + } + if (!pAdmin->IsInGame()) + { + return false; + } + } + else + { + pAdmin = NULL; + } + + if (strcmp(info->pattern, "@red") == 0 ) + { + team_index = 2; + } + else if (strcmp(info->pattern, "@blue") == 0) + { + team_index = 3; + } + else + { + return false; + } + + info->num_targets = 0; + + max_clients = playerhelpers->GetMaxClients(); + for (int i = 1; + i <= max_clients && (cell_t)info->num_targets < info->max_targets; + i++) + { + if ((pPlayer = playerhelpers->GetGamePlayer(i)) == NULL) + { + continue; + } + if (!pPlayer->IsInGame()) + { + continue; + } + if ((pInfo = pPlayer->GetPlayerInfo()) == NULL) + { + continue; + } + if (pInfo->GetTeamIndex() != (int)team_index) + { + continue; + } + if (playerhelpers->FilterCommandTarget(pAdmin, pPlayer, info->flags) + != COMMAND_TARGET_VALID) + { + continue; + } + info->targets[info->num_targets] = i; + info->num_targets++; + } + + if (info->num_targets == 0) + { + info->reason = COMMAND_TARGET_EMPTY_FILTER; + } + else + { + info->reason = COMMAND_TARGET_VALID; + } + + info->target_name_style = COMMAND_TARGETNAME_RAW; + if (team_index == 2) + { + UTIL_Format(info->target_name, info->target_name_maxlength, "Red Team"); + } + else if (team_index == 3) + { + UTIL_Format(info->target_name, info->target_name_maxlength, "Blue Team"); + } + + return true; +} + diff --git a/extensions/tf2/extension.h b/extensions/tf2/extension.h new file mode 100644 index 00000000..91090fd9 --- /dev/null +++ b/extensions/tf2/extension.h @@ -0,0 +1,130 @@ +/** + * vim: set ts=4 : + * ============================================================================= + * SourceMod Counter-Strike:Source Extension + * Copyright (C) 2004-2007 AlliedModders LLC. All rights reserved. + * ============================================================================= + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id$ + */ + +#ifndef _INCLUDE_SOURCEMOD_EXTENSION_PROPER_H_ +#define _INCLUDE_SOURCEMOD_EXTENSION_PROPER_H_ + +/** + * @file extension.h + * @brief Sample extension code header. + */ + +#include "smsdk_ext.h" +#include + +/** + * @brief Sample implementation of the SDK Extension. + * Note: Uncomment one of the pre-defined virtual functions in order to use it. + */ +class TF2Tools : + public SDKExtension, + public ICommandTargetProcessor +{ +public: + /** + * @brief This is called after the initial loading sequence has been processed. + * + * @param error Error message buffer. + * @param maxlength Size of error message buffer. + * @param late Whether or not the module was loaded after map load. + * @return True to succeed loading, false to fail. + */ + virtual bool SDK_OnLoad(char *error, size_t maxlength, bool late); + + /** + * @brief This is called right before the extension is unloaded. + */ + virtual void SDK_OnUnload(); + + /** + * @brief This is called once all known extensions have been loaded. + * Note: It is is a good idea to add natives here, if any are provided. + */ + virtual void SDK_OnAllLoaded(); + + /** + * @brief Called when the pause state is changed. + */ + //virtual void SDK_OnPauseChange(bool paused); + + /** + * @brief this is called when Core wants to know if your extension is working. + * + * @param error Error message buffer. + * @param maxlength Size of error message buffer. + * @return True if working, false otherwise. + */ + virtual bool QueryRunning(char *error, size_t maxlength); + + void NotifyInterfaceDrop(SMInterface *pInterface); + bool QueryInterfaceDrop(SMInterface *pInterface); +public: + bool ProcessCommandTarget(cmd_target_info_t *info); +public: +#if defined SMEXT_CONF_METAMOD + /** + * @brief Called when Metamod is attached, before the extension version is called. + * + * @param error Error buffer. + * @param maxlength Maximum size of error buffer. + * @param late Whether or not Metamod considers this a late load. + * @return True to succeed, false to fail. + */ + virtual bool SDK_OnMetamodLoad(ISmmAPI *ismm, char *error, size_t maxlength, bool late); + + /** + * @brief Called when Metamod is detaching, after the extension version is called. + * NOTE: By default this is blocked unless sent from SourceMod. + * + * @param error Error buffer. + * @param maxlength Maximum size of error buffer. + * @return True to succeed, false to fail. + */ + //virtual bool SDK_OnMetamodUnload(char *error, size_t maxlength); + + /** + * @brief Called when Metamod's pause state is changing. + * NOTE: By default this is blocked unless sent from SourceMod. + * + * @param paused Pause state being set. + * @param error Error buffer. + * @param maxlength Maximum size of error buffer. + * @return True to succeed, false to fail. + */ + //virtual bool SDK_OnMetamodPauseChange(bool paused, char *error, size_t maxlength); +#endif +}; + +extern IBinTools *g_pBinTools; +extern IGameConfig *g_pGameConf; +extern SendProp *playerSharedOffset; + +#endif // _INCLUDE_SOURCEMOD_EXTENSION_PROPER_H_ diff --git a/extensions/tf2/msvc8/tf2.sln b/extensions/tf2/msvc8/tf2.sln new file mode 100644 index 00000000..5aa5277e --- /dev/null +++ b/extensions/tf2/msvc8/tf2.sln @@ -0,0 +1,32 @@ + +Microsoft Visual Studio Solution File, Format Version 9.00 +# Visual Studio 2005 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "tf2", "tf2.vcproj", "{B3E797CF-4E77-4C9D-B8A8-7589B6902206}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug - Episode1|Win32 = Debug - Episode1|Win32 + Debug - Old Metamod|Win32 = Debug - Old Metamod|Win32 + Debug - Orange Box|Win32 = Debug - Orange Box|Win32 + Release - Episode 1|Win32 = Release - Episode 1|Win32 + Release - Old Metamod|Win32 = Release - Old Metamod|Win32 + Release - Orange Box|Win32 = Release - Orange Box|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {B3E797CF-4E77-4C9D-B8A8-7589B6902206}.Debug - Episode1|Win32.ActiveCfg = Debug - Episode1|Win32 + {B3E797CF-4E77-4C9D-B8A8-7589B6902206}.Debug - Episode1|Win32.Build.0 = Debug - Episode1|Win32 + {B3E797CF-4E77-4C9D-B8A8-7589B6902206}.Debug - Old Metamod|Win32.ActiveCfg = Debug - Old Metamod|Win32 + {B3E797CF-4E77-4C9D-B8A8-7589B6902206}.Debug - Old Metamod|Win32.Build.0 = Debug - Old Metamod|Win32 + {B3E797CF-4E77-4C9D-B8A8-7589B6902206}.Debug - Orange Box|Win32.ActiveCfg = Debug - Orange Box|Win32 + {B3E797CF-4E77-4C9D-B8A8-7589B6902206}.Debug - Orange Box|Win32.Build.0 = Debug - Orange Box|Win32 + {B3E797CF-4E77-4C9D-B8A8-7589B6902206}.Release - Episode 1|Win32.ActiveCfg = Release - Episode 1|Win32 + {B3E797CF-4E77-4C9D-B8A8-7589B6902206}.Release - Episode 1|Win32.Build.0 = Release - Episode 1|Win32 + {B3E797CF-4E77-4C9D-B8A8-7589B6902206}.Release - Old Metamod|Win32.ActiveCfg = Release - Old Metamod|Win32 + {B3E797CF-4E77-4C9D-B8A8-7589B6902206}.Release - Old Metamod|Win32.Build.0 = Release - Old Metamod|Win32 + {B3E797CF-4E77-4C9D-B8A8-7589B6902206}.Release - Orange Box|Win32.ActiveCfg = Release - Orange Box|Win32 + {B3E797CF-4E77-4C9D-B8A8-7589B6902206}.Release - Orange Box|Win32.Build.0 = Release - Orange Box|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/extensions/tf2/msvc8/tf2.vcproj b/extensions/tf2/msvc8/tf2.vcproj new file mode 100644 index 00000000..67172d95 --- /dev/null +++ b/extensions/tf2/msvc8/tf2.vcproj @@ -0,0 +1,727 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/extensions/tf2/natives.cpp b/extensions/tf2/natives.cpp new file mode 100644 index 00000000..7d6887f0 --- /dev/null +++ b/extensions/tf2/natives.cpp @@ -0,0 +1,159 @@ +/** + * vim: set ts=4 : + * ============================================================================= + * SourceMod Counter-Strike:Source Extension + * Copyright (C) 2004-2007 AlliedModders LLC. All rights reserved. + * ============================================================================= + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id$ + */ + +#include "extension.h" +#include "RegNatives.h" + +#define REGISTER_NATIVE_ADDR(name, code) \ + void *addr; \ + if (!g_pGameConf->GetMemSig(name, &addr)) \ + { \ + return pContext->ThrowNativeError("Failed to locate function"); \ + } \ + code; \ + g_RegNatives.Register(pWrapper); + +inline CBaseEntity *GetCBaseEntity(int num, bool onlyPlayers) +{ + edict_t *pEdict = engine->PEntityOfEntIndex(num); + if (!pEdict || pEdict->IsFree()) + { + return NULL; + } + + if (num > 0 && num < playerhelpers->GetMaxClients()) + { + IGamePlayer *pPlayer = playerhelpers->GetGamePlayer(pEdict); + if (!pPlayer || !pPlayer->IsConnected()) + { + return NULL; + } + } + else if (onlyPlayers) + { + return NULL; + } + + IServerUnknown *pUnk; + if ((pUnk=pEdict->GetUnknown()) == NULL) + { + return NULL; + } + + return pUnk->GetBaseEntity(); +} + + +// native TF2_Burn(client) +cell_t TF2_Burn(IPluginContext *pContext, const cell_t *params) +{ + static ICallWrapper *pWrapper = NULL; + + // CTFPlayerShared::Burn(CTFPlayer*) + if (!pWrapper) + { + REGISTER_NATIVE_ADDR("Burn", + PassInfo pass[1]; \ + pass[0].flags = PASSFLAG_BYVAL; \ + pass[0].size = sizeof(CBaseEntity *); \ + pass[0].type = PassType_Basic; \ + pWrapper = g_pBinTools->CreateCall(addr, CallConv_ThisCall, NULL, pass, 1)) + } + + CBaseEntity *pEntity; + if (!(pEntity=GetCBaseEntity(params[1], true))) + { + return pContext->ThrowNativeError("Client index %d is not valid", params[1]); + } + + void *obj = (void *)((uint8_t *)pEntity + playerSharedOffset->GetOffset()); + + unsigned char vstk[sizeof(void *) + sizeof(CBaseEntity *)]; + unsigned char *vptr = vstk; + + *(void **)vptr = obj; + vptr += sizeof(void *); + *(CBaseEntity **)vptr = pEntity; + + pWrapper->Execute(vstk, NULL); + + return 1; +} + +// native TF2_Invuln(client, bool:something, bool:anothersomething) +cell_t TF2_Invuln(IPluginContext *pContext, const cell_t *params) +{ + static ICallWrapper *pWrapper = NULL; + + //CTFPlayerShared::SetInvulnerable(bool, bool) + if (!pWrapper) + { + REGISTER_NATIVE_ADDR("Invuln", + PassInfo pass[2]; \ + pass[0].flags = PASSFLAG_BYVAL; \ + pass[0].size = sizeof(bool); \ + pass[0].type = PassType_Basic; \ + pass[1].flags = PASSFLAG_BYVAL; \ + pass[1].size = sizeof(bool); \ + pass[1].type = PassType_Basic; \ + pWrapper = g_pBinTools->CreateCall(addr, CallConv_ThisCall, NULL, pass, 2)) + } + + CBaseEntity *pEntity; + if (!(pEntity=GetCBaseEntity(params[1], true))) + { + return pContext->ThrowNativeError("Client index %d is not valid", params[1]); + } + + void *obj = (void *)((uint8_t *)pEntity + playerSharedOffset->GetOffset()); + + unsigned char vstk[sizeof(void *) + 2*sizeof(bool)]; + unsigned char *vptr = vstk; + + + *(void **)vptr = obj; + vptr += sizeof(bool); + *(bool *)vptr = !!params[2]; + vptr += sizeof(bool); + *(bool *)vptr = !!params[3]; + + pWrapper->Execute(vstk, NULL); + + return 1; +} + + +sp_nativeinfo_t g_TFNatives[] = +{ + {"TF2_Burn", TF2_Burn}, + {"TF2_Invuln", TF2_Invuln}, + {NULL, NULL} +}; diff --git a/extensions/tf2/sdk/smsdk_config.h b/extensions/tf2/sdk/smsdk_config.h new file mode 100644 index 00000000..09397393 --- /dev/null +++ b/extensions/tf2/sdk/smsdk_config.h @@ -0,0 +1,76 @@ +/** + * vim: set ts=4 : + * ============================================================================= + * SourceMod Counter-Strike:Source Extension + * Copyright (C) 2004-2007 AlliedModders LLC. All rights reserved. + * ============================================================================= + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id$ + */ + +#ifndef _INCLUDE_SOURCEMOD_EXTENSION_CONFIG_H_ +#define _INCLUDE_SOURCEMOD_EXTENSION_CONFIG_H_ + +/** + * @file smsdk_config.h + * @brief Contains macros for configuring basic extension information. + */ + +#include "svn_version.h" + +/* Basic information exposed publicly */ +#define SMEXT_CONF_NAME "TF2 Tools" +#define SMEXT_CONF_DESCRIPTION "TF2 extended functionality" +#define SMEXT_CONF_VERSION SVN_FULL_VERSION +#define SMEXT_CONF_AUTHOR "AlliedModders LLC" +#define SMEXT_CONF_URL "http://www.sourcemod.net/" +#define SMEXT_CONF_LOGTAG "TF2" +#define SMEXT_CONF_LICENSE "GPL" +#define SMEXT_CONF_DATESTRING __DATE__ + +/** + * @brief Exposes plugin's main interface. + */ +#define SMEXT_LINK(name) SDKExtension *g_pExtensionIface = name; + +/** + * @brief Sets whether or not this plugin required Metamod. + * NOTE: Uncomment to enable, comment to disable. + */ +#define SMEXT_CONF_METAMOD + +/** Enable interfaces you want to use here by uncommenting lines */ +//#define SMEXT_ENABLE_FORWARDSYS +//#define SMEXT_ENABLE_HANDLESYS +#define SMEXT_ENABLE_PLAYERHELPERS +//#define SMEXT_ENABLE_DBMANAGER +#define SMEXT_ENABLE_GAMECONF +//#define SMEXT_ENABLE_MEMUTILS +#define SMEXT_ENABLE_GAMEHELPERS +//#define SMEXT_ENABLE_TIMERSYS +//#define SMEXT_ENABLE_THREADER +//#define SMEXT_ENABLE_LIBSYS +//#define SMEXT_ENABLE_USERMSGS + +#endif // _INCLUDE_SOURCEMOD_EXTENSION_CONFIG_H_ diff --git a/extensions/tf2/sdk/smsdk_ext.cpp b/extensions/tf2/sdk/smsdk_ext.cpp new file mode 100644 index 00000000..f5e6f3fc --- /dev/null +++ b/extensions/tf2/sdk/smsdk_ext.cpp @@ -0,0 +1,422 @@ +/** + * vim: set ts=4 : + * ============================================================================= + * SourceMod Base Extension Code + * Copyright (C) 2004-2007 AlliedModders LLC. All rights reserved. + * ============================================================================= + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id$ + */ + +#include +#include +#include "smsdk_ext.h" + +/** + * @file smsdk_ext.cpp + * @brief Contains wrappers for making Extensions easier to write. + */ + +IExtension *myself = NULL; /**< Ourself */ +IShareSys *g_pShareSys = NULL; /**< Share system */ +IShareSys *sharesys = NULL; /**< Share system */ +ISourceMod *g_pSM = NULL; /**< SourceMod helpers */ +ISourceMod *smutils = NULL; /**< SourceMod helpers */ + +#if defined SMEXT_ENABLE_FORWARDSYS +IForwardManager *g_pForwards = NULL; /**< Forward system */ +IForwardManager *forwards = NULL; /**< Forward system */ +#endif +#if defined SMEXT_ENABLE_HANDLESYS +IHandleSys *g_pHandleSys = NULL; /**< Handle system */ +IHandleSys *handlesys = NULL; /**< Handle system */ +#endif +#if defined SMEXT_ENABLE_PLAYERHELPERS +IPlayerManager *playerhelpers = NULL; /**< Player helpers */ +#endif //SMEXT_ENABLE_PLAYERHELPERS +#if defined SMEXT_ENABLE_DBMANAGER +IDBManager *dbi = NULL; /**< DB Manager */ +#endif //SMEXT_ENABLE_DBMANAGER +#if defined SMEXT_ENABLE_GAMECONF +IGameConfigManager *gameconfs = NULL; /**< Game config manager */ +#endif //SMEXT_ENABLE_DBMANAGER +#if defined SMEXT_ENABLE_MEMUTILS +IMemoryUtils *memutils = NULL; +#endif //SMEXT_ENABLE_DBMANAGER +#if defined SMEXT_ENABLE_GAMEHELPERS +IGameHelpers *gamehelpers = NULL; +#endif +#if defined SMEXT_ENABLE_TIMERSYS +ITimerSystem *timersys = NULL; +#endif +#if defined SMEXT_ENABLE_ADTFACTORY +IADTFactory *adtfactory = NULL; +#endif +#if defined SMEXT_ENABLE_THREADER +IThreader *threader = NULL; +#endif +#if defined SMEXT_ENABLE_LIBSYS +ILibrarySys *libsys = NULL; +#endif +#if defined SMEXT_ENABLE_USERMSGS +IUserMessages *usermsgs = NULL; +#endif + +/** Exports the main interface */ +PLATFORM_EXTERN_C IExtensionInterface *GetSMExtAPI() +{ + return g_pExtensionIface; +} + +SDKExtension::SDKExtension() +{ +#if defined SMEXT_CONF_METAMOD + m_SourceMMLoaded = false; + m_WeAreUnloaded = false; + m_WeGotPauseChange = false; +#endif +} + +bool SDKExtension::OnExtensionLoad(IExtension *me, IShareSys *sys, char *error, size_t maxlength, bool late) +{ + g_pShareSys = sharesys = sys; + myself = me; + +#if defined SMEXT_CONF_METAMOD + m_WeAreUnloaded = true; + + if (!m_SourceMMLoaded) + { + if (error) + { + snprintf(error, maxlength, "Metamod attach failed"); + } + return false; + } +#endif + SM_GET_IFACE(SOURCEMOD, g_pSM); + smutils = g_pSM; +#if defined SMEXT_ENABLE_HANDLESYS + SM_GET_IFACE(HANDLESYSTEM, g_pHandleSys); + handlesys = g_pHandleSys; +#endif +#if defined SMEXT_ENABLE_FORWARDSYS + SM_GET_IFACE(FORWARDMANAGER, g_pForwards); + forwards = g_pForwards; +#endif +#if defined SMEXT_ENABLE_PLAYERHELPERS + SM_GET_IFACE(PLAYERMANAGER, playerhelpers); +#endif +#if defined SMEXT_ENABLE_DBMANAGER + SM_GET_IFACE(DBI, dbi); +#endif +#if defined SMEXT_ENABLE_GAMECONF + SM_GET_IFACE(GAMECONFIG, gameconfs); +#endif +#if defined SMEXT_ENABLE_MEMUTILS + SM_GET_IFACE(MEMORYUTILS, memutils); +#endif +#if defined SMEXT_ENABLE_GAMEHELPERS + SM_GET_IFACE(GAMEHELPERS, gamehelpers); +#endif +#if defined SMEXT_ENABLE_TIMERSYS + SM_GET_IFACE(TIMERSYS, timersys); +#endif +#if defined SMEXT_ENABLE_ADTFACTORY + SM_GET_IFACE(ADTFACTORY, adtfactory); +#endif +#if defined SMEXT_ENABLE_THREADER + SM_GET_IFACE(THREADER, threader); +#endif +#if defined SMEXT_ENABLE_LIBSYS + SM_GET_IFACE(LIBRARYSYS, libsys); +#endif +#if defined SMEXT_ENABLE_USERMSGS + SM_GET_IFACE(USERMSGS, usermsgs); +#endif + + if (SDK_OnLoad(error, maxlength, late)) + { +#if defined SMEXT_CONF_METAMOD + m_WeAreUnloaded = true; +#endif + return true; + } + + return false; +} + +bool SDKExtension::IsMetamodExtension() +{ +#if defined SMEXT_CONF_METAMOD + return true; +#else + return false; +#endif +} + +void SDKExtension::OnExtensionPauseChange(bool state) +{ +#if defined SMEXT_CONF_METAMOD + m_WeGotPauseChange = true; +#endif + SDK_OnPauseChange(state); +} + +void SDKExtension::OnExtensionsAllLoaded() +{ + SDK_OnAllLoaded(); +} + +void SDKExtension::OnExtensionUnload() +{ +#if defined SMEXT_CONF_METAMOD + m_WeAreUnloaded = true; +#endif + SDK_OnUnload(); +} + +const char *SDKExtension::GetExtensionAuthor() +{ + return SMEXT_CONF_AUTHOR; +} + +const char *SDKExtension::GetExtensionDateString() +{ + return SMEXT_CONF_DATESTRING; +} + +const char *SDKExtension::GetExtensionDescription() +{ + return SMEXT_CONF_DESCRIPTION; +} + +const char *SDKExtension::GetExtensionVerString() +{ + return SMEXT_CONF_VERSION; +} + +const char *SDKExtension::GetExtensionName() +{ + return SMEXT_CONF_NAME; +} + +const char *SDKExtension::GetExtensionTag() +{ + return SMEXT_CONF_LOGTAG; +} + +const char *SDKExtension::GetExtensionURL() +{ + return SMEXT_CONF_URL; +} + +bool SDKExtension::SDK_OnLoad(char *error, size_t maxlength, bool late) +{ + return true; +} + +void SDKExtension::SDK_OnUnload() +{ +} + +void SDKExtension::SDK_OnPauseChange(bool paused) +{ +} + +void SDKExtension::SDK_OnAllLoaded() +{ +} + +#if defined SMEXT_CONF_METAMOD + +PluginId g_PLID = 0; /**< Metamod plugin ID */ +ISmmPlugin *g_PLAPI = NULL; /**< Metamod plugin API */ +SourceHook::ISourceHook *g_SHPtr = NULL; /**< SourceHook pointer */ +ISmmAPI *g_SMAPI = NULL; /**< SourceMM API pointer */ + +IVEngineServer *engine = NULL; /**< IVEngineServer pointer */ +IServerGameDLL *gamedll = NULL; /**< IServerGameDLL pointer */ + +/** Exposes the extension to Metamod */ +SMM_API void *PL_EXPOSURE(const char *name, int *code) +{ + if (name && !strcmp(name, METAMOD_PLAPI_NAME)) + { + if (code) + { + *code = IFACE_OK; + } + return static_cast(g_pExtensionIface); + } + + if (code) + { + *code = IFACE_FAILED; + } + + return NULL; +} + +bool SDKExtension::Load(PluginId id, ISmmAPI *ismm, char *error, size_t maxlen, bool late) +{ + PLUGIN_SAVEVARS(); + + GET_V_IFACE_ANY(GetServerFactory, gamedll, IServerGameDLL, INTERFACEVERSION_SERVERGAMEDLL); + GET_V_IFACE_CURRENT(GetEngineFactory, engine, IVEngineServer, INTERFACEVERSION_VENGINESERVER); + + m_SourceMMLoaded = true; + + return SDK_OnMetamodLoad(ismm, error, maxlen, late); +} + +bool SDKExtension::Unload(char *error, size_t maxlen) +{ + if (!m_WeAreUnloaded) + { + if (error) + { + snprintf(error, maxlen, "This extension must be unloaded by SourceMod."); + } + return false; + } + + return SDK_OnMetamodUnload(error, maxlen); +} + +bool SDKExtension::Pause(char *error, size_t maxlen) +{ + if (!m_WeGotPauseChange) + { + if (error) + { + snprintf(error, maxlen, "This extension must be paused by SourceMod."); + } + return false; + } + + m_WeGotPauseChange = false; + + return SDK_OnMetamodPauseChange(true, error, maxlen); +} + +bool SDKExtension::Unpause(char *error, size_t maxlen) +{ + if (!m_WeGotPauseChange) + { + if (error) + { + snprintf(error, maxlen, "This extension must be unpaused by SourceMod."); + } + return false; + } + + m_WeGotPauseChange = false; + + return SDK_OnMetamodPauseChange(false, error, maxlen); +} + +const char *SDKExtension::GetAuthor() +{ + return GetExtensionAuthor(); +} + +const char *SDKExtension::GetDate() +{ + return GetExtensionDateString(); +} + +const char *SDKExtension::GetDescription() +{ + return GetExtensionDescription(); +} + +const char *SDKExtension::GetLicense() +{ + return SMEXT_CONF_LICENSE; +} + +const char *SDKExtension::GetLogTag() +{ + return GetExtensionTag(); +} + +const char *SDKExtension::GetName() +{ + return GetExtensionName(); +} + +const char *SDKExtension::GetURL() +{ + return GetExtensionURL(); +} + +const char *SDKExtension::GetVersion() +{ + return GetExtensionVerString(); +} + +bool SDKExtension::SDK_OnMetamodLoad(ISmmAPI *ismm, char *error, size_t maxlength, bool late) +{ + return true; +} + +bool SDKExtension::SDK_OnMetamodUnload(char *error, size_t maxlength) +{ + return true; +} + +bool SDKExtension::SDK_OnMetamodPauseChange(bool paused, char *error, size_t maxlength) +{ + return true; +} + +#endif + +/* Overload a few things to prevent libstdc++ linking */ +#if defined __linux__ +extern "C" void __cxa_pure_virtual(void) +{ +} + +void *operator new(size_t size) +{ + return malloc(size); +} + +void *operator new[](size_t size) +{ + return malloc(size); +} + +void operator delete(void *ptr) +{ + free(ptr); +} + +void operator delete[](void * ptr) +{ + free(ptr); +} +#endif diff --git a/extensions/tf2/sdk/smsdk_ext.h b/extensions/tf2/sdk/smsdk_ext.h new file mode 100644 index 00000000..c137616b --- /dev/null +++ b/extensions/tf2/sdk/smsdk_ext.h @@ -0,0 +1,310 @@ +/** + * vim: set ts=4 : + * ============================================================================= + * SourceMod Base Extension Code + * Copyright (C) 2004-2007 AlliedModders LLC. All rights reserved. + * ============================================================================= + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id$ + */ + +#ifndef _INCLUDE_SOURCEMOD_EXTENSION_BASESDK_H_ +#define _INCLUDE_SOURCEMOD_EXTENSION_BASESDK_H_ + +/** + * @file smsdk_ext.h + * @brief Contains wrappers for making Extensions easier to write. + */ + +#include "smsdk_config.h" +#include +#include +#include +#include +#include +#if defined SMEXT_ENABLE_FORWARDSYS +#include +#endif //SMEXT_ENABLE_FORWARDSYS +#if defined SMEXT_ENABLE_PLAYERHELPERS +#include +#endif //SMEXT_ENABLE_PlAYERHELPERS +#if defined SMEXT_ENABLE_DBMANAGER +#include +#endif //SMEXT_ENABLE_DBMANAGER +#if defined SMEXT_ENABLE_GAMECONF +#include +#endif +#if defined SMEXT_ENABLE_MEMUTILS +#include +#endif +#if defined SMEXT_ENABLE_GAMEHELPERS +#include +#endif +#if defined SMEXT_ENABLE_TIMERSYS +#include +#endif +#if defined SMEXT_ENABLE_ADTFACTORY +#include +#endif +#if defined SMEXT_ENABLE_THREADER +#include +#endif +#if defined SMEXT_ENABLE_LIBSYS +#include +#endif +#if defined SMEXT_ENABLE_USERMSGS +#include +#endif + +#if defined SMEXT_CONF_METAMOD +#include +#include +#endif + +#if !defined METAMOD_PLAPI_VERSION + #include +#endif + +using namespace SourceMod; +using namespace SourcePawn; + +class SDKExtension : +#if defined SMEXT_CONF_METAMOD + public ISmmPlugin, +#endif + public IExtensionInterface +{ +public: + /** Constructor */ + SDKExtension(); +public: + /** + * @brief This is called after the initial loading sequence has been processed. + * + * @param error Error message buffer. + * @param maxlength Size of error message buffer. + * @param late Whether or not the module was loaded after map load. + * @return True to succeed loading, false to fail. + */ + virtual bool SDK_OnLoad(char *error, size_t maxlength, bool late); + + /** + * @brief This is called right before the extension is unloaded. + */ + virtual void SDK_OnUnload(); + + /** + * @brief This is called once all known extensions have been loaded. + */ + virtual void SDK_OnAllLoaded(); + + /** + * @brief Called when the pause state is changed. + */ + virtual void SDK_OnPauseChange(bool paused); + +#if defined SMEXT_CONF_METAMOD + /** + * @brief Called when Metamod is attached, before the extension version is called. + * + * @param error Error buffer. + * @param maxlength Maximum size of error buffer. + * @param late Whether or not Metamod considers this a late load. + * @return True to succeed, false to fail. + */ + virtual bool SDK_OnMetamodLoad(ISmmAPI *ismm, char *error, size_t maxlength, bool late); + + /** + * @brief Called when Metamod is detaching, after the extension version is called. + * NOTE: By default this is blocked unless sent from SourceMod. + * + * @param error Error buffer. + * @param maxlength Maximum size of error buffer. + * @return True to succeed, false to fail. + */ + virtual bool SDK_OnMetamodUnload(char *error, size_t maxlength); + + /** + * @brief Called when Metamod's pause state is changing. + * NOTE: By default this is blocked unless sent from SourceMod. + * + * @param paused Pause state being set. + * @param error Error buffer. + * @param maxlength Maximum size of error buffer. + * @return True to succeed, false to fail. + */ + virtual bool SDK_OnMetamodPauseChange(bool paused, char *error, size_t maxlength); +#endif + +public: //IExtensionInterface + virtual bool OnExtensionLoad(IExtension *me, IShareSys *sys, char *error, size_t maxlength, bool late); + virtual void OnExtensionUnload(); + virtual void OnExtensionsAllLoaded(); + + /** Returns whether or not this is a Metamod-based extension */ + virtual bool IsMetamodExtension(); + + /** + * @brief Called when the pause state changes. + * + * @param state True if being paused, false if being unpaused. + */ + virtual void OnExtensionPauseChange(bool state); + + /** Returns name */ + virtual const char *GetExtensionName(); + /** Returns URL */ + virtual const char *GetExtensionURL(); + /** Returns log tag */ + virtual const char *GetExtensionTag(); + /** Returns author */ + virtual const char *GetExtensionAuthor(); + /** Returns version string */ + virtual const char *GetExtensionVerString(); + /** Returns description string */ + virtual const char *GetExtensionDescription(); + /** Returns date string */ + virtual const char *GetExtensionDateString(); +#if defined SMEXT_CONF_METAMOD +public: //ISmmPlugin + /** Called when the extension is attached to Metamod. */ + virtual bool Load(PluginId id, ISmmAPI *ismm, char *error, size_t maxlength, bool late); + /** Returns the author to MM */ + virtual const char *GetAuthor(); + /** Returns the name to MM */ + virtual const char *GetName(); + /** Returns the description to MM */ + virtual const char *GetDescription(); + /** Returns the URL to MM */ + virtual const char *GetURL(); + /** Returns the license to MM */ + virtual const char *GetLicense(); + /** Returns the version string to MM */ + virtual const char *GetVersion(); + /** Returns the date string to MM */ + virtual const char *GetDate(); + /** Returns the logtag to MM */ + virtual const char *GetLogTag(); + /** Called on unload */ + virtual bool Unload(char *error, size_t maxlength); + /** Called on pause */ + virtual bool Pause(char *error, size_t maxlength); + /** Called on unpause */ + virtual bool Unpause(char *error, size_t maxlength); +private: + bool m_SourceMMLoaded; + bool m_WeAreUnloaded; + bool m_WeGotPauseChange; +#endif +}; + +extern SDKExtension *g_pExtensionIface; +extern IExtension *myself; + +extern IShareSys *g_pShareSys; +extern IShareSys *sharesys; /* Note: Newer name */ +extern ISourceMod *g_pSM; +extern ISourceMod *smutils; /* Note: Newer name */ + +/* Optional interfaces are below */ +#if defined SMEXT_ENABLE_FORWARDSYS +extern IForwardManager *g_pForwards; +extern IForwardManager *forwards; /* Note: Newer name */ +#endif //SMEXT_ENABLE_FORWARDSYS +#if defined SMEXT_ENABLE_HANDLESYS +extern IHandleSys *g_pHandleSys; +extern IHandleSys *handlesys; /* Note: Newer name */ +#endif //SMEXT_ENABLE_HANDLESYS +#if defined SMEXT_ENABLE_PLAYERHELPERS +extern IPlayerManager *playerhelpers; +#endif //SMEXT_ENABLE_PLAYERHELPERS +#if defined SMEXT_ENABLE_DBMANAGER +extern IDBManager *dbi; +#endif //SMEXT_ENABLE_DBMANAGER +#if defined SMEXT_ENABLE_GAMECONF +extern IGameConfigManager *gameconfs; +#endif //SMEXT_ENABLE_DBMANAGER +#if defined SMEXT_ENABLE_MEMUTILS +extern IMemoryUtils *memutils; +#endif +#if defined SMEXT_ENABLE_GAMEHELPERS +extern IGameHelpers *gamehelpers; +#endif +#if defined SMEXT_ENABLE_TIMERSYS +extern ITimerSystem *timersys; +#endif +#if defined SMEXT_ENABLE_ADTFACTORY +extern IADTFactory *adtfactory; +#endif +#if defined SMEXT_ENABLE_THREADER +extern IThreader *threader; +#endif +#if defined SMEXT_ENABLE_LIBSYS +extern ILibrarySys *libsys; +#endif +#if defined SMEXT_ENABLE_USERMSGS +extern IUserMessages *usermsgs; +#endif + +#if defined SMEXT_CONF_METAMOD +PLUGIN_GLOBALVARS(); +extern IVEngineServer *engine; +extern IServerGameDLL *gamedll; +#endif + +/** Creates a SourceMod interface macro pair */ +#define SM_MKIFACE(name) SMINTERFACE_##name##_NAME, SMINTERFACE_##name##_VERSION +/** Automates retrieving SourceMod interfaces */ +#define SM_GET_IFACE(prefix, addr) \ + if (!g_pShareSys->RequestInterface(SM_MKIFACE(prefix), myself, (SMInterface **)&addr)) \ + { \ + if (error != NULL && maxlength) \ + { \ + size_t len = snprintf(error, maxlength, "Could not find interface: %s", SMINTERFACE_##prefix##_NAME); \ + if (len >= maxlength) \ + { \ + error[maxlength - 1] = '\0'; \ + } \ + } \ + return false; \ + } +/** Automates retrieving SourceMod interfaces when needed outside of SDK_OnLoad() */ +#define SM_GET_LATE_IFACE(prefix, addr) \ + g_pShareSys->RequestInterface(SM_MKIFACE(prefix), myself, (SMInterface **)&addr) +/** Validates a SourceMod interface pointer */ +#define SM_CHECK_IFACE(prefix, addr) \ + if (!addr) \ + { \ + if (error != NULL && maxlength) \ + { \ + size_t len = snprintf(error, maxlength, "Could not find interface: %s", SMINTERFACE_##prefix##_NAME); \ + if (len >= maxlength) \ + { \ + error[maxlength - 1] = '\0'; \ + } \ + } \ + return false; \ + } + +#endif // _INCLUDE_SOURCEMOD_EXTENSION_BASESDK_H_ diff --git a/extensions/tf2/sm-tf2.games.txt b/extensions/tf2/sm-tf2.games.txt new file mode 100644 index 00000000..3adc88b2 --- /dev/null +++ b/extensions/tf2/sm-tf2.games.txt @@ -0,0 +1,21 @@ +"Games" +{ + "tf" + { + "Signatures" + { + "Burn" + { + "library" "server" + "windows" "\x56\x8B\xF1\x8B\x4E\x7C\x8B\x01\x8B\x90\xF8\x00\x00\x00\xFF\xD2\x84\xC0\x2A\x2A\x2A\x2A\x2A\x2A\x8B\x46\x7C" + "linux" "@_ZN15CTFPlayerShared4BurnEP9CTFPlayer" + } + "Invuln" + { + "library" "server" + "windows" "\x8A\x44\x24\x04\x83\xEC\x20\x56\x8B\xF1\x8B\x4E\x08\x57\x8D\x7E\x08\xC1\xE9\x05\x80\xE1\x01\x3A\xC8\x75\x32\x84\xC0" + "linux" "@_ZN15CTFPlayerShared15SetInvulnerableEbb" + } + } + } +} \ No newline at end of file diff --git a/extensions/tf2/svn_version.h b/extensions/tf2/svn_version.h new file mode 100644 index 00000000..8873263e --- /dev/null +++ b/extensions/tf2/svn_version.h @@ -0,0 +1,42 @@ +/** + * vim: set ts=4 : + * ============================================================================= + * SourceMod SDKTools Extension + * Copyright (C) 2004-2007 AlliedModders LLC. All rights reserved. + * ============================================================================= + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id$ + */ + +/** + * Autogenerated by build scripts + */ + +#ifndef _INCLUDE_SDKTOOLS_VERSION_H_ +#define _INCLUDE_SDKTOOLS_VERSION_H_ + +#define SVN_FULL_VERSION "1.0.0.1336" +#define SVN_FILE_VERSION 1,0,0,1336 + +#endif //_INCLUDE_SDKTOOLS_VERSION_H_ diff --git a/extensions/tf2/svn_version.tpl b/extensions/tf2/svn_version.tpl new file mode 100644 index 00000000..ad458193 --- /dev/null +++ b/extensions/tf2/svn_version.tpl @@ -0,0 +1,42 @@ +/** + * vim: set ts=4 : + * ============================================================================= + * SourceMod SDKTools Extension + * Copyright (C) 2004-2007 AlliedModders LLC. All rights reserved. + * ============================================================================= + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id$ + */ + +/** + * Autogenerated by build scripts + */ + +#ifndef _INCLUDE_SDKTOOLS_VERSION_H_ +#define _INCLUDE_SDKTOOLS_VERSION_H_ + +#define SVN_FULL_VERSION "$PMAJOR$.$PMINOR$.$PREVISION$.$LOCAL_BUILD$" +#define SVN_FILE_VERSION $PMAJOR$,$PMINOR$,$PREVISION$,$LOCAL_BUILD$ + +#endif //_INCLUDE_SDKTOOLS_VERSION_H_ diff --git a/extensions/tf2/tf2-test.sp b/extensions/tf2/tf2-test.sp new file mode 100644 index 00000000..b1239543 --- /dev/null +++ b/extensions/tf2/tf2-test.sp @@ -0,0 +1,55 @@ +#include +#include "tf2.inc" + +public Plugin:myinfo = +{ + name = "TF2 Test", + author = "pRED*", + description = "Test of Tf2 functions", + version = "1.0", + url = "www.sourcemod.net" +} + +public OnPluginStart() +{ + RegConsoleCmd("sm_burnme", Command_Burn); + RegConsoleCmd("sm_invuln", Command_Invuln); +} + +public Action:Command_Burn(client, args) +{ + if (client == 0) + { + return Plugin_Continue; + } + + TF2_Burn(client); + + return Plugin_Continue; +} + +public Action:Command_Invuln(client, args) +{ + if (client == 0) + { + return Plugin_Continue; + } + + if (args < 2) + { + return Plugin_Continue; + } + + decl String:text[10]; + decl String:text2[10]; + GetCmdArg(1, text, sizeof(text)); + GetCmdArg(2, text2, sizeof(text2)); + + new bool:one = !!StringToInt(text); + new bool:two = !!StringToInt(text2); + + TF2_Invuln(client, one, two) + + return Plugin_Continue; +} + diff --git a/extensions/tf2/tf2.inc b/extensions/tf2/tf2.inc new file mode 100644 index 00000000..c2c4c39a --- /dev/null +++ b/extensions/tf2/tf2.inc @@ -0,0 +1,24 @@ +#if defined _tf2_included + #endinput +#endif +#define _tf2_included + + +native TF2_Invuln(client, bool:something, bool:anothersomething); + +native TF2_Burn(client); + +/** + * Do not edit below this line! + */ +public Extension:__ext_tf2 = +{ + name = "TF2 Tools", + file = "game.tf2.ext", + autoload = 0, +#if defined REQUIRE_EXTENSIONS + required = 1, +#else + required = 0, +#endif +}; diff --git a/extensions/tf2/version.rc b/extensions/tf2/version.rc new file mode 100644 index 00000000..37d14dad --- /dev/null +++ b/extensions/tf2/version.rc @@ -0,0 +1,104 @@ +// Microsoft Visual C++ generated resource script. +// +//#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "winres.h" + +#include "svn_version.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION SVN_FILE_VERSION + PRODUCTVERSION SVN_FILE_VERSION + FILEFLAGSMASK 0x17L +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x4L + FILETYPE 0x2L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "000004b0" + BEGIN + VALUE "Comments", "SourceMod CS:S Extension" + VALUE "FileDescription", "SourceMod CS:S Extension" + VALUE "FileVersion", SVN_FULL_VERSION + VALUE "InternalName", "SourceMod CS:S Extension" + VALUE "LegalCopyright", "Copyright (c) 2004-2007, AlliedModders LLC" + VALUE "OriginalFilename", "game.cstrike.ext.dll" + VALUE "ProductName", "SourceMod CS:S Extension" + VALUE "ProductVersion", SVN_FULL_VERSION + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x0, 1200 + END +END + + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""winres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/plugins/adminmenu.sp b/plugins/adminmenu.sp index 8a68b46c..d3985ed5 100644 --- a/plugins/adminmenu.sp +++ b/plugins/adminmenu.sp @@ -45,6 +45,8 @@ public Plugin:myinfo = url = "http://www.sourcemod.net/" }; +new g_maxPlayers; + /* Forwards */ new Handle:hOnAdminMenuReady = INVALID_HANDLE; new Handle:hOnAdminMenuCreated = INVALID_HANDLE; @@ -57,6 +59,8 @@ new TopMenuObject:obj_playercmds = INVALID_TOPMENUOBJECT; new TopMenuObject:obj_servercmds = INVALID_TOPMENUOBJECT; new TopMenuObject:obj_votingcmds = INVALID_TOPMENUOBJECT; +#include "adminmenu/dynamicmenu.sp" + public bool:AskPluginLoad(Handle:myself, bool:late, String:error[], err_max) { CreateNative("GetAdminTopMenu", __GetAdminTopMenu); @@ -91,27 +95,36 @@ public OnConfigsExecuted() } } +public OnMapStart() +{ + g_maxPlayers = GetMaxClients(); + + ParseConfigs(); +} + public OnAllPluginsLoaded() { - hAdminMenu = CreateTopMenu(CategoryHandler); + hAdminMenu = CreateTopMenu(DefaultCategoryHandler); obj_playercmds = AddToTopMenu(hAdminMenu, "PlayerCommands", TopMenuObject_Category, - CategoryHandler, + DefaultCategoryHandler, INVALID_TOPMENUOBJECT); obj_servercmds = AddToTopMenu(hAdminMenu, "ServerCommands", TopMenuObject_Category, - CategoryHandler, + DefaultCategoryHandler, INVALID_TOPMENUOBJECT); obj_votingcmds = AddToTopMenu(hAdminMenu, "VotingCommands", TopMenuObject_Category, - CategoryHandler, + DefaultCategoryHandler, INVALID_TOPMENUOBJECT); + + BuildDynamicMenu(); Call_StartForward(hOnAdminMenuCreated); Call_PushCell(hAdminMenu); @@ -122,7 +135,7 @@ public OnAllPluginsLoaded() Call_Finish(); } -public CategoryHandler(Handle:topmenu, +public DefaultCategoryHandler(Handle:topmenu, TopMenuAction:action, TopMenuObject:object_id, param, diff --git a/plugins/adminmenu/dynamicmenu.sp b/plugins/adminmenu/dynamicmenu.sp new file mode 100644 index 00000000..fcd8ec63 --- /dev/null +++ b/plugins/adminmenu/dynamicmenu.sp @@ -0,0 +1,520 @@ + +#define NAME_LENGTH 32 +#define CMD_LENGTH 255 + +#define ARRAY_STRING_LENGTH 32 + +new Handle:g_kvMenu; + +enum GroupCommands +{ + Handle:groupListName, + Handle:groupListCommand +}; + +new g_groupList[GroupCommands]; +new g_groupCount; + +new Handle:g_configParser = INVALID_HANDLE; + +enum Places +{ + Place_Category, + Place_Item, + Place_ReplaceNum +}; + +new String:g_command[MAXPLAYERS+1][CMD_LENGTH]; +new g_currentPlace[MAXPLAYERS+1][Places]; + +/** + * What to put in the 'info' menu field (for PlayerList and Player_Team menus only) + */ +enum PlayerMethod +{ + ClientId, /** Client id number ( 1 - Maxplayers) */ + UserId, /** Client userid */ + Name, /** Client Name */ + SteamId, /** Client Steamid */ + IpAddress, /** Client's Ip Address */ + UserId2 /** Userid (not prefixed with #) */ +}; + +BuildDynamicMenu() +{ + if (g_kvMenu != INVALID_HANDLE) + { + CloseHandle(g_kvMenu); + } + + g_kvMenu = CreateKeyValues("Commands"); + new String:file[256]; + BuildPath(Path_SM, file, 255, "configs/dynamicmenu/menu.ini"); + FileToKeyValues(g_kvMenu, file); + + new String:name[NAME_LENGTH]; + new String:buffer[NAME_LENGTH]; + + + if (!KvGotoFirstSubKey(g_kvMenu)) + { + return; + } + + decl String:admin[30]; + + new TopMenuObject:categoryId; + + new catId; + new id; + + do + { + KvGetSectionName(g_kvMenu, buffer, sizeof(buffer)); + + KvGetString(g_kvMenu, "admin", admin, sizeof(admin),"sm_admin"); + + if ((categoryId =FindTopMenuCategory(hAdminMenu, buffer)) == INVALID_TOPMENUOBJECT) + { + categoryId = AddToTopMenu(hAdminMenu, + buffer, + TopMenuObject_Category, + DynamicMenuCategoryHandler, + INVALID_TOPMENUOBJECT, + admin, + ADMFLAG_GENERIC, + name); + + } + + if (!KvGetSectionSymbol(g_kvMenu, catId)) + { + LogError("Key Id not found for section: %s", buffer); + break; + } + + if (!KvGotoFirstSubKey(g_kvMenu)) + { + return; + } + + do + { + KvGetSectionName(g_kvMenu, buffer, sizeof(buffer)); + + KvGetString(g_kvMenu, "admin", admin, sizeof(admin),""); + + if (admin[0] == '\0') + { + //No 'admin' keyvalue was found + //Use the first argument of the 'cmd' string instead + + decl String:temp[64]; + KvGetString(g_kvMenu, "cmd", temp, sizeof(temp),""); + + BreakString(temp, admin, sizeof(admin)); + } + + if (!KvGetSectionSymbol(g_kvMenu, id)) + { + LogError("Key Id not found for section: %s"); + break; + } + + decl String:keyId[64]; + + Format(keyId, sizeof(keyId), "%i %i", catId, id); + + AddToTopMenu(hAdminMenu, + buffer, + TopMenuObject_Item, + DynamicMenuItemHandler, + categoryId, + admin, + ADMFLAG_GENERIC, + keyId); + + } while (KvGotoNextKey(g_kvMenu)); + + KvGoBack(g_kvMenu); + + } while (KvGotoNextKey(g_kvMenu)); + + KvRewind(g_kvMenu); +} + +ParseConfigs() +{ + if (g_configParser == INVALID_HANDLE) + { + g_configParser = SMC_CreateParser(); + } + + SMC_SetReaders(g_configParser, NewSection, KeyValue, EndSection); + + if (g_groupList[groupListName] != INVALID_HANDLE) + { + CloseHandle(g_groupList[groupListName]); + } + + if (g_groupList[groupListCommand] != INVALID_HANDLE) + { + CloseHandle(g_groupList[groupListCommand]); + } + + g_groupList[groupListName] = CreateArray(ARRAY_STRING_LENGTH); + g_groupList[groupListCommand] = CreateArray(ARRAY_STRING_LENGTH); + + decl String:configPath[256]; + BuildPath(Path_SM, configPath, sizeof(configPath), "configs/dynamicmenu/adminmenu_grouping.txt"); + + if (!FileExists(configPath)) + { + LogError("Unable to locate admin menu groups file, no groups loaded."); + + return; + } + + new line; + new SMCError:err = SMC_ParseFile(g_configParser, configPath, line); + if (err != SMCError_Okay) + { + decl String:error[256]; + SMC_GetErrorString(err, error, sizeof(error)); + LogError("Could not parse file (line %d, file \"%s\"):", line, configPath); + LogError("Parser encountered error: %s", error); + } + + return; +} + +public SMCResult:NewSection(Handle:smc, const String:name[], bool:opt_quotes) +{ + +} + +public SMCResult:KeyValue(Handle:smc, const String:key[], const String:value[], bool:key_quotes, bool:value_quotes) +{ + PushArrayString(g_groupList[groupListName], key); + PushArrayString(g_groupList[groupListCommand], value); +} + +public SMCResult:EndSection(Handle:smc) +{ + g_groupCount = GetArraySize(g_groupList[groupListName]); +} + +public DynamicMenuCategoryHandler(Handle:topmenu, + TopMenuAction:action, + TopMenuObject:object_id, + param, + String:buffer[], + maxlength) +{ + if ((action == TopMenuAction_DisplayTitle) || (action == TopMenuAction_DisplayOption)) + { + GetTopMenuObjName(topmenu, object_id, buffer, maxlength); + } +} + +public DynamicMenuItemHandler(Handle:topmenu, + TopMenuAction:action, + TopMenuObject:object_id, + param, + String:buffer[], + maxlength) +{ + if (action == TopMenuAction_DisplayOption) + { + GetTopMenuObjName(topmenu, object_id, buffer, maxlength); + } + else if (action == TopMenuAction_SelectOption) + { + new String:keyId[64]; + new String:catId[64]; + GetTopMenuInfoString(topmenu, object_id, keyId, sizeof(keyId)); + + new start = BreakString(keyId, catId, sizeof(catId)); + + new id = StringToInt(keyId[start]); + new category = StringToInt(catId); + + KvJumpToKeySymbol(g_kvMenu, category); + KvJumpToKeySymbol(g_kvMenu, id); + + KvGetString(g_kvMenu, "cmd", g_command[param], sizeof(g_command[]),""); + KvRewind(g_kvMenu); + + g_currentPlace[param][Place_Category] = category; + g_currentPlace[param][Place_Item] = id; + + ParamCheck(param); + } +} + +public ParamCheck(client) +{ + new String:buffer[6]; + new String:buffer2[6]; + + KvJumpToKeySymbol(g_kvMenu, g_currentPlace[client][Place_Category]); + KvJumpToKeySymbol(g_kvMenu, g_currentPlace[client][Place_Item]); + + new String:type[NAME_LENGTH]; + + if (g_currentPlace[client][Place_ReplaceNum] < 1) + { + g_currentPlace[client][Place_ReplaceNum] = 1; + } + + Format(buffer, 5, "#%i", g_currentPlace[client][Place_ReplaceNum]); + Format(buffer2, 5, "@%i", g_currentPlace[client][Place_ReplaceNum]); + + if (StrContains(g_command[client], buffer) != -1 || StrContains(g_command[client], buffer2) != -1) + { + //user has a parameter to fill. lets do it. + KvJumpToKey(g_kvMenu, buffer[1]); // Jump to current param + KvGetString(g_kvMenu, "type", type, sizeof(type),"list"); + + PrintToChatAll("Checking param %s - type %s", buffer[1], type); + + new Handle:itemMenu = CreateMenu(Menu_Selection); + + new String:title[NAME_LENGTH]; + + if (strncmp(type,"group",5)==0 && g_groupCount) + { + decl String:nameBuffer[ARRAY_STRING_LENGTH]; + decl String:commandBuffer[ARRAY_STRING_LENGTH]; + + for (new i = 0; i. + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id$ + */ + +#pragma semicolon 1 +#include + +public Plugin:myinfo = +{ + name = "Map Manager", + author = "AlliedModders LLC", + description = "Map Management", + version = SOURCEMOD_VERSION, + url = "http://www.sourcemod.net/" +}; + +#include "mapmanagement/globals.sp" +#include "mapmanagement/commands.sp" +#include "mapmanagement/events.sp" +#include "mapmanagement/functions.sp" +#include "mapmanagement/menus.sp" +#include "mapmanagement/timers.sp" +#include "mapmanagement/votes.sp" + +public OnPluginStart() +{ + LoadTranslations("mapmanager.phrases"); + + // Prepare nextmap functionality. + g_VGUIMenu = GetUserMessageId("VGUIMenu"); + if (g_VGUIMenu == INVALID_MESSAGE_ID) + { + LogError("FATAL: Cannot find VGUIMenu user message id. MapManager crippled."); + g_NextMapEnabled = false; + } + HookUserMessage(g_VGUIMenu, UserMsg_VGUIMenu); + + // Create all of the arrays, sized for a 64 character string. + new arraySize = ByteCountToCells(64); + g_MapCycle = CreateArray(arraySize); + g_MapList = CreateArray(arraySize); + g_MapHistory = CreateArray(arraySize); + g_NextVoteMaps = CreateArray(arraySize); + g_SelectedMaps = CreateArray(arraySize); + g_NominatedMaps = CreateArray(arraySize); + + g_TeamScores = CreateArray(2); + + // Hook say + RegConsoleCmd("say", Command_Say); + RegConsoleCmd("say_team", Command_Say); + + // Register all commands. + RegAdminCmd("sm_map", Command_Map, ADMFLAG_CHANGEMAP, "sm_map [r/e]"); + RegAdminCmd("sm_setnextmap", Command_SetNextmap, ADMFLAG_CHANGEMAP, "sm_setnextmap "); + RegAdminCmd("sm_votemap", Command_Votemap, ADMFLAG_VOTE|ADMFLAG_CHANGEMAP, "sm_votemap [mapname2] ... [mapname5] "); + RegAdminCmd("sm_maplist", Command_List, ADMFLAG_GENERIC, "sm_maplist"); + RegAdminCmd("sm_nominate", Command_Addmap, ADMFLAG_CHANGEMAP, "sm_nominate - Nominates a map for RockTheVote and MapChooser. Overrides existing nominations."); + RegAdminCmd("sm_mapvote", Command_Mapvote, ADMFLAG_CHANGEMAP, "sm_mapvote - Forces the MapChooser vote to occur."); + + if (GetCommandFlags("nextmap") == INVALID_FCVAR_FLAGS) + { + RegServerCmd("nextmap", Command_Nextmap); + } + + // Register all convars + g_Cvar_Nextmap = CreateConVar("sm_nextmap", "", "Sets the Next Map", FCVAR_NOTIFY); + + g_Cvar_MapCount = CreateConVar("sm_mm_maps", "4", "Number of maps to be voted on at end of map or RTV. 2 to 6. (Def 4)", 0, true, 2.0, true, 6.0); + g_Cvar_Excludes = CreateConVar("sm_mm_exclude", "5", "Specifies how many past maps to exclude from end of map vote and RTV.", _, true, 0.0); + + g_Cvar_MapChooser = CreateConVar("sm_mm_mapchooser", "0", "Enables MapChooser (End of Map voting)", 0, true, 0.0, true, 1.0); + g_Cvar_RockTheVote = CreateConVar("sm_mm_rockthevote", "0", "Enables RockTheVote (Player initiated map votes)", 0, true, 0.0, true, 1.0); + g_Cvar_Randomize = CreateConVar("sm_mm_randomize", "0", "Enabled Randomizer (Randomly picks the next map)", 0, true, 0.0, true, 1.0); + g_Cvar_Nominate = CreateConVar("sm_mm_nominate", "1", "Enables nomination system.", 0, true, 0.0, true, 1.0); + + g_Cvar_VoteMap = CreateConVar("sm_mm_votemap", "0.60", "Percentage required for successful sm_votemap.", 0, true, 0.05, true, 1.0); + g_Cvar_RTVLimit = CreateConVar("sm_mm_rtvlimit", "0.60", "Required percentage of players needed to rockthevote", 0, true, 0.05, true, 1.0); + g_Cvar_MinPlayers = CreateConVar("sm_mm_minplayers", "0", "Number of players required before RTV will be enabled.", 0, true, 0.0, true, float(MAXPLAYERS)); + + g_Cvar_StartTime = CreateConVar("sm_mapvote_start", "3.0", "Specifies when to start the vote based on time remaining.", _, true, 1.0); + g_Cvar_StartRounds = CreateConVar("sm_mapvote_startround", "2.0", "Specifies when to start the vote based on rounds remaining.", _, true, 1.0); + g_Cvar_StartFrags = CreateConVar("sm_mapvote_startfrags", "5.0", "Specifies when to start the vote base on frags remaining.", _, true, 1.0); + g_Cvar_ExtendTimeMax = CreateConVar("sm_extendmap_maxtime", "90", "Specifies the maximum amount of time a map can be extended", _, true, 0.0); + g_Cvar_ExtendTimeStep = CreateConVar("sm_extendmap_timestep", "15", "Specifies how much many more minutes each extension makes", _, true, 5.0); + g_Cvar_ExtendRoundMax = CreateConVar("sm_extendmap_maxrounds", "30", "Specfies the maximum amount of rounds a map can be extended", _, true, 0.0); + g_Cvar_ExtendRoundStep = CreateConVar("sm_extendmap_roundstep", "5", "Specifies how many more rounds each extension makes", _, true, 5.0); + g_Cvar_ExtendFragMax = CreateConVar("sm_extendmap_maxfrags", "150", "Specfies the maximum frags allowed that a map can be extended.", _, true, 0.0); + g_Cvar_ExtendFragStep = CreateConVar("sm_extendmap_fragstep", "10", "Specifies how many more frags are allowed when map is extended.", _, true, 5.0); + g_Cvar_Mapfile = CreateConVar("sm_mapvote_file", "configs/maps.ini", "Map file to use. (Def sourcemod/configs/maps.ini)"); + g_Cvar_ExcludeMaps = CreateConVar("sm_mapvote_exclude", "5", "Specifies how many past maps to exclude from the vote.", _, true, 0.0); + g_Cvar_IncludeMaps = CreateConVar("sm_mapvote_include", "5", "Specifies how many maps to include in the vote.", _, true, 2.0, true, 6.0); + g_Cvar_NoVoteMode = CreateConVar("sm_mapvote_novote", "1", "Specifies whether or not MapChooser should pick a map if no votes are received.", _, true, 0.0, true, 1.0); + g_Cvar_Extend = CreateConVar("sm_mapvote_extend", "1", "Specifies whether or not MapChooser will allow the map to be extended.", _, true, 0.0, true, 1.0); + + // Find game convars + g_Cvar_Chattime = FindConVar("mp_chattime"); + g_Cvar_Winlimit = FindConVar("mp_winlimit"); + g_Cvar_Maxrounds = FindConVar("mp_maxrounds"); + g_Cvar_Fraglimit = FindConVar("mp_fraglimit"); + + if (GetCommandFlags("nextmap") == INVALID_FCVAR_FLAGS) + { + RegServerCmd("nextmap", Command_Nextmap); + } + + // Hook events + HookEvent("round_end", Event_RoundEnd); // We always require round_end + if (g_Cvar_Fraglimit != INVALID_HANDLE) + { + HookEvent("player_death", Event_PlayerDeath); + } + + // Set to the current map so OnMapStart() will know what to do + decl String:currentMap[64]; + GetCurrentMap(currentMap, 64); + SetNextmap(currentMap); + + // Create necessary menus for TopMenu + g_Menu_Map = CreateMenu(MenuHandler_Map); + SetMenuTitle(g_Menu_Map, "Please select a map"); + SetMenuExitBackButton(g_Menu_Map, true); + + g_Menu_Votemap = CreateMenu(MenuHandler_VoteMap, MenuAction_DrawItem); + SetMenuTitle(g_Menu_Votemap, "Please select a map"); + SetMenuExitBackButton(g_Menu_Votemap, true); + + // Bind TopMenu commands to adminmenu_maplist.ini, in cases it doesn't exist in maplists.cfg + decl String:mapListPath[PLATFORM_MAX_PATH]; + BuildPath(Path_SM, mapListPath, sizeof(mapListPath), "configs/adminmenu_maplist.ini"); + SetMapListCompatBind("sm_map menu", mapListPath); + SetMapListCompatBind("sm_votemap menu", mapListPath); + + // Account for late loading + new Handle:topmenu; + if (LibraryExists("adminmenu") && ((topmenu = GetAdminTopMenu()) != INVALID_HANDLE)) + { + OnAdminMenuReady(topmenu); + } + + AutoExecConfig(true, "mapmanager"); +} + +public OnAdminMenuReady(Handle:topmenu) +{ + /* Block us from being called twice */ + if (topmenu == hTopMenu) + { + return; + } + + /* Save the Handle */ + hTopMenu = topmenu; + + new TopMenuObject:server_commands = FindTopMenuCategory(hTopMenu, ADMINMENU_SERVERCOMMANDS); + + if (server_commands != INVALID_TOPMENUOBJECT) + { + AddToTopMenu(hTopMenu, + "sm_map", + TopMenuObject_Item, + AdminMenu_Map, + server_commands, + "sm_map", + ADMFLAG_CHANGEMAP); + } + + new TopMenuObject:voting_commands = FindTopMenuCategory(hTopMenu, ADMINMENU_VOTINGCOMMANDS); + + if (voting_commands != INVALID_TOPMENUOBJECT) + { + AddToTopMenu(hTopMenu, + "sm_votemap", + TopMenuObject_Item, + AdminMenu_VoteMap, + voting_commands, + "sm_votemap", + ADMFLAG_VOTE|ADMFLAG_CHANGEMAP); + } +} + +public OnLibraryRemoved(const String:name[]) +{ + if (strcmp(name, "adminmenu") == 0) + { + hTopMenu = INVALID_HANDLE; + } +} + +public OnConfigsExecuted() +{ + // Add map logic here + + // Get the current and last maps. + decl String:lastMap[64], String:currentMap[64]; + GetConVarString(g_Cvar_Nextmap, lastMap, 64); + GetCurrentMap(currentMap, 64); + + // Why am I doing this? If we switched to a new map, but it wasn't what we expected (Due to sm_map, sm_votemap, or + // some other plugin/command), we don't want to scramble the map cycle. Or for example, admin switches to a custom map + // not in mapcyclefile. So we keep it set to the last expected nextmap. - ferret + if (strcmp(lastMap, currentMap) == 0) + { + FindAndSetNextMap(); + } + + // Build map menus for sm_map, sm_votemap, and RTV. + BuildMapMenu(g_Menu_Map, list); + BuildMapMenu(g_Menu_VoteMap, list); + + // If the Randomize option is on, randomize! + if (GetConVarBool(g_Cvar_Randomize)) + { + CreateTimer(5.0, Timer_RandomizeNextmap); + } + + // If MapChooser is active, start it up! + if (GetConVarBool(g_Cvar_MapChooser)) + { + SetupTimeleftTimer(); + SetConVarString(g_Cvar_Nextmap, "Pending Vote"); + } + + // If RockTheVote is active, start it up! + if (GetConVarBool(g_Cvar_RockTheVote)) + { + BuildMapMenu(g_Menu_RTV, list); + CreateTimer(30.0, Timer_DelayRTV); + } +} + +// Reinitialize all our various globals +public OnMapStart() +{ + if (g_Nominate != INVALID_HANDLE) + { + ClearArray(g_Nominate); + } + + if (g_TeamScores != INVALID_HANDLE) + { + ClearArray(g_TeamScores); + } + + g_TotalRounds = 0; + + g_RTV_Voters = 0; + g_RTV_Votes = 0; + g_RTV_VotesNeeded = 0; + g_RTV_Started = false; + g_RTV_Ended = false; +} + +// Reset globals as necessary and kill timers +public OnMapEnd() +{ + g_IntermissionCalled = false; + g_HasVoteStarted = false; + + g_RTV_Allowed = false; + + if (g_VoteTimer != INVALID_HANDLE) + { + KillTimer(g_VoteTimer); + g_VoteTimer = INVALID_HANDLE; + } + + if (g_RetryTimer != INVALID_HANDLE) + { + KillTimer(g_RetryTimer); + g_RetryTimer = INVALID_HANDLE; + } +} + +public bool:OnClientConnect(client, String:rejectmsg[], maxlen) +{ + if (IsFakeClient(client)) + { + return true; + } + + // If RTV is active, deal with vote counting. + if (GetConVarBool(g_Cvar_RockTheVote)) + { + g_RTV_Voted[client] = false; + g_RTV_Voters++; + g_RTV_VotesNeeded = RoundToFloor(float(g_Voters) * GetConVarFloat(g_Cvar_Needed)); + } + + // If Nominate is active, let the new client nominate + if (GetConVarBool(g_Cvar_Nominate)) + { + g_Nominated[client] = false; + } + + return true; +} + +public OnClientDisconnect(client) +{ + if (IsFakeClient(client)) + { + return; + } + + // If RTV is active, deal with vote counting. + if (GetConVarBool(g_Cvar_RockTheVote)) + { + if(g_RTV_Voted[client]) + { + g_RTV_Votes--; + } + + g_RTV_Voters--; + g_RTV_VotesNeeded = RoundToFloor(float(g_RTV_Voters) * GetConVarFloat(g_Cvar_RTVLimit)); + + // If this client caused us to fall below the RTV threshold and its allowed be started, start it. + if (g_RTV_Votes && g_RTV_Voters && g_RTV_Votes >= g_RTV_VotesNeeded && g_RTV_Allowed && !g_RTV_Started) + { + g_RTV_Started = true; + CreateTimer(2.0, Timer_StartRTV, TIMER_FLAG_NO_MAPCHANGE); + } + } +} + +public OnMapTimeLeftChanged() +{ + if (GetConVarBool(g_Cvar_MapChooser)) + { + SetupTimeleftTimer(); + } +} \ No newline at end of file diff --git a/plugins/mapmanager/commands.sp b/plugins/mapmanager/commands.sp new file mode 100644 index 00000000..bba25765 --- /dev/null +++ b/plugins/mapmanager/commands.sp @@ -0,0 +1,398 @@ +/** + * vim: set ts=4 : + * ============================================================================= + * SourceMod Map Manager Plugin + * Contains callbacks for commands + * + * SourceMod (C)2004-2007 AlliedModders LLC. All rights reserved. + * ============================================================================= + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id$ + */ + + public Action:Command_Say(client, args) + { + decl String:text[192]; + if (GetCmdArgString(text, sizeof(text)) < 1) + { + return Plugin_Continue; + } + + new startidx; + if (text[strlen(text)-1] == '"') + { + text[strlen(text)-1] = '\0'; + startidx = 1; + } + + decl String:message[8]; + BreakString(text[startidx], message, sizeof(message)); + + if (strcmp(message, "nextmap", false) == 0) + { + decl String:map[32]; + GetConVarString(g_Cvar_Nextmap, map, sizeof(map)); + + PrintToChat(client, "%t", "Next Map", map); + } + else + { + if (GetConVarBool(g_Cvar_RockTheVote) && (strcmp(text[startidx], "rtv", false) == 0 || strcmp(text[startidx], "rockthevote", false) == 0)) + { + if (g_MapChanged) + { + ReplyToCommand(client, "[SM] %t", "Map change in progress"); + return Plugin_Continue; + } + + if (!g_RTV_Allowed) + { + PrintToChat(client, "[SM] %t", "RTV Not Allowed"); + return Plugin_Continue; + } + + if (g_RTV_Ended) + { + PrintToChat(client, "[SM] %t", "RTV Ended"); + return Plugin_Continue; + } + + if (g_RTV_Started) + { + PrintToChat(client, "[SM] %t", "RTV Started"); + return Plugin_Continue; + } + + if (GetClientCount(true) < GetConVarInt(g_Cvar_MinPlayers) && g_RTV_Votes == 0) // Should we keep checking g_Votes here? + { + PrintToChat(client, "[SM] %t", "Minimal Players Not Met"); + return Plugin_Continue; + } + + if (g_RTV_Voted[client]) + { + PrintToChat(client, "[SM] %t", "Already Voted"); + return Plugin_Continue; + } + + new String:name[64]; + GetClientName(client, name, sizeof(name)); + + g_RTV_Votes++; + g_RTV_Voted[client] = true; + + PrintToChatAll("[SM] %t", "RTV Requested", name, g_RTV_Votes, g_RTV_VotesNeeded); + + if (g_RTV_Votes >= g_RTV_VotesNeeded) + { + g_RTV_Started = true; + CreateTimer(2.0, Timer_StartRTV, TIMER_FLAG_NO_MAPCHANGE); + } + } + else if (GetConVarBool(g_Cvar_Nominate) && strcmp(text[startidx], "nominate", false) == 0) + { + if (g_MapChanged) + { + ReplyToCommand(client, "[SM] %t", "Map change in progress"); + return Plugin_Continue; + } + + if (g_RTV_Started || g_HasVoteStarted) + { + ReplyToCommand(client, "[SM] %t", "Map vote in progress"); + return Plugin_Continue; + } + + if (g_Nominated[client]) + { + PrintToChat(client, "[SM] %t", "Already Nominated"); + return Plugin_Continue; + } + + if (GetArraySize(g_Nominate) >= GetConVarInt(g_Cvar_Maps)) + { + PrintToChat(client, "[SM] %t", "Max Nominations"); + return Plugin_Continue; + } + + DisplayMenu(g_Menu_Nominate, client, MENU_TIME_FOREVER); + } + } + + return Plugin_Continue; +} + +public Action:Command_Mapvote(client, args) +{ + InitiateVote(); + + return Plugin_Handled; +} + +public Action:Command_Nextmap(args) +{ + decl String:map[64]; + + GetConVarString(g_Cvar_Nextmap, map, sizeof(map)); + + ReplyToCommand(0, "%t", "Next Map", map); + + return Plugin_Handled; +} + +public Action:Command_List(client, args) +{ + PrintToConsole(client, "Map Cycle:"); + + decl String:currentMap[64]; + GetCurrentMap(currentMap, 64); + + decl String:mapName[64]; + for (new i = 0; i < GetArraySize(g_MapCycle); i++) + { + GetArrayString(g_MapCycle, i, mapName, sizeof(mapName)); + if (strcmp(mapName, currentMap) == 0) + { + PrintToConsole(client, "%s <========= Current map", mapName); + } + else if (strcmp(mapName, g_NextMap) == 0) + { + PrintToConsole(client, "%s <========= Next map", mapName); + } + else + { + PrintToConsole(client, "%s", mapName); + } + } + + return Plugin_Handled; +} + +public Action:Command_SetNextmap(client, args) +{ + if (args < 1) + { + ReplyToCommand(client, "[SM] Usage: sm_setnextmap "); + return Plugin_Handled; + } + + if (g_MapChanged) + { + ReplyToCommand(client, "[SM] %t", "Map change in progress"); + return Plugin_Handled; + } + + decl String:map[64]; + GetCmdArg(1, map, sizeof(map)); + + if (!IsMapValid(map)) + { + ReplyToCommand(client, "[SM] %t", "Map was not found", map); + return Plugin_Handled; + } + + ShowActivity(client, "%t", "Cvar changed", "sm_nextmap", map); + LogMessage("\"%L\" changed sm_nextmap to \"%s\"", client, map); + + SetNextMap(map); + + return Plugin_Handled; +} + + public Action:Command_Map(client, args) + { + if (args < 1) + { + ReplyToCommand(client, "[SM] Usage: sm_map [r/e]"); + return Plugin_Handled; + } + + if (g_MapChanged) + { + ReplyToCommand(client, "[SM] %t", "Map change in progress"); + return Plugin_Handled; + } + + decl String:map[64]; + GetCmdArg(1, map, sizeof(map)); + + if (!IsMapValid(map)) + { + ReplyToCommand(client, "[SM] %t", "Map was not found", map); + return Plugin_Handled; + } + + decl String:when[2]; + if (args > 1) + { + GetCmdArg(2, when, sizeof(when)); + + when[0] = CharToLower(when[0]); + if (when[0] != 'r' && when[0] != 'e') + { + when[0] = 'i'; + } + } + + SetMapChange(client, map, when); + + return Plugin_Handled; +} + +public Action:Command_Votemap(client, args) +{ + if (args < 1) + { + ReplyToCommand(client, "[SM] Usage: sm_votemap [r/e] [mapname2] ... [mapname5]"); + return Plugin_Handled; + } + + if (g_MapChanged) + { + ReplyToCommand(client, "[SM] %t", "Map change in progress"); + return Plugin_Handled; + } + + if (IsVoteInProgress()) + { + ReplyToCommand(client, "[SM] %t", "Vote in Progress"); + return Plugin_Handled; + } + + if (!TestVoteDelay(client)) + { + return Plugin_Handled; + } + + decl String:text[256]; + GetCmdArgString(text, sizeof(text)); + + decl String:maps[5][64]; + new mapCount; + new len, pos; + + // Find out if the user specified "when" + decl String:when[64]; + pos = BreakString(text[len], when, sizeof(when)); + if (!IsMapValid(when)) + { + when[0] = CharToLower(when[0]); + if (when[0] != 'r' && when[0] != 'e') + { + ReplyToCommand(client, "[SM] %t", "Map was not found", maps[mapCount]); + return Plugin_Handled; + } + } + else + { + strcpy(maps[mapCount], sizeof(maps[]), when); + mapCount++; + when[0] = 'i'; + } + + len += pos; + + while (pos != -1 && mapCount < 5) + { + pos = BreakString(text[len], maps[mapCount], sizeof(maps[])); + + if (!IsMapValid(maps[mapCount])) + { + ReplyToCommand(client, "[SM] %t", "Map was not found", maps[mapCount]); + return Plugin_Handled; + } + + mapCount++; + + len += pos; + } + + g_VoteMapInUse = client; + g_Client_Data[client][0] = when[0]; + + DisplayVoteMapMenu(client, mapCount, maps); + + return Plugin_Handled; +} + +public Action:Command_Nominate(client, args) +{ + if (args < 1) + { + ReplyToCommand(client, "[SM] Usage: sm_nominate "); + return Plugin_Handled; + } + + if (!GetConVarBool(g_Cvar_Nominate)) + { + ReplyToCommand(client, "[SM] Nominations are currently disabled."); + return Plugin_Handled; + } + + decl String:mapname[64]; + GetCmdArg(1, mapname, sizeof(mapname)); + + if (FindStringInArray(g_MapList, mapname) == -1) + { + ReplyToCommand(client, "%t", "Map was not found", mapname); + return Plugin_Handled; + } + + if (GetArraySize(g_Nominated) > 0) + { + if (FindStringInArray(g_Nominated, mapname) != -1) + { + ReplyToCommand(client, "%t", "Map Already In Vote", mapname); + return Plugin_Handled; + } + + ShiftArrayUp(g_Nominated, 0); + SetArrayString(g_Nominated, 0, mapname); + + while (GetArraySize(g_Nominated) > GetConVarInt(g_Cvar_Maps)) + { + RemoveFromArray(g_Nominated, GetConVarInt(g_Cvar_Maps)); + } + } + else + { + PushArrayString(g_Nominated, mapname); + } + + decl String:item[64]; + for (new i = 0; i < GetMenuItemCount(g_Menu_Nominate); i++) + { + GetMenuItem(g_Menu_Nominate, i, item, sizeof(item)); + if (strcmp(item, mapname) == 0) + { + RemoveMenuItem(g_Menu_Nominate, i); + break; + } + } + + ReplyToCommand(client, "%t", "Map Inserted", mapname); + LogAction(client, -1, "\"%L\" inserted map \"%s\".", client, mapname); + + return Plugin_Handled; +} \ No newline at end of file diff --git a/plugins/mapmanager/events.sp b/plugins/mapmanager/events.sp new file mode 100644 index 00000000..be57776b --- /dev/null +++ b/plugins/mapmanager/events.sp @@ -0,0 +1,166 @@ +/** + * vim: set ts=4 : + * ============================================================================= + * SourceMod Map Manager Plugin + * Contains event callbacks + * + * SourceMod (C)2004-2007 AlliedModders LLC. All rights reserved. + * ============================================================================= + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id$ + */ + +public Action:UserMsg_VGUIMenu(UserMsg:msg_id, Handle:bf, const players[], playersNum, bool:reliable, bool:init) +{ + if (g_IntermissionCalled) + { + return Plugin_Handled; + } + + decl String:type[15]; + + /* If we don't get a valid string, bail out. */ + if (BfReadString(bf, type, sizeof(type)) < 0) + { + return Plugin_Handled; + } + + if (BfReadByte(bf) == 1 && BfReadByte(bf) == 0 && (strcmp(type, "scores", false) == 0)) + { + g_IntermissionCalled = true; + + decl String:map[32]; + new Float:fChatTime = GetConVarFloat(g_Cvar_Chattime); + + if (fChatTime < 2.0) + { + SetConVarFloat(g_Cvar_Chattime, 2.0); + } + + LogMessage("[MapManager] Changing map to '%s' due to map ending.", g_NextMap); + + g_MapChanged = true; + CreateTimer(fChatTime - 1.0, Timer_ChangeMap); + } + + return Plugin_Handled; +} + +public Event_RoundEnd(Handle:event, const String:name[], bool:dontBroadcast) +{ + if (g_MapChanged) + { + return; + } + + if (g_MapChangeSet && g_MapChangeWhen == 'r') + { + LogMessage("[MapManager] Changing map to '%s' due to round ending.", g_NextMap); + + g_MapChanged = true; + CreateTimer(1.0, Timer_ChangeMap); + + return; + } + + if (g_HasVoteStarted) + { + return; + } + + new winner = GetEventInt(event, "winner"); + + if (winner == 0 || winner == 1) + { + return; + } + + g_TotalRounds++; + + new team[2], teamPos = -1; + for (new i; i < GetArraySize(g_TeamScores); i++) + { + GetArrayArray(g_TeamScores, i, team); + if (team[0] == winner) + { + teamPos = i; + break; + } + } + + if (teamPos == -1) + { + team[0] = winner; + team[1] = 1; + PushArrayArray(g_TeamScores, team); + } + else + { + team[1]++; + SetArrayArray(g_TeamScores, teamPos, team); + } + + if (g_Cvar_Winlimit != INVALID_HANDLE) + { + new winlimit = GetConVarInt(g_Cvar_Winlimit); + if (winlimit) + { + if (team[1] >= (winlimit - GetConVarInt(g_Cvar_StartRounds))) + { + InitiateVote(); + } + } + } + + if (g_Cvar_Maxrounds != INVALID_HANDLE) + { + new maxrounds = GetConVarInt(g_Cvar_Maxrounds); + if (maxrounds) + { + if (g_TotalRounds >= (maxrounds - GetConVarInt(g_Cvar_StartRounds))) + { + InitiateVote(); + } + } + } +} + +public Event_PlayerDeath(Handle:event, const String:name[], bool:dontBroadcast) +{ + if (g_MapChanged || g_HasVoteStarted || g_Cvar_Fraglimit == INVALID_HANDLE) + { + return; + } + + if (!GetConVarInt(g_Cvar_Fraglimit)) + { + return; + } + + new fragger = GetClientOfUserId(GetEventInt(event, "attacker")); + if (fragger && GetClientFrags(fragger) >= (GetConVarInt(g_Cvar_Fraglimit) - GetConVarInt(g_Cvar_StartFrags))) + { + InitiateVote(); + } +} \ No newline at end of file diff --git a/plugins/mapmanager/functions.sp b/plugins/mapmanager/functions.sp new file mode 100644 index 00000000..963dcafc --- /dev/null +++ b/plugins/mapmanager/functions.sp @@ -0,0 +1,335 @@ +/** + * vim: set ts=4 : + * ============================================================================= + * SourceMod Map Management Plugin + * Contains misc functions. + * + * SourceMod (C)2004-2007 AlliedModders LLC. All rights reserved. + * ============================================================================= + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id$ + */ + +SetNextMap(String:map[]) +{ + strcopy(g_NextMap, sizeof(g_NextMap), map); + SetConVarString(g_Cvar_Nextmap, map); +} + +SetMapChange(client, map, when, Float:time = 3.0) +{ + g_MapChangeSet = true; + g_MapChangeWhen = when[0]; + SetNextMap(map); + + if (when[0] == 'r') + { + ShowActivity2(client, "[SM] ", "%t", "Changing map end of round", map); + LogAction(client, -1, "\"%L\" set end of round map change to \"%s\"", client, map); + } + else if (when[0] == 'e') + { + ShowActivity2(client, "[SM] ", "%t", "Set nextmap", map); + LogAction(client, -1, "\"%L\" set the next map to \"%s\"", client, map); + } + else + { + ShowActivity2(client, "[SM] ", "%t", "Change map", map); + LogAction(client, -1, "\"%L\" changed map to \"%s\"", client, map); + + g_MapChanged = true; + CreateTimer(3.0, Timer_ChangeMap); + } +} + +FindAndSetNextMap() +{ + if (ReadMapList(g_MapList, + g_MapListSerial, + "mapcyclefile", + MAPLIST_FLAG_CLEARARRAY|MAPLIST_FLAG_NO_DEFAULT) + == INVALID_HANDLE) + { + if (g_MapListSerial == -1) + { + LogError("FATAL: Cannot load map cycle. Nextmap not loaded."); + SetFailState("Mapcycle Not Found"); + } + } + + new mapCount = GetArraySize(g_MapList); + decl String:mapName[32]; + + if (g_MapPos == -1) + { + decl String:current[64]; + GetCurrentMap(current, 64); + + for (new i = 0; i < mapCount; i++) + { + GetArrayString(g_MapList, i, mapName, sizeof(mapName)); + if (strcmp(current, mapName, false) == 0) + { + g_MapPos = i; + break; + } + } + + if (g_MapPos == -1) + g_MapPos = 0; + } + + g_MapPos++; + if (g_MapPos >= mapCount) + g_MapPos = 0; + + GetArrayString(g_MapList, g_MapPos, mapName, sizeof(mapName)); + SetConVarString(g_Cvar_Nextmap, mapName); +} + +Float:GetVotePercent(votes, totalVotes) +{ + return FloatDiv(float(votes),float(totalVotes)); +} + +bool:TestVoteDelay(client) +{ + new delay = CheckVoteDelay(); + + if (delay > 0) + { + if (delay > 60) + { + ReplyToCommand(client, "[SM] %t", "Vote Delay Minutes", delay % 60); + } + else + { + ReplyToCommand(client, "[SM] %t", "Vote Delay Seconds", delay); + } + + return false; + } + + return true; +} + +BuildMapMenu() +{ + if (g_MapMenu != INVALID_HANDLE) + { + CloseHandle(g_MapMenu); + g_MapMenu = INVALID_HANDLE; + } + + g_MapMenu = CreateMenu(Handler_MapSelectMenu); + SetMenuTitle(g_MapMenu, "%t", "Nominate Title"); + + decl String:map[64]; + for (new i = 0; i < GetArraySize(g_MapList); i++) + { + GetArrayString(g_MapList, i, map, sizeof(map)); + AddMenuItem(g_MapMenu, map, map); + } + + SetMenuExitButton(g_MapMenu, false); +} + +SetupTimeleftTimer() +{ + new time; + if (GetMapTimeLeft(time) && time > 0) + { + new startTime = GetConVarInt(g_Cvar_StartTime) * 60; + if (time - startTime < 0) + { + InitiateVote(); + } + else + { + if (g_VoteTimer != INVALID_HANDLE) + { + KillTimer(g_VoteTimer); + g_VoteTimer = INVALID_HANDLE; + } + + g_VoteTimer = CreateTimer(float(time - startTime), Timer_StartMapVote); + } + } +} + +InitiateVote() +{ + if (g_HasVoteStarted || g_RetryTimer != INVALID_HANDLE) + { + return; + } + + if (IsVoteInProgress()) + { + // Can't start a vote, try again in 5 seconds. + g_RetryTimer = CreateTimer(5.0, Timer_StartMapVote); + return; + } + + g_HasVoteStarted = true; + g_VoteMenu = CreateMenu(Handler_MapVoteMenu, MenuAction:MENU_ACTIONS_ALL); + SetMenuTitle(g_VoteMenu, "Vote Nextmap"); + + decl String:map[32]; + for (new i = 0; i < GetArraySize(g_NextMapList); i++) + { + GetArrayString(g_NextMapList, i, map, sizeof(map)); + AddMenuItem(g_VoteMenu, map, map); + } + + if (GetConVarBool(g_Cvar_Extend)) + { + new bool:allowExtend, time; + if (GetMapTimeLimit(time) && time > 0 && time < GetConVarInt(g_Cvar_ExtendTimeMax)) + { + allowExtend = true; + } + + if (g_Cvar_Winlimit != INVALID_HANDLE && GetConVarInt(g_Cvar_Winlimit) < GetConVarInt(g_Cvar_ExtendRoundMax)) + { + allowExtend = true; + } + + if (g_Cvar_Maxrounds != INVALID_HANDLE && GetConVarInt(g_Cvar_Maxrounds) < GetConVarInt(g_Cvar_ExtendRoundMax)) + { + allowExtend = true; + } + + if (g_Cvar_Fraglimit != INVALID_HANDLE && GetConVarInt(g_Cvar_Fraglimit) < GetConVarInt(g_Cvar_ExtendFragMax)) + { + allowExtend = true; + } + + if (allowExtend) + { + AddMenuItem(g_VoteMenu, VOTE_EXTEND, "Extend Map"); + } + } + + SetMenuExitButton(g_VoteMenu, false); + VoteMenuToAll(g_VoteMenu, 20); + + LogMessage("Voting for next map has started."); + PrintToChatAll("[SM] %t", "Nextmap Voting Started"); +} + +SetNextMap(const String:map[]) +{ + SetConVarString(g_Cvar_Nextmap, map); + PushArrayString(g_OldMapList, map); + + if (GetArraySize(g_OldMapList) > GetConVarInt(g_Cvar_ExcludeMaps)) + { + RemoveFromArray(g_OldMapList, 0); + } + + PrintToChatAll("[SM] %t", "Nextmap Voting Finished", map); + LogMessage("Voting for next map has finished. Nextmap: %s.", map); +} + +CreateNextVote() +{ + if(g_NextMapList != INVALID_HANDLE) + { + ClearArray(g_NextMapList); + } + + decl String:map[32]; + new index, Handle:tempMaps = CloneArray(g_MapList); + + GetCurrentMap(map, sizeof(map)); + index = FindStringInArray(tempMaps, map); + if (index != -1) + { + RemoveFromArray(tempMaps, index); + } + + if (GetConVarInt(g_Cvar_ExcludeMaps) && GetArraySize(tempMaps) > GetConVarInt(g_Cvar_ExcludeMaps)) + { + for (new i = 0; i < GetArraySize(g_OldMapList); i++) + { + GetArrayString(g_OldMapList, i, map, sizeof(map)); + index = FindStringInArray(tempMaps, map); + if (index != -1) + { + RemoveFromArray(tempMaps, index); + } + } + } + + new limit = (GetConVarInt(g_Cvar_IncludeMaps) < GetArraySize(tempMaps) ? GetConVarInt(g_Cvar_IncludeMaps) : GetArraySize(tempMaps)); + for (new i = 0; i < limit; i++) + { + new b = GetRandomInt(0, GetArraySize(tempMaps) - 1); + GetArrayString(tempMaps, b, map, sizeof(map)); + PushArrayString(g_NextMapList, map); + RemoveFromArray(tempMaps, b); + } + + CloseHandle(tempMaps); +} + +/* + +new Handle:g_map_array = INVALID_HANDLE; +new g_map_serial = -1; + +LoadMapList(Handle:menu) +{ + new Handle:map_array; + + if ((map_array = ReadMapList(g_map_array, + g_map_serial, + "sm_map menu", + MAPLIST_FLAG_CLEARARRAY|MAPLIST_FLAG_NO_DEFAULT|MAPLIST_FLAG_MAPSFOLDER)) + != INVALID_HANDLE) + { + g_map_array = map_array; + } + + if (g_map_array == INVALID_HANDLE) + { + return 0; + } + + RemoveAllMenuItems(menu); + + decl String:map_name[64]; + new map_count = GetArraySize(g_map_array); + + for (new i = 0; i < map_count; i++) + { + GetArrayString(g_map_array, i, map_name, sizeof(map_name)); + AddMenuItem(menu, map_name, map_name); + } + + return map_count; +} + +*/ \ No newline at end of file diff --git a/plugins/mapmanager/functions_menu.sp b/plugins/mapmanager/functions_menu.sp new file mode 100644 index 00000000..51f78f57 --- /dev/null +++ b/plugins/mapmanager/functions_menu.sp @@ -0,0 +1,115 @@ +/** + * vim: set ts=4 : + * ============================================================================= + * SourceMod Map Management Plugin + * Contains misc functions. + * + * SourceMod (C)2004-2007 AlliedModders LLC. All rights reserved. + * ============================================================================= + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id$ + */ + +DisplayWhenMenu(client, bool:vote = false) +{ + new Handle: + + if (!vote) + { + menu = CreateMenu(MenuHandler_ChangeMap); + } + else + { + menu = CreateMenu(MenuHandler_VoteWhen); + } + + decl String:title[100]; + Format(title, sizeof(title), "%T:", "Map Change When", client); + SetMenuTitle(menu, title); + SetMenuExitBackButton(menu, true); + + AddMenuItem(menu, "i", "Immediately"); + AddMenuItem(menu, "r", "Round End"); + AddMenuItem(menu, "e", "Map End"); + + DisplayMenu(menu, client, MENU_TIME_FOREVER); +} + +DisplayConfirmVoteMenu(client) +{ + new Handle:menu = CreateMenu(MenuHandler_Confirm); + + decl String:title[100]; + Format(title, sizeof(title), "%T:", "Confirm Vote", client); + SetMenuTitle(menu, title); + SetMenuExitBackButton(menu, true); + + AddMenuItem(menu, "Confirm", "Start the Vote"); + + DisplayMenu(menu, client, MENU_TIME_FOREVER); +} + +DisplayAcceptVoteMenu(String:map[]) +{ + new Handle:menu = CreateMenu(MenuHandler_Accept); + + decl String:title[100]; + Format(title, sizeof(title), "%T:", "Accept Vote", client); + SetMenuTitle(menu, title); + SetMenuExitBackButton(menu, true); + + AddMenuItem(menu, map, "Accept Vote"); + + DisplayMenu(menu, g_VoteMapInUse, MENU_TIME_FOREVER); +} + +DisplayVoteMapMenu(client, mapCount, String:maps[5][]) +{ + LogAction(client, -1, "\"%L\" initiated a map vote.", client); + ShowActivity2(client, "[SM] ", "%t", "Initiated Vote Map"); + + new Handle:menu = CreateMenu(Handler_VoteCallback, MenuAction:MENU_ACTIONS_ALL); + + if (mapCount == 1) + { + //strcopy(g_voteInfo[VOTE_NAME], sizeof(g_voteInfo[]), maps[0]); + + SetMenuTitle(menu, "Change Map To"); + AddMenuItem(menu, maps[0], "Yes"); + AddMenuItem(menu, VOTE_NO, "No"); + } + else + { + //g_voteInfo[VOTE_NAME][0] = '\0'; + + SetMenuTitle(menu, "Map Vote"); + for (new i = 0; i < mapCount; i++) + { + AddMenuItem(menu, maps[i], maps[i]); + } + } + + SetMenuExitButton(menu, false); + VoteMenuToAll(menu, 20); +} diff --git a/plugins/mapmanager/globals.sp b/plugins/mapmanager/globals.sp new file mode 100644 index 00000000..171c3a16 --- /dev/null +++ b/plugins/mapmanager/globals.sp @@ -0,0 +1,104 @@ +/** + * vim: set ts=4 : + * ============================================================================= + * SourceMod Map Manager Plugin + * Contains globals and defines + * + * SourceMod (C)2004-2007 AlliedModders LLC. All rights reserved. + * ============================================================================= + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id$ + */ + +#define VOTE_EXTEND "##extend##" +#define VOTE_YES "###yes###" +#define VOTE_NO "###no###" + +new Handle:hTopMenu = INVALID_HANDLE; // TopMenu + +new Handle:g_MapCycle = INVALID_HANDLE; // mapcyclefile maps +new Handle:g_MapList = INVALID_HANDLE; // maplist.txt maps +new Handle:g_MapHistory = INVALID_HANDLE; // History of maps played +new Handle:g_NextVoteMaps = INVALID_HANDLE; // Array of maps for next RTV or MC vote +new Handle:g_NominatedMaps = INVALID_HANDLE; // Array of maps that have been nominated + +new Handle:g_Menu_Map = INVALID_HANDLE; // Menu of maps used by sm_map in admin menu +new Handle:g_Menu_Votemap = INVALID_HANDLE; // Menu of maps used by sm_votemap in admin menu +new Handle:g_Menu_Nominate = INVALID_HANDLE; // Menu of maps used by Nomination system + +new Handle:g_SelectedMaps; // List of maps chosen so far by a user in sm_votemap admin menu +new g_VoteMapInUse; // Client index of admin using sm_votemap +new String:g_Client_Data[MAXCLIENTS+1][64]; // Used to hold bits of client data during sm_votemap + +new bool:g_MapChangeSet; // True if a command or vote has set the map +new bool:g_MapChanged; // True if a map change has been issued +new g_MapChangeWhen; // Either 'i' for immediate, 'r' for round end, or 'e' for end of map. + +new UserMsg:g_VGUIMenu; // VGUI usermsg id for nextmap +new bool:g_NextMapEnabled = true // If set to false, all features requiring nextmap are disabled. +new bool:g_IntermissionCalled; // Has the end of map intermission begun? +new String:g_NextMap[64]; // All important! This is the next map! +new g_MapPos = -1; // Position in mapcycle + +new bool:g_RTV_Voted[MAXPLAYERS+1] = {false, ...}; // Whether or not a player has voted for RTV +new bool:g_RTV_Allowed = false; // True if RTV is available to players. Used to delay rtv votes. +new bool:g_RTV_Started = false; // Indicates that the actual map vote has started +new bool:g_RTV_Ended = false; // Indicates that the actual map vote has concluded +new g_RTV_Voters = 0; // Total voters connected. Doesn't include fake clients. +new g_RTV_Votes = 0; // Total number of "say rtv" votes +new g_RTV_VotesNeeded = 0; // Necessary votes before map vote begins. (voters * percent_needed) + +new bool:g_Nominated[MAXPLAYERS+1] = {false, ...}; // Whether or not a player has nominated a map + +new Handle:g_TeamScores = INVALID_HANDLE; // Array of team scores +new g_TotalRounds; // Total rounds played this map +new bool:g_HasVoteStarted; // Whether or not MapChooser has begun + +// ConVar handles +new Handle:g_Cvar_NextMap = INVALID_HANDLE; +new Handle:g_Cvar_VoteMap = INVALID_HANDLE; +new Handle:g_Cvar_Excludes = INVALID_HANDLE; +new Handle:g_Cvar_StartTime = INVALID_HANDLE; +new Handle:g_Cvar_StartRounds = INVALID_HANDLE; +new Handle:g_Cvar_StartFrags = INVALID_HANDLE; +new Handle:g_Cvar_ExtendTimeMax = INVALID_HANDLE; +new Handle:g_Cvar_ExtendTimeStep = INVALID_HANDLE; +new Handle:g_Cvar_ExtendRoundMax = INVALID_HANDLE; +new Handle:g_Cvar_ExtendRoundStep = INVALID_HANDLE; +new Handle:g_Cvar_ExtendFragMax = INVALID_HANDLE; +new Handle:g_Cvar_ExtendFragStep = INVALID_HANDLE; +new Handle:g_Cvar_Mapfile = INVALID_HANDLE; +new Handle:g_Cvar_ExcludeMaps = INVALID_HANDLE; +new Handle:g_Cvar_IncludeMaps = INVALID_HANDLE; +new Handle:g_Cvar_NoVoteMode = INVALID_HANDLE; +new Handle:g_Cvar_Extend = INVALID_HANDLE; + +new Handle:g_VoteTimer = INVALID_HANDLE; +new Handle:g_RetryTimer = INVALID_HANDLE; + +// VALVe ConVars +new Handle:g_Cvar_Chattime = INVALID_HANDLE; +new Handle:g_Cvar_Winlimit = INVALID_HANDLE; +new Handle:g_Cvar_Maxrounds = INVALID_HANDLE; +new Handle:g_Cvar_Fraglimit = INVALID_HANDLE; diff --git a/plugins/mapmanager/menus.sp b/plugins/mapmanager/menus.sp new file mode 100644 index 00000000..c47e76be --- /dev/null +++ b/plugins/mapmanager/menus.sp @@ -0,0 +1,263 @@ +/** + * vim: set ts=4 : + * ============================================================================= + * SourceMod Map Manager Plugin + * Contains menu callbacks + * + * SourceMod (C)2004-2007 AlliedModders LLC. All rights reserved. + * ============================================================================= + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id$ + */ + +// Following callbacks are for sm_map +public AdminMenu_Map(Handle:topmenu, + TopMenuAction:action, + TopMenuObject:object_id, + param, + String:buffer[], + maxlength) +{ + if (action == TopMenuAction_DisplayOption) + { + Format(buffer, maxlength, "%T", "Choose Map", param); + } + else if (action == TopMenuAction_SelectOption) + { + DisplayMenu(g_Menu_Map, param, MENU_TIME_FOREVER); + } +} + +public MenuHandler_Map(Handle:menu, MenuAction:action, param1, param2) +{ + if (action == MenuAction_Cancel) + { + if (param2 == MenuCancel_ExitBack && hTopMenu != INVALID_HANDLE) + { + DisplayTopMenu(hTopMenu, param1, TopMenuPosition_LastCategory); + } + } + else if (action == MenuAction_Select) + { + GetMenuItem(menu, param2, g_Client_Data[param1], sizeof(g_Client_Data[])); + DisplayWhenMenu(param1); + } +} + +public MenuHandler_ChangeMap(Handle:menu, MenuAction:action, param1, param2) +{ + if (action == MenuAction_Cancel) + { + if (param2 == MenuCancel_ExitBack && hTopMenu != INVALID_HANDLE) + { + DisplayTopMenu(hTopMenu, param1, TopMenuPosition_LastCategory); + } + } + else if (action == MenuAction_Select) + { + decl String:when[2]; + GetMenuItem(menu, param2, when, sizeof(when)); + SetMapChange(client, g_Client_Data[param1], when[0]) + } +} + +// Following callbacks are for sm_votemap +public AdminMenu_VoteMap(Handle:topmenu, + TopMenuAction:action, + TopMenuObject:object_id, + param, + String:buffer[], + maxlength) +{ + if (action == TopMenuAction_DisplayOption) + { + Format(buffer, maxlength, "%T", "Map vote", param); + } + else if (action == TopMenuAction_SelectOption) + { + g_VoteMapInUse = param; + ClearArray(g_SelectedMaps); + DisplayMenu(g_Menu_Votemap, param, MENU_TIME_FOREVER); + } + else if (action == TopMenuAction_DrawOption) + { + /* disable this option if a vote is already running, theres no maps listed or someone else has already accessed this menu */ + buffer[0] = (!IsNewVoteAllowed() || g_VoteMapInUse) ? ITEMDRAW_IGNORE : ITEMDRAW_DEFAULT; + } +} + +public MenuHandler_VoteMap(Handle:menu, MenuAction:action, param1, param2) +{ + if (action == MenuAction_Cancel) + { + if (param2 == MenuCancel_ExitBack && hTopMenu != INVALID_HANDLE) + { + DisplayWhenMenu(param1, true); + } + else // no action was selected. + { + /* Re-enable the menu option */ + g_VoteMapInUse = 0; + } + } + else if (action == MenuAction_DrawItem) + { + decl String:info[32]; + + GetMenuItem(menu, param2, info, sizeof(info)); + + if (FindStringInArray(g_SelectedMaps, info) != -1) + { + return ITEMDRAW_IGNORE; + } + else + { + return ITEMDRAW_DEFAULT; + } + } + else if (action == MenuAction_Select) + { + decl String:info[32]; + + GetMenuItem(menu, param2, info, sizeof(info)); + + PushArrayString(g_SelectedMaps, info); + + /* Redisplay the list */ + if (GetArraySize(g_SelectedMaps) < 5) + { + DisplayMenu(g_MapList, param1, MENU_TIME_FOREVER); + } + else + { + DisplayWhenMenu(param1, true); + } + } + + return 0; +} + +public MenuHandler_VoteWhen(Handle:menu, MenuAction:action, param1, param2) +{ + if (action == MenuAction_End) + { + CloseHandle(menu); + } + else if (action == MenuAction_Cancel) + { + if (param2 == MenuCancel_ExitBack && hTopMenu != INVALID_HANDLE) + { + g_Menu_Info[param1][0] = 'i'; + DisplayConfirmVoteMenu(param1); + } + else + { + g_VoteMapInUse = 0; + } + } + else if (action == MenuAction_Select) + { + GetMenuItem(menu, param2, g_Menu_Info[param1], sizeof(g_Menu_Info[])); + + DisplayConfirmVoteMenu(param1); + } +} + +public MenuHandler_Confirm(Handle:menu, MenuAction:action, param1, param2) +{ + if (action == MenuAction_End) + { + CloseHandle(menu); + } + else if (action == MenuAction_Cancel) + { + g_VoteMapInUse = 0; + + if (param2 == MenuCancel_ExitBack && hTopMenu != INVALID_HANDLE) + { + DisplayTopMenu(hTopMenu, param1, TopMenuPosition_LastCategory); + } + } + else if (action == MenuAction_Select) + { + decl String:maps[5][64]; + new selectedmaps = GetArraySize(g_SelectedMaps); + + for (new i = 0; i < selectedmaps; i++) + { + GetArrayString(g_SelectedMaps, i, maps[i], sizeof(maps[])); + } + + DisplayVoteMapMenu(param1, selectedmaps, maps); + } +} + +public MenuHandler_Accept(Handle:menu, MenuAction:action, param1, param2) +{ + if (action == MenuAction_End) + { + CloseHandle(menu); + g_VoteMapInUse = 0; + } + else if (action == MenuAction_Select) + { + decl String:map[64] + GetMenuItem(menu, 1, map, sizeof(map)); + + SetMapChange(param1, map, g_Client_Data[param1][0]) + } +} + +public Handler_MapSelectMenu(Handle:menu, MenuAction:action, param1, param2) +{ + switch (action) + { + case MenuAction_Select: + { + if (GetArraySize(g_RTVMapList) >= GetConVarInt(g_Cvar_Maps)) + { + PrintToChat(param1, "[SM] %t", "Max Nominations"); + return; + } + + decl String:map[64], String:name[64]; + GetMenuItem(menu, param2, map, sizeof(map)); + + if (FindStringInArray(g_RTVMapList, map) != -1) + { + PrintToChat(param1, "[SM] %t", "Map Already Nominated"); + return; + } + + GetClientName(param1, name, 64); + + PushArrayString(g_RTVMapList, map); + RemoveMenuItem(menu, param2); + + g_Nominated[param1] = true; + + PrintToChatAll("[SM] %t", "Map Nominated", name, map); + } + } +} \ No newline at end of file diff --git a/plugins/mapmanager/timers.sp b/plugins/mapmanager/timers.sp new file mode 100644 index 00000000..c0d5a3e3 --- /dev/null +++ b/plugins/mapmanager/timers.sp @@ -0,0 +1,166 @@ +/** + * vim: set ts=4 : + * ============================================================================= + * SourceMod Map Manager Plugin + * Contains timer callbacks + * + * SourceMod (C)2004-2007 AlliedModders LLC. All rights reserved. + * ============================================================================= + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id$ + */ + +public Action:Timer_ChangeMap(Handle:timer) +{ + ServerCommand("changelevel \"%s\"", g_NextMap); + PushArrayString(g_MapHistory, g_NextMap); + + return Plugin_Stop; +} + +public Action:Timer_RandomizeNextmap(Handle:timer) +{ + decl String:map[64]; + + new exclusion = GetConVarInt(g_Cvar_ExcludeMaps); + if (exclusion > GetArraySize(g_MapCycle)) + { + exclusion = GetArraySize(g_MapCycle) - 1; + } + + new b = GetRandomInt(0, GetArraySize(g_MapCycle) - 1); + GetArrayString(g_MapCycle, b, map, sizeof(map)); + + while (FindStringInArray(g_MapHistory, map) != -1) + { + b = GetRandomInt(0, GetArraySize(g_MapCycle) - 1); + GetArrayString(g_MapCycle, b, map, sizeof(map)); + } + + SetNextmap(map); + + LogMessage("[MapManager] Randomizer has chosen '%s' as the next map.", map); + + return Plugin_Stop; +} + +public Action:Timer_DelayRTV(Handle:timer) +{ + g_RTVAllowed = true; + g_RTVStarted = false; + g_RTVEnded = false; +} + +public Action:Timer_StartRTV(Handle:timer) +{ + if (timer == g_RetryTimer) + { + g_RetryTimer = INVALID_HANDLE; + } + + if (g_RetryTimer != INVALID_HANDLE) + { + return; + } + + if (IsVoteInProgress()) + { + // Can't start a vote, try again in 5 seconds. + g_RetryTimer = CreateTimer(5.0, Timer_StartRTV, TIMER_FLAG_NO_MAPCHANGE); + return; + } + + PrintToChatAll("[SM] %t", "RTV Vote Ready"); + + new Handle:MapVoteMenu = CreateMenu(Handler_MapMapVoteMenu, MenuAction:MENU_ACTIONS_ALL); + SetMenuTitle(MapVoteMenu, "Rock The Vote"); + + new Handle:tempMaps = CloneArray(g_MapList); + decl String:map[32]; + + GetCurrentMap(map, sizeof(map)); + new index = FindStringInArray(tempMaps, map); + if (index != -1) + { + RemoveFromArray(tempMaps, index); + } + + // We assume that g_RTVMapList is within the correct limits, based on the logic for nominations + for (new i = 0; i < GetArraySize(g_RTVMapList); i++) + { + GetArrayString(g_RTVMapList, i, map, sizeof(map)); + AddMenuItem(MapVoteMenu, map, map); + + index = FindStringInArray(tempMaps, map); + if (index != -1) + { + RemoveFromArray(tempMaps, index); + } + } + + new limit = GetConVarInt(g_Cvar_Maps) - GetArraySize(g_RTVMapList); + if (limit > GetArraySize(tempMaps)) + { + limit = GetArraySize(tempMaps); + } + + for (new i = 0; i < limit; i++) + { + new b = GetRandomInt(0, GetArraySize(tempMaps) - 1); + GetArrayString(tempMaps, b, map, sizeof(map)); + PushArrayString(g_RTVMapList, map); + AddMenuItem(MapVoteMenu, map, map); + RemoveFromArray(tempMaps, b); + } + + CloseHandle(tempMaps); + + AddMenuItem(MapVoteMenu, "Don't Change", "Don't Change"); + + SetMenuExitButton(MapVoteMenu, false); + VoteMenuToAll(MapVoteMenu, 20); + + LogMessage("[SM] Rockthevote was successfully started."); +} + +public Action:Timer_StartMapVote(Handle:timer) +{ + if (!GetArraySize(g_MapList)) + { + return Plugin_Stop; + } + + if (timer == g_RetryTimer) + { + g_RetryTimer = INVALID_HANDLE; + } + else + { + g_VoteTimer = INVALID_HANDLE; + } + + InitiateVote(); + + return Plugin_Stop; +} \ No newline at end of file diff --git a/plugins/mapmanager/votes.sp b/plugins/mapmanager/votes.sp new file mode 100644 index 00000000..c8a1d6f7 --- /dev/null +++ b/plugins/mapmanager/votes.sp @@ -0,0 +1,318 @@ +/** + * vim: set ts=4 : + * ============================================================================= + * SourceMod Map Manager Plugin + * Contains vote callbacks + * + * SourceMod (C)2004-2007 AlliedModders LLC. All rights reserved. + * ============================================================================= + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id$ + */ + +// sm_votemap +public Handler_VoteCallback(Handle:menu, MenuAction:action, param1, param2) +{ + if (action == MenuAction_End) + { + CloseHandle(menu); + } + else if (action == MenuAction_Display) + { + decl String:title[64]; + GetMenuTitle(menu, title, sizeof(title)); + + decl String:map[64]; + GetMenuItem(menu, 1, map, sizeof(map)); + + decl String:buffer[255]; + Format(buffer, sizeof(buffer), "%T", title, param1, map); + + new Handle:panel = Handle:param2; + SetPanelTitle(panel, buffer); + } + else if (action == MenuAction_DisplayItem) + { + decl String:display[64]; + GetMenuItem(menu, param2, "", 0, _, display, sizeof(display)); + + if (strcmp(display, "No") == 0 || strcmp(display, "Yes") == 0) + { + decl String:buffer[255]; + Format(buffer, sizeof(buffer), "%T", display, param1); + + return RedrawMenuItem(buffer); + } + } + else if (action == MenuAction_VoteCancel && param1 == VoteCancel_NoVotes) + { + PrintToChatAll("[SM] %t", "No Votes Cast"); + } + else if (action == MenuAction_VoteEnd) + { + decl String:item[64], String:display[64]; + new Float:percent, Float:limit, votes, totalVotes; + + GetMenuVoteInfo(param2, votes, totalVotes); + GetMenuItem(menu, param1, item, sizeof(item), _, display, sizeof(display)); + + if (strcmp(item, VOTE_NO) == 0 && param1 == 1) + { + votes = totalVotes - votes; // Reverse the votes to be in relation to the Yes option. + } + + percent = GetVotePercent(votes, totalVotes); + + limit = GetConVarFloat(g_Cvar_VoteMap); + + // A multi-argument vote is "always successful", but have to check if its a Yes/No vote. + if ((strcmp(item, VOTE_YES) == 0 && FloatCompare(percent,limit) < 0 && param1 == 0) || (strcmp(item, VOTE_NO) == 0 && param1 == 1)) + { + LogAction(-1, -1, "Vote failed."); + PrintToChatAll("[SM] %t", "Votemap Failed", RoundToNearest(100.0*limit), RoundToNearest(100.0*percent), totalVotes); + } + else + { + PrintToChatAll("[SM] %t", "Votemap Successful", RoundToNearest(100.0*percent), totalVotes); + + if (g_VoteMapInUse && IsClientInGame(g_VoteMapInUse)) + { + DisplayAcceptVoteMenu(item); + } + else + { + LogAction(-1, -1, "Changing map to %s due to vote.", item); + PrintToChatAll("[SM] %t", "Changing map", item); + SetMapChange(0, item, g_Client_Data[g_VoteMapInUse][0]) + } + } + } + + return 0; +} + +public Handler_MapMapVoteMenu(Handle:menu, MenuAction:action, param1, param2) +{ + switch (action) + { + case MenuAction_End: + { + CloseHandle(menu); + } + + case MenuAction_Display: + { + decl String:oldTitle[255], String:buffer[255]; + GetMenuTitle(menu, oldTitle, sizeof(oldTitle)); + Format(buffer, sizeof(buffer), "%T", oldTitle, param1); + + new Handle:panel = Handle:param2; + SetPanelTitle(panel, buffer); + } + + case MenuAction_DisplayItem: + { + if (GetMenuItemCount(menu) - 1 == param2) + { + decl String:buffer[255]; + Format(buffer, sizeof(buffer), "%T", "Don't Change", param1); + return RedrawMenuItem(buffer); + } + } + + // Why am I commented out? Because BAIL hasn't decided yet if + // vote notification will be built into the Vote API. + /*case MenuAction_Select: + { + decl String:Name[32], String:Map[32]; + GetClientName(param1, Name, sizeof(Name)); + GetMenuItem(menu, param2, Map, sizeof(Map)); + + PrintToChatAll("[SM] %s has voted for map '%s'", Name, Map); + }*/ + + case MenuAction_VoteCancel: + { + if (param1 == VoteCancel_NoVotes) + { + PrintToChatAll("[SM] %t", "No Votes"); + g_RTVEnded = true; + } + } + + case MenuAction_VoteEnd: + { + new String:map[64]; + + GetMenuItem(menu, param1, map, sizeof(map)); + + if (GetMenuItemCount(menu) - 1 == param1) // This should always match the "Keep Current" option + { + PrintToChatAll("[SM] %t", "Current Map Stays"); + LogMessage("[SM] Rockthevote has ended, current map kept."); + } + else + { + PrintToChatAll("[SM] %t", "Changing Maps", map); + LogMessage("[SM] Rockthevote has ended, changing to map %s.", map); + new Handle:dp; + CreateDataTimer(5.0, Timer_ChangeMap, dp); + WritePackString(dp, map); + } + + g_RTVEnded = true; + } + } + + return 0; +} + +public Handler_MapVoteMenu(Handle:menu, MenuAction:action, param1, param2) +{ + switch (action) + { + case MenuAction_End: + { + g_VoteMenu = INVALID_HANDLE; + CloseHandle(menu); + } + + case MenuAction_Display: + { + decl String:buffer[255]; + Format(buffer, sizeof(buffer), "%T", "Vote Nextmap", param1); + + new Handle:panel = Handle:param2; + SetPanelTitle(panel, buffer); + } + + case MenuAction_DisplayItem: + { + if (GetMenuItemCount(menu) - 1 == param2) + { + decl String:map[64], String:buffer[255]; + GetMenuItem(menu, param2, map, sizeof(map)); + if (strcmp(map, VOTE_EXTEND, false) == 0) + { + Format(buffer, sizeof(buffer), "%T", "Extend Map", param1); + return RedrawMenuItem(buffer); + } + } + } + + // Why am I commented out? Because BAIL hasn't decided yet if + // vote notification will be built into the Vote API. + /*case MenuAction_Select: + { + decl String:Name[32], String:Map[32]; + GetClientName(param1, Name, sizeof(Name)); + GetMenuItem(menu, param2, Map, sizeof(Map)); + + PrintToChatAll("[SM] %s has voted for map '%s'", Name, Map); + }*/ + + case MenuAction_VoteCancel: + { + // If we receive 0 votes, pick at random. + if (param1 == VoteCancel_NoVotes && GetConVarBool(g_Cvar_NoVoteMode)) + { + new count = GetMenuItemCount(menu); + new item = GetRandomInt(0, count - 1); + decl String:map[32]; + GetMenuItem(menu, item, map, sizeof(map)); + + while (strcmp(map, VOTE_EXTEND, false) == 0) + { + item = GetRandomInt(0, count - 1); + GetMenuItem(menu, item, map, sizeof(map)); + } + + SetNextMap(map); + } + else + { + // We were actually cancelled. What should we do? + } + } + + case MenuAction_VoteEnd: + { + decl String:map[32]; + GetMenuItem(menu, param1, map, sizeof(map)); + + if (strcmp(map, VOTE_EXTEND, false) == 0) + { + new time; + if (GetMapTimeLimit(time)) + { + if (time > 0 && time < GetConVarInt(g_Cvar_ExtendTimeMax)) + { + ExtendMapTimeLimit(GetConVarInt(g_Cvar_ExtendTimeStep)*60); + } + } + + if (g_Cvar_Winlimit != INVALID_HANDLE) + { + new winlimit = GetConVarInt(g_Cvar_Winlimit); + if (winlimit && winlimit < GetConVarInt(g_Cvar_ExtendRoundMax)) + { + SetConVarInt(g_Cvar_Winlimit, winlimit + GetConVarInt(g_Cvar_ExtendRoundStep)); + } + } + + if (g_Cvar_Maxrounds != INVALID_HANDLE) + { + new maxrounds = GetConVarInt(g_Cvar_Maxrounds); + if (maxrounds && maxrounds < GetConVarInt(g_Cvar_ExtendRoundMax)) + { + SetConVarInt(g_Cvar_Maxrounds, maxrounds + GetConVarInt(g_Cvar_ExtendRoundStep)); + } + } + + if (g_Cvar_Fraglimit != INVALID_HANDLE) + { + new fraglimit = GetConVarInt(g_Cvar_Fraglimit); + if (fraglimit && fraglimit < GetConVarInt(g_Cvar_ExtendFragMax)) + { + SetConVarInt(g_Cvar_Fraglimit, fraglimit + GetConVarInt(g_Cvar_ExtendFragStep)); + } + } + + PrintToChatAll("[SM] %t", "Current Map Extended"); + LogMessage("Voting for next map has finished. The current map has been extended."); + + // We extended, so we'll have to vote again. + g_HasVoteStarted = false; + CreateNextVote(); + SetupTimeleftTimer(); + } + else + { + SetNextMap(map); + } + } + } + + return 0; +} \ No newline at end of file diff --git a/plugins/testsuite/goto_test.sp b/plugins/testsuite/goto_test.sp new file mode 100644 index 00000000..2c6721c8 --- /dev/null +++ b/plugins/testsuite/goto_test.sp @@ -0,0 +1,28 @@ +#include + +public OnPluginStart() +{ + RegServerCmd("test_goto", Test_Goto); +} + +public Action:Test_Goto(args) +{ + new bool:hello = false; + new String:crab[] = "space crab"; + +sample_label: + + new String:yam[] = "yams"; + + PrintToServer("%s %s", crab, yam); + + if (!hello) + { + new bool:what = true; + hello = what + goto sample_label; + } + + return Plugin_Handled; +} + diff --git a/sourcepawn/compiler/sc4.c b/sourcepawn/compiler/sc4.c index 91887f5c..1eb19208 100644 --- a/sourcepawn/compiler/sc4.c +++ b/sourcepawn/compiler/sc4.c @@ -854,19 +854,10 @@ SC_FUNC void modstk(int delta) /* set the stack to a hard offset from the frame */ SC_FUNC void setstk(cell value) { - stgwrite("\tlctrl 5\n"); /* get FRM in PRI */ + stgwrite("\tstackadjust "); assert(value<=0); /* STK should always become <= FRM */ - if (value<0) { - stgwrite("\tadd.c "); - outval(value, TRUE); /* add (negative) offset */ - code_idx+=opcodes(1)+opargs(1); - // ??? write zeros in the space between STK and the value in PRI (the new stk) - // get value of STK in ALT - // zero PRI - // need new FILL opcode that takes a variable size - } /* if */ - stgwrite("\tsctrl 4\n"); /* store in STK */ - code_idx+=opcodes(2)+opargs(2); + outval(value, TRUE); /* add (negative) offset */ + code_idx+=opcodes(1)+opargs(1); } SC_FUNC void modheap(int delta) diff --git a/sourcepawn/compiler/sc6.c b/sourcepawn/compiler/sc6.c index a19b62cd..6a431eac 100644 --- a/sourcepawn/compiler/sc6.c +++ b/sourcepawn/compiler/sc6.c @@ -596,6 +596,7 @@ static OPCODEC opcodelist[] = { { 21, "sref.s.pri", sIN_CSEG, parm1 }, { 67, "sshr", sIN_CSEG, parm0 }, { 44, "stack", sIN_CSEG, parm1 }, + {165, "stackadjust",sIN_CSEG, parm1 }, { 0, "stksize", 0, noop }, { 16, "stor.alt", sIN_CSEG, parm1 }, { 23, "stor.i", sIN_CSEG, parm0 }, diff --git a/sourcepawn/jit/x86/jit_x86.cpp b/sourcepawn/jit/x86/jit_x86.cpp index 6103f3da..23e9a22c 100644 --- a/sourcepawn/jit/x86/jit_x86.cpp +++ b/sourcepawn/jit/x86/jit_x86.cpp @@ -1209,6 +1209,28 @@ inline void WriteOp_Stack(JitWriter *jit) } } +inline void WriteOp_StackAdjust(JitWriter *jit) +{ + cell_t val = jit->read_cell(); + + assert(val <= 0); + + //lea edi, [ebx-val] + + if (val < SCHAR_MAX && val > SCHAR_MIN) + { + IA32_Lea_DispRegImm8(jit, AMX_REG_STK, AMX_REG_FRM, val); + } + else + { + IA32_Lea_DispRegImm32(jit, AMX_REG_STK, AMX_REG_FRM, val); + } + + /* We assume the compiler has generated good code -- + * That is, we do not bother validating this amount! + */ +} + inline void WriteOp_Heap(JitWriter *jit) { //mov edx, [esi+hea] diff --git a/sourcepawn/jit/x86/opcode_helpers.h b/sourcepawn/jit/x86/opcode_helpers.h index eb74eb30..c7bb2c75 100644 --- a/sourcepawn/jit/x86/opcode_helpers.h +++ b/sourcepawn/jit/x86/opcode_helpers.h @@ -124,8 +124,8 @@ typedef enum OP_LOAD_ALT, //~!VERIFIED (load.pri) OP_LOAD_S_PRI, //VERIFIED OP_LOAD_S_ALT, //VERIFIED - OP_LREF_PRI, // !GEN :TODO: we will need this for dynarrays - OP_LREF_ALT, // !GEN :TODO: we will need this for dynarrays + OP_LREF_PRI, //VERIFIED + OP_LREF_ALT, //~VERIFIED OP_LREF_S_PRI, //VERIFIED OP_LREF_S_ALT, //~VERIFIED (lref.s.pri) OP_LOAD_I, //VERIFIED @@ -138,8 +138,8 @@ typedef enum OP_STOR_ALT, //~VERIFIED (stor.pri) OP_STOR_S_PRI, //VERIFIED OP_STOR_S_ALT, //~VERIFIED (stor.s.pri) - OP_SREF_PRI, // !GEN :TODO: we will need this for dynarrays - OP_SREF_ALT, // !GEN :TODO: we will need this for dynarrays + OP_SREF_PRI, //VERIFIED + OP_SREF_ALT, //~VERIFIED OP_SREF_S_PRI, //VERIFIED OP_SREF_S_ALT, //~VERIFIED (stor.s.alt) OP_STOR_I, //VERIFIED @@ -286,6 +286,7 @@ typedef enum OP_GENARRAY, //VERIFIED OP_GENARRAY_Z, //-VERIFIED (not tested for 1D arrays) OP_STRADJUST_PRI, //VERIFIED + OP_STACKADJUST, //:TODO: VERIFY OP_FABS, //VERIFIED OP_FLOAT, //VERIFIED OP_FLOATADD, //VERIFIED @@ -301,22 +302,4 @@ typedef enum OP_NUM_OPCODES } OPCODE; -/* -* :TODO: List of ASM Opts -* from jit_x86.cpp -* DONE: Rest of opcodes including the SYSREQ.N inlined version (rev2) -* Including genarray and the 2 tracker ones -* DONE: ALL ungen opcodes (rev1) -* from opcode_helpers.cpp -* DONE: SYSREQ.N .C (rev2) -* MACRO OPCODES (rev2) -* ERROR CHECKS\{VERIFY ADDR} (rev2) -* ARRAY STUFF -* TODO: BrkDebug -* EXEC FUNCTION -* VERIFY ADDR -* -* Oh and ALIGN to 16BYTES all stuff that is called via CALL and frequently used jump labels like what's done with PROC. -*/ - #endif //_INCLUDE_SOURCEPAWN_JIT_X86_OPCODE_INFO_H_ diff --git a/sourcepawn/jit/x86/opcode_switch.inc b/sourcepawn/jit/x86/opcode_switch.inc index 2ac2d2f0..e62c5791 100644 --- a/sourcepawn/jit/x86/opcode_switch.inc +++ b/sourcepawn/jit/x86/opcode_switch.inc @@ -748,6 +748,11 @@ WriteOp_FloatCompare(jit); break; } + case OP_STACKADJUST: + { + WriteOp_StackAdjust(jit); + break; + } #if defined USE_UNGEN_OPCODES #include "ungen_opcode_switch.inc" #endif