From 924cfb4f9f38a967c88f897024e891c78ab90467 Mon Sep 17 00:00:00 2001 From: David Anderson Date: Wed, 26 Sep 2007 14:38:40 +0000 Subject: [PATCH] initial import of UNTESTED, UNFINISHED topmenus extension --HG-- extra : convert_revision : svn%3A39bc706e-5318-0410-9160-8a85361fbb7c/trunk%401494 --- extensions/topmenus/Makefile | 87 ++ extensions/topmenus/TopMenu.cpp | 942 ++++++++++++++++++++++ extensions/topmenus/TopMenu.h | 167 ++++ extensions/topmenus/TopMenuManager.cpp | 101 +++ extensions/topmenus/TopMenuManager.h | 64 ++ extensions/topmenus/extension.cpp | 55 ++ extensions/topmenus/extension.h | 66 ++ extensions/topmenus/msvc8/topmenus.sln | 20 + extensions/topmenus/msvc8/topmenus.vcproj | 256 ++++++ extensions/topmenus/sdk/sm_memtable.cpp | 112 +++ extensions/topmenus/sdk/sm_memtable.h | 105 +++ extensions/topmenus/sdk/smsdk_config.h | 78 ++ extensions/topmenus/sdk/smsdk_ext.cpp | 440 ++++++++++ extensions/topmenus/sdk/smsdk_ext.h | 313 +++++++ public/extensions/ITopMenus.h | 219 +++++ 15 files changed, 3025 insertions(+) create mode 100644 extensions/topmenus/Makefile create mode 100644 extensions/topmenus/TopMenu.cpp create mode 100644 extensions/topmenus/TopMenu.h create mode 100644 extensions/topmenus/TopMenuManager.cpp create mode 100644 extensions/topmenus/TopMenuManager.h create mode 100644 extensions/topmenus/extension.cpp create mode 100644 extensions/topmenus/extension.h create mode 100644 extensions/topmenus/msvc8/topmenus.sln create mode 100644 extensions/topmenus/msvc8/topmenus.vcproj create mode 100644 extensions/topmenus/sdk/sm_memtable.cpp create mode 100644 extensions/topmenus/sdk/sm_memtable.h create mode 100644 extensions/topmenus/sdk/smsdk_config.h create mode 100644 extensions/topmenus/sdk/smsdk_ext.cpp create mode 100644 extensions/topmenus/sdk/smsdk_ext.h create mode 100644 public/extensions/ITopMenus.h diff --git a/extensions/topmenus/Makefile b/extensions/topmenus/Makefile new file mode 100644 index 00000000..8c735d7d --- /dev/null +++ b/extensions/topmenus/Makefile @@ -0,0 +1,87 @@ +#(C)2004-2006 SourceMM Development Team +# Makefile written by David "BAILOPAN" Anderson + +SMSDK = ../.. +SRCDS = ~/srcds +SOURCEMM = ../../../../sourcemm + +##################################### +### EDIT BELOW FOR OTHER PROJECTS ### +##################################### + +PROJECT = sample + +#Uncomment for SourceMM-enabled extensions +#LINK_HL2 = $(HL2LIB)/tier1_i486.a vstdlib_i486.so tier0_i486.so + +OBJECTS = sdk/smsdk_ext.cpp extension.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 +HL2SDK = $(SOURCEMM)/hl2sdk +SMM_TRUNK = $(SOURCEMM)/trunk + +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$(SMM_TRUNK) -I$(SMM_TRUNK)/sourcehook -I$(SMM_TRUNK)/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 -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) extension + +extension: $(OBJ_LINUX) + $(CPP) $(INCLUDE) $(CFLAGS) $(CPPFLAGS) $(OBJ_LINUX) $(LINK) -shared -ldl -lm -o$(BIN_DIR)/$(BINARY) + +debug: + $(MAKE) 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/topmenus/TopMenu.cpp b/extensions/topmenus/TopMenu.cpp new file mode 100644 index 00000000..c7bf307d --- /dev/null +++ b/extensions/topmenus/TopMenu.cpp @@ -0,0 +1,942 @@ +/** + * 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: extension.cpp 1336 2007-08-15 06:19:30Z damagedsoul $ + */ + +#include +#include "TopMenu.h" + +struct obj_by_name_t +{ + unsigned int obj_index; + char name[64]; +}; + +int _SortObjectNamesDescending(const void *ptr1, const void *ptr2); +unsigned int strncopy(char *dest, const char *src, size_t count); +size_t UTIL_Format(char *buffer, size_t maxlength, const char *fmt, ...); + +TopMenu::TopMenu(ITopMenuObjectCallbacks *callbacks) +{ + m_clients = NULL; + m_SerialNo = 1; + m_pTitle = callbacks; + m_max_clients = 0; + + if (playerhelpers->IsServerActivated()) + { + CreatePlayers(playerhelpers->GetMaxClients()); + } +} + +TopMenu::~TopMenu() +{ + /* Delete all categories */ + for (size_t i = 0; i < m_Categories.size(); i++) + { + delete m_Categories[i]; + } + + /* Delete all items */ + for (size_t i = 0; i < m_Objects.size(); i++) + { + delete m_Objects[i]; + } + + /* Delete all cached config entries */ + for (size_t i = 0; i < m_Config.cats.size(); i++) + { + delete m_Config.cats[i]; + } + + /* Sweep players */ + for (int i = 0; i <= m_max_clients; i++) + { + TearDownClient(&m_clients[i]); + } + delete [] m_clients; +} + +void TopMenu::OnClientConnected(int client) +{ + if (m_clients == NULL) + { + return; + } + + topmenu_player_t *player = &m_clients[client]; + TearDownClient(player); +} + +void TopMenu::OnClientDisconnected(int client) +{ + if (m_clients == NULL) + { + return; + } + + topmenu_player_t *player = &m_clients[client]; + TearDownClient(player); +} + +void TopMenu::OnServerActivated(int max_clients) +{ + if (m_clients == NULL) + { + CreatePlayers(max_clients); + } +} + +unsigned int TopMenu::AddToMenu(const char *name, + TopMenuObjectType type, + ITopMenuObjectCallbacks *callbacks, + IdentityToken_t *owner, + const char *cmdname, + FlagBits flags, + unsigned int parent) +{ + /* Sanity checks */ + if (type == TopMenuObject_Category && parent != 0) + { + return 0; + } + else if (type == TopMenuObject_Item && parent == 0) + { + return 0; + } + else if (m_ObjLookup.retrieve(name) != NULL) + { + return 0; + } + + /* If we're adding an item, make sure the parent is valid, + * and that the parent is a category. + */ + topmenu_object_t *parent_obj = NULL; + topmenu_category_t *parent_cat = NULL; + if (type == TopMenuObject_Item) + { + /* Check parent index. Note it will be >= 1 here. */ + if (parent > m_Objects.size() || m_Objects[parent - 1]->is_free) + { + return 0; + } + parent_obj = m_Objects[parent - 1]; + + /* Find an equivalent pointer in the category array. */ + for (size_t i = 0; i < m_Categories.size(); i++) + { + if (m_Categories[i]->obj == parent_obj) + { + parent_cat = m_Categories[i]; + break; + } + } + + /* If none was found, leave. */ + if (parent_cat == NULL) + { + return 0; + } + } + + /* Re-use an old object pointer if we can. */ + topmenu_object_t *obj = NULL; + for (size_t i = 0; i < m_Objects.size(); i++) + { + if (m_Objects[i]->is_free == true) + { + obj = m_Objects[i]; + break; + } + } + + /* Otherwise, allocate a new one. */ + if (obj == NULL) + { + obj = new topmenu_object_t; + obj->object_id = ((unsigned int)m_Objects.size()) + 1; + m_Objects.push_back(obj); + } + + /* Initialize the object's properties. */ + obj->callbacks = callbacks; + obj->flags = flags; + obj->owner = owner; + obj->type = type; + obj->is_free = false; + obj->parent = parent_obj; + strncopy(obj->name, name, sizeof(obj->name)); + strncopy(obj->cmdname, cmdname ? cmdname : "", sizeof(obj->cmdname)); + + if (obj->type == TopMenuObject_Category) + { + /* Create a new category entry */ + topmenu_category_t *cat = new topmenu_category_t; + cat->obj = obj; + cat->reorder = false; + cat->serial = 1; + + /* Add it, then update our serial change number. */ + m_Categories.push_back(cat); + m_SerialNo++; + + /* Updating sorting info */ + m_bCatsNeedResort = true; + } + else if (obj->type == TopMenuObject_Item) + { + /* Update the category, mark it as needing changes */ + parent_cat->obj_list.push_back(obj); + parent_cat->reorder = true; + parent_cat->serial++; + } + + m_ObjLookup.insert(name, obj); + + return obj->object_id; +} + +void TopMenu::RemoveFromMenu(unsigned int object_id) +{ + if (object_id == 0 + || object_id > m_Objects.size() + || m_Objects[object_id - 1]->is_free) + { + return; + } + + topmenu_object_t *obj = m_Objects[object_id - 1]; + + m_ObjLookup.remove(obj->name); + + if (obj->type == TopMenuObject_Category) + { + /* Find it in the category list */ + for (size_t i = 0; i < m_Categories.size(); i++) + { + if (m_Categories[i]->obj == obj) + { + /* Erase from here */ + delete m_Categories[i]; + m_Categories.erase(m_Categories.iterAt(i)); + break; + } + } + /* Update us as changed */ + m_SerialNo++; + m_bCatsNeedResort = true; + } + else if (obj->type == TopMenuObject_Item) + { + /* Find the category this item is in. */ + topmenu_category_t *parent_cat = NULL; + for (size_t i = 0; i < m_Categories.size(); i++) + { + if (m_Categories[i]->obj == obj->parent) + { + parent_cat = m_Categories[i]; + break; + } + } + + /* Erase it from the category's lists. */ + if (parent_cat) + { + for (size_t i = 0; i < parent_cat->obj_list.size(); i++) + { + if (parent_cat->obj_list[i] == obj) + { + parent_cat->obj_list.erase(parent_cat->obj_list.iterAt(i)); + break; + } + } + parent_cat->reorder = true; + parent_cat->serial++; + } + } + + /* Finally, mark the object as free. */ + obj->is_free = true; +} + +bool TopMenu::DisplayMenu(int client, unsigned int hold_time, TopMenuPosition position) +{ + if (m_clients == NULL) + { + return false; + } + + IGamePlayer *pPlayer = playerhelpers->GetGamePlayer(client); + if (!pPlayer->IsInGame()) + { + return false; + } + + UpdateClientRoot(client, pPlayer); + + topmenu_player_t *pClient = &m_clients[client]; + if (pClient->root == NULL) + { + return false; + } + + bool return_value = false; + + if (position == TopMenuPosition_LastCategory && + pClient->last_category < m_Categories.size()) + { + return_value = DisplayCategory(client, pClient->last_category, hold_time, true); + } + else if (position == TopMenuPosition_LastRoot) + { + pClient->root->DisplayAtItem(client, hold_time, pClient->last_root_pos); + } + else if (position == TopMenuPosition_Start) + { + pClient->last_position = 0; + pClient->last_category = 0; + return_value = pClient->root->Display(client, hold_time); + } + + return return_value; +} + +bool TopMenu::DisplayCategory(int client, unsigned int category, unsigned int hold_time, bool last_position) +{ + UpdateClientCategory(client, category); + + topmenu_player_t *pClient = &m_clients[client]; + if (category >= pClient->cat_count + || pClient->cats[category].menu == NULL) + { + return false; + } + + bool return_value = false; + + topmenu_player_category_t *player_cat = &(pClient->cats[category]); + + pClient->last_category = category; + if (last_position) + { + return_value = player_cat->menu->DisplayAtItem(client, hold_time, pClient->last_position); + } + else + { + return_value = player_cat->menu->Display(client, hold_time); + } + + return return_value; +} + +void TopMenu::OnMenuSelect2(IBaseMenu *menu, int client, unsigned int item, unsigned int item_on_page) +{ + const char *item_name = menu->GetItemInfo(item, NULL); + if (!item_name) + { + return; + } + + topmenu_object_t *obj; + topmenu_player_t *pClient = &m_clients[client]; + topmenu_object_t **pObject = m_ObjLookup.retrieve(item_name); + if (pObject == NULL) + { + return; + } + + obj = *pObject; + + /* We now have the object... what do we do with it? */ + if (obj->type == TopMenuObject_Category) + { + /* If it's a category, the user wants to view it.. */ + for (size_t i = 0; i < m_Categories.size(); i++) + { + if (m_Categories[i]->obj == obj) + { + pClient->last_root_pos = item_on_page; + DisplayCategory(client, (unsigned int)i, MENU_TIME_FOREVER, false); + break; + } + } + } + else + { + pClient->last_position = item_on_page; + + /* Re-check access in case this user had their credentials revoked */ + if (obj->cmdname[0] != '\0' && !adminsys->CheckAccess(client, obj->cmdname, obj->flags, false)) + { + DisplayMenu(client, 0, TopMenuPosition_LastCategory); + return; + } + + /* Pass the information on to the callback */ + obj->callbacks->OnTopMenuSelectOption(client, obj->object_id); + } +} + +void TopMenu::OnMenuDrawItem(IBaseMenu *menu, int client, unsigned int item, unsigned int &style) +{ + const char *item_name = menu->GetItemInfo(item, NULL); + if (!item_name) + { + return; + } + + topmenu_object_t *obj; + topmenu_object_t **pObject = m_ObjLookup.retrieve(item_name); + if (pObject == NULL) + { + return; + } + + obj = *pObject; + + if (obj->cmdname[0] == '\0') + { + return; + } + + if (!adminsys->CheckAccess(client, obj->cmdname, obj->flags, false)) + { + style = ITEMDRAW_IGNORE; + } +} + +unsigned int TopMenu::OnMenuDisplayItem(IBaseMenu *menu, + int client, + IMenuPanel *panel, + unsigned int item, + const ItemDrawInfo &dr) +{ + const char *item_name = menu->GetItemInfo(item, NULL); + if (!item_name) + { + return 0; + } + + topmenu_object_t *obj; + topmenu_object_t **pObject = m_ObjLookup.retrieve(item_name); + if (pObject == NULL) + { + return 0; + } + + obj = *pObject; + + /* Ask the object to render the text for this client */ + char renderbuf[64]; + obj->callbacks->OnTopMenuDrawOption(client, obj->object_id, renderbuf, sizeof(renderbuf)); + + /* Build the new draw info */ + ItemDrawInfo new_dr = dr; + new_dr.display = renderbuf; + + /* Man I love the menu API. Ask the panel to draw the item and give the position + * back to Core's renderer. This way we don't have to worry about keeping the + * render buffer static! + */ + return panel->DrawItem(new_dr); +} + +void TopMenu::OnMenuCancel(IBaseMenu *menu, int client, MenuCancelReason reason) +{ + if (reason == MenuCancel_ExitBack) + { + /* If the client chose exit back, they were on a category menu, and we can + * now display the root menu from the last known position. + */ + DisplayMenu(client, 0, TopMenuPosition_LastRoot); + } +} + +void TopMenu::UpdateClientRoot(int client, IGamePlayer *pGamePlayer) +{ + topmenu_player_t *pClient = &m_clients[client]; + IGamePlayer *pPlayer = pGamePlayer ? pGamePlayer : playerhelpers->GetGamePlayer(client); + + /* Determine if an update is necessary */ + bool is_update_needed = false; + if (pClient->menu_serial != m_SerialNo) + { + is_update_needed = true; + } + else if (pPlayer->GetUserId() != pClient->user_id) + { + is_update_needed = true; + } + + /* If no update is needed at the root level, just leave now */ + if (!is_update_needed) + { + return; + } + + /* First we need to flush the cache... */ + TearDownClient(pClient); + + /* Now, rebuild the category list, but don't create menus */ + if (m_Categories.size() == 0) + { + pClient->cat_count = 0; + pClient->cats = NULL; + } + else + { + pClient->cat_count = (unsigned int)m_Categories.size(); + pClient->cats = new topmenu_player_category_t[pClient->cat_count]; + memset(pClient->cats, 0, sizeof(topmenu_player_category_t) * pClient->cat_count); + } + + /* Re-sort the root categories if needed */ + SortCategoriesIfNeeded(); + + /* Build the root menu */ + IBaseMenu *root_menu = menus->GetDefaultStyle()->CreateMenu(this, myself->GetIdentity()); + + /* Add the sorted items */ + for (size_t i = 0; i < m_SortedCats.size(); i++) + { + root_menu->AppendItem(m_Categories[m_SortedCats[i]]->obj->name, ItemDrawInfo("")); + } + + /* Now we need to handle un-sorted items. This is slightly trickier, as we need to + * pre-render each category name, and cache those names. Phew! + */ + if (m_UnsortedCats.size()) + { + obj_by_name_t *item_list = new obj_by_name_t[m_UnsortedCats.size()]; + for (size_t i = 0; i < m_UnsortedCats.size(); i++) + { + obj_by_name_t *temp_obj = &item_list[i]; + topmenu_object_t *obj = m_Categories[m_UnsortedCats[i]]->obj; + obj->callbacks->OnTopMenuDrawOption(client, + obj->object_id, + temp_obj->name, + sizeof(temp_obj->name)); + temp_obj->obj_index = m_UnsortedCats[i]; + } + + /* Sort our temp list */ + qsort(item_list, m_UnsortedCats.size(), sizeof(obj_by_name_t), _SortObjectNamesDescending); + + /* Add the new sorted categories */ + for (size_t i = 0; i < m_SortedCats.size(); i++) + { + root_menu->AppendItem(m_Categories[item_list[i].obj_index]->obj->name, ItemDrawInfo("")); + } + + delete [] item_list; + } + + /* Set the menu's title */ + char renderbuf[128]; + m_pTitle->OnTopMenuDrawTitle(client, 0, renderbuf, sizeof(renderbuf)); + root_menu->SetDefaultTitle(renderbuf); + + /* The client is now fully updated */ + pClient->root = root_menu; + pClient->user_id = pPlayer->GetUserId(); + pClient->menu_serial = m_SerialNo; + pClient->last_position = 0; + pClient->last_category = 0; + pClient->last_root_pos = 0; +} + +void TopMenu::UpdateClientCategory(int client, unsigned int category) +{ + /* Update the client's root menu just in case it needs it. This + * will validate that we have both a valid client and a valid + * category structure for that client. + */ + UpdateClientRoot(client); + + /* Now it's guaranteed that our category tables will be usable */ + topmenu_player_t *pClient = &m_clients[client]; + topmenu_category_t *cat = m_Categories[category]; + topmenu_player_category_t *player_cat = &(pClient->cats[category]); + + /* Does the category actually need updating? */ + if (player_cat->serial == cat->serial) + { + return; + } + + /* Destroy any existing menu */ + if (player_cat->menu) + { + player_cat->menu->Destroy(); + player_cat->menu = NULL; + } + + if (pClient->last_category == category) + { + pClient->last_position = 0; + } + + IBaseMenu *cat_menu = menus->GetDefaultStyle()->CreateMenu(this, myself->GetIdentity()); + + /* Categories get an "exit back" button */ + cat_menu->SetMenuOptionFlags(cat_menu->GetMenuOptionFlags() | MENUFLAG_BUTTON_EXITBACK); + + /* Re-sort the category if needed */ + SortCategoryIfNeeded(category); + + /* Build the menu with the sorted items first */ + for (size_t i = 0; i < cat->sorted.size(); i++) + { + cat_menu->AppendItem(cat->sorted[i]->name, ItemDrawInfo("")); + } + + /* Now handle unsorted items */ + if (cat->unsorted.size()) + { + /* Build a list of the item names */ + obj_by_name_t *item_list = new obj_by_name_t[cat->unsorted.size()]; + for (size_t i = 0; i < cat->unsorted.size(); i++) + { + obj_by_name_t *item = &item_list[i]; + topmenu_object_t *obj = cat->unsorted[i]; + obj->callbacks->OnTopMenuDrawOption(client, + obj->object_id, + item->name, + sizeof(item->name)); + item->obj_index = (unsigned int)i; + } + + /* Sort the names */ + qsort(item_list, cat->unsorted.size(), sizeof(obj_by_name_t), _SortObjectNamesDescending); + + /* Add to the menu */ + for (size_t i = 0; i < cat->unsorted.size(); i++) + { + cat_menu->AppendItem(cat->unsorted[item_list[i].obj_index]->name, ItemDrawInfo("")); + } + + delete [] item_list; + } + + /* Set the menu's title */ + char renderbuf[128]; + cat->obj->callbacks->OnTopMenuDrawTitle(client, cat->obj->object_id, renderbuf, sizeof(renderbuf)); + cat_menu->SetDefaultTitle(renderbuf); + + /* We're done! */ + player_cat->menu = cat_menu; + player_cat->serial = cat->serial; +} + +void TopMenu::SortCategoryIfNeeded(unsigned int category) +{ + topmenu_category_t *cat = m_Categories[category]; + if (!cat->reorder) + { + return; + } + + cat->sorted.clear(); + cat->unsorted.clear(); + + if (cat->obj_list.size() == 0) + { + cat->reorder = false; + return; + } + + CVector to_sort; + for (size_t i = 0; i < cat->obj_list.size(); i++) + { + to_sort.push_back(i); + } + + /* Find a matching category in the configs */ + config_category_t *config_cat = NULL; + for (size_t i = 0; i < m_Config.cats.size(); i++) + { + if (strcmp(m_Config.strings.GetString(m_Config.cats[i]->name), cat->obj->name) == 0) + { + config_cat = m_Config.cats[i]; + break; + } + } + + /* If there is a matching category, build a pre-sorted item list */ + if (config_cat != NULL) + { + /* Find matching objects in this category */ + for (size_t i = 0; i < config_cat->commands.size(); i++) + { + const char *config_name = m_Config.strings.GetString(config_cat->commands[i]); + for (size_t j = 0; j < to_sort.size(); j++) + { + if (strcmp(config_name, cat->obj_list[to_sort[j]]->name) == 0) + { + /* Place in the final list, then remove from the temporary list */ + cat->sorted.push_back(cat->obj_list[to_sort[j]]); + to_sort.erase(to_sort.iterAt(j)); + break; + } + } + } + } + + /* Push any remaining items onto the unsorted list */ + for (size_t i = 0; i < to_sort.size(); i++) + { + cat->unsorted.push_back(cat->obj_list[to_sort[i]]); + } + + cat->reorder = false; +} + +void TopMenu::SortCategoriesIfNeeded() +{ + if (!m_bCatsNeedResort) + { + return; + } + + /* Clear sort results */ + m_SortedCats.clear(); + m_UnsortedCats.clear(); + + if (m_Categories.size() == 0) + { + m_bCatsNeedResort = false; + return; + } + + CVector to_sort; + for (unsigned int i = 0; i < (unsigned int)m_Categories.size(); i++) + { + to_sort.push_back(i); + } + + /* If we have any predefined categories, add them in as they appear. */ + for (size_t i= 0; i < m_Config.cats.size(); i++) + { + /* Find this category and map it in if we can */ + for (size_t j = 0; j < to_sort.size(); j++) + { + if (strcmp(m_Config.strings.GetString(m_Config.cats[i]->name), + m_Categories[to_sort[j]]->obj->name) == 0) + { + /* Add to the real list and remove from the temporary */ + m_SortedCats.push_back(to_sort[j]); + to_sort.erase(to_sort.iterAt(j)); + break; + } + } + } + + /* Push any remaining items onto the unsorted list */ + for (size_t i = 0; i < to_sort.size(); i++) + { + m_UnsortedCats.push_back(to_sort[i]); + } + + m_bCatsNeedResort = false; +} + +void TopMenu::CreatePlayers(int max_clients) +{ + m_max_clients = max_clients; + m_clients = (topmenu_player_t *)malloc(sizeof(topmenu_player_t) * (max_clients + 1)); + memset(m_clients, 0, sizeof(topmenu_player_t) * (max_clients + 1)); +} + +void TopMenu::TearDownClient(topmenu_player_t *player) +{ + if (player->cats != NULL) + { + for (unsigned int i = 0; i < player->cat_count; i++) + { + topmenu_player_category_t *player_cat = &(player->cats[i]); + if (player_cat->menu != NULL) + { + player_cat->menu->Destroy(); + } + } + delete [] player->cats; + } + + if (player->root != NULL) + { + player->root->Destroy(); + } + + memset(player, 0, sizeof(topmenu_player_t)); +} + +bool TopMenu::LoadConfiguration(const char *file, char *error, size_t maxlength) +{ + SMCParseError err; + unsigned int line = 0, col = 0; + + if ((err = textparsers->ParseFile_SMC(file, this, &line, &col)) + != SMCParse_Okay) + { + const char *err_string = textparsers->GetSMCErrorString(err); + if (!err_string) + { + err_string = "Unknown"; + } + + UTIL_Format(error, maxlength, "%s", err_string); + + return false; + } + + return true; +} + +#define PARSE_STATE_NONE 0 +#define PARSE_STATE_CATEGORY 1 +unsigned int ignore_parse_level = 0; +unsigned int current_parse_state = 0; +config_category_t *cur_cat = NULL; + +void TopMenu::ReadSMC_ParseStart() +{ + current_parse_state = PARSE_STATE_NONE; + ignore_parse_level = 0; + cur_cat = NULL; + + /* Reset the old config */ + m_Config.strings.Reset(); + for (size_t i = 0; i < m_Config.cats.size(); i++) + { + delete m_Config.cats[i]; + } + m_Config.cats.clear(); +} + +SMCParseResult TopMenu::ReadSMC_NewSection(const char *name, bool opt_quotes) +{ + if (ignore_parse_level) + { + ignore_parse_level++; + } + else + { + if (current_parse_state == PARSE_STATE_NONE) + { + cur_cat = new config_category_t; + cur_cat->name = m_Config.strings.AddString(name); + m_Config.cats.push_back(cur_cat); + current_parse_state = PARSE_STATE_CATEGORY; + } + else + { + ignore_parse_level = 1; + } + } + + return SMCParse_Continue; +} + +SMCParseResult TopMenu::ReadSMC_KeyValue(const char *key, const char *value, bool key_quotes, bool value_quotes) +{ + if (ignore_parse_level > 0 + || current_parse_state != PARSE_STATE_CATEGORY + || cur_cat == NULL) + { + return SMCParse_Continue; + } + + if (strcmp(key, "item") == 0) + { + cur_cat->commands.push_back(m_Config.strings.AddString(value)); + } + + return SMCParse_Continue; +} + +SMCParseResult TopMenu::ReadSMC_LeavingSection() +{ + if (ignore_parse_level) + { + ignore_parse_level--; + } + else + { + if (current_parse_state == PARSE_STATE_CATEGORY) + { + cur_cat = NULL; + current_parse_state = PARSE_STATE_NONE; + } + } + + return SMCParse_Continue; +} + +int _SortObjectNamesDescending(const void *ptr1, const void *ptr2) +{ + obj_by_name_t *obj1 = (obj_by_name_t *)ptr1; + obj_by_name_t *obj2 = (obj_by_name_t *)ptr2; + return strcmp(obj1->name, obj2->name); +} + +unsigned int strncopy(char *dest, const char *src, size_t count) +{ + if (!count) + { + return 0; + } + + char *start = dest; + while ((*src) && (--count)) + { + *dest++ = *src++; + } + *dest = '\0'; + + return (dest - start); +} + +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; + } +} diff --git a/extensions/topmenus/TopMenu.h b/extensions/topmenus/TopMenu.h new file mode 100644 index 00000000..b4b37487 --- /dev/null +++ b/extensions/topmenus/TopMenu.h @@ -0,0 +1,167 @@ +/** + * 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: extension.cpp 1336 2007-08-15 06:19:30Z damagedsoul $ + */ + +#ifndef _INCLUDE_SOURCEMOD_TOP_MENU_H_ +#define _INCLUDE_SOURCEMOD_TOP_MENU_H_ + +#include +#include +#include +#include +#include "smsdk_ext.h" +#include "sm_memtable.h" + +using namespace SourceHook; +using namespace SourceMod; + +struct config_category_t +{ + int name; + CVector commands; +}; + +struct config_root_t +{ + config_root_t() : strings(1024) + { + } + BaseStringTable strings; + CVector cats; +}; + +struct topmenu_object_t +{ + char name[64]; /** Name */ + char cmdname[64]; /** Command name */ + FlagBits flags; /** Admin flags */ + ITopMenuObjectCallbacks *callbacks; /** Callbacks */ + IdentityToken_t *owner; /** Owner */ + unsigned int object_id; /** Object ID */ + topmenu_object_t *parent; /** Parent, if any */ + TopMenuObjectType type; /** Object Type */ + bool is_free; /** Free or not? */ +}; + +struct topmenu_category_t +{ + CVector obj_list; /** Full object list */ + CVector sorted; /** Sorted items */ + CVector unsorted; /** Unsorted items */ + topmenu_object_t *obj; /** Bound object */ + unsigned int serial; /** Serial number */ + bool reorder; /** Whether ordering needs updating */ +}; + +struct topmenu_player_category_t +{ + IBaseMenu *menu; /** menu pointer */ + unsigned int serial; /** last known serial */ +}; + +struct topmenu_player_t +{ + int user_id; /** userid on server */ + unsigned int menu_serial; /** menu serial no */ + IBaseMenu *root; /** root menu display */ + topmenu_player_category_t *cats; /** category display */ + unsigned int cat_count; /** number of categories */ + unsigned int last_category; /** last category they selected */ + unsigned int last_position; /** last position in that category */ + unsigned int last_root_pos; /** last page in the root menu */ +}; + +class TopMenu : + public ITopMenu, + public IMenuHandler, + public ITextListener_SMC +{ + friend class TopMenuManager; +public: + TopMenu(ITopMenuObjectCallbacks *callbacks); + ~TopMenu(); +public: //ITopMenu + virtual unsigned int AddToMenu(const char *name, + TopMenuObjectType type, + ITopMenuObjectCallbacks *callbacks, + IdentityToken_t *owner, + const char *cmdname, + FlagBits flags, + unsigned int parent); + virtual void RemoveFromMenu(unsigned int object_id); + virtual bool DisplayMenu(int client, + unsigned int hold_time, + TopMenuPosition position); + virtual bool LoadConfiguration(const char *file, char *error, size_t maxlength); +public: //IMenuHandler + virtual void OnMenuSelect2(IBaseMenu *menu, int client, unsigned int item, unsigned int item_on_page); + virtual void OnMenuDrawItem(IBaseMenu *menu, int client, unsigned int item, unsigned int &style); + virtual unsigned int OnMenuDisplayItem(IBaseMenu *menu, + int client, + IMenuPanel *panel, + unsigned int item, + const ItemDrawInfo &dr); + virtual void OnMenuCancel(IBaseMenu *menu, int client, MenuCancelReason reason); +public: //ITextListener_SMC + virtual void ReadSMC_ParseStart(); + SMCParseResult ReadSMC_NewSection(const char *name, bool opt_quotes); + SMCParseResult ReadSMC_KeyValue(const char *key, + const char *value, + bool key_quotes, + bool value_quotes); + SMCParseResult ReadSMC_LeavingSection(); +private: + void SortCategoriesIfNeeded(); + void SortCategoryIfNeeded(unsigned int category); +private: + bool DisplayCategory(int client, unsigned int category, unsigned int hold_time, bool last_position); + void CreatePlayers(int max_clients); + void UpdateClientRoot(int client, IGamePlayer *pGamePlayer=NULL); + void UpdateClientCategory(int client, unsigned int category); + void TearDownClient(topmenu_player_t *player); +private: + void OnClientConnected(int client); + void OnClientDisconnected(int client); + void OnServerActivated(int max_clients); +private: + config_root_t m_Config; /* Configuration from file */ + topmenu_player_t *m_clients; /* Client array */ + CVector m_SortedCats; /* Sorted categories */ + CVector m_UnsortedCats; /* Un-sorted categories */ + CVector m_Categories; /* Category array */ + CVector m_Objects; /* Object array */ + KTrie m_ObjLookup; /* Object lookup trie */ + unsigned int m_SerialNo; /* Serial number for updating */ + ITopMenuObjectCallbacks *m_pTitle; /* Title callbacks */ + int m_max_clients; /* Maximum number of clients */ + bool m_bCatsNeedResort; /* True if categories need a resort */ +}; + +#endif //_INCLUDE_SOURCEMOD_TOP_MENU_H_ diff --git a/extensions/topmenus/TopMenuManager.cpp b/extensions/topmenus/TopMenuManager.cpp new file mode 100644 index 00000000..6123456e --- /dev/null +++ b/extensions/topmenus/TopMenuManager.cpp @@ -0,0 +1,101 @@ +/** + * 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: extension.cpp 1336 2007-08-15 06:19:30Z damagedsoul $ + */ + +#include "TopMenuManager.h" +#include "TopMenu.h" + +TopMenuManager g_TopMenus; +bool is_server_activated = false; + +const char *TopMenuManager::GetInterfaceName() +{ + return SMINTERFACE_TOPMENUS_NAME; +} + +unsigned int TopMenuManager::GetInterfaceVersion() +{ + return SMINTERFACE_TOPMENUS_VERSION; +} + +ITopMenu *TopMenuManager::CreateTopMenu(ITopMenuObjectCallbacks *callbacks) +{ + TopMenu *pMenu = new TopMenu(callbacks); + + m_TopMenus.push_back(pMenu); + + return pMenu; +} + +void TopMenuManager::OnClientConnected(int client) +{ + List::iterator iter; + + for (iter = m_TopMenus.begin(); iter != m_TopMenus.end(); iter++) + { + (*iter)->OnClientConnected(client); + } +} + +void TopMenuManager::OnClientDisconnected(int client) +{ + List::iterator iter; + + for (iter = m_TopMenus.begin(); iter != m_TopMenus.end(); iter++) + { + (*iter)->OnClientDisconnected(client); + } +} + +void TopMenuManager::OnServerActivated(int max_clients) +{ + if (is_server_activated) + { + return; + } + + List::iterator iter; + + for (iter = m_TopMenus.begin(); iter != m_TopMenus.end(); iter++) + { + (*iter)->OnServerActivated(max_clients); + } + + is_server_activated = true; +} + +void TopMenuManager::DestroyTopMenu(ITopMenu *topmenu) +{ + TopMenu *pMenu = (TopMenu *)topmenu; + + m_TopMenus.remove(pMenu); + + delete pMenu; +} diff --git a/extensions/topmenus/TopMenuManager.h b/extensions/topmenus/TopMenuManager.h new file mode 100644 index 00000000..5d28bd52 --- /dev/null +++ b/extensions/topmenus/TopMenuManager.h @@ -0,0 +1,64 @@ +/** + * 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: extension.cpp 1336 2007-08-15 06:19:30Z damagedsoul $ + */ + + +#ifndef _INCLUDE_SOURCEMOD_TOP_MENU_MANAGER_IMPL_H_ +#define _INCLUDE_SOURCEMOD_TOP_MENU_MANAGER_IMPL_H_ + +#include +#include +#include + +using namespace SourceMod; +using namespace SourceHook; + +class TopMenu; + +class TopMenuManager : + public ITopMenuManager, + public IClientListener +{ +public: + const char *GetInterfaceName(); + unsigned int GetInterfaceVersion(); + ITopMenu *CreateTopMenu(ITopMenuObjectCallbacks *callbacks); + void DestroyTopMenu(ITopMenu *topmenu); +public: + void OnClientConnected(int client); + void OnClientDisconnected(int client); + void OnServerActivated(int max_clients); +private: + List m_TopMenus; +}; + +extern TopMenuManager g_TopMenus; + +#endif //_INCLUDE_SOURCEMOD_TOP_MENU_MANAGER_IMPL_H_ diff --git a/extensions/topmenus/extension.cpp b/extensions/topmenus/extension.cpp new file mode 100644 index 00000000..21cace90 --- /dev/null +++ b/extensions/topmenus/extension.cpp @@ -0,0 +1,55 @@ +/** + * 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: extension.cpp 1336 2007-08-15 06:19:30Z damagedsoul $ + */ + +#include "extension.h" +#include "TopMenuManager.h" + +/** + * @file extension.cpp + * @brief Implement extension code here. + */ + +TopMenuExtension g_TopMenuExt; /**< Global singleton for extension's main interface */ + +SMEXT_LINK(&g_TopMenuExt); + +bool TopMenuExtension::SDK_OnLoad(char *error, size_t maxlength, bool late) +{ + sharesys->AddInterface(myself, &g_TopMenus); + playerhelpers->AddClientListener(&g_TopMenus); + + return true; +} + +void TopMenuExtension::SDK_OnUnload() +{ + playerhelpers->RemoveClientListener(&g_TopMenus); +} diff --git a/extensions/topmenus/extension.h b/extensions/topmenus/extension.h new file mode 100644 index 00000000..b9680c71 --- /dev/null +++ b/extensions/topmenus/extension.h @@ -0,0 +1,66 @@ +/** + * 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: extension.h 1336 2007-08-15 06:19:30Z damagedsoul $ + */ + +#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 TopMenuExtension : 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(); +}; + +#endif // _INCLUDE_SOURCEMOD_EXTENSION_PROPER_H_ diff --git a/extensions/topmenus/msvc8/topmenus.sln b/extensions/topmenus/msvc8/topmenus.sln new file mode 100644 index 00000000..59d994d4 --- /dev/null +++ b/extensions/topmenus/msvc8/topmenus.sln @@ -0,0 +1,20 @@ + +Microsoft Visual Studio Solution File, Format Version 9.00 +# Visual Studio 2005 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "topmenus", "topmenus.vcproj", "{B3E797CF-4E77-4C9D-B8A8-7589B6902206}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {B3E797CF-4E77-4C9D-B8A8-7589B6902206}.Debug|Win32.ActiveCfg = Debug|Win32 + {B3E797CF-4E77-4C9D-B8A8-7589B6902206}.Debug|Win32.Build.0 = Debug|Win32 + {B3E797CF-4E77-4C9D-B8A8-7589B6902206}.Release|Win32.ActiveCfg = Release|Win32 + {B3E797CF-4E77-4C9D-B8A8-7589B6902206}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/extensions/topmenus/msvc8/topmenus.vcproj b/extensions/topmenus/msvc8/topmenus.vcproj new file mode 100644 index 00000000..9a4b0c22 --- /dev/null +++ b/extensions/topmenus/msvc8/topmenus.vcproj @@ -0,0 +1,256 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/extensions/topmenus/sdk/sm_memtable.cpp b/extensions/topmenus/sdk/sm_memtable.cpp new file mode 100644 index 00000000..1ef4cda5 --- /dev/null +++ b/extensions/topmenus/sdk/sm_memtable.cpp @@ -0,0 +1,112 @@ +/** + * vim: set ts=4 : + * ============================================================================= + * SourceMod + * 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: sm_memtable.cpp 1336 2007-08-15 06:19:30Z damagedsoul $ + */ + +#include +#include +#include "sm_memtable.h" + +BaseMemTable::BaseMemTable(unsigned int init_size) +{ + membase = (unsigned char *)malloc(init_size); + size = init_size; + tail = 0; +} + +BaseMemTable::~BaseMemTable() +{ + free(membase); + membase = NULL; +} + +int BaseMemTable::CreateMem(unsigned int addsize, void **addr) +{ + int idx = (int)tail; + + while (tail + addsize >= size) + { + size *= 2; + membase = (unsigned char *)realloc(membase, size); + } + + tail += addsize; + + if (addr) + { + *addr = (void *)&membase[idx]; + } + + return idx; +} + +void *BaseMemTable::GetAddress(int index) +{ + if (index < 0 || (unsigned int)index >= tail) + { + return NULL; + } + + return &membase[index]; +} + +void BaseMemTable::Reset() +{ + tail = 0; +} + +BaseStringTable::BaseStringTable(unsigned int init_size) : m_table(init_size) +{ +} + +BaseStringTable::~BaseStringTable() +{ +} + +int BaseStringTable::AddString(const char *string) +{ + size_t len = strlen(string) + 1; + int idx; + char *addr; + + idx = m_table.CreateMem(len, (void **)&addr); + strcpy(addr, string); + + return idx; +} + +/*const char *BaseStringTable::GetString(int str) +{ + return (const char *)m_table.GetAddress(str); +}*/ + +void BaseStringTable::Reset() +{ + m_table.Reset(); +} diff --git a/extensions/topmenus/sdk/sm_memtable.h b/extensions/topmenus/sdk/sm_memtable.h new file mode 100644 index 00000000..6bd5e7c4 --- /dev/null +++ b/extensions/topmenus/sdk/sm_memtable.h @@ -0,0 +1,105 @@ +/** + * vim: set ts=4 : + * ============================================================================= + * SourceMod + * 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: sm_memtable.h 1336 2007-08-15 06:19:30Z damagedsoul $ + */ + +#ifndef _INCLUDE_SOURCEMOD_CORE_STRINGTABLE_H_ +#define _INCLUDE_SOURCEMOD_CORE_STRINGTABLE_H_ + +class BaseMemTable +{ +public: + BaseMemTable(unsigned int init_size); + ~BaseMemTable(); +public: + /** + * Allocates 'size' bytes of memory. + * Optionally outputs the address through 'addr'. + * Returns an index >= 0 on success, < 0 on failure. + */ + int CreateMem(unsigned int size, void **addr); + + /** + * Given an index into the memory table, returns its address. + * Returns NULL if invalid. + */ + void *GetAddress(int index); + + /** + * Scraps the memory table. For caching purposes, the memory + * is not freed, however subsequent calls to CreateMem() will + * begin at the first index again. + */ + void Reset(); + + +private: + unsigned char *membase; + unsigned int size; + unsigned int tail; +}; + +class BaseStringTable +{ +public: + BaseStringTable(unsigned int init_size); + ~BaseStringTable(); +public: + /** + * Adds a string to the string table and returns its index. + */ + int AddString(const char *string); + + /** + * Given an index into the string table, returns the associated string. + */ + inline const char *GetString(int str) + { + return (const char *)m_table.GetAddress(str); + } + + /** + * Scraps the string table. For caching purposes, the memory + * is not freed, however subsequent calls to AddString() will + * begin at the first index again. + */ + void Reset(); + + /** + * Returns the parent BaseMemTable that this string table uses. + */ + inline BaseMemTable *GetMemTable() + { + return &m_table; + } +private: + BaseMemTable m_table; +}; + +#endif //_INCLUDE_SOURCEMOD_CORE_STRINGTABLE_H_ diff --git a/extensions/topmenus/sdk/smsdk_config.h b/extensions/topmenus/sdk/smsdk_config.h new file mode 100644 index 00000000..409506fc --- /dev/null +++ b/extensions/topmenus/sdk/smsdk_config.h @@ -0,0 +1,78 @@ +/** + * 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: smsdk_config.h 1336 2007-08-15 06:19:30Z damagedsoul $ + */ + +#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 "Main Menu" +#define SMEXT_CONF_DESCRIPTION "Manages SourceMod's main menus" +#define SMEXT_CONF_VERSION "1.0.0.0" +#define SMEXT_CONF_AUTHOR "AlliedModders" +#define SMEXT_CONF_URL "http://www.sourcemod.net/" +#define SMEXT_CONF_LOGTAG "MAINMENU" +#define SMEXT_CONF_LICENSE "GPLv3" +#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 + +#endif // _INCLUDE_SOURCEMOD_EXTENSION_CONFIG_H_ diff --git a/extensions/topmenus/sdk/smsdk_ext.cpp b/extensions/topmenus/sdk/smsdk_ext.cpp new file mode 100644 index 00000000..51fc62d1 --- /dev/null +++ b/extensions/topmenus/sdk/smsdk_ext.cpp @@ -0,0 +1,440 @@ +/** + * 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: smsdk_ext.cpp 1336 2007-08-15 06:19:30Z damagedsoul $ + */ + +#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 + +/** 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 (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, 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(serverFactory, gamedll, IServerGameDLL, INTERFACEVERSION_SERVERGAMEDLL); + GET_V_IFACE_CURRENT(engineFactory, 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/topmenus/sdk/smsdk_ext.h b/extensions/topmenus/sdk/smsdk_ext.h new file mode 100644 index 00000000..b38dcfe5 --- /dev/null +++ b/extensions/topmenus/sdk/smsdk_ext.h @@ -0,0 +1,313 @@ +/** + * 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: smsdk_ext.h 1336 2007-08-15 06:19:30Z damagedsoul $ + */ + +#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_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_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) \ + { \ + snprintf(error, maxlength, "Could not find interface: %s (version: %d)", SMINTERFACE_##prefix##_NAME, SMINTERFACE_##prefix##_VERSION); \ + 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) \ + { \ + snprintf(error, maxlength, "Could not find interface: %s", SMINTERFACE_##prefix##_NAME); \ + return false; \ + } \ + } + +#endif // _INCLUDE_SOURCEMOD_EXTENSION_BASESDK_H_ diff --git a/public/extensions/ITopMenus.h b/public/extensions/ITopMenus.h new file mode 100644 index 00000000..48b700f6 --- /dev/null +++ b/public/extensions/ITopMenus.h @@ -0,0 +1,219 @@ +/** + * vim: set ts=4 : + * ============================================================================= + * SourceMod + * 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_MAIN_MENU_INTERFACE_H_ +#define _INCLUDE_SOURCEMOD_MAIN_MENU_INTERFACE_H_ + +#include +#include +#include +#include + +/** + * @file ITopMenus.h + * @brief Interface header for creating and managing top-level menus. + */ + +#define SMINTERFACE_TOPMENUS_NAME "ITopMenus" +#define SMINTERFACE_TOPMENUS_VERSION 1 + +namespace SourceMod +{ + /** + * @brief Top menu object types. + */ + enum TopMenuObjectType + { + TopMenuObject_Category = 0, /**< Category (sub-menu branching from root) */ + TopMenuObject_Item = 1 /**< Item on a sub-menu */ + }; + + /** + * @brief Top menu starting positions for display. + */ + enum TopMenuPosition + { + TopMenuPosition_Start = 0, /**< Start/root of the menu */ + TopMenuPosition_LastRoot = 1, /**< Last position in the root menu */ + TopMenuPosition_LastCategory = 3, /**< Last position in their last category */ + }; + + /** + * @brief Top Menu callbacks for rendering/drawing. + */ + class ITopMenuObjectCallbacks + { + public: + /** + * @brief Must return the topmenu API version. + * + * @return Top Menu API version. + */ + virtual unsigned int GetTopMenuAPIVersion1() + { + return SMINTERFACE_TOPMENUS_VERSION; + } + + /** + * @brief Requests how the given item should be drawn for a client. + * + * @param client Client index. + * @param object_id Object ID returned from ITopMenu::AddToMenu(). + * @param buffer Buffer to store rendered text. + * @param maxlength Maximum length of the rendering buffer. + * @return ITEMDRAW flags to disable or not draw the + * option for this operation. + */ + virtual unsigned int OnTopMenuDrawOption(int client, + unsigned int object_id, + char buffer[], + size_t maxlength) =0; + + /** + * @brief Requests how the given item's title should be drawn for + * a client. This is called on any object_id that is a category. + * + * @param client Client index. + * @param object_id Object ID returned from ITopMenu::AddToMenu(), + * or 0 if the title is the root menu title. + * @param buffer Buffer to store rendered text. + * @param maxlength Maximum length of the rendering buffer. + */ + virtual void OnTopMenuDrawTitle(int client, + unsigned int object_id, + char buffer[], + size_t maxlength) =0; + + /** + * @brief Notifies the listener that the menu option has been selected. + * + * @param client Client index. + * @param object_id Object ID returned from ITopMenu::AddToMenu(). + */ + virtual void OnTopMenuSelectOption(int client, unsigned int object_id) =0; + }; + + /** + * @brief "Top menu" interface, for managing top-level categorized menus. + */ + class ITopMenu + { + public: + /** + * @brief Creates and adds an object type type to the top menu. + * + * @param name Unique, string name to give the object. + * @param type Object type. + * @param callbacks ITopMenuObjectCallbacks pointer. + * @param owner IdentityToken_t owner of the object. + * @param cmdname Command name used for override access checks. + * If NULL or empty, access will not be Checked. + * @param flags Default flag(s) to use for access checks. + * @param parent Parent object, or 0 if none. + * Currently, categories cannot have a parent, + * and items must have a category parent. + * @return An object ID, or 0 on failure. + */ + virtual unsigned int AddToMenu(const char *name, + TopMenuObjectType type, + ITopMenuObjectCallbacks *callbacks, + IdentityToken_t *owner, + const char *cmdname, + FlagBits flags, + unsigned int parent) =0; + + /** + * @brief Removes an object from a menu. If the object has any + * children, those will be removed. + * + * @param command_id Command ID returned from AddToMenu. + */ + virtual void RemoveFromMenu(unsigned int object_id) =0; + + /** + * @brief Sends the main menu to a given client. + * + * Once the menu is drawn to a client, the drawing order is cached. + * If text on the menu is rendered differently for the client's next + * viewing, the text will render properly, but its order will not + * change. The menu is sorted by its configuration. Remaining items + * are sorted in alphabetical order using the initial display text. + * + * @param client Client index. + * @param hold_time Time to hold the menu on the screen for. + * @param position TopMenuPosition enumeration value. + * @return True on success, false if nothing displayed. + */ + virtual bool DisplayMenu(int client, unsigned int hold_time, TopMenuPosition position) =0; + + /** + * @brief Loads a configuration file for organizing the menu. This + * forces all known categories to be re-sorted. + * + * Only one configuration can be active at a time. Loading a new one + * will cause the old sorting to disappear. + * + * @param file File path. + * @param error Error buffer. + * @param maxlength Maximum length of the error buffer. + * @return True on success, false on failure. + */ + virtual bool LoadConfiguration(const char *file, char *error, size_t maxlength) =0; + }; + + /** + * @brief Top menu manager. + */ + class ITopMenuManager : public SMInterface + { + public: + virtual const char *GetInterfaceName() =0; + virtual unsigned int GetInterfaceVersion() =0; + public: + /** + * @brief Creates a new top-level menu. + * + * @param callbacks Callbacks for the title text. + * The object_id for the title will always be 0. + * @return A new ITopMenu pointer. + */ + virtual ITopMenu *CreateTopMenu(ITopMenuObjectCallbacks *callbacks) =0; + + /** + * @brief Destroys a top-level menu. + * + * @param topmenu Pointer to an ITopMenu. + */ + virtual void DestroyTopMenu(ITopMenu *topmenu) =0; + }; +} + +#endif //_INCLUDE_SOURCEMOD_MAIN_MENU_INTERFACE_H_