redid API yet again - IMenuHandler is now bound to an IBaseMenu instance

implemented MOST natives
removed broadcasting/voting handlers for the time being
fixed a crash bug in basecontext when i didn't obey the api
moved radio displays to a caching stack

--HG--
extra : convert_revision : svn%3A39bc706e-5318-0410-9160-8a85361fbb7c/trunk%40796
This commit is contained in:
David Anderson 2007-05-14 04:46:06 +00:00
parent d1dea716a6
commit 49369d07bc
13 changed files with 1669 additions and 44 deletions

View File

@ -21,6 +21,7 @@
#include "PlayerManager.h" #include "PlayerManager.h"
#include "MenuStyle_Valve.h" #include "MenuStyle_Valve.h"
#include "ShareSys.h" #include "ShareSys.h"
#include "HandleSys.h"
MenuManager g_Menus; MenuManager g_Menus;
@ -150,10 +151,24 @@ MenuManager::MenuManager()
void MenuManager::OnSourceModAllInitialized() void MenuManager::OnSourceModAllInitialized()
{ {
g_ShareSys.AddInterface(NULL, this); g_ShareSys.AddInterface(NULL, this);
HandleAccess access;
g_HandleSys.InitAccessDefaults(NULL, &access);
/* Deny cloning to menus */
access.access[HandleAccess_Clone] = HANDLE_RESTRICT_OWNER|HANDLE_RESTRICT_IDENTITY;
m_MenuType = g_HandleSys.CreateType("IBaseMenu", this, 0, NULL, &access, g_pCoreIdent, NULL);
/* Also deny deletion to styles */
access.access[HandleAccess_Delete] = HANDLE_RESTRICT_OWNER|HANDLE_RESTRICT_IDENTITY;
m_StyleType = g_HandleSys.CreateType("IMenuStyle", this, 0, NULL, &access, g_pCoreIdent, NULL);
} }
void MenuManager::OnSourceModAllShutdown() void MenuManager::OnSourceModAllShutdown()
{ {
g_HandleSys.RemoveType(m_MenuType, g_pCoreIdent);
g_HandleSys.RemoveType(m_StyleType, g_pCoreIdent);
while (!m_BroadcastHandlers.empty()) while (!m_BroadcastHandlers.empty())
{ {
delete m_BroadcastHandlers.front(); delete m_BroadcastHandlers.front();
@ -167,6 +182,57 @@ void MenuManager::OnSourceModAllShutdown()
} }
} }
void MenuManager::OnHandleDestroy(HandleType_t type, void *object)
{
if (type == m_MenuType)
{
IBaseMenu *menu = (IBaseMenu *)object;
menu->Destroy(false);
} else if (type == m_StyleType) {
/* Do nothing */
}
}
Handle_t MenuManager::CreateMenuHandle(IBaseMenu *menu, IdentityToken_t *pOwner)
{
if (m_MenuType == NO_HANDLE_TYPE)
{
return BAD_HANDLE;
}
return g_HandleSys.CreateHandle(m_MenuType, menu, pOwner, g_pCoreIdent, NULL);
}
Handle_t MenuManager::CreateStyleHandle(IMenuStyle *style)
{
if (m_StyleType == NO_HANDLE_TYPE)
{
return BAD_HANDLE;
}
return g_HandleSys.CreateHandle(m_StyleType, style, g_pCoreIdent, g_pCoreIdent, NULL);
}
HandleError MenuManager::ReadMenuHandle(Handle_t handle, IBaseMenu **menu)
{
HandleSecurity sec;
sec.pIdentity = g_pCoreIdent;
sec.pOwner = NULL;
return g_HandleSys.ReadHandle(handle, m_MenuType, &sec, (void **)menu);
}
HandleError MenuManager::ReadStyleHandle(Handle_t handle, IMenuStyle **style)
{
HandleSecurity sec;
sec.pIdentity = g_pCoreIdent;
sec.pOwner = g_pCoreIdent;
return g_HandleSys.ReadHandle(handle, m_StyleType, &sec, (void **)style);
}
bool MenuManager::SetDefaultStyle(IMenuStyle *style) bool MenuManager::SetDefaultStyle(IMenuStyle *style)
{ {
if (!style) if (!style)
@ -503,6 +569,13 @@ skip_search:
ItemDrawInfo padItem(NULL, ITEMDRAW_SPACER); ItemDrawInfo padItem(NULL, ITEMDRAW_SPACER);
if (exitButton || (displayNext || displayPrev)) if (exitButton || (displayNext || displayPrev))
{ {
/* If there are no control options,
* Instead just pad with invisible slots.
*/
if (!displayPrev && !displayPrev)
{
padItem.style = ITEMDRAW_NOTEXT;
}
/* Add spacers so we can pad to the end */ /* Add spacers so we can pad to the end */
for (unsigned int i=0; i<padding; i++) for (unsigned int i=0; i<padding; i++)
{ {
@ -585,6 +658,7 @@ skip_search:
return display; return display;
} }
#if 0
unsigned int MenuManager::BroadcastMenu(IBaseMenu *menu, unsigned int MenuManager::BroadcastMenu(IBaseMenu *menu,
IMenuHandler *handler, IMenuHandler *handler,
int clients[], int clients[],
@ -683,6 +757,7 @@ unsigned int MenuManager::VoteMenu(IBaseMenu *menu,
return total; return total;
} }
#endif
void MenuManager::FreeBroadcastHandler(BroadcastHandler *bh) void MenuManager::FreeBroadcastHandler(BroadcastHandler *bh)
{ {

View File

@ -59,10 +59,13 @@ private:
class MenuManager : class MenuManager :
public IMenuManager, public IMenuManager,
public SMGlobalClass public SMGlobalClass,
public IHandleTypeDispatch
{ {
friend class BroadcastHandler; friend class BroadcastHandler;
friend class VoteHandler; friend class VoteHandler;
friend class CBaseMenu;
friend class BaseMenuStyle;
public: public:
MenuManager(); MenuManager();
public: //SMGlobalClass public: //SMGlobalClass
@ -95,15 +98,24 @@ public:
void AddStyle(IMenuStyle *style); void AddStyle(IMenuStyle *style);
bool SetDefaultStyle(IMenuStyle *style); bool SetDefaultStyle(IMenuStyle *style);
IMenuPanel *RenderMenu(int client, menu_states_t &states, ItemOrder order); IMenuPanel *RenderMenu(int client, menu_states_t &states, ItemOrder order);
public: //IHandleTypeDispatch
void OnHandleDestroy(HandleType_t type, void *object);
public:
HandleError ReadMenuHandle(Handle_t handle, IBaseMenu **menu);
HandleError ReadStyleHandle(Handle_t handle, IMenuStyle **style);
protected: protected:
void FreeBroadcastHandler(BroadcastHandler *bh); void FreeBroadcastHandler(BroadcastHandler *bh);
void FreeVoteHandler(VoteHandler *vh); void FreeVoteHandler(VoteHandler *vh);
Handle_t CreateMenuHandle(IBaseMenu *menu, IdentityToken_t *pOwner);
Handle_t CreateStyleHandle(IMenuStyle *style);
private: private:
int m_ShowMenu; int m_ShowMenu;
IMenuStyle *m_pDefaultStyle; IMenuStyle *m_pDefaultStyle;
CStack<BroadcastHandler *> m_BroadcastHandlers; CStack<BroadcastHandler *> m_BroadcastHandlers;
CStack<VoteHandler *> m_VoteHandlers; CStack<VoteHandler *> m_VoteHandlers;
CVector<IMenuStyle *> m_Styles; CVector<IMenuStyle *> m_Styles;
HandleType_t m_StyleType;
HandleType_t m_MenuType;
}; };
extern MenuManager g_Menus; extern MenuManager g_Menus;

View File

@ -17,11 +17,23 @@
#include "MenuStyle_Base.h" #include "MenuStyle_Base.h"
#include "PlayerManager.h" #include "PlayerManager.h"
#include "MenuManager.h" #include "MenuManager.h"
#include "HandleSys.h"
BaseMenuStyle::BaseMenuStyle() : m_WatchList(256) BaseMenuStyle::BaseMenuStyle() : m_WatchList(256), m_hHandle(BAD_HANDLE)
{ {
} }
Handle_t BaseMenuStyle::GetHandle()
{
/* Don't create the handle until we need it */
if (m_hHandle == BAD_HANDLE)
{
m_hHandle = g_Menus.CreateStyleHandle(this);
}
return m_hHandle;
}
void BaseMenuStyle::AddClientToWatch(int client) void BaseMenuStyle::AddClientToWatch(int client)
{ {
m_WatchList.push_back(client); m_WatchList.push_back(client);
@ -56,7 +68,12 @@ void BaseMenuStyle::_CancelClientMenu(int client, bool bAutoIgnore/* =false */,
/* Fire callbacks */ /* Fire callbacks */
mh->OnMenuCancel(menu, client, reason); mh->OnMenuCancel(menu, client, reason);
mh->OnMenuEnd(menu);
/* Only fire end if there's a valid menu */
if (menu)
{
mh->OnMenuEnd(menu);
}
if (bAutoIgnore) if (bAutoIgnore)
{ {
@ -145,6 +162,7 @@ void BaseMenuStyle::OnClientDisconnected(int client)
_CancelClientMenu(client, true, MenuCancel_Disconnect); _CancelClientMenu(client, true, MenuCancel_Disconnect);
player->bInMenu = false; player->bInMenu = false;
player->bInExternMenu = false;
} }
static int do_lookup[256]; static int do_lookup[256];
@ -248,7 +266,11 @@ void BaseMenuStyle::ClientPressedKey(int client, unsigned int key_press)
mh->OnMenuSelect(menu, client, item); mh->OnMenuSelect(menu, client, item);
} }
mh->OnMenuEnd(menu); /* Only fire end for valid menus */
if (menu)
{
mh->OnMenuEnd(menu);
}
} }
bool BaseMenuStyle::DoClientMenu(int client, IMenuPanel *menu, IMenuHandler *mh, unsigned int time) bool BaseMenuStyle::DoClientMenu(int client, IMenuPanel *menu, IMenuHandler *mh, unsigned int time)
@ -276,13 +298,7 @@ bool BaseMenuStyle::DoClientMenu(int client, IMenuPanel *menu, IMenuHandler *mh,
menu_states_t &states = player->states; menu_states_t &states = player->states;
if (player->bInMenu) if (player->bInMenu)
{ {
/* We need to cancel the old menu */ _CancelClientMenu(client, true);
if (player->menuHoldTime)
{
RemoveClientFromWatch(client);
}
states.mh->OnMenuCancel(states.menu, client, MenuCancel_Interrupt);
states.mh->OnMenuEnd(states.menu);
} }
states.firstItem = 0; states.firstItem = 0;
@ -291,6 +307,7 @@ bool BaseMenuStyle::DoClientMenu(int client, IMenuPanel *menu, IMenuHandler *mh,
states.mh = mh; states.mh = mh;
states.apiVers = SMINTERFACE_MENUMANAGER_VERSION; states.apiVers = SMINTERFACE_MENUMANAGER_VERSION;
player->bInMenu = true; player->bInMenu = true;
player->bInExternMenu = false;
player->menuStartTime = gpGlobals->curtime; player->menuStartTime = gpGlobals->curtime;
player->menuHoldTime = time; player->menuHoldTime = time;
@ -367,6 +384,7 @@ bool BaseMenuStyle::DoClientMenu(int client, CBaseMenu *menu, IMenuHandler *mh,
/* Finally, set our states */ /* Finally, set our states */
player->bInMenu = true; player->bInMenu = true;
player->bInExternMenu = false;
player->menuStartTime = gpGlobals->curtime; player->menuStartTime = gpGlobals->curtime;
player->menuHoldTime = time; player->menuHoldTime = time;
@ -413,8 +431,10 @@ bool BaseMenuStyle::RedoClientMenu(int client, ItemOrder order)
return true; return true;
} }
CBaseMenu::CBaseMenu(IMenuStyle *pStyle) : CBaseMenu::CBaseMenu(IMenuHandler *pHandler, IMenuStyle *pStyle, IdentityToken_t *pOwner) :
m_pStyle(pStyle), m_Strings(512), m_Pagination(7), m_ExitButton(true), m_bShouldDelete(false), m_bCancelling(false) m_pStyle(pStyle), m_Strings(512), m_Pagination(7), m_ExitButton(true),
m_bShouldDelete(false), m_bCancelling(false), m_pOwner(pOwner ? pOwner : g_pCoreIdent),
m_bDeleting(false), m_bWillFreeHandle(false), m_hHandle(BAD_HANDLE), m_pHandler(pHandler)
{ {
} }
@ -422,6 +442,16 @@ CBaseMenu::~CBaseMenu()
{ {
} }
Handle_t CBaseMenu::GetHandle()
{
if (!m_hHandle)
{
m_hHandle = g_Menus.CreateMenuHandle(this, m_pOwner);
}
return m_hHandle;
}
bool CBaseMenu::AppendItem(const char *info, const ItemDrawInfo &draw) bool CBaseMenu::AppendItem(const char *info, const ItemDrawInfo &draw)
{ {
if (m_Pagination == (unsigned)MENU_NO_PAGINATION if (m_Pagination == (unsigned)MENU_NO_PAGINATION
@ -572,18 +602,48 @@ void CBaseMenu::Cancel()
if (m_bShouldDelete) if (m_bShouldDelete)
{ {
delete this; InternalDelete();
} }
} }
void CBaseMenu::Destroy() void CBaseMenu::Destroy(bool releaseHandle)
{ {
/* Check if we shouldn't be here */
if (m_bDeleting)
{
return;
}
/* Save the destruction hint about our handle */
m_bWillFreeHandle = releaseHandle;
/* Now actually do stuff */
if (!m_bCancelling || m_bShouldDelete) if (!m_bCancelling || m_bShouldDelete)
{ {
Cancel(); Cancel();
delete this; InternalDelete();
} else { } else {
m_bShouldDelete = true; m_bShouldDelete = true;
} }
} }
void CBaseMenu::InternalDelete()
{
if (m_bWillFreeHandle && m_hHandle != BAD_HANDLE)
{
Handle_t hndl = m_hHandle;
HandleSecurity sec;
sec.pOwner = m_pOwner;
sec.pIdentity = g_pCoreIdent;
m_hHandle = BAD_HANDLE;
m_bDeleting = true;
g_HandleSys.FreeHandle(hndl, &sec);
}
m_pHandler->OnMenuDestroy(this);
delete this;
}

View File

@ -65,6 +65,7 @@ public:
public: //IMenuStyle public: //IMenuStyle
bool CancelClientMenu(int client, bool autoIgnore/* =false */); bool CancelClientMenu(int client, bool autoIgnore/* =false */);
MenuSource GetClientMenu(int client, void **object); MenuSource GetClientMenu(int client, void **object);
Handle_t GetHandle();
public: //IClientListener public: //IClientListener
void OnClientDisconnected(int client); void OnClientDisconnected(int client);
public: //what derived must implement public: //what derived must implement
@ -84,12 +85,13 @@ protected:
bool RedoClientMenu(int client, ItemOrder order); bool RedoClientMenu(int client, ItemOrder order);
protected: protected:
FastLink<int> m_WatchList; FastLink<int> m_WatchList;
Handle_t m_hHandle;
}; };
class CBaseMenu : public IBaseMenu class CBaseMenu : public IBaseMenu
{ {
public: public:
CBaseMenu(IMenuStyle *pStyle); CBaseMenu(IMenuHandler *pHandler, IMenuStyle *pStyle, IdentityToken_t *pOwner);
virtual ~CBaseMenu(); virtual ~CBaseMenu();
public: public:
virtual bool AppendItem(const char *info, const ItemDrawInfo &draw); virtual bool AppendItem(const char *info, const ItemDrawInfo &draw);
@ -106,8 +108,11 @@ public:
virtual bool GetExitButton(); virtual bool GetExitButton();
virtual bool SetExitButton(bool set); virtual bool SetExitButton(bool set);
virtual void Cancel(); virtual void Cancel();
virtual void Destroy(); virtual void Destroy(bool releaseHandle);
virtual void Cancel_Finally() =0; virtual void Cancel_Finally() =0;
virtual Handle_t GetHandle();
private:
void InternalDelete();
protected: protected:
String m_Title; String m_Title;
IMenuStyle *m_pStyle; IMenuStyle *m_pStyle;
@ -117,6 +122,11 @@ protected:
bool m_ExitButton; bool m_ExitButton;
bool m_bShouldDelete; bool m_bShouldDelete;
bool m_bCancelling; bool m_bCancelling;
bool m_bDeleting;
bool m_bWillFreeHandle;
IdentityToken_t *m_pOwner;
Handle_t m_hHandle;
IMenuHandler *m_pHandler;
}; };
#endif //_INCLUDE_MENUSTYLE_BASE_H #endif //_INCLUDE_MENUSTYLE_BASE_H

View File

@ -56,6 +56,12 @@ void CRadioStyle::OnSourceModLevelChange(const char *mapName)
void CRadioStyle::OnSourceModShutdown() void CRadioStyle::OnSourceModShutdown()
{ {
g_UserMsgs.UnhookUserMessage(g_ShowMenuId, this, false); g_UserMsgs.UnhookUserMessage(g_ShowMenuId, this, false);
while (!m_FreeDisplays.empty())
{
delete m_FreeDisplays.front();
m_FreeDisplays.pop();
}
} }
bool CRadioStyle::IsSupported() bool CRadioStyle::IsSupported()
@ -125,12 +131,12 @@ void CRadioStyle::SendDisplay(int client, IMenuPanel *display)
IMenuPanel *CRadioStyle::CreatePanel() IMenuPanel *CRadioStyle::CreatePanel()
{ {
return new CRadioDisplay(); return g_RadioMenuStyle.MakeRadioDisplay();
} }
IBaseMenu *CRadioStyle::CreateMenu() IBaseMenu *CRadioStyle::CreateMenu(IMenuHandler *pHandler, IdentityToken_t *pOwner)
{ {
return new CRadioMenu(); return new CRadioMenu(pHandler, pOwner);
} }
unsigned int CRadioStyle::GetMaxPageItems() unsigned int CRadioStyle::GetMaxPageItems()
@ -148,6 +154,25 @@ CBaseMenuPlayer *CRadioStyle::GetMenuPlayer(int client)
return &m_players[client]; return &m_players[client];
} }
CRadioDisplay *CRadioStyle::MakeRadioDisplay(CRadioMenu *menu)
{
CRadioDisplay *display;
if (m_FreeDisplays.empty())
{
display = new CRadioDisplay();
} else {
display = m_FreeDisplays.front();
m_FreeDisplays.pop();
display->Reset();
}
return display;
}
void CRadioStyle::FreeRadioDisplay(CRadioDisplay *display)
{
m_FreeDisplays.push(display);
}
CRadioDisplay::CRadioDisplay() CRadioDisplay::CRadioDisplay()
{ {
Reset(); Reset();
@ -292,7 +317,8 @@ void CRadioDisplay::DeleteThis()
delete this; delete this;
} }
CRadioMenu::CRadioMenu() : CBaseMenu(&g_RadioMenuStyle) CRadioMenu::CRadioMenu(IMenuHandler *pHandler, IdentityToken_t *pOwner) :
CBaseMenu(pHandler, &g_RadioMenuStyle, pOwner)
{ {
} }
@ -303,12 +329,12 @@ bool CRadioMenu::SetExtOption(MenuOption option, const void *valuePtr)
IMenuPanel *CRadioMenu::CreatePanel() IMenuPanel *CRadioMenu::CreatePanel()
{ {
return new CRadioDisplay(this); return g_RadioMenuStyle.MakeRadioDisplay(this);
} }
bool CRadioMenu::Display(int client, IMenuHandler *handler, unsigned int time) bool CRadioMenu::Display(int client, unsigned int time)
{ {
return g_RadioMenuStyle.DoClientMenu(client, this, handler, time); return g_RadioMenuStyle.DoClientMenu(client, this, m_pHandler, time);
} }
void CRadioMenu::Cancel_Finally() void CRadioMenu::Cancel_Finally()

View File

@ -22,9 +22,13 @@
#include <IPlayerHelpers.h> #include <IPlayerHelpers.h>
#include <IUserMessages.h> #include <IUserMessages.h>
#include "sm_fastlink.h" #include "sm_fastlink.h"
#include <sh_stack.h>
using namespace SourceMod; using namespace SourceMod;
class CRadioDisplay;
class CRadioMenu;
class CRadioStyle : class CRadioStyle :
public BaseMenuStyle, public BaseMenuStyle,
public SMGlobalClass, public SMGlobalClass,
@ -41,7 +45,7 @@ public: //BaseMenuStyle
public: //IMenuStyle public: //IMenuStyle
const char *GetStyleName(); const char *GetStyleName();
IMenuPanel *CreatePanel(); IMenuPanel *CreatePanel();
IBaseMenu *CreateMenu(); IBaseMenu *CreateMenu(IMenuHandler *pHandler, IdentityToken_t *pOwner);
unsigned int GetMaxPageItems(); unsigned int GetMaxPageItems();
public: //IUserMessageListener public: //IUserMessageListener
void OnUserMessage(int msg_id, bf_write *bf, IRecipientFilter *pFilter); void OnUserMessage(int msg_id, bf_write *bf, IRecipientFilter *pFilter);
@ -49,14 +53,17 @@ public: //IUserMessageListener
public: public:
bool IsSupported(); bool IsSupported();
bool OnClientCommand(int client); bool OnClientCommand(int client);
public:
CRadioDisplay *MakeRadioDisplay(CRadioMenu *menu=NULL);
void FreeRadioDisplay(CRadioDisplay *display);
private: private:
CBaseMenuPlayer *m_players; CBaseMenuPlayer *m_players;
CStack<CRadioDisplay *> m_FreeDisplays;
}; };
class CRadioMenu;
class CRadioDisplay : public IMenuPanel class CRadioDisplay : public IMenuPanel
{ {
friend class CRadioStyle;
public: public:
CRadioDisplay(); CRadioDisplay();
CRadioDisplay(CRadioMenu *menu); CRadioDisplay(CRadioMenu *menu);
@ -81,11 +88,11 @@ private:
class CRadioMenu : public CBaseMenu class CRadioMenu : public CBaseMenu
{ {
public: public:
CRadioMenu(); CRadioMenu(IMenuHandler *pHandler, IdentityToken_t *pOwner);
public: public:
bool SetExtOption(MenuOption option, const void *valuePtr); bool SetExtOption(MenuOption option, const void *valuePtr);
IMenuPanel *CreatePanel(); IMenuPanel *CreatePanel();
bool Display(int client, IMenuHandler *handler, unsigned int time); bool Display(int client, unsigned int time);
void Cancel_Finally(); void Cancel_Finally();
}; };

View File

@ -109,9 +109,9 @@ IMenuPanel *ValveMenuStyle::CreatePanel()
return new CValveMenuDisplay(); return new CValveMenuDisplay();
} }
IBaseMenu *ValveMenuStyle::CreateMenu() IBaseMenu *ValveMenuStyle::CreateMenu(IMenuHandler *pHandler, IdentityToken_t *pOwner)
{ {
return new CValveMenu(); return new CValveMenu(pHandler, pOwner);
} }
const char *ValveMenuStyle::GetStyleName() const char *ValveMenuStyle::GetStyleName()
@ -305,7 +305,8 @@ bool CValveMenuDisplay::SendDisplay(int client, IMenuHandler *handler, unsigned
return g_ValveMenuStyle.DoClientMenu(client, this, handler, time); return g_ValveMenuStyle.DoClientMenu(client, this, handler, time);
} }
CValveMenu::CValveMenu() : CBaseMenu(&g_ValveMenuStyle), CValveMenu::CValveMenu(IMenuHandler *pHandler, IdentityToken_t *pOwner) :
CBaseMenu(pHandler, &g_ValveMenuStyle, pOwner),
m_IntroColor(255, 0, 0, 255) m_IntroColor(255, 0, 0, 255)
{ {
strcpy(m_IntroMsg, "You have a menu, press ESC"); strcpy(m_IntroMsg, "You have a menu, press ESC");
@ -344,14 +345,14 @@ bool CValveMenu::SetExtOption(MenuOption option, const void *valuePtr)
return false; return false;
} }
bool CValveMenu::Display(int client, IMenuHandler *handler, unsigned int time) bool CValveMenu::Display(int client, unsigned int time)
{ {
if (m_bCancelling) if (m_bCancelling)
{ {
return false; return false;
} }
return g_ValveMenuStyle.DoClientMenu(client, this, handler, time); return g_ValveMenuStyle.DoClientMenu(client, this, m_pHandler, time);
} }
IMenuPanel *CValveMenu::CreatePanel() IMenuPanel *CValveMenu::CreatePanel()

View File

@ -55,7 +55,7 @@ public: //SMGlobalClass
public: //IMenuStyle public: //IMenuStyle
const char *GetStyleName(); const char *GetStyleName();
IMenuPanel *CreatePanel(); IMenuPanel *CreatePanel();
IBaseMenu *CreateMenu(); IBaseMenu *CreateMenu(IMenuHandler *pHandler, IdentityToken_t *pOwner);
unsigned int GetMaxPageItems(); unsigned int GetMaxPageItems();
private: private:
void HookCreateMessage(edict_t *pEdict, DIALOG_TYPE type, KeyValues *kv, IServerPluginCallbacks *plugin); void HookCreateMessage(edict_t *pEdict, DIALOG_TYPE type, KeyValues *kv, IServerPluginCallbacks *plugin);
@ -92,14 +92,14 @@ class CValveMenu : public CBaseMenu
{ {
friend class CValveMenuDisplay; friend class CValveMenuDisplay;
public: public:
CValveMenu(); CValveMenu(IMenuHandler *pHandler, IdentityToken_t *pOwner);
public: //IBaseMenu public: //IBaseMenu
bool SetExtOption(MenuOption option, const void *valuePtr); bool SetExtOption(MenuOption option, const void *valuePtr);
IMenuPanel *CreatePanel(); IMenuPanel *CreatePanel();
bool GetExitButton(); bool GetExitButton();
bool SetExitButton(bool set); bool SetExitButton(bool set);
bool SetPagination(unsigned int itemsPerPage); bool SetPagination(unsigned int itemsPerPage);
bool Display(int client, IMenuHandler *handler, unsigned int time); bool Display(int client, unsigned int time);
public: //CBaseMenu public: //CBaseMenu
void Cancel_Finally(); void Cancel_Finally();
private: private:

View File

@ -842,6 +842,10 @@
RelativePath="..\smn_lang.cpp" RelativePath="..\smn_lang.cpp"
> >
</File> </File>
<File
RelativePath="..\smn_menus.cpp"
>
</File>
<File <File
RelativePath="..\smn_player.cpp" RelativePath="..\smn_player.cpp"
> >

890
core/smn_menus.cpp Normal file
View File

@ -0,0 +1,890 @@
/**
* vim: set ts=4 :
* ===============================================================
* SourceMod (C)2004-2007 AlliedModders LLC. All rights reserved.
* ===============================================================
*
* This file is not open source and may not be copied without explicit
* written permission of AlliedModders LLC. This file may not be redistributed
* in whole or significant part.
* For information, see LICENSE.txt or http://www.sourcemod.net/license.php
*
* Version: $Id$
*/
#include "sm_globals.h"
#include <sh_stack.h>
#include "MenuManager.h"
#include "MenuStyle_Valve.h"
#include "MenuStyle_Radio.h"
#include "HandleSys.h"
#include "PluginSys.h"
#if defined CreateMenu
#undef CreateMenu
#endif
/**
* And God said, "let there be menus," and behold, there were menus.
* God saw the menus and they were good. And the evening and the morning
* were the third day.
*/
enum MenuAction
{
MenuAction_Start = (1<<0), /**< A menu has been started (nothing passed) */
MenuAction_Display = (1<<1), /**< A menu is about to be displayed (param1=client, param2=MenuPanel Handle) */
MenuAction_Select = (1<<2), /**< An item was selected (param1=client, param2=item) */
MenuAction_Cancel = (1<<3), /**< The menu was cancelled (param1=client, param2=item) */
MenuAction_End = (1<<4), /**< A menu's display/selection cycle is complete (nothing passed). */
MenuAction_VoteEnd = (1<<5), /**< (VOTE ONLY): A vote sequence has ended (param1=chosen item) */
};
class CPanelHandler : public IMenuHandler
{
friend class MenuNativeHelpers;
public:
CPanelHandler()
{
}
void OnMenuCancel(IBaseMenu *menu, int client, MenuCancelReason reason);
void OnMenuSelect(IBaseMenu *menu, int client, unsigned int item);
private:
IPluginFunction *m_pFunc;
IPlugin *m_pPlugin;
};
class CMenuHandler : public IMenuHandler
{
friend class MenuNativeHelpers;
public:
CMenuHandler(IPluginFunction *pBasic, int flags);
public:
void OnMenuStart(IBaseMenu *menu);
void OnMenuDisplay(IBaseMenu *menu, int client, IMenuPanel *display);
void OnMenuSelect(IBaseMenu *menu, int client, unsigned int item);
void OnMenuCancel(IBaseMenu *menu, int client, MenuCancelReason reason);
void OnMenuEnd(IBaseMenu *menu);
void OnMenuDestroy(IBaseMenu *menu);
#if 0
void OnMenuDrawItem(IBaseMenu *menu, int client, unsigned int item, unsigned int &style);
void OnMenuDisplayItem(IBaseMenu *menu, int client, unsigned int item, const char **display);
#endif
private:
IPluginFunction *m_pBasic;
int m_Flags;
};
/**
* GLOBAL CLASS FOR HELPERS
*/
class MenuNativeHelpers :
public SMGlobalClass,
public IHandleTypeDispatch,
public IPluginsListener
{
public:
virtual void OnSourceModAllInitialized()
{
m_PanelType = g_HandleSys.CreateType("IMenuPanel", this, 0, NULL, NULL, g_pCoreIdent, NULL);
g_PluginSys.AddPluginsListener(this);
}
virtual void OnSourceModShutdown()
{
g_HandleSys.RemoveType(m_PanelType, g_pCoreIdent);
while (!m_FreePanelHandlers.empty())
{
delete m_FreePanelHandlers.front();
m_FreePanelHandlers.pop();
}
while (!m_FreeMenuHandlers.empty())
{
delete m_FreeMenuHandlers.front();
m_FreeMenuHandlers.pop();
}
}
virtual void OnHandleDestroy(HandleType_t type, void *object)
{
IMenuPanel *panel = (IMenuPanel *)object;
panel->DeleteThis();
}
/**
* It is extremely important that unloaded plugins don't crash.
* Thus, if a plugin unloads, we run through every handler we have.
* This means we do almost no runtime work for keeping track of
* our panel handlers (we don't have to store a list of the running
* ones), but when push comes to shove, we have to scan them all
* in case any of them are active.
*/
virtual void OnPluginUnloaded(IPlugin *plugin)
{
IPluginContext *pContext = plugin->GetBaseContext();
for (size_t i = 0; i < m_PanelHandlers.size(); i++)
{
if (m_PanelHandlers[i]->m_pPlugin == plugin)
{
m_PanelHandlers[i]->m_pPlugin = NULL;
m_PanelHandlers[i]->m_pFunc = NULL;
}
}
}
inline HandleType_t GetPanelType()
{
return m_PanelType;
}
CPanelHandler *GetPanelHandler(IPluginFunction *pFunction)
{
CPanelHandler *handler;
if (m_FreePanelHandlers.empty())
{
handler = new CPanelHandler;
m_PanelHandlers.push_back(handler);
} else {
handler = m_FreePanelHandlers.front();
m_FreePanelHandlers.pop();
}
handler->m_pFunc = pFunction;
handler->m_pPlugin = g_PluginSys.GetPluginByCtx(pFunction->GetParentContext()->GetContext());
return handler;
}
void FreePanelHandler(CPanelHandler *handler)
{
handler->m_pFunc = NULL;
handler->m_pPlugin = NULL;
m_FreePanelHandlers.push(handler);
}
CMenuHandler *GetMenuHandler(IPluginFunction *pFunction, int flags)
{
CMenuHandler *handler;
if (m_FreeMenuHandlers.empty())
{
handler = new CMenuHandler(pFunction, flags);
} else {
handler = m_FreeMenuHandlers.front();
m_FreeMenuHandlers.pop();
handler->m_pBasic = pFunction;
handler->m_Flags = flags;
}
return handler;
}
void FreeMenuHandler(CMenuHandler *handler)
{
m_FreeMenuHandlers.push(handler);
}
private:
HandleType_t m_PanelType;
CStack<CPanelHandler *> m_FreePanelHandlers;
CStack<CMenuHandler *> m_FreeMenuHandlers;
CVector<CPanelHandler *> m_PanelHandlers;
} g_MenuHelpers;
/**
* BASIC PANEL HANDLER WRAPPER
*/
void CPanelHandler::OnMenuCancel(IBaseMenu *menu, int client, MenuCancelReason reason)
{
if (m_pFunc)
{
m_pFunc->PushCell(BAD_HANDLE);
m_pFunc->PushCell(MenuAction_Cancel);
m_pFunc->PushCell(client);
m_pFunc->PushCell(reason);
m_pFunc->Execute(NULL);
}
g_MenuHelpers.FreePanelHandler(this);
}
void CPanelHandler::OnMenuSelect(IBaseMenu *menu, int client, unsigned int item)
{
if (m_pFunc)
{
m_pFunc->PushCell(BAD_HANDLE);
m_pFunc->PushCell(MenuAction_Select);
m_pFunc->PushCell(client);
m_pFunc->PushCell(item);
m_pFunc->Execute(NULL);
}
g_MenuHelpers.FreePanelHandler(this);
}
/**
* MENU HANDLER WRAPPER
*/
CMenuHandler::CMenuHandler(IPluginFunction *pBasic, int flags) :
m_pBasic(pBasic), m_Flags(flags)
{
/* :TODO: We can probably cache the handle ahead of time */
}
void CMenuHandler::OnMenuStart(IBaseMenu *menu)
{
if ((m_Flags & (int)MenuAction_Start) == (int)MenuAction_Start)
{
m_pBasic->PushCell(menu->GetHandle());
m_pBasic->PushCell(MenuAction_Start);
m_pBasic->PushCell(0);
m_pBasic->PushCell(0);
m_pBasic->Execute(NULL);
}
}
void CMenuHandler::OnMenuDisplay(IBaseMenu *menu, int client, IMenuPanel *panel)
{
if ((m_Flags & (int)MenuAction_Display) == (int)MenuAction_Display)
{
HandleSecurity sec;
sec.pIdentity = m_pBasic->GetParentContext()->GetIdentity();
sec.pOwner = g_pCoreIdent;
HandleAccess access;
g_HandleSys.InitAccessDefaults(NULL, &access);
access.access[HandleAccess_Delete] = HANDLE_RESTRICT_IDENTITY|HANDLE_RESTRICT_OWNER;
Handle_t hndl = g_HandleSys.CreateHandleEx(g_MenuHelpers.GetPanelType(), panel, &sec, &access, NULL);
m_pBasic->PushCell(menu->GetHandle());
m_pBasic->PushCell(MenuAction_Display);
m_pBasic->PushCell(client);
m_pBasic->PushCell(hndl);
m_pBasic->Execute(NULL);
g_HandleSys.FreeHandle(hndl, &sec);
}
}
void CMenuHandler::OnMenuSelect(IBaseMenu *menu, int client, unsigned int item)
{
m_pBasic->PushCell(menu->GetHandle());
m_pBasic->PushCell(MenuAction_Select);
m_pBasic->PushCell(client);
m_pBasic->PushCell(item);
m_pBasic->Execute(NULL);
}
void CMenuHandler::OnMenuCancel(IBaseMenu *menu, int client, MenuCancelReason reason)
{
m_pBasic->PushCell(menu->GetHandle());
m_pBasic->PushCell(MenuAction_Cancel);
m_pBasic->PushCell(client);
m_pBasic->PushCell(reason);
m_pBasic->Execute(NULL);
}
void CMenuHandler::OnMenuEnd(IBaseMenu *menu)
{
m_pBasic->PushCell(menu->GetHandle());
m_pBasic->PushCell(MenuAction_End);
m_pBasic->PushCell(0);
m_pBasic->PushCell(0);
m_pBasic->Execute(NULL);
}
void CMenuHandler::OnMenuDestroy(IBaseMenu *menu)
{
g_MenuHelpers.FreeMenuHandler(this);
}
/**
* INLINE FUNCTIONS FOR NATIVES
*/
inline Handle_t MakePanelHandle(IMenuPanel *panel, IPluginContext *pContext)
{
return g_HandleSys.CreateHandle(g_MenuHelpers.GetPanelType(), panel, pContext->GetIdentity(), g_pCoreIdent, NULL);
}
inline HandleError ReadPanelHandle(Handle_t hndl, IMenuPanel **panel)
{
HandleSecurity sec;
sec.pIdentity = g_pCoreIdent;
sec.pOwner = NULL;
return g_HandleSys.ReadHandle(hndl, g_MenuHelpers.GetPanelType(), &sec, (void **)panel);
}
inline IMenuStyle *GetStyleFromCell(cell_t cell)
{
enum MenuStyle
{
MenuStyle_Default = 0, /**< The "default" menu style for the mod */
MenuStyle_Valve = 1, /**< The Valve provided menu style (Used on HL2DM) */
MenuStyle_Radio = 2, /**< The simpler menu style commonly used on CS:S */
};
if (cell == MenuStyle_Valve)
{
return &g_ValveMenuStyle;
} else if (cell == MenuStyle_Radio
&& g_RadioMenuStyle.IsSupported())
{
return &g_RadioMenuStyle;
}
return g_Menus.GetDefaultStyle();
}
/***********************************
**** NATIVE DEFINITIONS ***********
***********************************/
static cell_t CreateMenu(IPluginContext *pContext, const cell_t *params)
{
IMenuStyle *style = g_Menus.GetDefaultStyle();
IPluginFunction *pFunction;
if ((pFunction=pContext->GetFunctionById(params[1])) == NULL)
{
return pContext->ThrowNativeError("Function id %x is invalid", params[1]);
}
CMenuHandler *handler = g_MenuHelpers.GetMenuHandler(pFunction, params[2]);
IBaseMenu *menu = style->CreateMenu(handler, pContext->GetIdentity());
Handle_t hndl = menu->GetHandle();
if (!hndl)
{
menu->Destroy();
return BAD_HANDLE;
}
return hndl;
}
static cell_t DisplayMenu(IPluginContext *pContext, const cell_t *params)
{
Handle_t hndl = (Handle_t)params[1];
HandleError err;
IBaseMenu *menu;
if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
{
return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
}
return menu->Display(params[2], params[3]) ? 1 : 0;
}
static cell_t AddMenuItem(IPluginContext *pContext, const cell_t *params)
{
Handle_t hndl = (Handle_t)params[1];
HandleError err;
IBaseMenu *menu;
if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
{
return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
}
char *info;
ItemDrawInfo dr;
pContext->LocalToString(params[2], &info);
pContext->LocalToString(params[3], (char **)&dr.display);
dr.style = params[4];
return menu->AppendItem(info, dr) ? 1 : 0;
}
static cell_t InsertMenuItem(IPluginContext *pContext, const cell_t *params)
{
Handle_t hndl = (Handle_t)params[1];
HandleError err;
IBaseMenu *menu;
if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
{
return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
}
char *info;
ItemDrawInfo dr;
pContext->LocalToString(params[3], &info);
pContext->LocalToString(params[4], (char **)&dr.display);
dr.style = params[5];
return menu->InsertItem(params[2], info, dr) ? 1 : 0;
}
static cell_t RemoveMenuItem(IPluginContext *pContext, const cell_t *params)
{
Handle_t hndl = (Handle_t)params[1];
HandleError err;
IBaseMenu *menu;
if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
{
return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
}
return menu->RemoveItem(params[2]) ? 1 : 0;
}
static cell_t RemoveAllMenuItems(IPluginContext *pContext, const cell_t *params)
{
Handle_t hndl = (Handle_t)params[1];
HandleError err;
IBaseMenu *menu;
if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
{
return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
}
menu->RemoveAllItems();
return 1;
}
static cell_t GetMenuItem(IPluginContext *pContext, const cell_t *params)
{
Handle_t hndl = (Handle_t)params[1];
HandleError err;
IBaseMenu *menu;
if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
{
return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
}
ItemDrawInfo dr;
const char *info;
if ((info=menu->GetItemInfo(params[2], &dr)) == NULL)
{
return 0;
}
pContext->StringToLocalUTF8(params[3], params[4], info, NULL);
pContext->StringToLocalUTF8(params[6], params[7], dr.display ? dr.display : "", NULL);
cell_t *addr;
pContext->LocalToPhysAddr(params[5], &addr);
*addr = dr.style;
return 1;
}
static cell_t SetMenuPagination(IPluginContext *pContext, const cell_t *params)
{
Handle_t hndl = (Handle_t)params[1];
HandleError err;
IBaseMenu *menu;
if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
{
return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
}
return menu->SetPagination(params[2]) ? 1 : 0;
}
static cell_t GetMenuPagination(IPluginContext *pContext, const cell_t *params)
{
Handle_t hndl = (Handle_t)params[1];
HandleError err;
IBaseMenu *menu;
if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
{
return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
}
return menu->GetPagination();
}
static cell_t GetMenuItemCount(IPluginContext *pContext, const cell_t *params)
{
Handle_t hndl = (Handle_t)params[1];
HandleError err;
IBaseMenu *menu;
if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
{
return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
}
return menu->GetItemCount();
}
static cell_t SetMenuTitle(IPluginContext *pContext, const cell_t *params)
{
Handle_t hndl = (Handle_t)params[1];
HandleError err;
IBaseMenu *menu;
if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
{
return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
}
char buffer[1024];
g_SourceMod.FormatString(buffer, sizeof(buffer), pContext, params, 2);
menu->SetDefaultTitle(buffer);
return 1;
}
static cell_t CreatePanelFromMenu(IPluginContext *pContext, const cell_t *params)
{
Handle_t hndl = (Handle_t)params[1];
HandleError err;
IBaseMenu *menu;
if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
{
return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
}
IMenuPanel *panel = menu->CreatePanel();
hndl = MakePanelHandle(panel, pContext);
if (!hndl)
{
panel->DeleteThis();
}
return hndl;
}
static cell_t GetMenuExitButton(IPluginContext *pContext, const cell_t *params)
{
Handle_t hndl = (Handle_t)params[1];
HandleError err;
IBaseMenu *menu;
if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
{
return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
}
return menu->GetExitButton() ? 1 : 0;
}
static cell_t SetMenuExitButton(IPluginContext *pContext, const cell_t *params)
{
Handle_t hndl = (Handle_t)params[1];
HandleError err;
IBaseMenu *menu;
if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
{
return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
}
return menu->SetExitButton(params[2] ? true : false) ? 1 : 0;
}
static cell_t CancelMenu(IPluginContext *pContext, const cell_t *params)
{
Handle_t hndl = (Handle_t)params[1];
HandleError err;
IBaseMenu *menu;
if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
{
return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
}
menu->Cancel();
return 1;
}
static cell_t GetMenuStyle(IPluginContext *pContext, const cell_t *params)
{
Handle_t hndl = (Handle_t)params[1];
HandleError err;
IBaseMenu *menu;
if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
{
return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
}
return menu->GetDrawStyle()->GetHandle();
}
static cell_t GetMenuStyleHandle(IPluginContext *pContext, const cell_t *params)
{
IMenuStyle *style = GetStyleFromCell(params[1]);
if (!style)
{
return BAD_HANDLE;
}
return style->GetHandle();
}
static cell_t CreatePanel(IPluginContext *pContext, const cell_t *params)
{
Handle_t hndl = (Handle_t)params[1];
HandleError err;
IMenuStyle *style;
if (hndl != 0)
{
if ((err=g_Menus.ReadStyleHandle(params[1], &style)) != HandleError_None)
{
return pContext->ThrowNativeError("MenuStyle handle %x is invalid (error %d)", hndl, err);
}
} else {
style = g_Menus.GetDefaultStyle();
}
IMenuPanel *panel = style->CreatePanel();
hndl = MakePanelHandle(panel, pContext);
if (!hndl)
{
panel->DeleteThis();
return BAD_HANDLE;
}
return hndl;
}
static cell_t CreateMenuEx(IPluginContext *pContext, const cell_t *params)
{
Handle_t hndl = (Handle_t)params[1];
HandleError err;
IMenuStyle *style;
if (hndl != 0)
{
if ((err=g_Menus.ReadStyleHandle(params[1], &style)) != HandleError_None)
{
return pContext->ThrowNativeError("MenuStyle handle %x is invalid (error %d)", hndl, err);
}
} else {
style = g_Menus.GetDefaultStyle();
}
IPluginFunction *pFunction;
if ((pFunction=pContext->GetFunctionById(params[2])) == NULL)
{
return pContext->ThrowNativeError("Function id %x is invalid", params[2]);
}
CMenuHandler *handler = g_MenuHelpers.GetMenuHandler(pFunction, params[3]);
IBaseMenu *pMenu = style->CreateMenu(handler, pContext->GetIdentity());
hndl = pMenu->GetHandle();
if (!hndl)
{
pMenu->Destroy();
return BAD_HANDLE;
}
return hndl;
}
static cell_t GetClientMenu(IPluginContext *pContext, const cell_t *params)
{
Handle_t hndl = (Handle_t)params[2];
HandleError err;
IMenuStyle *style;
if (hndl != 0)
{
if ((err=g_Menus.ReadStyleHandle(params[1], &style)) != HandleError_None)
{
return pContext->ThrowNativeError("MenuStyle handle %x is invalid (error %d)", hndl, err);
}
} else {
style = g_Menus.GetDefaultStyle();
}
return style->GetClientMenu(params[1], NULL);
}
static cell_t CancelClientMenu(IPluginContext *pContext, const cell_t *params)
{
Handle_t hndl = (Handle_t)params[3];
HandleError err;
IMenuStyle *style;
if (hndl != 0)
{
if ((err=g_Menus.ReadStyleHandle(params[1], &style)) != HandleError_None)
{
return pContext->ThrowNativeError("MenuStyle handle %x is invalid (error %d)", hndl, err);
}
} else {
style = g_Menus.GetDefaultStyle();
}
return style->CancelClientMenu(params[1], params[2] ? true : false) ? 1 : 0;
}
static cell_t GetMaxPageItems(IPluginContext *pContext, const cell_t *params)
{
Handle_t hndl = (Handle_t)params[1];
HandleError err;
IMenuStyle *style;
if (hndl != 0)
{
if ((err=g_Menus.ReadStyleHandle(params[1], &style)) != HandleError_None)
{
return pContext->ThrowNativeError("MenuStyle handle %x is invalid (error %d)", hndl, err);
}
} else {
style = g_Menus.GetDefaultStyle();
}
return style->GetMaxPageItems();
}
static cell_t GetPanelStyle(IPluginContext *pContext, const cell_t *params)
{
Handle_t hndl = (Handle_t)params[1];
HandleError err;
IMenuPanel *panel;
if ((err=ReadPanelHandle(hndl, &panel)) != HandleError_None)
{
return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
}
return panel->GetParentStyle()->GetHandle();
}
static cell_t SetPanelTitle(IPluginContext *pContext, const cell_t *params)
{
Handle_t hndl = (Handle_t)params[1];
HandleError err;
IMenuPanel *panel;
if ((err=ReadPanelHandle(hndl, &panel)) != HandleError_None)
{
return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
}
char *text;
pContext->LocalToString(params[2], &text);
panel->DrawTitle(text, params[3] ? true : false);
return 1;
}
static cell_t DrawPanelItem(IPluginContext *pContext, const cell_t *params)
{
Handle_t hndl = (Handle_t)params[1];
HandleError err;
IMenuPanel *panel;
if ((err=ReadPanelHandle(hndl, &panel)) != HandleError_None)
{
return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
}
ItemDrawInfo dr;
pContext->LocalToString(params[2], (char **)&dr.display);
dr.style = params[3];
return panel->DrawItem(dr);
}
static cell_t DrawPanelText(IPluginContext *pContext, const cell_t *params)
{
Handle_t hndl = (Handle_t)params[1];
HandleError err;
IMenuPanel *panel;
if ((err=ReadPanelHandle(hndl, &panel)) != HandleError_None)
{
return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
}
char *text;
pContext->LocalToString(params[2], &text);
return panel->DrawRawLine(text) ? 1 : 0;
}
static cell_t CanPanelDrawFlags(IPluginContext *pContext, const cell_t *params)
{
Handle_t hndl = (Handle_t)params[1];
HandleError err;
IMenuPanel *panel;
if ((err=ReadPanelHandle(hndl, &panel)) != HandleError_None)
{
return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
}
return panel->CanDrawItem(params[2]);
}
static cell_t SendPanelToClient(IPluginContext *pContext, const cell_t *params)
{
Handle_t hndl = (Handle_t)params[1];
HandleError err;
IMenuPanel *panel;
if ((err=ReadPanelHandle(hndl, &panel)) != HandleError_None)
{
return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
}
IPluginFunction *pFunction;
if ((pFunction=pContext->GetFunctionById(params[3])) == NULL)
{
return pContext->ThrowNativeError("Function id %x is invalid", params[3]);
}
CPanelHandler *handler = g_MenuHelpers.GetPanelHandler(pFunction);
if (!panel->SendDisplay(params[2], handler, params[4]))
{
g_MenuHelpers.FreePanelHandler(handler);
}
return 1;
}
REGISTER_NATIVES(menuNatives)
{
{"AddMenuItem", AddMenuItem},
{"CanPanelDrawFlags", CanPanelDrawFlags},
{"CancelClientMenu", CancelClientMenu},
{"CancelMenu", CancelMenu},
{"CreateMenu", CreateMenu},
{"CreateMenuEx", CreateMenuEx},
{"CreatePanel", CreatePanel},
{"CreatePanelFromMenu", CreatePanelFromMenu},\
{"DisplayMenu", DisplayMenu},
{"DrawPanelItem", DrawPanelItem},
{"DrawPanelText", DrawPanelText},
{"GetClientMenu", GetClientMenu},
{"GetMaxPageItems", GetMaxPageItems},
{"GetMenuExitButton", GetMenuExitButton},
{"GetMenuItem", GetMenuItem},
{"GetMenuItemCount", GetMenuItemCount},
{"GetMenuPagination", GetMenuPagination},
{"GetMenuStyle", GetMenuStyle},
{"GetMenuStyleHandle", GetMenuStyleHandle},
{"GetPanelStyle", GetPanelStyle},
{"InsertMenuItem", InsertMenuItem},
{"RemoveAllMenuItems", RemoveAllMenuItems},
{"RemoveMenuItem", RemoveMenuItem},
{"SendPanelToClient", SendPanelToClient},
{"SetMenuExitButton", SetMenuExitButton},
{"SetMenuPagination", SetMenuPagination},
{"SetMenuTitle", SetMenuTitle},
{"SetPanelTitle", SetPanelTitle},
{NULL, NULL},
};

View File

@ -194,6 +194,13 @@ int BaseContext::Execute(uint32_t code_addr, cell_t *result)
return SP_ERROR_NOT_RUNNABLE; return SP_ERROR_NOT_RUNNABLE;
} }
/* tada, prevent a crash */
cell_t _ignore_result;
if (!result)
{
result = &_ignore_result;
}
IVirtualMachine *vm = (IVirtualMachine *)ctx->vmbase; IVirtualMachine *vm = (IVirtualMachine *)ctx->vmbase;
uint32_t pushcount = ctx->pushcount; uint32_t pushcount = ctx->pushcount;

500
plugins/include/menus.inc Normal file
View File

@ -0,0 +1,500 @@
/**
* vim: set ts=4 :
* ===============================================================
* SourceMod (C)2004-2007 AlliedModders LLC. All rights reserved.
* ===============================================================
*
* This file is part of the SourceMod/SourcePawn SDK. This file may only be used
* or modified under the Terms and Conditions of its License Agreement, which is found
* in LICENSE.txt. The Terms and Conditions for making SourceMod extensions/plugins
* may change at any time. To view the latest information, see:
* http://www.sourcemod.net/license.php
*
* Version: $Id$
*/
#if defined _menus_included
#endinput
#endif
#define _menus_included
/**
* Low-level drawing style of the menu.
*/
enum MenuStyle
{
MenuStyle_Default = 0, /**< The "default" menu style for the mod */
MenuStyle_Valve = 1, /**< The Valve provided menu style (Used on HL2DM) */
MenuStyle_Radio = 2, /**< The simpler menu style commonly used on CS:S */
};
/**
* Different actions for the menu "pump" callback
*/
enum MenuAction
{
MenuAction_Start = (1<<0), /**< A menu has been started (nothing passed) */
MenuAction_Display = (1<<1), /**< A menu is about to be displayed (param1=client, param2=MenuPanel Handle) */
MenuAction_Select = (1<<2), /**< An item was selected (param1=client, param2=item) */
MenuAction_Cancel = (1<<3), /**< The menu was cancelled (param1=client, param2=item) */
MenuAction_End = (1<<4), /**< A menu's display/selection cycle is complete (nothing passed). */
MenuAction_VoteEnd = (1<<5), /**< (VOTE ONLY): A vote sequence has ended (param1=chosen item) */
};
/** Default menu actions */
#define MENU_ACTIONS_DEFAULT MenuAction_Select|MenuAction_Cancel|MenuAction_End
/** All menu actions */
#define MENU_ACTIONS_ALL -1
#define MENU_NO_PAGINATION 0 /**< Menu should not be paginated (10 items max) */
#define MENU_TIME_FOREVER 0 /**< Menu should be displayed as long as possible */
#define ITEMDRAW_DEFAULT (0) /**< Item should be drawn normally */
#define ITEMDRAW_DISABLED (1<<0) /**< Item is drawn but not selectable */
#define ITEMDRAW_RAWLINE (1<<1) /**< Item should be a raw line, without a slot */
#define ITEMDRAW_NOTEXT (1<<2) /**< No text should be drawn */
#define ITEMDRAW_SPACER (1<<3) /**< Item should be drawn as a spacer, if possible */
#define ITEMDRAW_IGNORE ((1<<1)|(1<<2)) /**< Item should be completely ignored (rawline + notext) */
#define ITEMDRAW_CONTROL (1<<4) /**< Item is control text (back/next/exit) */
/**
* Reasons a menu can be cancelled.
*/
enum
{
MenuCancel_Disconnect = -1, /**< Client dropped from the server */
MenuCancel_Interrupt = -2, /**< Client was interrupted with another menu */
MenuCancel_Exit = -3, /**< Client selected "exit" on a paginated menu */
MenuCancel_NoDisplay = -4, /**< Menu could not be displayed to the client */
};
/**
* Describes a menu's source
*/
enum MenuSource
{
MenuSource_None = 0, /**< No menu is being displayed */
MenuSource_External = 1, /**< External menu */
MenuSource_Normal = 2, /**< A basic menu is being displayed */
MenuSource_RawPanel = 3, /**< A display is active, but it is not tied to a menu */
};
/**
* Called when a menu action is completed.
*
* @param menu The menu being acted upon.
* @param action The action of the menu.
* @param param1 First action parameter (usually the client).
* @param param2 Second action parameter (usually the item).
* @noreturn
*/
functag MenuHandler public(Handle:menu, MenuAction:action, param1, param2);
/**
* Creates a new, empty menu using the default style.
*
* @param handler Function which will receive menu actions.
* @param actions Optionally set which actions to receive. Select,
* Cancel, and End will always be received regardless
* of whether they are set or not. They are also
* the only default actions.
* @return A new menu Handle.
*/
native Handle:CreateMenu(MenuHandler:handler, MenuAction:actions=MENU_ACTIONS_DEFAULT);
/**
* Displays a menu to a client.
*
* @param menu Menu Handle.
* @param client Client index.
* @param time Maximum time to leave menu on the screen.
* @return True on success, false on failure.
* @error Invalid Handle or client not in game.
*/
native bool:DisplayMenu(Handle:menu, client, time);
/**
* Appends a new item to the end of a menu.
*
* @param menu Menu Handle.
* @param info Item information string.
* @param display Default item display string.
* @param style Drawing style flags.
* @return True on success, false on failure.
* @error Invalid Handle or item limit reached.
*/
native AddMenuItem(Handle:menu,
const String:info[],
const String:display[],
style=ITEMDRAW_DEFAULT);
/**
* Inserts an item into the menu before a certain position; the new item will
* be at the given position and all next items pushed forward.
*
* @param menu Menu Handle.
* @param position Position, starting from 0.
* @param info Item information string.
* @param display Default item display string.
* @param style Drawing style flags.
* @return True on success, false on failure.
* @error Invalid Handle or menu position.
*/
native bool:InsertMenuItem(Handle:menu,
position,
const String:info[],
const String:display[],
style=ITEMDRAW_DEFAULT);
/**
* Removes an item from the menu.
*
* @param menu Menu Handle.
* @param position Position, starting from 0.
* @return True on success, false on failure.
* @error Invalid Handle or menu position.
*/
native bool:RemoveMenuItem(Handle:menu, position);
/**
* Removes all items from a menu.
*
* @param menu Menu Handle.
* @noreturn
* @error Invalid Handle or menu position.
*/
native RemoveAllMenuItems(Handle:menu);
/**
* Retrieves information about a menu item.
*
* @param menu Menu Handle.
* @param position Position, starting from 0.
* @param infoBuf Info buffer.
* @param infoBufLen Maximum length of the info buffer.
* @param style By-reference variable to store drawing flags.
* @param dispBuf Display buffer.
* @param dispBufLen Maximum length of the display buffer.
* @return True on success, false if position is invalid.
* @error Invalid Handle.
*/
native bool:GetMenuItem(Handle:menu, position, String:infoBuf[], infoBufLen, &style=0, String:dispBuf[]="", dispBufLen=0);
/**
* Returns the number of items in a menu.
*
* @param menu Menu Handle.
* @return Number of items in the menu.
* @error Invalid Handle.
*/
native GetMenuItemCount(Handle:menu);
/**
* Sets whether the menu should be paginated or not.
*
* @param menu Handle to the menu.
* @param itemsPerPage Number of items per page, or MENU_NO_PAGINATION.
* @return True on success, false if pagination is too high or low.
* @error Invalid Handle.
*/
native bool:SetMenuPagination(Handle:menu, itemsPerPage);
/**
* Returns a menu's pagination setting.
*
* @param menu Handle to the menu.
* @return Pagination setting.
* @error Invalid Handle.
*/
native GetMenuPagination(Handle:menu);
/**
* Returns a menu's MenuStyle Handle. The Handle
* is global and cannot be freed.
*
* @param menu Handle to the menu.
* @return Handle to the menu's draw style.
* @error Invalid Handle.
*/
native Handle:GetMenuStyle(Handle:menu);
/**
* Sets the menu's default title/instruction message.
*
* @param menu Menu Handle.
* @param fmt Message string format
* @param ... Message string arguments.
* @noreturn
* @error Invalid Handle.
*/
native SetMenuTitle(Handle:menu, const String:fmt[], any:...);
/**
* Creates a raw MenuPanel based off the menu's style.
* The Handle must be freed with CloseHandle().
*
* @return A new MenuPanel Handle.
* @error Invalid Handle.
*/
native Handle:CreatePanelFromMenu(Handle:menu);
/**
* Returns whether or not the menu has an exit button.
* By default, menus have an exit button.
*
* @param menu Menu Handle.
* @return True if the menu has an exit button; false otherwise.
* @error Invalid Handle.
*/
native bool:GetMenuExitButton(Handle:menu);
/**
* Sets whether or not the menu has an exit button.
* By default, menus have an exit button.
*
* @param menu Menu Handle.
* @param button True to enable the button, false to remove it.
* @return True if allowed; false on failure.
* @error Invalid Handle.
*/
native bool:SetMenuExitButton(Handle:menu, bool:button);
/**
* Cancels a menu from displaying on all clients. While the
* cancellation is in progress, this menu cannot be re-displayed
* to any clients.
*
* The menu may still exist on the client's screen after this command.
* This simply verifies that the menu is not being used anywhere.
*
* @param menu Menu Handle.
* @noreturn
* @error Invalid Handle.
*/
native CancelMenu(Handle:menu);
#if 0
/**
* Broadcasts a menu to a list of clients.
*
* @param menu Menu Handle.
* @param handler MenuHandler callback to receive actions.
* @param players Array of players to broadcast to.
* @param numPlayers Number of players in the array.
* @param time Maximum time to leave menu on the screen.
* @return Number of clients that broadcast will wait upon.
* @error Invalid Handle.
*/
native BroadcastMenu(Handle:menu, MenuHandler:handler, players[], numPlayers, time);
/**
* Broadcasts a menu to a list of clients. The most selected
* item will be returned through MenuAction_End. On a tie, a random
* item will be returned.
*
* @param menu Menu Handle.
* @param handler MenuHandler callback to receive actions.
* @param players Array of players to broadcast to.
* @param numPlayers Number of players in the array.
* @param time Maximum time to leave menu on the screen.
* @return Number of clients that vote will wait upon.
* @error Invalid Handle.
*/
native VoteMenu(Handle:menu, MenuHandler:handler, players[], numPlayers, time);
/**
* Broadcasts a menu to all clients.
*
* @param menu Menu Handle.
* @param handler MenuHandler callback to receive actions.
* @param time Maximum time to leave menu on the screen.
* @return Number of clients that broadcast will wait upon.
* @error Invalid Handle.
*/
stock BroadcastMenuToAll(Handle:menu, MenuHandler:handler, time)
{
new num = GetMaxClients();
new total;
decl players[num];
for (new i=1; i<=num; i++)
{
if (!IsClientConnected(i))
{
continue;
}
players[total++] = i;
}
return BroadcastMenu(menu, handler, players, total, time);
}
/**
* Broadcasts a menu to all clients. The most selected item will
* be returned through MenuAction_End. On a tie, a random item
* will be returned.
*
* @param menu Menu Handle.
* @param handler MenuHandler callback to receive actions.
* @param time Maximum time to leave menu on the screen.
* @return Number of clients that the vote will wait upon.
* @error Invalid Handle.
*/
native VoteMenuToAll(Handle:menu, MenuHandler:handler, time)
{
new num = GetMaxClients();
new total;
decl players[num];
for (new i=1; i<=num; i++)
{
if (!IsClientConnected(i))
{
continue;
}
players[total++] = i;
}
return VoteMenu(menu, handler, players, total, time);
}
#endif
/**
* Returns a style's global Handle.
*
* @param style Menu Style.
* @return A Handle, or INVALID_HANDLE if not found or unusable.
*/
native Handle:GetMenuStyleHandle(MenuStyle:style);
/**
* Creates a MenuPanel from a MenuStyle. Panels are used for drawing raw
* menus without any extra helper functions. The Handle must be closed
* with CloseHandle().
*
* @param hStyle MenuStyle Handle, or INVALID_HANDLE to use the default style.
* @return A new MenuPanel Handle.
* @error Invalid Handle other than INVALID_HANDLE.
*/
native Handle:CreatePanel(Handle:hStyle=INVALID_HANDLE);
/**
* Creates a Menu from a MenuStyle. The Handle must be closed with
* CloseHandle().
*
* @parma hStyle MenuStyle Handle, or INVALID_HANDLE to use the default style.
* @param handler Function which will receive menu actions.
* @param actions Optionally set which actions to receive. Select,
* Cancel, and End will always be received regardless
* of whether they are set or not. They are also
* the only default actions.
* @return A new menu Handle.
* @error Invalid Handle other than INVALID_HANDLE.
*/
native Handle:CreateMenuEx(Handle:hStyle=INVALID_HANDLE, MenuHandler:handler, MenuAction:actions=MENU_ACTIONS_DEFAULT);
/**
* Returns whether a client is viewing a menu. If the menu source
* is MenuSource_Normal, a menu Handle will also be returned.
*
* @param client Client index.
* @param hStyle MenuStyle Handle, or INVALID_HANDLE to use the default style.
* @return A MenuSource value.
* @error Invalid Handle other than INVALID_HANDLE.
*/
native MenuSource:GetClientMenu(client, Handle:hStyle=INVALID_HANDLE);
/**
* Cancels a menu on a client. This will only affect non-external menus.
*
* @param hstyle MenuStyle Handle, or INVALID_HANDLE to use the default style.
* @param client Client index.
* @param autoIgnore If true, no menus can be re-drawn on the client during
* the cancellation process.
* @return True if a menu was cancelled, false otherwise.
*/
native bool:CancelClientMenu(client, bool:autoIgnore=false, Handle:hStyle=INVALID_HANDLE);
/**
* Returns a style's maximum items per page.
*
* @param hStyle MenuStyle Handle, or INVALID_HANDLE to use the default style.
* @return Maximum items per page.
* @error Invalid Handle other than INVALID_HANDLE.
*/
native GetMaxPageItems(Handle:hStyle=INVALID_HANDLE);
/**
* Returns a MenuPanel's parent style.
*
* @param panel A MenuPanel Handle.
* @return The MenuStyle Handle that created the panel.
* @error Invalid Handle.
*/
native Handle:GetPanelStyle(Handle:panel);
/**
* Sets the panel's title.
*
* @param panel A MenuPanel Handle.
* @param title Text to set as the title.
* @param onlyIfEmpty If true, the title will only be set if no title is set.
* @noreturn
* @error Invalid Handle.
*/
native Handle:SetPanelTitle(Handle:panel, const String:text[], bool:onlyIfEmpty=false);
/**
* Draws an item on a panel. If the item takes up a slot, the position
* is returned.
*
* @param panel A MenuPanel Handle.
* @param text Display text to use. If not a raw line,
* the style may automatically add color markup.
* No numbering or newlines are needed.
* @param style ITEMDRAW style flags.
* @return A slot position, or 0 if item was a rawline or could not be drawn.
* @error Invalid Handle.
*/
native DrawPanelItem(Handle:panel, const String:text[], style=ITEMDRAW_DEFAULT);
/**
* Draws a raw line of text on a panel, without any markup other than a newline.
*
* @param panel A MenuPanel Handle.
* @param text Display text to use.
* @return True on success, false if raw lines are not supported.
* @error Invalid Handle.
*/
native DrawPanelText(Handle:panel, const String:text[]);
/**
* Returns whether or not the given drawing flags are supported by
* the menu style.
*
* @param panel A MenuPanel Handle.
* @param style ITEMDRAW style flags.
* @return True if item is drawable, false otherwise.
* @error Invalid Handle.
*/
native CanPanelDrawFlags(Handle:panel, style);
/**
* Sends a panel to a client. Unlike full menus, the handler
* function will only receive the following actions, both of
* which will have INVALID_HANDLE for a menu, and the client
* as param1.
*
* MenuAction_Select (param2 will be the key pressed)
* MenuAction_Cancel (param2 will be the reason)
*
* Also, if the menu fails to display, no callbacks will be called.
*
* @param panel A MenuPanel Handle.
* @param client A client to draw to.
* @param handler The MenuHandler function to catch actions with.
* @param time Time to hold the menu for.
* @return True on success, false on failure.
* @error Invalid Handle.
*/
native bool:SendPanelToClient(Handle:panel, client, MenuHandler:handler, time);

View File

@ -20,6 +20,7 @@
#define _INCLUDE_SOURCEMOD_MENU_SYSTEM_H_ #define _INCLUDE_SOURCEMOD_MENU_SYSTEM_H_
#include <IShareSys.h> #include <IShareSys.h>
#include <IHandleSys.h>
#define SMINTERFACE_MENUMANAGER_NAME "IMenuManager" #define SMINTERFACE_MENUMANAGER_NAME "IMenuManager"
#define SMINTERFACE_MENUMANAGER_VERSION 1 #define SMINTERFACE_MENUMANAGER_VERSION 1
@ -263,9 +264,12 @@ namespace SourceMod
* *
* Note: the object should be freed using IBaseMenu::Destroy. * Note: the object should be freed using IBaseMenu::Destroy.
* *
* @param handler IMenuHandler pointer.
* @param pOwner Optional IdentityToken_t owner for handle
* creation.
* @return An IBaseMenu pointer. * @return An IBaseMenu pointer.
*/ */
virtual IBaseMenu *CreateMenu() =0; virtual IBaseMenu *CreateMenu(IMenuHandler *handler, IdentityToken_t *pOwner=NULL) =0;
/** /**
* @brief Returns the maximum number of items per page. * @brief Returns the maximum number of items per page.
@ -293,6 +297,13 @@ namespace SourceMod
* @return True if a menu was cancelled, false otherwise. * @return True if a menu was cancelled, false otherwise.
*/ */
virtual bool CancelClientMenu(int client, bool autoIgnore=false) =0; virtual bool CancelClientMenu(int client, bool autoIgnore=false) =0;
/**
* @brief Returns a Handle the IMenuStyle object.
*
* @return Handle_t pointing to this object.
*/
virtual Handle_t GetHandle() =0;
}; };
/** /**
@ -428,16 +439,20 @@ namespace SourceMod
* @brief Sends the menu to a client. * @brief Sends the menu to a client.
* *
* @param client Client index to display to. * @param client Client index to display to.
* @param handler Menu handler to use.
* @param time Time to hold menu for. * @param time Time to hold menu for.
* @return True on success, false otherwise. * @return True on success, false otherwise.
*/ */
virtual bool Display(int client, IMenuHandler *handler, unsigned int time) =0; virtual bool Display(int client, unsigned int time) =0;
/** /**
* @brief Destroys the menu and frees all associated resources.1 * @brief Destroys the menu and frees all associated resources.
*
* @param releaseHandle If true, the Handle will be released
* in the destructor. This should be set
* to true except for IHandleTypeDispatch
* destructors.
*/ */
virtual void Destroy() =0; virtual void Destroy(bool releaseHandle=true) =0;
/** /**
* @brief Cancels the menu on all client's displays. While the menu is * @brief Cancels the menu on all client's displays. While the menu is
@ -446,6 +461,14 @@ namespace SourceMod
* @return Number of menus cancelled. * @return Number of menus cancelled.
*/ */
virtual void Cancel() =0; virtual void Cancel() =0;
/**
* @brief Returns the menu's Handle. The Handle is automatically
* removed when the menu is destroyed.
*
* @return Handle_t handle value.
*/
virtual Handle_t GetHandle() =0;
}; };
/** /**
@ -516,6 +539,15 @@ namespace SourceMod
{ {
} }
/**
* @brief Called when the menu object is destroyed.
*
* @param menu Menu pointer.
*/
virtual void OnMenuDestroy(IBaseMenu *menu)
{
}
/** /**
* @brief Called when requesting how to render an item. * @brief Called when requesting how to render an item.
* *
@ -579,11 +611,11 @@ namespace SourceMod
*/ */
virtual IMenuStyle *FindStyleByName(const char *name) =0; virtual IMenuStyle *FindStyleByName(const char *name) =0;
#if 0
/** /**
* @brief Broadcasts a menu to a number of clients. * @brief Broadcasts a menu to a number of clients.
* *
* @param menu Menu pointer. * @param menu Menu pointer.
* @param handler IMenuHandler pointer.
* @param clients Array of client indexes. * @param clients Array of client indexes.
* @param numClients Number of clients in the array. * @param numClients Number of clients in the array.
* @param time Time to hold the menu. * @param time Time to hold the menu.
@ -610,6 +642,7 @@ namespace SourceMod
int clients[], int clients[],
unsigned int numClients, unsigned int numClients,
unsigned int time) =0; unsigned int time) =0;
#endif
/** /**
* @brief Returns the default draw style Core is using. * @brief Returns the default draw style Core is using.