1.1 --- a/core/DebugReporter.cpp Tue Aug 07 22:11:00 2007 +0000
1.2 +++ b/core/DebugReporter.cpp Wed Aug 08 02:27:10 2007 +0000
1.3 @@ -33,14 +33,56 @@
1.4 #include "DebugReporter.h"
1.5 #include "Logger.h"
1.6 #include "PluginSys.h"
1.7 +#include "sm_stringutil.h"
1.8
1.9 DebugReport g_DbgReporter;
1.10
1.11 +/* I'm really lazy. This should probably be exported to ISourcePawnEngine someday,
1.12 + * but we need to make sure the JIT will deal with the version bump.
1.13 + */
1.14 +extern const char *GetSourcePawnErrorMessage(int error);
1.15 +
1.16 void DebugReport::OnSourceModAllInitialized()
1.17 {
1.18 g_pSourcePawn->SetDebugListener(this);
1.19 }
1.20
1.21 +void DebugReport::GenerateError(IPluginContext *ctx, cell_t func_idx, int err, const char *message, ...)
1.22 +{
1.23 + va_list ap;
1.24 + char buffer[512];
1.25 +
1.26 + va_start(ap, message);
1.27 + UTIL_FormatArgs(buffer, sizeof(buffer), message, ap);
1.28 + va_end(ap);
1.29 +
1.30 + const char *plname = g_PluginSys.FindPluginByContext(ctx->GetContext())->GetFilename();
1.31 + const char *error = GetSourcePawnErrorMessage(err);
1.32 +
1.33 + if (error)
1.34 + {
1.35 + g_Logger.LogError("[SM] Plugin \"%s\" encountered error %d: %s", plname, err, error);
1.36 + } else {
1.37 + g_Logger.LogError("[SM] Plugin \"%s\" encountered unknown error %d", plname, err);
1.38 + }
1.39 +
1.40 + g_Logger.LogError("[SM] %s", buffer);
1.41 +
1.42 + const char *func_name = NULL;
1.43 + if (func_idx != -1)
1.44 + {
1.45 + if (func_idx & 1)
1.46 + {
1.47 + func_idx >>= 1;
1.48 + sp_public_t *function;
1.49 + if (ctx->GetPublicByIndex(func_idx, &function) == SP_ERROR_NONE)
1.50 + {
1.51 + g_Logger.LogError("[SM] Unable to call function \"%s\" due to above errors.", function->name);
1.52 + }
1.53 + }
1.54 + }
1.55 +}
1.56 +
1.57 void DebugReport::OnContextExecuteError(IPluginContext *ctx, IContextTrace *error)
1.58 {
1.59 const char *lastname;
2.1 --- a/core/DebugReporter.h Tue Aug 07 22:11:00 2007 +0000
2.2 +++ b/core/DebugReporter.h Wed Aug 08 02:27:10 2007 +0000
2.3 @@ -44,6 +44,7 @@
2.4 void OnSourceModAllInitialized();
2.5 public: // IDebugListener
2.6 void OnContextExecuteError(IPluginContext *ctx, IContextTrace *error);
2.7 + void GenerateError(IPluginContext *ctx, cell_t func_idx, int err, const char *message, ...);
2.8 private:
2.9 int _GetPluginIndex(IPluginContext *ctx);
2.10 };
3.1 --- a/core/Makefile Tue Aug 07 22:11:00 2007 +0000
3.2 +++ b/core/Makefile Wed Aug 08 02:27:10 2007 +0000
3.3 @@ -24,7 +24,7 @@
3.4 MemoryUtils.cpp PlayerManager.cpp TextParsers.cpp TimerSys.cpp Translator.cpp UserMessages.cpp \
3.5 sm_autonatives.cpp sm_memtable.cpp sm_srvcmds.cpp sm_stringutil.cpp sm_trie.cpp \
3.6 sourcemm_api.cpp sourcemod.cpp MenuStyle_Base.cpp MenuStyle_Valve.cpp MenuManager.cpp \
3.7 - MenuStyle_Radio.cpp ChatTriggers.cpp ADTFactory.cpp
3.8 + MenuStyle_Radio.cpp ChatTriggers.cpp ADTFactory.cpp MenuVoting.cpp
3.9 OBJECTS += smn_admin.cpp smn_bitbuffer.cpp smn_console.cpp smn_core.cpp \
3.10 smn_datapacks.cpp smn_entities.cpp smn_events.cpp smn_fakenatives.cpp \
3.11 smn_filesystem.cpp smn_float.cpp smn_functions.cpp smn_gameconfigs.cpp smn_halflife.cpp smn_handles.cpp smn_keyvalues.cpp \
4.1 --- a/core/MenuManager.cpp Tue Aug 07 22:11:00 2007 +0000
4.2 +++ b/core/MenuManager.cpp Wed Aug 08 02:27:10 2007 +0000
4.3 @@ -34,6 +34,7 @@
4.4 #include <stdarg.h>
4.5 #include <stdlib.h>
4.6 #include "MenuManager.h"
4.7 +#include "MenuVoting.h"
4.8 #include "sm_stringutil.h"
4.9 #include "sourcemm_api.h"
4.10 #include "PlayerManager.h"
4.11 @@ -43,220 +44,10 @@
4.12 #include "sourcemm_api.h"
4.13
4.14 MenuManager g_Menus;
4.15 +VoteMenuHandler s_VoteHandler;
4.16
4.17 ConVar sm_menu_sounds("sm_menu_sounds", "1", 0, "Sets whether SourceMod menus play trigger sounds");
4.18
4.19 -/*******************************
4.20 - *******************************
4.21 - ******** VOTE HANDLER *********
4.22 - *******************************
4.23 - *******************************/
4.24 -
4.25 -unsigned int VoteMenuHandler::GetMenuAPIVersion2()
4.26 -{
4.27 - return m_pHandler->GetMenuAPIVersion2();
4.28 -}
4.29 -
4.30 -bool VoteMenuHandler::IsVoteInProgress()
4.31 -{
4.32 - return (m_pCurMenu != NULL);
4.33 -}
4.34 -
4.35 -void VoteMenuHandler::InitializeVoting(IBaseMenu *menu)
4.36 -{
4.37 - m_Items = menu->GetItemCount();
4.38 -
4.39 - if (m_Votes.size() < (size_t)m_Items)
4.40 - {
4.41 - /* Only clear the items we need to... */
4.42 - size_t size = m_Votes.size();
4.43 - for (size_t i=0; i<size; i++)
4.44 - {
4.45 - m_Votes[i] = 0;
4.46 - }
4.47 - m_Votes.resize(m_Items, 0);
4.48 - } else {
4.49 - for (unsigned int i=0; i<m_Items; i++)
4.50 - {
4.51 - m_Votes[i] = 0;
4.52 - }
4.53 - }
4.54 -
4.55 - m_pCurMenu = menu;
4.56 -
4.57 - m_pHandler->OnMenuStart(m_pCurMenu);
4.58 -}
4.59 -
4.60 -void VoteMenuHandler::StartVoting()
4.61 -{
4.62 - m_bStarted = true;
4.63 -
4.64 - m_pHandler->OnMenuVoteStart(m_pCurMenu);
4.65 -
4.66 - /* By now we know how many clients were set.
4.67 - * If there are none, we should end IMMEDIATELY.
4.68 - */
4.69 - if (m_Clients == 0)
4.70 - {
4.71 - EndVoting();
4.72 - }
4.73 -}
4.74 -
4.75 -void VoteMenuHandler::DecrementPlayerCount()
4.76 -{
4.77 - assert(m_Clients > 0);
4.78 -
4.79 - m_Clients--;
4.80 -
4.81 - if (m_bStarted && m_Clients == 0)
4.82 - {
4.83 - EndVoting();
4.84 - }
4.85 -}
4.86 -
4.87 -void VoteMenuHandler::EndVoting()
4.88 -{
4.89 - if (m_bCancelled)
4.90 - {
4.91 - /* If we were cancelled, don't bother tabulating anything.
4.92 - * Reset just in case someone tries to redraw, which means
4.93 - * we need to save our states.
4.94 - */
4.95 - IBaseMenu *menu = m_pCurMenu;
4.96 - InternalReset();
4.97 - m_pHandler->OnMenuVoteCancel(menu);
4.98 - m_pHandler->OnMenuEnd(menu, MenuEnd_VotingCancelled);
4.99 - return;
4.100 - }
4.101 -
4.102 - unsigned int chosen = 0;
4.103 - unsigned int highest = 0;
4.104 - unsigned int dup_count = 0;
4.105 - unsigned int total = m_Votes.size() ? m_Votes[0] : 0;
4.106 -
4.107 - /* If we got zero votes, take a shortcut. */
4.108 - if (m_NumVotes == 0)
4.109 - {
4.110 - /* Pick a random item and then jump far, far away. */
4.111 - srand((unsigned int)(time(NULL)));
4.112 - chosen = (unsigned int)rand() % m_Items;
4.113 - goto picked_item;
4.114 - }
4.115 -
4.116 - /* We can't have more dups than this!
4.117 - * This is the max number of players.
4.118 - */
4.119 - unsigned int dup_array[256];
4.120 -
4.121 - for (size_t i=1; i<m_Items; i++)
4.122 - {
4.123 - if (m_Votes[i] > m_Votes[highest])
4.124 - {
4.125 - /* If we have a new highest count, mark it and trash the duplicate
4.126 - * list by setting the total to 0.
4.127 - */
4.128 - highest = i;
4.129 - dup_count = 0;
4.130 - } else if (m_Votes[i] == m_Votes[highest]) {
4.131 - /* If they're equal, mark it in the duplicate list.
4.132 - * We'll add in the original later.
4.133 - */
4.134 - dup_array[dup_count++] = i;
4.135 - }
4.136 - total += m_Votes[i];
4.137 - }
4.138 -
4.139 - /* Check if we need to pick from the duplicate list */
4.140 - if (dup_count)
4.141 - {
4.142 - /* Re-add the original to the list because it's not in there. */
4.143 - dup_array[dup_count++] = highest;
4.144 -
4.145 - /* Pick a random slot. */
4.146 - srand((unsigned int)(time(NULL)));
4.147 - unsigned int r = (unsigned int)rand() % dup_count;
4.148 -
4.149 - /* Pick the item. */
4.150 - chosen = dup_array[r];
4.151 - } else {
4.152 - chosen = highest;
4.153 - }
4.154 -
4.155 -picked_item:
4.156 - m_pHandler->OnMenuVoteEnd(m_pCurMenu, chosen, m_Votes[highest], total);
4.157 - m_pHandler->OnMenuEnd(m_pCurMenu, MenuEnd_VotingDone);
4.158 - InternalReset();
4.159 -}
4.160 -
4.161 -void VoteMenuHandler::OnMenuStart(IBaseMenu *menu)
4.162 -{
4.163 - m_Clients++;
4.164 -}
4.165 -
4.166 -void VoteMenuHandler::OnMenuEnd(IBaseMenu *menu, MenuEndReason reason)
4.167 -{
4.168 - DecrementPlayerCount();
4.169 -}
4.170 -
4.171 -void VoteMenuHandler::OnMenuCancel(IBaseMenu *menu, int client, MenuCancelReason reason)
4.172 -{
4.173 - m_pHandler->OnMenuCancel(menu, client, reason);
4.174 -}
4.175 -
4.176 -void VoteMenuHandler::OnMenuDisplay(IBaseMenu *menu, int client, IMenuPanel *display)
4.177 -{
4.178 - m_pHandler->OnMenuDisplay(menu, client, display);
4.179 -}
4.180 -
4.181 -unsigned int VoteMenuHandler::OnMenuDisplayItem(IBaseMenu *menu, int client, IMenuPanel *panel, unsigned int item, const ItemDrawInfo &dr)
4.182 -{
4.183 - return m_pHandler->OnMenuDisplayItem(menu, client, panel, item, dr);
4.184 -}
4.185 -
4.186 -void VoteMenuHandler::OnMenuDrawItem(IBaseMenu *menu, int client, unsigned int item, unsigned int &style)
4.187 -{
4.188 - m_pHandler->OnMenuDrawItem(menu, client, item, style);
4.189 -}
4.190 -
4.191 -void VoteMenuHandler::OnMenuSelect(IBaseMenu *menu, int client, unsigned int item)
4.192 -{
4.193 - /* Check by our item count, NOT the vote array size */
4.194 - if (item < m_Items)
4.195 - {
4.196 - m_Votes[item]++;
4.197 - m_NumVotes++;
4.198 - }
4.199 -
4.200 - m_pHandler->OnMenuSelect(menu, client, item);
4.201 -}
4.202 -
4.203 -void VoteMenuHandler::Reset(IMenuHandler *mh)
4.204 -{
4.205 - m_pHandler = mh;
4.206 - InternalReset();
4.207 -}
4.208 -
4.209 -void VoteMenuHandler::InternalReset()
4.210 -{
4.211 - m_Clients = 0;
4.212 - m_Items = 0;
4.213 - m_bStarted = false;
4.214 - m_pCurMenu = NULL;
4.215 - m_NumVotes = 0;
4.216 - m_bCancelled = false;
4.217 -}
4.218 -
4.219 -void VoteMenuHandler::CancelVoting()
4.220 -{
4.221 - m_bCancelled = true;
4.222 -}
4.223 -
4.224 -/*******************************
4.225 - *******************************
4.226 - ******** MENU MANAGER *********
4.227 - *******************************
4.228 - *******************************/
4.229 -
4.230 MenuManager::MenuManager()
4.231 {
4.232 m_Styles.push_back(&g_ValveMenuStyle);
4.233 @@ -283,12 +74,6 @@
4.234 {
4.235 g_HandleSys.RemoveType(m_MenuType, g_pCoreIdent);
4.236 g_HandleSys.RemoveType(m_StyleType, g_pCoreIdent);
4.237 -
4.238 - while (!m_VoteHandlers.empty())
4.239 - {
4.240 - delete m_VoteHandlers.front();
4.241 - m_VoteHandlers.pop();
4.242 - }
4.243 }
4.244
4.245 void MenuManager::OnHandleDestroy(HandleType_t type, void *object)
4.246 @@ -649,8 +434,8 @@
4.247 if (pgn != MENU_NO_PAGINATION)
4.248 {
4.249 bool canDrawDisabled = display->CanDrawItem(ITEMDRAW_DISABLED|ITEMDRAW_CONTROL);
4.250 - bool exitButton = menu->GetExitButton();
4.251 - bool exitBackButton = menu->GetExitBackButton();
4.252 + bool exitButton = (menu->GetMenuOptionFlags() & MENUFLAG_BUTTON_EXIT) == MENUFLAG_BUTTON_EXIT;
4.253 + bool exitBackButton = (menu->GetMenuOptionFlags() & MENUFLAG_BUTTON_EXITBACK) == MENUFLAG_BUTTON_EXITBACK;
4.254 char text[50];
4.255
4.256 /* Calculate how many items we are allowed for control stuff */
4.257 @@ -798,33 +583,6 @@
4.258 return m_pDefaultStyle;
4.259 }
4.260
4.261 -IVoteMenuHandler *MenuManager::CreateVoteWrapper(IMenuHandler *mh)
4.262 -{
4.263 - VoteMenuHandler *vh = NULL;
4.264 -
4.265 - if (m_VoteHandlers.empty())
4.266 - {
4.267 - vh = new VoteMenuHandler;
4.268 - } else {
4.269 - vh = m_VoteHandlers.front();
4.270 - m_VoteHandlers.pop();
4.271 - }
4.272 -
4.273 - vh->Reset(mh);
4.274 -
4.275 - return vh;
4.276 -}
4.277 -
4.278 -void MenuManager::ReleaseVoteWrapper(IVoteMenuHandler *mh)
4.279 -{
4.280 - if (mh == NULL)
4.281 - {
4.282 - return;
4.283 - }
4.284 -
4.285 - m_VoteHandlers.push((VoteMenuHandler *)mh);
4.286 -}
4.287 -
4.288 bool MenuManager::MenuSoundsEnabled()
4.289 {
4.290 return (sm_menu_sounds.GetInt() != 0);
4.291 @@ -879,7 +637,7 @@
4.292 {
4.293 if (m_ExitSound.size() > 0)
4.294 {
4.295 - sound= m_ExitSound.c_str();
4.296 + sound = m_ExitSound.c_str();
4.297 }
4.298 break;
4.299 }
4.300 @@ -907,3 +665,30 @@
4.301 enginesound->PrecacheSound(m_ExitSound.c_str(), true);
4.302 }
4.303 }
4.304 +
4.305 +void MenuManager::CancelMenu(IBaseMenu *menu)
4.306 +{
4.307 + if (s_VoteHandler.GetCurrentMenu() == menu
4.308 + && !s_VoteHandler.IsCancelling())
4.309 + {
4.310 + s_VoteHandler.CancelVoting();
4.311 + return;
4.312 + }
4.313 +
4.314 + menu->Cancel();
4.315 +}
4.316 +
4.317 +bool MenuManager::StartVote(IBaseMenu *menu, unsigned int num_clients, int clients[], unsigned int max_time, unsigned int flags)
4.318 +{
4.319 + return s_VoteHandler.StartVote(menu, num_clients, clients, max_time, flags);
4.320 +}
4.321 +
4.322 +bool MenuManager::IsVoteInProgress()
4.323 +{
4.324 + return s_VoteHandler.IsVoteInProgress();
4.325 +}
4.326 +
4.327 +void MenuManager::CancelVoting()
4.328 +{
4.329 + s_VoteHandler.CancelVoting();
4.330 +}
5.1 --- a/core/MenuManager.h Tue Aug 07 22:11:00 2007 +0000
5.2 +++ b/core/MenuManager.h Wed Aug 08 02:27:10 2007 +0000
5.3 @@ -44,39 +44,6 @@
5.4 using namespace SourceMod;
5.5 using namespace SourceHook;
5.6
5.7 -class VoteMenuHandler : public IVoteMenuHandler
5.8 -{
5.9 -public: //IMenuHandler
5.10 - unsigned int GetMenuAPIVersion2();
5.11 - void OnMenuStart(IBaseMenu *menu);
5.12 - void OnMenuDisplay(IBaseMenu *menu, int client, IMenuPanel *display);
5.13 - void OnMenuSelect(IBaseMenu *menu, int client, unsigned int item);
5.14 - void OnMenuCancel(IBaseMenu *menu, int client, MenuCancelReason reason);
5.15 - void OnMenuEnd(IBaseMenu *menu, MenuEndReason reason);
5.16 - void OnMenuDrawItem(IBaseMenu *menu, int client, unsigned int item, unsigned int &style);
5.17 - unsigned int OnMenuDisplayItem(IBaseMenu *menu, int client, IMenuPanel *panel, unsigned int item, const ItemDrawInfo &dr);
5.18 -public: //IVoteMenuHandler
5.19 - bool IsVoteInProgress();
5.20 - void InitializeVoting(IBaseMenu *menu);
5.21 - void StartVoting();
5.22 - void CancelVoting();
5.23 -public:
5.24 - void Reset(IMenuHandler *mh);
5.25 -private:
5.26 - void DecrementPlayerCount();
5.27 - void EndVoting();
5.28 - void InternalReset();
5.29 -private:
5.30 - IMenuHandler *m_pHandler;
5.31 - unsigned int m_Clients;
5.32 - unsigned int m_Items;
5.33 - CVector<unsigned int> m_Votes;
5.34 - IBaseMenu *m_pCurMenu;
5.35 - bool m_bStarted;
5.36 - bool m_bCancelled;
5.37 - unsigned int m_NumVotes;
5.38 -};
5.39 -
5.40 class MenuManager :
5.41 public IMenuManager,
5.42 public SMGlobalClass,
5.43 @@ -114,8 +81,14 @@
5.44 void AddStyle(IMenuStyle *style);
5.45 bool SetDefaultStyle(IMenuStyle *style);
5.46 IMenuPanel *RenderMenu(int client, menu_states_t &states, ItemOrder order);
5.47 - IVoteMenuHandler *CreateVoteWrapper(IMenuHandler *mh);
5.48 - void ReleaseVoteWrapper(IVoteMenuHandler *mh);
5.49 + void CancelMenu(IBaseMenu *menu);
5.50 + bool StartVote(IBaseMenu *menu,
5.51 + unsigned int num_clients,
5.52 + int clients[],
5.53 + unsigned int max_time,
5.54 + unsigned int flags=0);
5.55 + bool IsVoteInProgress();
5.56 + void CancelVoting();
5.57 public: //IHandleTypeDispatch
5.58 void OnHandleDestroy(HandleType_t type, void *object);
5.59 public:
5.60 @@ -130,7 +103,6 @@
5.61 private:
5.62 int m_ShowMenu;
5.63 IMenuStyle *m_pDefaultStyle;
5.64 - CStack<VoteMenuHandler *> m_VoteHandlers;
5.65 CVector<IMenuStyle *> m_Styles;
5.66 HandleType_t m_StyleType;
5.67 HandleType_t m_MenuType;
6.1 --- a/core/MenuStyle_Base.cpp Tue Aug 07 22:11:00 2007 +0000
6.2 +++ b/core/MenuStyle_Base.cpp Wed Aug 08 02:27:10 2007 +0000
6.3 @@ -584,14 +584,12 @@
6.4 CBaseMenu::CBaseMenu(IMenuHandler *pHandler, IMenuStyle *pStyle, IdentityToken_t *pOwner) :
6.5 m_pStyle(pStyle), m_Strings(512), m_Pagination(7), m_bShouldDelete(false), m_bCancelling(false),
6.6 m_pOwner(pOwner ? pOwner : g_pCoreIdent), m_bDeleting(false), m_bWillFreeHandle(false),
6.7 -m_hHandle(BAD_HANDLE), m_pHandler(pHandler), m_pVoteHandler(NULL),
6.8 -m_nFlags(MENUFLAG_BUTTON_EXIT)
6.9 +m_hHandle(BAD_HANDLE), m_pHandler(pHandler), m_nFlags(MENUFLAG_BUTTON_EXIT)
6.10 {
6.11 }
6.12
6.13 CBaseMenu::~CBaseMenu()
6.14 {
6.15 - g_Menus.ReleaseVoteWrapper(m_pVoteHandler);
6.16 }
6.17
6.18 Handle_t CBaseMenu::GetHandle()
6.19 @@ -732,22 +730,6 @@
6.20 return m_Title.c_str();
6.21 }
6.22
6.23 -bool CBaseMenu::GetExitButton()
6.24 -{
6.25 - return ((m_nFlags & MENUFLAG_BUTTON_EXIT) == MENUFLAG_BUTTON_EXIT);
6.26 -}
6.27 -
6.28 -bool CBaseMenu::SetExitButton(bool set)
6.29 -{
6.30 - if (set)
6.31 - {
6.32 - m_nFlags |= MENUFLAG_BUTTON_EXIT;
6.33 - } else {
6.34 - m_nFlags &= ~MENUFLAG_BUTTON_EXIT;
6.35 - }
6.36 - return true;
6.37 -}
6.38 -
6.39 void CBaseMenu::Cancel()
6.40 {
6.41 if (m_bCancelling)
6.42 @@ -756,18 +738,11 @@
6.43 }
6.44
6.45 #if defined MENU_DEBUG
6.46 - g_Logger.LogMessage("[SM_MENU] CBaseMenu::Cancel(%p) (m_pVote %p) (inVote %d) (m_bShouldDelete %d)",
6.47 + g_Logger.LogMessage("[SM_MENU] CBaseMenu::Cancel(%p) (m_bShouldDelete %d)",
6.48 this,
6.49 - m_pVoteHandler,
6.50 - m_pVoteHandler ? m_pVoteHandler->IsVoteInProgress() : false,
6.51 m_bShouldDelete);
6.52 #endif
6.53
6.54 - if (m_pVoteHandler && m_pVoteHandler->IsVoteInProgress())
6.55 - {
6.56 - m_pVoteHandler->CancelVoting();
6.57 - }
6.58 -
6.59 m_bCancelling = true;
6.60 Cancel_Finally();
6.61 m_bCancelling = false;
6.62 @@ -827,58 +802,6 @@
6.63 delete this;
6.64 }
6.65
6.66 -bool CBaseMenu::BroadcastVote(int clients[],
6.67 - unsigned int numClients,
6.68 - unsigned int maxTime,
6.69 - unsigned int flags)
6.70 -{
6.71 -#if defined MENU_DEBUG
6.72 - g_Logger.LogMessage("[SM_MENU] CBaseMenu::BroadcastVote(%p) (maxTime %d) (numClients %d) (m_pVote %p) (inVote %d)",
6.73 - this,
6.74 - maxTime,
6.75 - numClients,
6.76 - m_pVoteHandler,
6.77 - m_pVoteHandler ? m_pVoteHandler->IsVoteInProgress() : false);
6.78 -#endif
6.79 - if (!m_pVoteHandler)
6.80 - {
6.81 - m_pVoteHandler = g_Menus.CreateVoteWrapper(m_pHandler);
6.82 - } else if (m_pVoteHandler->IsVoteInProgress()) {
6.83 - return false;
6.84 - }
6.85 -
6.86 - m_pVoteHandler->InitializeVoting(this);
6.87 -
6.88 - for (unsigned int i=0; i<numClients; i++)
6.89 - {
6.90 - VoteDisplay(clients[i], maxTime);
6.91 - }
6.92 -
6.93 - m_pVoteHandler->StartVoting();
6.94 -
6.95 - return true;
6.96 -}
6.97 -
6.98 -bool CBaseMenu::IsVoteInProgress()
6.99 -{
6.100 - return (m_pVoteHandler && m_pVoteHandler->IsVoteInProgress());
6.101 -}
6.102 -
6.103 -bool CBaseMenu::GetExitBackButton()
6.104 -{
6.105 - return ((m_nFlags & MENUFLAG_BUTTON_EXITBACK) == MENUFLAG_BUTTON_EXITBACK);
6.106 -}
6.107 -
6.108 -void CBaseMenu::SetExitBackButton(bool set)
6.109 -{
6.110 - if (set)
6.111 - {
6.112 - m_nFlags |= MENUFLAG_BUTTON_EXITBACK;
6.113 - } else {
6.114 - m_nFlags &= ~MENUFLAG_BUTTON_EXITBACK;
6.115 - }
6.116 -}
6.117 -
6.118 unsigned int CBaseMenu::GetMenuOptionFlags()
6.119 {
6.120 return m_nFlags;
6.121 @@ -888,3 +811,8 @@
6.122 {
6.123 m_nFlags = flags;
6.124 }
6.125 +
6.126 +IMenuHandler *CBaseMenu::GetHandler()
6.127 +{
6.128 + return m_pHandler;
6.129 +}
7.1 --- a/core/MenuStyle_Base.h Tue Aug 07 22:11:00 2007 +0000
7.2 +++ b/core/MenuStyle_Base.h Wed Aug 08 02:27:10 2007 +0000
7.3 @@ -125,23 +125,13 @@
7.4 virtual IMenuStyle *GetDrawStyle();
7.5 virtual void SetDefaultTitle(const char *message);
7.6 virtual const char *GetDefaultTitle();
7.7 - virtual bool GetExitButton();
7.8 - virtual bool SetExitButton(bool set);
7.9 virtual void Cancel();
7.10 virtual void Destroy(bool releaseHandle);
7.11 virtual void Cancel_Finally() =0;
7.12 virtual Handle_t GetHandle();
7.13 - virtual bool BroadcastVote(int clients[],
7.14 - unsigned int numClients,
7.15 - unsigned int maxTime,
7.16 - unsigned int flags=0);
7.17 - virtual bool IsVoteInProgress();
7.18 - virtual bool GetExitBackButton();
7.19 - virtual void SetExitBackButton(bool set);
7.20 virtual unsigned int GetMenuOptionFlags();
7.21 virtual void SetMenuOptionFlags(unsigned int flags);
7.22 -public:
7.23 - virtual void VoteDisplay(int client, unsigned int maxTime) =0;
7.24 + virtual IMenuHandler *GetHandler();
7.25 private:
7.26 void InternalDelete();
7.27 protected:
7.28 @@ -157,7 +147,6 @@
7.29 bool m_bWillFreeHandle;
7.30 Handle_t m_hHandle;
7.31 IMenuHandler *m_pHandler;
7.32 - IVoteMenuHandler *m_pVoteHandler;
7.33 unsigned int m_nFlags;
7.34 };
7.35
8.1 --- a/core/MenuStyle_Radio.cpp Tue Aug 07 22:11:00 2007 +0000
8.2 +++ b/core/MenuStyle_Radio.cpp Wed Aug 08 02:27:10 2007 +0000
8.3 @@ -390,7 +390,7 @@
8.4 return g_RadioMenuStyle.MakeRadioDisplay(this);
8.5 }
8.6
8.7 -bool CRadioMenu::Display(int client, unsigned int time)
8.8 +bool CRadioMenu::Display(int client, unsigned int time, IMenuHandler *alt_handler)
8.9 {
8.10 #if defined MENU_DEBUG
8.11 g_Logger.LogMessage("[SM_MENU] CRadioMenu::Display(%p) (client %d) (time %d)",
8.12 @@ -403,23 +403,7 @@
8.13 return false;
8.14 }
8.15
8.16 - return g_RadioMenuStyle.DoClientMenu(client, this, m_pHandler, time);
8.17 -}
8.18 -
8.19 -void CRadioMenu::VoteDisplay(int client, unsigned int maxTime)
8.20 -{
8.21 -#if defined MENU_DEBUG
8.22 - g_Logger.LogMessage("[SM_MENU] CRadioMenu::VoteDisplay(%p) (client %d) (time %d)",
8.23 - this,
8.24 - client,
8.25 - maxTime);
8.26 -#endif
8.27 - if (m_bCancelling)
8.28 - {
8.29 - return;
8.30 - }
8.31 -
8.32 - g_RadioMenuStyle.DoClientMenu(client, this, m_pVoteHandler, maxTime);
8.33 + return g_RadioMenuStyle.DoClientMenu(client, this, alt_handler ? alt_handler : m_pHandler, time);
8.34 }
8.35
8.36 void CRadioMenu::Cancel_Finally()
9.1 --- a/core/MenuStyle_Radio.h Tue Aug 07 22:11:00 2007 +0000
9.2 +++ b/core/MenuStyle_Radio.h Wed Aug 08 02:27:10 2007 +0000
9.3 @@ -114,9 +114,8 @@
9.4 public:
9.5 bool SetExtOption(MenuOption option, const void *valuePtr);
9.6 IMenuPanel *CreatePanel();
9.7 - bool Display(int client, unsigned int time);
9.8 + bool Display(int client, unsigned int time, IMenuHandler *alt_handler=NULL);
9.9 void Cancel_Finally();
9.10 - void VoteDisplay(int client, unsigned int maxTime);
9.11 };
9.12
9.13 extern CRadioStyle g_RadioMenuStyle;
10.1 --- a/core/MenuStyle_Valve.cpp Tue Aug 07 22:11:00 2007 +0000
10.2 +++ b/core/MenuStyle_Valve.cpp Wed Aug 08 02:27:10 2007 +0000
10.3 @@ -383,24 +383,14 @@
10.4 return false;
10.5 }
10.6
10.7 -bool CValveMenu::Display(int client, unsigned int time)
10.8 +bool CValveMenu::Display(int client, unsigned int time, IMenuHandler *alt_handler)
10.9 {
10.10 if (m_bCancelling)
10.11 {
10.12 return false;
10.13 }
10.14
10.15 - return g_ValveMenuStyle.DoClientMenu(client, this, m_pHandler, time);
10.16 -}
10.17 -
10.18 -void CValveMenu::VoteDisplay(int client, unsigned int maxTime)
10.19 -{
10.20 - if (m_bCancelling)
10.21 - {
10.22 - return;
10.23 - }
10.24 -
10.25 - g_ValveMenuStyle.DoClientMenu(client, this, m_pVoteHandler, maxTime);
10.26 + return g_ValveMenuStyle.DoClientMenu(client, this, alt_handler ? alt_handler : m_pHandler, time);
10.27 }
10.28
10.29 IMenuPanel *CValveMenu::CreatePanel()
10.30 @@ -408,19 +398,9 @@
10.31 return new CValveMenuDisplay(this);
10.32 }
10.33
10.34 -bool CValveMenu::GetExitButton()
10.35 -{
10.36 - return true;
10.37 -}
10.38 -
10.39 -bool CValveMenu::SetExitButton(bool set)
10.40 -{
10.41 - return false;
10.42 -}
10.43 -
10.44 void CValveMenu::SetMenuOptionFlags(unsigned int flags)
10.45 {
10.46 - flags &= ~MENUFLAG_BUTTON_EXIT;
10.47 + flags |= MENUFLAG_BUTTON_EXIT;
10.48 CBaseMenu::SetMenuOptionFlags(flags);
10.49 }
10.50
11.1 --- a/core/MenuStyle_Valve.h Tue Aug 07 22:11:00 2007 +0000
11.2 +++ b/core/MenuStyle_Valve.h Wed Aug 08 02:27:10 2007 +0000
11.3 @@ -120,8 +120,7 @@
11.4 bool GetExitButton();
11.5 bool SetExitButton(bool set);
11.6 bool SetPagination(unsigned int itemsPerPage);
11.7 - bool Display(int client, unsigned int time);
11.8 - void VoteDisplay(int client, unsigned int maxTime);
11.9 + bool Display(int client, unsigned int time, IMenuHandler *alt_handler=NULL);
11.10 void SetMenuOptionFlags(unsigned int flags);
11.11 public: //CBaseMenu
11.12 void Cancel_Finally();
12.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
12.2 +++ b/core/MenuVoting.cpp Wed Aug 08 02:27:10 2007 +0000
12.3 @@ -0,0 +1,326 @@
12.4 +/**
12.5 + * vim: set ts=4 :
12.6 + * ================================================================
12.7 + * SourceMod
12.8 + * Copyright (C) 2004-2007 AlliedModders LLC. All rights reserved.
12.9 + * ================================================================
12.10 + *
12.11 + * This program is free software; you can redistribute it and/or
12.12 + * modify it under the terms of the GNU General Public License,
12.13 + * version 3.0, as published by the Free Software Foundation.
12.14 + *
12.15 + * This program is distributed in the hope that it will be useful,
12.16 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
12.17 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12.18 + * GNU General Public License for more details.
12.19 + *
12.20 + * You should have received a copy of the GNU General Public License
12.21 + * along with this program. If not, see <http://www.gnu.org/licenses/>.
12.22 + *
12.23 + * As a special exception, AlliedModders LLC gives you permission to
12.24 + * link the code of this program (as well as its derivative works) to
12.25 + * "Half-Life 2," the "Source Engine," the "SourcePawn JIT," and any
12.26 + * Game MODs that run on software by the Valve Corporation. You must
12.27 + * obey the GNU General Public License in all respects for all other
12.28 + * code used. Additionally, AlliedModders LLC grants this exception
12.29 + * to all derivative works. AlliedModders LLC defines further
12.30 + * exceptions, found in LICENSE.txt (as of this writing, version
12.31 + * JULY-31-2007), or <http://www.sourcemod.net/license.php>.
12.32 + *
12.33 + * Version: $Id$
12.34 + */
12.35 +
12.36 +#include <string.h>
12.37 +#include <stdlib.h>
12.38 +#include "MenuVoting.h"
12.39 +#include "PlayerManager.h"
12.40 +#include "sourcemm_api.h"
12.41 +
12.42 +void VoteMenuHandler::OnSourceModAllInitialized()
12.43 +{
12.44 + g_Players.AddClientListener(this);
12.45 +}
12.46 +
12.47 +void VoteMenuHandler::OnSourceModShutdown()
12.48 +{
12.49 + g_Players.RemoveClientListener(this);
12.50 +}
12.51 +
12.52 +unsigned int VoteMenuHandler::GetMenuAPIVersion2()
12.53 +{
12.54 + return m_pHandler->GetMenuAPIVersion2();
12.55 +}
12.56 +
12.57 +void VoteMenuHandler::OnClientDisconnected(int client)
12.58 +{
12.59 + if (!IsVoteInProgress())
12.60 + {
12.61 + return;
12.62 + }
12.63 +
12.64 + /* Wipe out their vote if they had one */
12.65 + int item;
12.66 + if ((item = m_ClientVotes[client]) >= 0)
12.67 + {
12.68 + assert((unsigned)item < m_Items);
12.69 + assert(m_Votes[item] > 0);
12.70 + m_Votes[item]--;
12.71 + m_ClientVotes[client] = -1;
12.72 + }
12.73 +}
12.74 +
12.75 +bool VoteMenuHandler::IsVoteInProgress()
12.76 +{
12.77 + return (m_pCurMenu != NULL);
12.78 +}
12.79 +
12.80 +bool VoteMenuHandler::StartVote(IBaseMenu *menu, unsigned int num_clients, int clients[], unsigned int max_time, unsigned int flags/* =0 */)
12.81 +{
12.82 + if (!InitializeVoting(menu, menu->GetHandler(), max_time, flags))
12.83 + {
12.84 + return false;
12.85 + }
12.86 +
12.87 + for (unsigned int i=0; i<num_clients; i++)
12.88 + {
12.89 + menu->Display(clients[i], max_time, this);
12.90 + }
12.91 +
12.92 + StartVoting();
12.93 +
12.94 + return true;
12.95 +}
12.96 +
12.97 +bool VoteMenuHandler::InitializeVoting(IBaseMenu *menu,
12.98 + IMenuHandler *handler,
12.99 + unsigned int time,
12.100 + unsigned int flags)
12.101 +{
12.102 + if (IsVoteInProgress())
12.103 + {
12.104 + return false;
12.105 + }
12.106 +
12.107 + InternalReset();
12.108 +
12.109 + /* Mark all clients as not voting */
12.110 + for (int i=1; i<=gpGlobals->maxClients; i++)
12.111 + {
12.112 + m_ClientVotes[i] = -2;
12.113 + }
12.114 +
12.115 + m_Items = menu->GetItemCount();
12.116 +
12.117 + if (m_Votes.size() < (size_t)m_Items)
12.118 + {
12.119 + /* Only clear the items we need to... */
12.120 + size_t size = m_Votes.size();
12.121 + for (size_t i=0; i<size; i++)
12.122 + {
12.123 + m_Votes[i] = 0;
12.124 + }
12.125 + m_Votes.resize(m_Items, 0);
12.126 + } else {
12.127 + for (unsigned int i=0; i<m_Items; i++)
12.128 + {
12.129 + m_Votes[i] = 0;
12.130 + }
12.131 + }
12.132 +
12.133 + m_pCurMenu = menu;
12.134 + m_VoteTime = time;
12.135 + m_VoteFlags = flags;
12.136 + m_pHandler = handler;
12.137 +
12.138 + m_pHandler->OnMenuStart(m_pCurMenu);
12.139 +
12.140 + return true;
12.141 +}
12.142 +
12.143 +void VoteMenuHandler::StartVoting()
12.144 +{
12.145 + if (!m_pCurMenu)
12.146 + {
12.147 + return;
12.148 + }
12.149 +
12.150 + m_bStarted = true;
12.151 +
12.152 + m_pHandler->OnMenuVoteStart(m_pCurMenu);
12.153 +
12.154 + /* By now we know how many clients were set.
12.155 + * If there are none, we should end IMMEDIATELY.
12.156 + */
12.157 + if (m_Clients == 0)
12.158 + {
12.159 + EndVoting();
12.160 + }
12.161 +}
12.162 +
12.163 +void VoteMenuHandler::DecrementPlayerCount()
12.164 +{
12.165 + assert(m_Clients > 0);
12.166 +
12.167 + m_Clients--;
12.168 +
12.169 + if (m_bStarted && m_Clients == 0)
12.170 + {
12.171 + EndVoting();
12.172 + }
12.173 +}
12.174 +
12.175 +int SortVoteItems(const void *item1, const void *item2)
12.176 +{
12.177 + return ((menu_vote_result_t::menu_item_vote_t *)item2)->count
12.178 + - ((menu_vote_result_t::menu_item_vote_t *)item1)->count;
12.179 +}
12.180 +
12.181 +void VoteMenuHandler::EndVoting()
12.182 +{
12.183 + if (m_bCancelled)
12.184 + {
12.185 + /* If we were cancelled, don't bother tabulating anything.
12.186 + * Reset just in case someone tries to redraw, which means
12.187 + * we need to save our states.
12.188 + */
12.189 + IBaseMenu *menu = m_pCurMenu;
12.190 + IMenuHandler *handler = m_pHandler;
12.191 + InternalReset();
12.192 + handler->OnMenuVoteCancel(menu);
12.193 + handler->OnMenuEnd(menu, MenuEnd_VotingCancelled);
12.194 + return;
12.195 + }
12.196 +
12.197 + menu_vote_result_t vote;
12.198 + menu_vote_result_t::menu_client_vote_t client_vote[256];
12.199 + menu_vote_result_t::menu_item_vote_t item_vote[256];
12.200 +
12.201 + memset(&vote, 0, sizeof(vote));
12.202 +
12.203 + /* Build the item list */
12.204 + for (unsigned int i=0; i<m_Items; i++)
12.205 + {
12.206 + if (m_Votes[i] > 0)
12.207 + {
12.208 + item_vote[vote.num_items].count = m_Votes[i];
12.209 + item_vote[vote.num_items].item = i;
12.210 + vote.num_votes += m_Votes[i];
12.211 + vote.num_items++;
12.212 + }
12.213 + }
12.214 + vote.item_list = item_vote;
12.215 +
12.216 + if (!vote.num_votes)
12.217 + {
12.218 + IBaseMenu *menu = m_pCurMenu;
12.219 + IMenuHandler *handler = m_pHandler;
12.220 + InternalReset();
12.221 + handler->OnMenuVoteCancel(menu);
12.222 + handler->OnMenuEnd(menu, MenuEnd_NoVotes);
12.223 + return;
12.224 + }
12.225 +
12.226 + /* Build the client list */
12.227 + for (int i=1; i<=gpGlobals->maxClients; i++)
12.228 + {
12.229 + if (m_ClientVotes[i] >= -1)
12.230 + {
12.231 + client_vote[vote.num_clients].client = i;
12.232 + client_vote[vote.num_clients].item = m_ClientVotes[i];
12.233 + vote.num_clients++;
12.234 + }
12.235 + }
12.236 + vote.client_list = client_vote;
12.237 +
12.238 + /* Sort the item list descending like we promised */
12.239 + qsort(item_vote,
12.240 + vote.num_items,
12.241 + sizeof(menu_vote_result_t::menu_item_vote_t),
12.242 + SortVoteItems);
12.243 +
12.244 + /* Save states, then clear what we've saved.
12.245 + * This makes us re-entrant, which is always the safe way to go.
12.246 + */
12.247 + IBaseMenu *menu = m_pCurMenu;
12.248 + IMenuHandler *handler = m_pHandler;
12.249 + InternalReset();
12.250 +
12.251 + /* Send vote info */
12.252 + handler->OnMenuVoteResults(menu, &vote);
12.253 + handler->OnMenuEnd(menu, MenuEnd_VotingDone);
12.254 +}
12.255 +
12.256 +void VoteMenuHandler::OnMenuStart(IBaseMenu *menu)
12.257 +{
12.258 + m_Clients++;
12.259 +}
12.260 +
12.261 +void VoteMenuHandler::OnMenuEnd(IBaseMenu *menu, MenuEndReason reason)
12.262 +{
12.263 + DecrementPlayerCount();
12.264 +}
12.265 +
12.266 +void VoteMenuHandler::OnMenuCancel(IBaseMenu *menu, int client, MenuCancelReason reason)
12.267 +{
12.268 + m_pHandler->OnMenuCancel(menu, client, reason);
12.269 +}
12.270 +
12.271 +void VoteMenuHandler::OnMenuDisplay(IBaseMenu *menu, int client, IMenuPanel *display)
12.272 +{
12.273 + m_ClientVotes[client] = -1;
12.274 + m_pHandler->OnMenuDisplay(menu, client, display);
12.275 +}
12.276 +
12.277 +unsigned int VoteMenuHandler::OnMenuDisplayItem(IBaseMenu *menu, int client, IMenuPanel *panel, unsigned int item, const ItemDrawInfo &dr)
12.278 +{
12.279 + return m_pHandler->OnMenuDisplayItem(menu, client, panel, item, dr);
12.280 +}
12.281 +
12.282 +void VoteMenuHandler::OnMenuDrawItem(IBaseMenu *menu, int client, unsigned int item, unsigned int &style)
12.283 +{
12.284 + m_pHandler->OnMenuDrawItem(menu, client, item, style);
12.285 +}
12.286 +
12.287 +void VoteMenuHandler::OnMenuSelect(IBaseMenu *menu, int client, unsigned int item)
12.288 +{
12.289 + /* Check by our item count, NOT the vote array size */
12.290 + if (item < m_Items)
12.291 + {
12.292 + m_ClientVotes[client] = item;
12.293 + m_Votes[item]++;
12.294 + m_NumVotes++;
12.295 + }
12.296 +
12.297 + m_pHandler->OnMenuSelect(menu, client, item);
12.298 +}
12.299 +
12.300 +void VoteMenuHandler::InternalReset()
12.301 +{
12.302 + m_Clients = 0;
12.303 + m_Items = 0;
12.304 + m_bStarted = false;
12.305 + m_pCurMenu = NULL;
12.306 + m_NumVotes = 0;
12.307 + m_bCancelled = false;
12.308 + m_pHandler = NULL;
12.309 +}
12.310 +
12.311 +void VoteMenuHandler::CancelVoting()
12.312 +{
12.313 + if (m_bCancelled || !m_pCurMenu)
12.314 + {
12.315 + return;
12.316 + }
12.317 + m_bCancelled = true;
12.318 + m_pCurMenu->Cancel();
12.319 +}
12.320 +
12.321 +IBaseMenu *VoteMenuHandler::GetCurrentMenu()
12.322 +{
12.323 + return m_pCurMenu;
12.324 +}
12.325 +
12.326 +bool VoteMenuHandler::IsCancelling()
12.327 +{
12.328 + return m_bCancelled;
12.329 +}
13.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
13.2 +++ b/core/MenuVoting.h Wed Aug 08 02:27:10 2007 +0000
13.3 @@ -0,0 +1,97 @@
13.4 +/**
13.5 + * vim: set ts=4 :
13.6 + * ================================================================
13.7 + * SourceMod
13.8 + * Copyright (C) 2004-2007 AlliedModders LLC. All rights reserved.
13.9 + * ================================================================
13.10 + *
13.11 + * This program is free software; you can redistribute it and/or
13.12 + * modify it under the terms of the GNU General Public License,
13.13 + * version 3.0, as published by the Free Software Foundation.
13.14 + *
13.15 + * This program is distributed in the hope that it will be useful,
13.16 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
13.17 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13.18 + * GNU General Public License for more details.
13.19 + *
13.20 + * You should have received a copy of the GNU General Public License
13.21 + * along with this program. If not, see <http://www.gnu.org/licenses/>.
13.22 + *
13.23 + * As a special exception, AlliedModders LLC gives you permission to
13.24 + * link the code of this program (as well as its derivative works) to
13.25 + * "Half-Life 2," the "Source Engine," the "SourcePawn JIT," and any
13.26 + * Game MODs that run on software by the Valve Corporation. You must
13.27 + * obey the GNU General Public License in all respects for all other
13.28 + * code used. Additionally, AlliedModders LLC grants this exception
13.29 + * to all derivative works. AlliedModders LLC defines further
13.30 + * exceptions, found in LICENSE.txt (as of this writing, version
13.31 + * JULY-31-2007), or <http://www.sourcemod.net/license.php>.
13.32 + *
13.33 + * Version: $Id$
13.34 + */
13.35 +
13.36 +#ifndef _INCLUDE_SOURCEMOD_MENUVOTING_H_
13.37 +#define _INCLUDE_SOURCEMOD_MENUVOTING_H_
13.38 +
13.39 +#include <IMenuManager.h>
13.40 +#include <IPlayerHelpers.h>
13.41 +#include <sh_vector.h>
13.42 +#include "sm_globals.h"
13.43 +
13.44 +using namespace SourceHook;
13.45 +using namespace SourceMod;
13.46 +
13.47 +class VoteMenuHandler :
13.48 + public IMenuHandler,
13.49 + public SMGlobalClass,
13.50 + public IClientListener
13.51 +{
13.52 +public: //SMGlobalClass
13.53 + void OnSourceModAllInitialized();
13.54 + void OnSourceModShutdown();
13.55 +public: //IClientListener
13.56 + void OnClientDisconnected(int client);
13.57 +public: //IMenuHandler
13.58 + unsigned int GetMenuAPIVersion2();
13.59 + void OnMenuStart(IBaseMenu *menu);
13.60 + void OnMenuDisplay(IBaseMenu *menu, int client, IMenuPanel *display);
13.61 + void OnMenuSelect(IBaseMenu *menu, int client, unsigned int item);
13.62 + void OnMenuCancel(IBaseMenu *menu, int client, MenuCancelReason reason);
13.63 + void OnMenuEnd(IBaseMenu *menu, MenuEndReason reason);
13.64 + void OnMenuDrawItem(IBaseMenu *menu, int client, unsigned int item, unsigned int &style);
13.65 + unsigned int OnMenuDisplayItem(IBaseMenu *menu, int client, IMenuPanel *panel, unsigned int item, const ItemDrawInfo &dr);
13.66 +public:
13.67 + bool StartVote(IBaseMenu *menu,
13.68 + unsigned int num_clients,
13.69 + int clients[],
13.70 + unsigned int max_time,
13.71 + unsigned int flags=0);
13.72 + bool IsVoteInProgress();
13.73 + void CancelVoting();
13.74 + IBaseMenu *GetCurrentMenu();
13.75 + bool IsCancelling();
13.76 +private:
13.77 + void Reset(IMenuHandler *mh);
13.78 + void DecrementPlayerCount();
13.79 + void EndVoting();
13.80 + void InternalReset();
13.81 + bool InitializeVoting(IBaseMenu *menu,
13.82 + IMenuHandler *handler,
13.83 + unsigned int time,
13.84 + unsigned int flags);
13.85 + void StartVoting();
13.86 +private:
13.87 + IMenuHandler *m_pHandler;
13.88 + unsigned int m_Clients;
13.89 + unsigned int m_Items;
13.90 + CVector<unsigned int> m_Votes;
13.91 + IBaseMenu *m_pCurMenu;
13.92 + bool m_bStarted;
13.93 + bool m_bCancelled;
13.94 + unsigned int m_NumVotes;
13.95 + unsigned int m_VoteTime;
13.96 + unsigned int m_VoteFlags;
13.97 + int m_ClientVotes[256+1];
13.98 +};
13.99 +
13.100 +#endif //_INCLUDE_SOURCEMOD_MENUVOTING_H_
14.1 --- a/core/msvc8/sourcemod_mm.vcproj Tue Aug 07 22:11:00 2007 +0000
14.2 +++ b/core/msvc8/sourcemod_mm.vcproj Wed Aug 08 02:27:10 2007 +0000
14.3 @@ -337,6 +337,10 @@
14.4 >
14.5 </File>
14.6 <File
14.7 + RelativePath="..\MenuVoting.cpp"
14.8 + >
14.9 + </File>
14.10 + <File
14.11 RelativePath="..\PlayerManager.cpp"
14.12 >
14.13 </File>
14.14 @@ -475,6 +479,10 @@
14.15 >
14.16 </File>
14.17 <File
14.18 + RelativePath="..\MenuVoting.h"
14.19 + >
14.20 + </File>
14.21 + <File
14.22 RelativePath="..\PlayerManager.h"
14.23 >
14.24 </File>
15.1 --- a/core/smn_menus.cpp Tue Aug 07 22:11:00 2007 +0000
15.2 +++ b/core/smn_menus.cpp Wed Aug 08 02:27:10 2007 +0000
15.3 @@ -32,6 +32,7 @@
15.4
15.5 #include "sm_globals.h"
15.6 #include <sh_stack.h>
15.7 +#include "DebugReporter.h"
15.8 #include "MenuManager.h"
15.9 #include "MenuStyle_Valve.h"
15.10 #include "MenuStyle_Radio.h"
15.11 @@ -93,13 +94,11 @@
15.12 void OnMenuEnd(IBaseMenu *menu, MenuEndReason reason);
15.13 void OnMenuDestroy(IBaseMenu *menu);
15.14 void OnMenuVoteStart(IBaseMenu *menu);
15.15 - void OnMenuVoteEnd(IBaseMenu *menu,
15.16 - unsigned int item,
15.17 - unsigned int winningVotes,
15.18 - unsigned int totalVotes);
15.19 + void OnMenuVoteResults(IBaseMenu *menu, const menu_vote_result_t *results);
15.20 void OnMenuVoteCancel(IBaseMenu *menu);
15.21 void OnMenuDrawItem(IBaseMenu *menu, int client, unsigned int item, unsigned int &style);
15.22 unsigned int OnMenuDisplayItem(IBaseMenu *menu, int client, IMenuPanel *panel, unsigned int item, const ItemDrawInfo &dr);
15.23 + bool OnSetHandlerOption(const char *option, const void *data);
15.24 #if 0
15.25 void OnMenuDrawItem(IBaseMenu *menu, int client, unsigned int item, unsigned int &style);
15.26 void OnMenuDisplayItem(IBaseMenu *menu, int client, unsigned int item, const char **display);
15.27 @@ -109,6 +108,8 @@
15.28 private:
15.29 IPluginFunction *m_pBasic;
15.30 int m_Flags;
15.31 + IPluginFunction *m_pVoteResults;
15.32 + cell_t m_fnVoteResult;
15.33 };
15.34
15.35 /**
15.36 @@ -158,7 +159,7 @@
15.37 }
15.38
15.39 /**
15.40 - * It is extremely important that unloaded plugins don't crash.
15.41 + * It is extremely important that unloaded plugins don't crash.
15.42 * Thus, if a plugin unloads, we run through every handler we have.
15.43 * This means we do almost no runtime work for keeping track of
15.44 * our panel handlers (we don't have to store a list of the running
15.45 @@ -221,6 +222,7 @@
15.46 m_FreeMenuHandlers.pop();
15.47 handler->m_pBasic = pFunction;
15.48 handler->m_Flags = flags;
15.49 + handler->m_pVoteResults = NULL;
15.50 }
15.51 return handler;
15.52 }
15.53 @@ -276,7 +278,7 @@
15.54 * MENU HANDLER WRAPPER
15.55 */
15.56 CMenuHandler::CMenuHandler(IPluginFunction *pBasic, int flags) :
15.57 - m_pBasic(pBasic), m_Flags(flags)
15.58 + m_pBasic(pBasic), m_Flags(flags), m_pVoteResults(NULL)
15.59 {
15.60 /* :TODO: We can probably cache the handle ahead of time */
15.61 }
15.62 @@ -334,14 +336,6 @@
15.63 DoAction(menu, MenuAction_VoteStart, 0, 0);
15.64 }
15.65
15.66 -void CMenuHandler::OnMenuVoteEnd(IBaseMenu *menu,
15.67 - unsigned int item,
15.68 - unsigned int winningVotes,
15.69 - unsigned int totalVotes)
15.70 -{
15.71 - DoAction(menu, MenuAction_VoteEnd, item, (totalVotes << 16) | (winningVotes & 0xFFFF));
15.72 -}
15.73 -
15.74 void CMenuHandler::OnMenuVoteCancel(IBaseMenu *menu)
15.75 {
15.76 DoAction(menu, MenuAction_VoteCancel, 0, 0);
15.77 @@ -407,6 +401,145 @@
15.78 return res;
15.79 }
15.80
15.81 +void CMenuHandler::OnMenuVoteResults(IBaseMenu *menu, const menu_vote_result_t *results)
15.82 +{
15.83 + if (!m_pVoteResults)
15.84 + {
15.85 + /* Call MenuAction_VoteEnd instead. See if there are any extra winners. */
15.86 + unsigned int num_items = 1;
15.87 + for (unsigned int i=1; i<results->num_items; i++)
15.88 + {
15.89 + if (results->item_list[i].count != results->item_list[0].count)
15.90 + {
15.91 + break;
15.92 + }
15.93 + num_items++;
15.94 + }
15.95 +
15.96 + /* See if we need to pick a random winner. */
15.97 + unsigned int winning_item;
15.98 + if (num_items > 1)
15.99 + {
15.100 + /* Yes, we do. */
15.101 + srand(time(NULL));
15.102 + winning_item = rand() % num_items;
15.103 + winning_item = results->item_list[winning_item].item;
15.104 + } else {
15.105 + /* No, take the first. */
15.106 + winning_item = results->item_list[0].item;
15.107 + }
15.108 +
15.109 + unsigned int total_votes = results->num_votes;
15.110 + unsigned int winning_votes = results->item_list[0].count;
15.111 +
15.112 + DoAction(menu, MenuAction_VoteEnd, winning_item, (total_votes << 16) | (winning_votes & 0xFFFF));
15.113 + } else {
15.114 + IPluginContext *pContext = m_pVoteResults->GetParentContext();
15.115 + bool no_call = false;
15.116 + int err;
15.117 +
15.118 + /* First array */
15.119 + cell_t client_array_address = -1;
15.120 + cell_t *client_array_base = NULL;
15.121 + cell_t client_array_size = results->num_clients + (results->num_clients * 2);
15.122 + if (client_array_size)
15.123 + {
15.124 + if ((err = pContext->HeapAlloc(client_array_size, &client_array_address, &client_array_base))
15.125 + != SP_ERROR_NONE)
15.126 + {
15.127 + g_DbgReporter.GenerateError(pContext, m_fnVoteResult, err, "Menu callback could not allocate %d bytes for client list.", client_array_size * sizeof(cell_t));
15.128 + no_call = true;
15.129 + } else {
15.130 + cell_t target_offs = sizeof(cell_t) * results->num_clients;
15.131 + cell_t *cur_index = client_array_base;
15.132 + cell_t *cur_array;
15.133 + for (unsigned int i=0; i<results->num_clients; i++)
15.134 + {
15.135 + /* Copy the array index */
15.136 + *cur_index = target_offs;
15.137 + /* Get the current array address */
15.138 + cur_array = (cell_t *)((char *)cur_index + target_offs);
15.139 + /* Store information */
15.140 + cur_array[0] = results->client_list[i].client;
15.141 + cur_array[1] = results->client_list[i].item;
15.142 + /* Adjust for the new target by subtracting one indirection
15.143 + * and adding one array.
15.144 + */
15.145 + target_offs += (sizeof(cell_t) * 2) - sizeof(cell_t);
15.146 + cur_index++;
15.147 + }
15.148 + }
15.149 + }
15.150 +
15.151 + /* Second array */
15.152 + cell_t item_array_address = -1;
15.153 + cell_t *item_array_base = NULL;
15.154 + cell_t item_array_size = results->num_items + (results->num_items * 2);
15.155 + if (item_array_size)
15.156 + {
15.157 + if ((err = pContext->HeapAlloc(item_array_size, &item_array_address, &item_array_base))
15.158 + != SP_ERROR_NONE)
15.159 + {
15.160 + g_DbgReporter.GenerateError(pContext, m_fnVoteResult, err, "Menu callback could not allocate %d bytes for item list.", item_array_size);
15.161 + no_call = true;
15.162 + } else {
15.163 + cell_t target_offs = sizeof(cell_t) * results->num_items;
15.164 + cell_t *cur_index = item_array_base;
15.165 + cell_t *cur_array;
15.166 + for (unsigned int i=0; i<results->num_items; i++)
15.167 + {
15.168 + /* Copy the array index */
15.169 + *cur_index = target_offs;
15.170 + /* Get the current array address */
15.171 + cur_array = (cell_t *)((char *)cur_index + target_offs);
15.172 + /* Store information */
15.173 + cur_array[0] = results->item_list[i].item;
15.174 + cur_array[1] = results->item_list[i].count;
15.175 + /* Adjust for the new target by subtracting one indirection
15.176 + * and adding one array.
15.177 + */
15.178 + target_offs += (sizeof(cell_t) * 2) - sizeof(cell_t);
15.179 + cur_index++;
15.180 + }
15.181 + }
15.182 + }
15.183 +
15.184 + /* Finally, push everything */
15.185 + if (!no_call)
15.186 + {
15.187 + m_pVoteResults->PushCell(results->num_votes);
15.188 + m_pVoteResults->PushCell(results->num_clients);
15.189 + m_pVoteResults->PushCell(client_array_address);
15.190 + m_pVoteResults->PushCell(results->num_items);
15.191 + m_pVoteResults->PushCell(item_array_address);
15.192 + m_pVoteResults->Execute(NULL);
15.193 + }
15.194 +
15.195 + /* Free what we allocated, in reverse order as required */
15.196 + if (item_array_address != -1)
15.197 + {
15.198 + pContext->HeapPop(item_array_address);
15.199 + }
15.200 + if (client_array_address != -1)
15.201 + {
15.202 + pContext->HeapPop(client_array_address);
15.203 + }
15.204 + }
15.205 +}
15.206 +
15.207 +bool CMenuHandler::OnSetHandlerOption(const char *option, const void *data)
15.208 +{
15.209 + if (strcmp(option, "set_vote_results_handler") == 0)
15.210 + {
15.211 + void **array = (void **)data;
15.212 + m_pVoteResults = (IPluginFunction *)array[0];
15.213 + m_fnVoteResult = *(cell_t *)((cell_t *)array[1]);
15.214 + return true;
15.215 + }
15.216 +
15.217 + return false;
15.218 +}
15.219 +
15.220 /**
15.221 * INLINE FUNCTIONS FOR NATIVES
15.222 */
15.223 @@ -488,6 +621,11 @@
15.224
15.225 static cell_t VoteMenu(IPluginContext *pContext, const cell_t *params)
15.226 {
15.227 + if (g_Menus.IsVoteInProgress())
15.228 + {
15.229 + return pContext->ThrowNativeError("A vote is already in progress");
15.230 + }
15.231 +
15.232 Handle_t hndl = (Handle_t)params[1];
15.233 HandleError err;
15.234 IBaseMenu *menu;
15.235 @@ -500,7 +638,12 @@
15.236 cell_t *addr;
15.237 pContext->LocalToPhysAddr(params[2], &addr);
15.238
15.239 - return menu->BroadcastVote(addr, params[3], params[4]) ? 1 : 0;
15.240 + if (!g_Menus.StartVote(menu, params[3], addr, params[4]))
15.241 + {
15.242 + return 0;
15.243 + }
15.244 +
15.245 + return 1;
15.246 }
15.247
15.248 static cell_t AddMenuItem(IPluginContext *pContext, const cell_t *params)
15.249 @@ -699,7 +842,7 @@
15.250 return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
15.251 }
15.252
15.253 - return menu->GetExitButton() ? 1 : 0;
15.254 + return ((menu->GetMenuOptionFlags() & MENUFLAG_BUTTON_EXIT) == MENUFLAG_BUTTON_EXIT) ? 1 : 0;
15.255 }
15.256
15.257 static cell_t GetMenuExitBackButton(IPluginContext *pContext, const cell_t *params)
15.258 @@ -713,7 +856,7 @@
15.259 return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
15.260 }
15.261
15.262 - return menu->GetExitBackButton() ? 1 : 0;
15.263 + return ((menu->GetMenuOptionFlags() & MENUFLAG_BUTTON_EXITBACK) == MENUFLAG_BUTTON_EXIT) ? 1 : 0;
15.264 }
15.265
15.266 static cell_t SetMenuExitButton(IPluginContext *pContext, const cell_t *params)
15.267 @@ -727,7 +870,11 @@
15.268 return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
15.269 }
15.270
15.271 - return menu->SetExitButton(params[2] ? true : false) ? 1 : 0;
15.272 + unsigned int flags = menu->GetMenuOptionFlags();
15.273 + flags |= MENUFLAG_BUTTON_EXIT;
15.274 + menu->SetMenuOptionFlags(flags);
15.275 + flags = menu->GetMenuOptionFlags();
15.276 + return ((flags & MENUFLAG_BUTTON_EXIT) == MENUFLAG_BUTTON_EXIT) ? 1 : 0;
15.277 }
15.278
15.279 static cell_t SetMenuExitBackButton(IPluginContext *pContext, const cell_t *params)
15.280 @@ -741,7 +888,9 @@
15.281 return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
15.282 }
15.283
15.284 - menu->SetExitBackButton(params[2] ? true : false);
15.285 + unsigned int flags = menu->GetMenuOptionFlags();
15.286 + flags |= MENUFLAG_BUTTON_EXITBACK;
15.287 + menu->SetMenuOptionFlags(flags);
15.288
15.289 return 1;
15.290 }
15.291 @@ -757,23 +906,14 @@
15.292 return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
15.293 }
15.294
15.295 - menu->Cancel();
15.296 + g_Menus.CancelMenu(menu);
15.297
15.298 return 1;
15.299 }
15.300
15.301 static cell_t IsVoteInProgress(IPluginContext *pContext, const cell_t *params)
15.302 {
15.303 - Handle_t hndl = (Handle_t)params[1];
15.304 - HandleError err;
15.305 - IBaseMenu *menu;
15.306 -
15.307 - if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
15.308 - {
15.309 - return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
15.310 - }
15.311 -
15.312 - return menu->IsVoteInProgress() ? 1 : 0;
15.313 + return g_Menus.IsVoteInProgress() ? 1 : 0;
15.314 }
15.315
15.316 static cell_t GetMenuStyle(IPluginContext *pContext, const cell_t *params)
15.317 @@ -1122,12 +1262,55 @@
15.318 return 1;
15.319 }
15.320
15.321 +static cell_t CancelVote(IPluginContext *pContxt, const cell_t *params)
15.322 +{
15.323 + if (!g_Menus.IsVoteInProgress())
15.324 + {
15.325 + return pContxt->ThrowNativeError("No vote is in progress");
15.326 + }
15.327 +
15.328 + g_Menus.CancelVoting();
15.329 +
15.330 + return 1;
15.331 +}
15.332 +
15.333 +static cell_t SetVoteResultCallback(IPluginContext *pContext, const cell_t *params)
15.334 +{
15.335 + Handle_t hndl = (Handle_t)params[1];
15.336 + HandleError err;
15.337 + IBaseMenu *menu;
15.338 +
15.339 + if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
15.340 + {
15.341 + return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
15.342 + }
15.343 +
15.344 + IPluginFunction *pFunction = pContext->GetFunctionById(params[2]);
15.345 + if (!pFunction)
15.346 + {
15.347 + return pContext->ThrowNativeError("Invalid function %x", params[2]);
15.348 + }
15.349 +
15.350 + void *array[2];
15.351 + array[0] = pFunction;
15.352 + array[1] = (void *)¶ms[2];
15.353 +
15.354 + IMenuHandler *pHandler = menu->GetHandler();
15.355 + if (!pHandler->OnSetHandlerOption("set_vote_results_handler", (const void *)array))
15.356 + {
15.357 + return pContext->ThrowNativeError("The given menu does not support this option");
15.358 + }
15.359 +
15.360 + return 1;
15.361 +}
15.362 +
15.363 REGISTER_NATIVES(menuNatives)
15.364 {
15.365 {"AddMenuItem", AddMenuItem},
15.366 {"CanPanelDrawFlags", CanPanelDrawFlags},
15.367 {"CancelClientMenu", CancelClientMenu},
15.368 {"CancelMenu", CancelMenu},
15.369 + {"CancelVote", CancelVote},
15.370 {"CreateMenu", CreateMenu},
15.371 {"CreateMenuEx", CreateMenuEx},
15.372 {"CreatePanel", CreatePanel},
15.373 @@ -1161,6 +1344,7 @@
15.374 {"SetPanelCurrentKey", SetPanelCurrentKey},
15.375 {"SetPanelTitle", SetPanelTitle},
15.376 {"SetPanelKeys", SetPanelKeys},
15.377 + {"SetVoteResultCallback", SetVoteResultCallback},
15.378 {"VoteMenu", VoteMenu},
15.379 {NULL, NULL},
15.380 };
16.1 --- a/core/vm/sp_vm_basecontext.cpp Tue Aug 07 22:11:00 2007 +0000
16.2 +++ b/core/vm/sp_vm_basecontext.cpp Wed Aug 08 02:27:10 2007 +0000
16.3 @@ -28,1031 +28,1031 @@
16.4 * JULY-31-2007), or <http://www.sourcemod.net/license.php>.
16.5 *
16.6 * Version: $Id$
16.7 - */
16.8 -
16.9 -#include <string.h>
16.10 -#include <stdarg.h>
16.11 -#include <assert.h>
16.12 -#include <limits.h>
16.13 -#include "sp_vm_api.h"
16.14 -#include "sp_vm_basecontext.h"
16.15 -#include "sp_vm_engine.h"
16.16 -
16.17 -#ifdef SOURCEMOD_BUILD
16.18 -#include "Logger.h"
16.19 -#endif
16.20 -
16.21 -using namespace SourcePawn;
16.22 -
16.23 -extern SourcePawnEngine g_SourcePawn;
16.24 -
16.25 -#define CELLBOUNDMAX (INT_MAX/sizeof(cell_t))
16.26 -#define STACKMARGIN ((cell_t)(16*sizeof(cell_t)))
16.27 -
16.28 -int GlobalDebugBreak(sp_context_t *ctx, uint32_t frm, uint32_t cip)
16.29 -{
16.30 - g_SourcePawn.RunTracer(ctx, frm, cip);
16.31 -
16.32 - return SP_ERROR_NONE;
16.33 -}
16.34 -
16.35 -BaseContext::BaseContext(sp_context_t *_ctx)
16.36 -{
16.37 - ctx = _ctx;
16.38 - ctx->context = this;
16.39 - ctx->dbreak = GlobalDebugBreak;
16.40 - m_InExec = false;
16.41 - m_CustomMsg = false;
16.42 - m_funcsnum = ctx->vmbase->FunctionCount(ctx);
16.43 -#if 0
16.44 - m_priv_funcs = NULL;
16.45 -#endif
16.46 - m_pub_funcs = NULL;
16.47 -
16.48 -#if 0
16.49 - /**
16.50 - * Note: Since the m_plugin member will never change,
16.51 - * it is safe to assume the function count will never change
16.52 - */
16.53 - if (m_funcsnum && m_priv_funcs == NULL)
16.54 - {
16.55 - m_priv_funcs = new CFunction *[m_funcsnum];
16.56 - memset(m_priv_funcs, 0, sizeof(CFunction *) * m_funcsnum);
16.57 - } else {
16.58 - m_priv_funcs = NULL;
16.59 - }
16.60 -#endif
16.61 -
16.62 - if (ctx->plugin->info.publics_num && m_pub_funcs == NULL)
16.63 - {
16.64 - m_pub_funcs = new CFunction *[ctx->plugin->info.publics_num];
16.65 - memset(m_pub_funcs, 0, sizeof(CFunction *) * ctx->plugin->info.publics_num);
16.66 - } else {
16.67 - m_pub_funcs = NULL;
16.68 - }
16.69 -
16.70 - /* Initialize the null references */
16.71 - uint32_t index;
16.72 - if (FindPubvarByName("NULL_VECTOR", &index) == SP_ERROR_NONE)
16.73 - {
16.74 - sp_pubvar_t *pubvar;
16.75 - GetPubvarByIndex(index, &pubvar);
16.76 - m_pNullVec = pubvar->offs;
16.77 - } else {
16.78 - m_pNullVec = NULL;
16.79 - }
16.80 -
16.81 - if (FindPubvarByName("NULL_STRING", &index) == SP_ERROR_NONE)
16.82 - {
16.83 - sp_pubvar_t *pubvar;
16.84 - GetPubvarByIndex(index, &pubvar);
16.85 - m_pNullString = pubvar->offs;
16.86 - } else {
16.87 - m_pNullString = NULL;
16.88 - }
16.89 -}
16.90 -
16.91 -void BaseContext::FlushFunctionCache()
16.92 -{
16.93 - if (m_pub_funcs)
16.94 - {
16.95 - for (uint32_t i=0; i<ctx->plugin->info.publics_num; i++)
16.96 - {
16.97 - delete m_pub_funcs[i];
16.98 - m_pub_funcs[i] = NULL;
16.99 - }
16.100 - }
16.101 -
16.102 -#if 0
16.103 - if (m_priv_funcs)
16.104 - {
16.105 - for (unsigned int i=0; i<m_funcsnum; i++)
16.106 - {
16.107 - delete m_priv_funcs[i];
16.108 - m_priv_funcs[i] = NULL;
16.109 - }
16.110 - }
16.111 -#endif
16.112 -}
16.113 -
16.114 -void BaseContext::RefreshFunctionCache()
16.115 -{
16.116 - if (m_pub_funcs)
16.117 - {
16.118 - sp_public_t *pub;
16.119 - for (uint32_t i=0; i<ctx->plugin->info.publics_num; i++)
16.120 - {
16.121 - if (!m_pub_funcs[i])
16.122 - {
16.123 - continue;
16.124 - }
16.125 - if (GetPublicByIndex(i, &pub) != SP_ERROR_NONE)
16.126 - {
16.127 - continue;
16.128 - }
16.129 - m_pub_funcs[i]->Set(pub->code_offs, this);
16.130 - }
16.131 - }
16.132 -
16.133 -#if 0
16.134 - if (m_priv_funcs)
16.135 - {
16.136 - for (unsigned int i=0; i<m_funcsnum; i++)
16.137 - {
16.138 - if (!m_priv_funcs[i])
16.139 - {
16.140 - continue;
16.141 - }
16.142 - g_pVM->
16.143 - }
16.144 - }
16.145 -#endif
16.146 -}
16.147 -
16.148 -BaseContext::~BaseContext()
16.149 -{
16.150 - FlushFunctionCache();
16.151 - delete [] m_pub_funcs;
16.152 - m_pub_funcs = NULL;
16.153 -#if 0
16.154 - delete [] m_priv_funcs;
16.155 - m_priv_funcs = NULL;
16.156 -#endif
16.157 -}
16.158 -
16.159 -void BaseContext::SetContext(sp_context_t *_ctx)
16.160 -{
16.161 - if (!_ctx)
16.162 - {
16.163 - return;
16.164 - }
16.165 - ctx = _ctx;
16.166 - ctx->context = this;
16.167 - ctx->dbreak = GlobalDebugBreak;
16.168 - RefreshFunctionCache();
16.169 -}
16.170 -
16.171 -IVirtualMachine *BaseContext::GetVirtualMachine()
16.172 -{
16.173 - return (IVirtualMachine *)ctx->vmbase;
16.174 -}
16.175 -
16.176 -sp_context_t *BaseContext::GetContext()
16.177 -{
16.178 - return ctx;
16.179 -}
16.180 -
16.181 -bool BaseContext::IsDebugging()
16.182 -{
16.183 - return (ctx->flags & SPFLAG_PLUGIN_DEBUG);
16.184 -}
16.185 -
16.186 -int BaseContext::SetDebugBreak(SPVM_DEBUGBREAK newpfn, SPVM_DEBUGBREAK *oldpfn)
16.187 -{
16.188 - if (!IsDebugging())
16.189 - {
16.190 - return SP_ERROR_NOTDEBUGGING;
16.191 - }
16.192 -
16.193 - *oldpfn = ctx->dbreak;
16.194 - ctx->dbreak = newpfn;
16.195 -
16.196 - return SP_ERROR_NONE;
16.197 -}
16.198 -
16.199 -IPluginDebugInfo *BaseContext::GetDebugInfo()
16.200 -{
16.201 - return this;
16.202 -}
16.203 -
16.204 -int BaseContext::Execute(uint32_t code_addr, cell_t *result)
16.205 -{
16.206 - if ((ctx->flags & SPFLAG_PLUGIN_PAUSED) == SPFLAG_PLUGIN_PAUSED)
16.207 - {
16.208 - return SP_ERROR_NOT_RUNNABLE;
16.209 - }
16.210 -
16.211 - /* tada, prevent a crash */
16.212 - cell_t _ignore_result;
16.213 - if (!result)
16.214 - {
16.215 - result = &_ignore_result;
16.216 - }
16.217 -
16.218 - IVirtualMachine *vm = (IVirtualMachine *)ctx->vmbase;
16.219 -
16.220 - uint32_t pushcount = ctx->pushcount;
16.221 - int err;
16.222 -
16.223 - PushCell(pushcount++);
16.224 - ctx->pushcount = 0;
16.225 -
16.226 - cell_t save_sp = ctx->sp;
16.227 - cell_t save_hp = ctx->hp;
16.228 - uint32_t n_idx = ctx->n_idx;
16.229 -
16.230 - bool wasExec = m_InExec;
16.231 -
16.232 - /* Clear the error state, if any */
16.233 - ctx->n_err = SP_ERROR_NONE;
16.234 - ctx->n_idx = 0;
16.235 - m_InExec = true;
16.236 - m_MsgCache[0] = '\0';
16.237 - m_CustomMsg = false;
16.238 -
16.239 - g_SourcePawn.PushTracer(ctx);
16.240 -
16.241 - err = vm->ContextExecute(ctx, code_addr, result);
16.242 -
16.243 - m_InExec = wasExec;
16.244 -
16.245 - /**
16.246 - * :TODO: Calling from a plugin in here will erase the cached message...
16.247 - * Should that be documented?
16.248 - */
16.249 - g_SourcePawn.PopTracer(err, m_CustomMsg ? m_MsgCache : NULL);
16.250 -
16.251 -#if 1//defined _DEBUG
16.252 - //:TODO: debug code for leak detection, remove before the release?
16.253 - if (err == SP_ERROR_NONE)
16.254 - {
16.255 - if ((ctx->sp - (cell_t)(pushcount * sizeof(cell_t))) != save_sp)
16.256 - {
16.257 - const char *name;
16.258 - ctx->context->GetDebugInfo()->LookupFunction(code_addr, &name);
16.259 - g_Logger.LogError("Stack leak detected: sp:%d should be %d on function %s", ctx->sp, save_sp, name);
16.260 - }
16.261 - if (ctx->hp != save_hp)
16.262 - {
16.263 - const char *name;
16.264 - ctx->context->GetDebugInfo()->LookupFunction(code_addr, &name);
16.265 - g_Logger.LogError("Heap leak detected: hp:%d should be %d on function %s", ctx->hp, save_hp, name);
16.266 - }
16.267 - //assert(ctx->sp - pushcount * sizeof(cell_t) == save_sp);
16.268 - //assert(ctx->hp == save_hp);
16.269 - }
16.270 -#endif
16.271 - if (err != SP_ERROR_NONE)
16.272 - {
16.273 - ctx->sp = save_sp;
16.274 - ctx->hp = save_hp;
16.275 - }
16.276 -
16.277 - ctx->n_idx = n_idx;
16.278 -
16.279 - return err;
16.280 -}
16.281 -
16.282 -void BaseContext::SetErrorMessage(const char *msg, va_list ap)
16.283 -{
16.284 - m_CustomMsg = true;
16.285 -
16.286 - vsnprintf(m_MsgCache, sizeof(m_MsgCache), msg, ap);
16.287 -}
16.288 -
16.289 -cell_t BaseContext::ThrowNativeErrorEx(int error, const char *msg, ...)
16.290 -{
16.291 - if (!m_InExec)
16.292 - {
16.293 - return 0;
16.294 - }
16.295 -
16.296 - ctx->n_err = error;
16.297 -
16.298 - if (msg)
16.299 - {
16.300 - va_list ap;
16.301 - va_start(ap, msg);
16.302 - SetErrorMessage(msg, ap);
16.303 - va_end(ap);
16.304 - }
16.305 -
16.306 - return 0;
16.307 -}
16.308 -
16.309 -cell_t BaseContext::ThrowNativeError(const char *msg, ...)
16.310 -{
16.311 - if (!m_InExec)
16.312 - {
16.313 - return 0;
16.314 - }
16.315 -
16.316 - ctx->n_err = SP_ERROR_NATIVE;
16.317 -
16.318 - if (msg)
16.319 - {
16.320 - va_list ap;
16.321 - va_start(ap, msg);
16.322 - SetErrorMessage(msg, ap);
16.323 - va_end(ap);
16.324 - }
16.325 -
16.326 - return 0;
16.327 -}
16.328 -
16.329 -int BaseContext::HeapAlloc(unsigned int cells, cell_t *local_addr, cell_t **phys_addr)
16.330 -{
16.331 - cell_t *addr;
16.332 - ucell_t realmem;
16.333 -
16.334 -#if 0
16.335 - if (cells > CELLBOUNDMAX)
16.336 - {
16.337 - return SP_ERROR_ARAM;
16.338 - }
16.339 -#else
16.340 - assert(cells < CELLBOUNDMAX);
16.341 -#endif
16.342 -
16.343 - realmem = cells * sizeof(cell_t);
16.344 -
16.345 - /**
16.346 - * Check if the space between the heap and stack is sufficient.
16.347 - */
16.348 - if ((cell_t)(ctx->sp - ctx->hp - realmem) < STACKMARGIN)
16.349 - {
16.350 - return SP_ERROR_HEAPLOW;
16.351 - }
16.352 -
16.353 - addr = (cell_t *)(ctx->memory + ctx->hp);
16.354 - /* store size of allocation in cells */
16.355 - *addr = (cell_t)cells;
16.356 - addr++;
16.357 - ctx->hp += sizeof(cell_t);
16.358 -
16.359 - *local_addr = ctx->hp;
16.360 -
16.361 - if (phys_addr)
16.362 - {
16.363 - *phys_addr = addr;
16.364 - }
16.365 -
16.366 - ctx->hp += realmem;
16.367 -
16.368 - return SP_ERROR_NONE;
16.369 -}
16.370 -
16.371 -int BaseContext::HeapPop(cell_t local_addr)
16.372 -{
16.373 - cell_t cellcount;
16.374 - cell_t *addr;
16.375 -
16.376 - /* check the bounds of this address */
16.377 - local_addr -= sizeof(cell_t);
16.378 - if (local_addr < ctx->heap_base || local_addr >= ctx->sp)
16.379 - {
16.380 - return SP_ERROR_INVALID_ADDRESS;
16.381 - }
16.382 -
16.383 - addr = (cell_t *)(ctx->memory + local_addr);
16.384 - cellcount = (*addr) * sizeof(cell_t);
16.385 - /* check if this memory count looks valid */
16.386 - if ((signed)(ctx->hp - cellcount - sizeof(cell_t)) != local_addr)
16.387 - {
16.388 - return SP_ERROR_INVALID_ADDRESS;
16.389 - }
16.390 -
16.391 - ctx->hp = local_addr;
16.392 -
16.393 - return SP_ERROR_NONE;
16.394 -}
16.395 -
16.396 -
16.397 -int BaseContext::HeapRelease(cell_t local_addr)
16.398 -{
16.399 - if (local_addr < ctx->heap_base)
16.400 - {
16.401 - return SP_ERROR_INVALID_ADDRESS;
16.402 - }
16.403 -
16.404 - ctx->hp = local_addr - sizeof(cell_t);
16.405 -
16.406 - return SP_ERROR_NONE;
16.407 -}
16.408 -
16.409 -int BaseContext::FindNativeByName(const char *name, uint32_t *index)
16.410 -{
16.411 - int high;
16.412 -
16.413 - high = ctx->plugin->info.natives_num - 1;
16.414 -
16.415 - for (uint32_t i=0; i<ctx->plugin->info.natives_num; i++)
16.416 - {
16.417 - if (strcmp(ctx->natives[i].name, name) == 0)
16.418 - {
16.419 - if (index)
16.420 - {
16.421 - *index = i;
16.422 - }
16.423 - return SP_ERROR_NONE;
16.424 - }
16.425 - }
16.426 -
16.427 - return SP_ERROR_NOT_FOUND;
16.428 -}
16.429 -
16.430 -int BaseContext::GetNativeByIndex(uint32_t index, sp_native_t **native)
16.431 -{
16.432 - if (index >= ctx->plugin->info.natives_num)
16.433 - {
16.434 - return SP_ERROR_INDEX;
16.435 - }
16.436 -
16.437 - if (native)
16.438 - {
16.439 - *native = &(ctx->natives[index]);
16.440 - }
16.441 -
16.442 - return SP_ERROR_NONE;
16.443 -}
16.444 -
16.445 -
16.446 -uint32_t BaseContext::GetNativesNum()
16.447 -{
16.448 - return ctx->plugin->info.natives_num;
16.449 -}
16.450 -
16.451 -int BaseContext::FindPublicByName(const char *name, uint32_t *index)
16.452 -{
16.453 - int diff, high, low;
16.454 - uint32_t mid;
16.455 -
16.456 - high = ctx->plugin->info.publics_num - 1;
16.457 - low = 0;
16.458 -
16.459 - while (low <= high)
16.460 - {
16.461 - mid = (low + high) / 2;
16.462 - diff = strcmp(ctx->publics[mid].name, name);
16.463 - if (diff == 0)
16.464 - {
16.465 - if (index)
16.466 - {
16.467 - *index = mid;
16.468 - }
16.469 - return SP_ERROR_NONE;
16.470 - } else if (diff < 0) {
16.471 - low = mid + 1;
16.472 - } else {
16.473 - high = mid - 1;
16.474 - }
16.475 - }
16.476 -
16.477 - return SP_ERROR_NOT_FOUND;
16.478 -}
16.479 -
16.480 -int BaseContext::GetPublicByIndex(uint32_t index, sp_public_t **pblic)
16.481 -{
16.482 - if (index >= ctx->plugin->info.publics_num)
16.483 - {
16.484 - return SP_ERROR_INDEX;
16.485 - }
16.486 -
16.487 - if (pblic)
16.488 - {
16.489 - *pblic = &(ctx->publics[index]);
16.490 - }
16.491 -
16.492 - return SP_ERROR_NONE;
16.493 -}
16.494 -
16.495 -uint32_t BaseContext::GetPublicsNum()
16.496 -{
16.497 - return ctx->plugin->info.publics_num;
16.498 -}
16.499 -
16.500 -int BaseContext::GetPubvarByIndex(uint32_t index, sp_pubvar_t **pubvar)
16.501 -{
16.502 - if (index >= ctx->plugin->info.pubvars_num)
16.503 - {
16.504 - return SP_ERROR_INDEX;
16.505 - }
16.506 -
16.507 - if (pubvar)
16.508 - {
16.509 - *pubvar = &(ctx->pubvars[index]);
16.510 - }
16.511 -
16.512 - return SP_ERROR_NONE;
16.513 -}
16.514 -
16.515 -int BaseContext::FindPubvarByName(const char *name, uint32_t *index)
16.516 -{
16.517 - int diff, high, low;
16.518 - uint32_t mid;
16.519 -
16.520 - high = ctx->plugin->info.pubvars_num - 1;
16.521 - low = 0;
16.522 -
16.523 - while (low <= high)
16.524 - {
16.525 - mid = (low + high) / 2;
16.526 - diff = strcmp(ctx->pubvars[mid].name, name);
16.527 - if (diff == 0)
16.528 - {
16.529 - if (index)
16.530 - {
16.531 - *index = mid;
16.532 - }
16.533 - return SP_ERROR_NONE;
16.534 - } else if (diff < 0) {
16.535 - low = mid + 1;
16.536 - } else {
16.537 - high = mid - 1;
16.538 - }
16.539 - }
16.540 -
16.541 - return SP_ERROR_NOT_FOUND;
16.542 -}
16.543 -
16.544 -int BaseContext::GetPubvarAddrs(uint32_t index, cell_t *local_addr, cell_t **phys_addr)
16.545 -{
16.546 - if (index >= ctx->plugin->info.pubvars_num)
16.547 - {
16.548 - return SP_ERROR_INDEX;
16.549 - }
16.550 -
16.551 - *local_addr = ctx->plugin->info.pubvars[index].address;
16.552 - *phys_addr = ctx->pubvars[index].offs;
16.553 -
16.554 - return SP_ERROR_NONE;
16.555 -}
16.556 -
16.557 -uint32_t BaseContext::GetPubVarsNum()
16.558 -{
16.559 - return ctx->plugin->info.pubvars_num;
16.560 -}
16.561 -
16.562 -int BaseContext::BindNatives(const sp_nativeinfo_t *natives, unsigned int num, int overwrite)
16.563 -{
16.564 - uint32_t i, j, max;
16.565 -
16.566 - max = ctx->plugin->info.natives_num;
16.567 -
16.568 - for (i=0; i<max; i++)
16.569 - {
16.570 - if ((ctx->natives[i].status == SP_NATIVE_BOUND) && !overwrite)
16.571 - {
16.572 - continue;
16.573 - }
16.574 -
16.575 - for (j=0; (natives[j].name) && (!num || j<num); j++)
16.576 - {
16.577 - if (!strcmp(ctx->natives[i].name, natives[j].name))
16.578 - {
16.579 - ctx->natives[i].pfn = natives[j].func;
16.580 - ctx->natives[i].status = SP_NATIVE_BOUND;
16.581 - }
16.582 - }
16.583 - }
16.584 -
16.585 - return SP_ERROR_NONE;
16.586 -}
16.587 -
16.588 -int BaseContext::BindNative(const sp_nativeinfo_t *native)
16.589 -{
16.590 - uint32_t index;
16.591 - int err;
16.592 -
16.593 - if ((err = FindNativeByName(native->name, &index)) != SP_ERROR_NONE)
16.594 - {
16.595 - return err;
16.596 - }
16.597 -
16.598 - ctx->natives[index].pfn = native->func;
16.599 - ctx->natives[index].status = SP_NATIVE_BOUND;
16.600 -
16.601 - return SP_ERROR_NONE;
16.602 -}
16.603 -
16.604 -int BaseContext::BindNativeToAny(SPVM_NATIVE_FUNC native)
16.605 -{
16.606 - uint32_t nativesnum, i;
16.607 -
16.608 - nativesnum = ctx->plugin->info.natives_num;
16.609 -
16.610 - for (i=0; i<nativesnum; i++)
16.611 - {
16.612 - if (ctx->natives[i].status == SP_NATIVE_UNBOUND)
16.613 - {
16.614 - ctx->natives[i].pfn = native;
16.615 - ctx->natives[i].status = SP_NATIVE_BOUND;
16.616 - }
16.617 - }
16.618 -
16.619 - return SP_ERROR_NONE;
16.620 -}
16.621 -
16.622 -int BaseContext::LocalToPhysAddr(cell_t local_addr, cell_t **phys_addr)
16.623 -{
16.624 - if (((local_addr >= ctx->hp) && (local_addr < ctx->sp)) || (local_addr < 0) || ((ucell_t)local_addr >= ctx->mem_size))
16.625 - {
16.626 - return SP_ERROR_INVALID_ADDRESS;
16.627 - }
16.628 -
16.629 - if (phys_addr)
16.630 - {
16.631 - *phys_addr = (cell_t *)(ctx->memory + local_addr);
16.632 - }
16.633 -
16.634 - return SP_ERROR_NONE;
16.635 -}
16.636 -
16.637 -int BaseContext::PushCell(cell_t value)
16.638 -{
16.639 - if ((ctx->hp + STACKMARGIN) > (cell_t)(ctx->sp - sizeof(cell_t)))
16.640 - {
16.641 - return SP_ERROR_STACKLOW;
16.642 - }
16.643 -
16.644 - ctx->sp -= sizeof(cell_t);
16.645 - *(cell_t *)(ctx->memory + ctx->sp) = value;
16.646 - ctx->pushcount++;
16.647 -
16.648 - return SP_ERROR_NONE;
16.649 -}
16.650 -
16.651 -int BaseContext::PushCellsFromArray(cell_t array[], unsigned int numcells)
16.652 -{
16.653 - unsigned int i;
16.654 - int err;
16.655 -
16.656 - for (i=0; i<numcells; i++)
16.657 - {
16.658 - if ((err = PushCell(array[i])) != SP_ERROR_NONE)
16.659 - {
16.660 - ctx->sp += (cell_t)(i * sizeof(cell_t));
16.661 - ctx->pushcount -= i;
16.662 - return err;
16.663 - }
16.664 - }
16.665 -
16.666 - return SP_ERROR_NONE;
16.667 -}
16.668 -
16.669 -int BaseContext::PushCellArray(cell_t *local_addr, cell_t **phys_addr, cell_t array[], unsigned int numcells)
16.670 -{
16.671 - cell_t *ph_addr;
16.672 - int err;
16.673 -
16.674 - if ((err = HeapAlloc(numcells, local_addr, &ph_addr)) != SP_ERROR_NONE)
16.675 - {
16.676 - return err;
16.677 - }
16.678 -
16.679 - memcpy(ph_addr, array, numcells * sizeof(cell_t));
16.680 -
16.681 - if ((err = PushCell(*local_addr)) != SP_ERROR_NONE)
16.682 - {
16.683 - HeapRelease(*local_addr);
16.684 - return err;
16.685 - }
16.686 -
16.687 - if (phys_addr)
16.688 - {
16.689 - *phys_addr = ph_addr;
16.690 - }
16.691 -
16.692 - return SP_ERROR_NONE;
16.693 -}
16.694 -
16.695 -int BaseContext::LocalToString(cell_t local_addr, char **addr)
16.696 -{
16.697 - if (((local_addr >= ctx->hp) && (local_addr < ctx->sp)) || (local_addr < 0) || ((ucell_t)local_addr >= ctx->mem_size))
16.698 - {
16.699 - return SP_ERROR_INVALID_ADDRESS;
16.700 - }
16.701 - *addr = (char *)(ctx->memory + local_addr);
16.702 -
16.703 - return SP_ERROR_NONE;
16.704 -}
16.705 -
16.706 -int BaseContext::PushString(cell_t *local_addr, char **phys_addr, const char *string)
16.707 -{
16.708 - char *ph_addr;
16.709 - int err;
16.710 - unsigned int len, numcells = ((len=strlen(string)) + sizeof(cell_t)) / sizeof(cell_t);
16.711 -
16.712 - if ((err = HeapAlloc(numcells, local_addr, (cell_t **)&ph_addr)) != SP_ERROR_NONE)
16.713 - {
16.714 - return err;
16.715 - }
16.716 -
16.717 - memcpy(ph_addr, string, len);
16.718 - ph_addr[len] = '\0';
16.719 -
16.720 - if ((err = PushCell(*local_addr)) != SP_ERROR_NONE)
16.721 - {
16.722 - HeapRelease(*local_addr);
16.723 - return err;
16.724 - }
16.725 -
16.726 - if (phys_addr)
16.727 - {
16.728 - *phys_addr = ph_addr;
16.729 - }
16.730 -
16.731 - return SP_ERROR_NONE;
16.732 -}
16.733 -
16.734 -int BaseContext::StringToLocal(cell_t local_addr, size_t bytes, const char *source)
16.735 -{
16.736 - char *dest;
16.737 - size_t len;
16.738 -
16.739 - if (((local_addr >= ctx->hp) && (local_addr < ctx->sp)) || (local_addr < 0) || ((ucell_t)local_addr >= ctx->mem_size))
16.740 - {
16.741 - return SP_ERROR_INVALID_ADDRESS;
16.742 - }
16.743 -
16.744 - if (bytes == 0)
16.745 - {
16.746 - return SP_ERROR_NONE;
16.747 - }
16.748 -
16.749 - len = strlen(source);
16.750 - dest = (char *)(ctx->memory + local_addr);
16.751 -
16.752 - if (len >= bytes)
16.753 - {
16.754 - len = bytes - 1;
16.755 - }
16.756 -
16.757 - memcpy(dest, source, len);
16.758 - dest[len] = '\0';
16.759 -
16.760 - return SP_ERROR_NONE;
16.761 -}
16.762 -
16.763 -inline int __CheckValidChar(char *c)
16.764 -{
16.765 - int count;
16.766 - int bytecount = 0;
16.767 -
16.768 - for (count=1; (*c & 0xC0) == 0x80; count++)
16.769 - {
16.770 - c--;
16.771 - }
16.772 -
16.773 - switch (*c & 0xF0)
16.774 - {
16.775 - case 0xC0:
16.776 - case 0xD0:
16.777 - {
16.778 - bytecount = 2;
16.779 - break;
16.780 - }
16.781 - case 0xE0:
16.782 - {
16.783 - bytecount = 3;
16.784 - break;
16.785 - }
16.786 - case 0xF0:
16.787 - {
16.788 - bytecount = 4;
16.789 - break;
16.790 - }
16.791 - }
16.792 -
16.793 - if (bytecount != count)
16.794 - {
16.795 - return count;
16.796 - }
16.797 -
16.798 - return 0;
16.799 -}
16.800 -
16.801 -int BaseContext::StringToLocalUTF8(cell_t local_addr, size_t maxbytes, const char *source, size_t *wrtnbytes)
16.802 -{
16.803 - char *dest;
16.804 - size_t len;
16.805 - bool needtocheck = false;
16.806 -
16.807 - if (((local_addr >= ctx->hp) && (local_addr < ctx->sp)) || (local_addr < 0) || ((ucell_t)local_addr >= ctx->mem_size))
16.808 - {
16.809 - return SP_ERROR_INVALID_ADDRESS;
16.810 - }
16.811 -
16.812 - if (maxbytes == 0)
16.813 - {
16.814 - return SP_ERROR_NONE;
16.815 - }
16.816 -
16.817 - len = strlen(source);
16.818 - dest = (char *)(ctx->memory + local_addr);
16.819 -
16.820 - if ((size_t)len >= maxbytes)
16.821 - {
16.822 - len = maxbytes - 1;
16.823 - needtocheck = true;
16.824 - }
16.825 -
16.826 - memcpy(dest, source, len);
16.827 - if ((dest[len-1] & 1<<7) && needtocheck)
16.828 - {
16.829 - len -= __CheckValidChar(dest+len-1);
16.830 - }
16.831 - dest[len] = '\0';
16.832 -
16.833 - if (wrtnbytes)
16.834 - {
16.835 - *wrtnbytes = len;
16.836 - }
16.837 -
16.838 - return SP_ERROR_NONE;
16.839 -}
16.840 -
16.841 -#define USHR(x) ((unsigned int)(x)>>1)
16.842 -
16.843 -int BaseContext::LookupFile(ucell_t addr, const char **filename)
16.844 -{
16.845 - int high, low, mid;
16.846 -
16.847 - high = ctx->plugin->debug.files_num;
16.848 - low = -1;
16.849 -
16.850 - while (high - low > 1)
16.851 - {
16.852 - mid = USHR(low + high);
16.853 - if (ctx->files[mid].addr <= addr)
16.854 - {
16.855 - low = mid;
16.856 - } else {
16.857 - high = mid;
16.858 - }
16.859 - }
16.860 -
16.861 - if (low == -1)
16.862 - {
16.863 - return SP_ERROR_NOT_FOUND;
16.864 - }
16.865 -
16.866 - *filename = ctx->files[low].name;
16.867 -
16.868 - return SP_ERROR_NONE;
16.869 -}
16.870 -
16.871 -int BaseContext::LookupFunction(ucell_t addr, const char **name)
16.872 -{
16.873 - uint32_t iter, max = ctx->plugin->debug.syms_num;
16.874 -
16.875 - for (iter=0; iter<max; iter++)
16.876 - {
16.877 - if ((ctx->symbols[iter].sym->ident == SP_SYM_FUNCTION)
16.878 - && (ctx->symbols[iter].codestart <= addr)
16.879 - && (ctx->symbols[iter].codeend > addr))
16.880 - {
16.881 - break;
16.882 - }
16.883 - }
16.884 -
16.885 - if (iter >= max)
16.886 - {
16.887 - return SP_ERROR_NOT_FOUND;
16.888 - }
16.889 -
16.890 - *name = ctx->symbols[iter].name;
16.891 -
16.892 - return SP_ERROR_NONE;
16.893 -}
16.894 -
16.895 -int BaseContext::LookupLine(ucell_t addr, uint32_t *line)
16.896 -{
16.897 - int high, low, mid;
16.898 -
16.899 - high = ctx->plugin->debug.lines_num;
16.900 - low = -1;
16.901 -
16.902 - while (high - low > 1)
16.903 - {
16.904 - mid = USHR(low + high);
16.905 - if (ctx->lines[mid].addr <= addr)
16.906 - {
16.907 - low = mid;
16.908 - } else {
16.909 - high = mid;
16.910 - }
16.911 - }
16.912 -
16.913 - if (low == -1)
16.914 - {
16.915 - return SP_ERROR_NOT_FOUND;
16.916 - }
16.917 -
16.918 - /* Since the CIP occurs BEFORE the line, we have to add one */
16.919 - *line = ctx->lines[low].line + 1;
16.920 -
16.921 - return SP_ERROR_NONE;
16.922 -}
16.923 -
16.924 -IPluginFunction *BaseContext::GetFunctionById(funcid_t func_id)
16.925 -{
16.926 - CFunction *pFunc = NULL;
16.927 -
16.928 - if (func_id & 1)
16.929 - {
16.930 - func_id >>= 1;
16.931 - if (func_id >= ctx->plugin->info.publics_num)
16.932 - {
16.933 - return NULL;
16.934 - }
16.935 - pFunc = m_pub_funcs[func_id];
16.936 - if (!pFunc)
16.937 - {
16.938 - m_pub_funcs[func_id] = new CFunction(ctx->publics[func_id].code_offs, this);
16.939 - pFunc = m_pub_funcs[func_id];
16.940 - } else if (pFunc->IsInvalidated()) {
16.941 - pFunc->Set(ctx->publics[func_id].code_offs, this);
16.942 - }
16.943 - } else {
16.944 - /* :TODO: currently not used */
16.945 -#if 0
16.946 - func_id >>= 1;
16.947 - unsigned int index;
16.948 - if (!g_pVM->FunctionLookup(ctx, func_id, &index))
16.949 - {
16.950 - return NULL;
16.951 - }
16.952 - pFunc = m_priv_funcs[func_id];
16.953 - if (!pFunc)
16.954 - {
16.955 - m_priv_funcs[func_id] = new CFunction(save, this);
16.956 - pFunc = m_priv_funcs[func_id];
16.957 - }
16.958 -#endif
16.959 - assert(false);
16.960 - }
16.961 -
16.962 - return pFunc;
16.963 -}
16.964 -
16.965 -IPluginFunction *BaseContext::GetFunctionByName(const char *public_name)
16.966 -{
16.967 - uint32_t index;
16.968 -
16.969 - if (FindPublicByName(public_name, &index) != SP_ERROR_NONE)
16.970 - {
16.971 - return NULL;
16.972 - }
16.973 -
16.974 - CFunction *pFunc = m_pub_funcs[index];
16.975 - if (!pFunc)
16.976 - {
16.977 - sp_public_t *pub = NULL;
16.978 - GetPublicByIndex(index, &pub);
16.979 - if (pub)
16.980 - {
16.981 - m_pub_funcs[index] = new CFunction(pub->code_offs, this);
16.982 - }
16.983 - pFunc = m_pub_funcs[index];
16.984 - } else if (pFunc->IsInvalidated()) {
16.985 - sp_public_t *pub = NULL;
16.986 - GetPublicByIndex(index, &pub);
16.987 - if (pub)
16.988 - {
16.989 - pFunc->Set(pub->code_offs, this);
16.990 - } else {
16.991 - pFunc = NULL;
16.992 - }
16.993 - }
16.994 -
16.995 - return pFunc;
16.996 -}
16.997 -
16.998 -int BaseContext::LocalToStringNULL(cell_t local_addr, char **addr)
16.999 -{
16.1000 - int err;
16.1001 - if ((err = LocalToString(local_addr, addr)) != SP_ERROR_NONE)
16.1002 - {
16.1003 - return err;
16.1004 - }
16.1005 -
16.1006 - if ((cell_t *)*addr == m_pNullString)
16.1007 - {
16.1008 - *addr = NULL;
16.1009 - }
16.1010 -
16.1011 - return SP_ERROR_NONE;
16.1012 -}
16.1013 -
16.1014 -#if defined SOURCEMOD_BUILD
16.1015 -SourceMod::IdentityToken_t *BaseContext::GetIdentity()
16.1016 -{
16.1017 - return m_pToken;
16.1018 -}
16.1019 -
16.1020 -void BaseContext::SetIdentity(SourceMod::IdentityToken_t *token)
16.1021 -{
16.1022 - m_pToken = token;
16.1023 -}
16.1024 -
16.1025 -cell_t *BaseContext::GetNullRef(SP_NULL_TYPE type)
16.1026 -{
16.1027 - if (type == SP_NULL_VECTOR)
16.1028 - {
16.1029 - return m_pNullVec;
16.1030 - }
16.1031 -
16.1032 - return NULL;
16.1033 -}
16.1034 -#endif
16.1035 + */
16.1036 +
16.1037 +#include <string.h>
16.1038 +#include <stdarg.h>
16.1039 +#include <assert.h>
16.1040 +#include <limits.h>
16.1041 +#include "sp_vm_api.h"
16.1042 +#include "sp_vm_basecontext.h"
16.1043 +#include "sp_vm_engine.h"
16.1044 +
16.1045 +#ifdef SOURCEMOD_BUILD
16.1046 +#include "Logger.h"
16.1047 +#endif
16.1048 +
16.1049 +using namespace SourcePawn;
16.1050 +
16.1051 +extern SourcePawnEngine g_SourcePawn;
16.1052 +
16.1053 +#define CELLBOUNDMAX (INT_MAX/sizeof(cell_t))
16.1054 +#define STACKMARGIN ((cell_t)(16*sizeof(cell_t)))
16.1055 +
16.1056 +int GlobalDebugBreak(sp_context_t *ctx, uint32_t frm, uint32_t cip)
16.1057 +{
16.1058 + g_SourcePawn.RunTracer(ctx, frm, cip);
16.1059 +
16.1060 + return SP_ERROR_NONE;
16.1061 +}
16.1062 +
16.1063 +BaseContext::BaseContext(sp_context_t *_ctx)
16.1064 +{
16.1065 + ctx = _ctx;
16.1066 + ctx->context = this;
16.1067 + ctx->dbreak = GlobalDebugBreak;
16.1068 + m_InExec = false;
16.1069 + m_CustomMsg = false;
16.1070 + m_funcsnum = ctx->vmbase->FunctionCount(ctx);
16.1071 +#if 0
16.1072 + m_priv_funcs = NULL;
16.1073 +#endif
16.1074 + m_pub_funcs = NULL;
16.1075 +
16.1076 +#if 0
16.1077 + /**
16.1078 + * Note: Since the m_plugin member will never change,
16.1079 + * it is safe to assume the function count will never change
16.1080 + */
16.1081 + if (m_funcsnum && m_priv_funcs == NULL)
16.1082 + {
16.1083 + m_priv_funcs = new CFunction *[m_funcsnum];
16.1084 + memset(m_priv_funcs, 0, sizeof(CFunction *) * m_funcsnum);
16.1085 + } else {
16.1086 + m_priv_funcs = NULL;
16.1087 + }
16.1088 +#endif
16.1089 +
16.1090 + if (ctx->plugin->info.publics_num && m_pub_funcs == NULL)
16.1091 + {
16.1092 + m_pub_funcs = new CFunction *[ctx->plugin->info.publics_num];
16.1093 + memset(m_pub_funcs, 0, sizeof(CFunction *) * ctx->plugin->info.publics_num);
16.1094 + } else {
16.1095 + m_pub_funcs = NULL;
16.1096 + }
16.1097 +
16.1098 + /* Initialize the null references */
16.1099 + uint32_t index;
16.1100 + if (FindPubvarByName("NULL_VECTOR", &index) == SP_ERROR_NONE)
16.1101 + {
16.1102 + sp_pubvar_t *pubvar;
16.1103 + GetPubvarByIndex(index, &pubvar);
16.1104 + m_pNullVec = pubvar->offs;
16.1105 + } else {
16.1106 + m_pNullVec = NULL;
16.1107 + }
16.1108 +
16.1109 + if (FindPubvarByName("NULL_STRING", &index) == SP_ERROR_NONE)
16.1110 + {
16.1111 + sp_pubvar_t *pubvar;
16.1112 + GetPubvarByIndex(index, &pubvar);
16.1113 + m_pNullString = pubvar->offs;
16.1114 + } else {
16.1115 + m_pNullString = NULL;
16.1116 + }
16.1117 +}
16.1118 +
16.1119 +void BaseContext::FlushFunctionCache()
16.1120 +{
16.1121 + if (m_pub_funcs)
16.1122 + {
16.1123 + for (uint32_t i=0; i<ctx->plugin->info.publics_num; i++)
16.1124 + {
16.1125 + delete m_pub_funcs[i];
16.1126 + m_pub_funcs[i] = NULL;
16.1127 + }
16.1128 + }
16.1129 +
16.1130 +#if 0
16.1131 + if (m_priv_funcs)
16.1132 + {
16.1133 + for (unsigned int i=0; i<m_funcsnum; i++)
16.1134 + {
16.1135 + delete m_priv_funcs[i];
16.1136 + m_priv_funcs[i] = NULL;
16.1137 + }
16.1138 + }
16.1139 +#endif
16.1140 +}
16.1141 +
16.1142 +void BaseContext::RefreshFunctionCache()
16.1143 +{
16.1144 + if (m_pub_funcs)
16.1145 + {
16.1146 + sp_public_t *pub;
16.1147 + for (uint32_t i=0; i<ctx->plugin->info.publics_num; i++)
16.1148 + {
16.1149 + if (!m_pub_funcs[i])
16.1150 + {
16.1151 + continue;
16.1152 + }
16.1153 + if (GetPublicByIndex(i, &pub) != SP_ERROR_NONE)
16.1154 + {
16.1155 + continue;
16.1156 + }
16.1157 + m_pub_funcs[i]->Set(pub->code_offs, this);
16.1158 + }
16.1159 + }
16.1160 +
16.1161 +#if 0
16.1162 + if (m_priv_funcs)
16.1163 + {
16.1164 + for (unsigned int i=0; i<m_funcsnum; i++)
16.1165 + {
16.1166 + if (!m_priv_funcs[i])
16.1167 + {
16.1168 + continue;
16.1169 + }
16.1170 + g_pVM->
16.1171 + }
16.1172 + }
16.1173 +#endif
16.1174 +}
16.1175 +
16.1176 +BaseContext::~BaseContext()
16.1177 +{
16.1178 + FlushFunctionCache();
16.1179 + delete [] m_pub_funcs;
16.1180 + m_pub_funcs = NULL;
16.1181 +#if 0
16.1182 + delete [] m_priv_funcs;
16.1183 + m_priv_funcs = NULL;
16.1184 +#endif
16.1185 +}
16.1186 +
16.1187 +void BaseContext::SetContext(sp_context_t *_ctx)
16.1188 +{
16.1189 + if (!_ctx)
16.1190 + {
16.1191 + return;
16.1192 + }
16.1193 + ctx = _ctx;
16.1194 + ctx->context = this;
16.1195 + ctx->dbreak = GlobalDebugBreak;
16.1196 + RefreshFunctionCache();
16.1197 +}
16.1198 +
16.1199 +IVirtualMachine *BaseContext::GetVirtualMachine()
16.1200 +{
16.1201 + return (IVirtualMachine *)ctx->vmbase;
16.1202 +}
16.1203 +
16.1204 +sp_context_t *BaseContext::GetContext()
16.1205 +{
16.1206 + return ctx;
16.1207 +}
16.1208 +
16.1209 +bool BaseContext::IsDebugging()
16.1210 +{
16.1211 + return (ctx->flags & SPFLAG_PLUGIN_DEBUG);
16.1212 +}
16.1213 +
16.1214 +int BaseContext::SetDebugBreak(SPVM_DEBUGBREAK newpfn, SPVM_DEBUGBREAK *oldpfn)
16.1215 +{
16.1216 + if (!IsDebugging())
16.1217 + {
16.1218 + return SP_ERROR_NOTDEBUGGING;
16.1219 + }
16.1220 +
16.1221 + *oldpfn = ctx->dbreak;
16.1222 + ctx->dbreak = newpfn;
16.1223 +
16.1224 + return SP_ERROR_NONE;
16.1225 +}
16.1226 +
16.1227 +IPluginDebugInfo *BaseContext::GetDebugInfo()
16.1228 +{
16.1229 + return this;
16.1230 +}
16.1231 +
16.1232 +int BaseContext::Execute(uint32_t code_addr, cell_t *result)
16.1233 +{
16.1234 + if ((ctx->flags & SPFLAG_PLUGIN_PAUSED) == SPFLAG_PLUGIN_PAUSED)
16.1235 + {
16.1236 + return SP_ERROR_NOT_RUNNABLE;
16.1237 + }
16.1238 +
16.1239 + /* tada, prevent a crash */
16.1240 + cell_t _ignore_result;
16.1241 + if (!result)
16.1242 + {
16.1243 + result = &_ignore_result;
16.1244 + }
16.1245 +
16.1246 + IVirtualMachine *vm = (IVirtualMachine *)ctx->vmbase;
16.1247 +
16.1248 + uint32_t pushcount = ctx->pushcount;
16.1249 + int err;
16.1250 +
16.1251 + PushCell(pushcount++);
16.1252 + ctx->pushcount = 0;
16.1253 +
16.1254 + cell_t save_sp = ctx->sp;
16.1255 + cell_t save_hp = ctx->hp;
16.1256 + uint32_t n_idx = ctx->n_idx;
16.1257 +
16.1258 + bool wasExec = m_InExec;
16.1259 +
16.1260 + /* Clear the error state, if any */
16.1261 + ctx->n_err = SP_ERROR_NONE;
16.1262 + ctx->n_idx = 0;
16.1263 + m_InExec = true;
16.1264 + m_MsgCache[0] = '\0';
16.1265 + m_CustomMsg = false;
16.1266 +
16.1267 + g_SourcePawn.PushTracer(ctx);
16.1268 +
16.1269 + err = vm->ContextExecute(ctx, code_addr, result);
16.1270 +
16.1271 + m_InExec = wasExec;
16.1272 +
16.1273 + /**
16.1274 + * :TODO: Calling from a plugin in here will erase the cached message...
16.1275 + * Should that be documented?
16.1276 + */
16.1277 + g_SourcePawn.PopTracer(err, m_CustomMsg ? m_MsgCache : NULL);
16.1278 +
16.1279 +#if 1//defined _DEBUG
16.1280 + //:TODO: debug code for leak detection, remove before the release?
16.1281 + if (err == SP_ERROR_NONE)
16.1282 + {
16.1283 + if ((ctx->sp - (cell_t)(pushcount * sizeof(cell_t))) != save_sp)
16.1284 + {
16.1285 + const char *name;
16.1286 + ctx->context->GetDebugInfo()->LookupFunction(code_addr, &name);
16.1287 + g_Logger.LogError("Stack leak detected: sp:%d should be %d on function %s", ctx->sp, save_sp, name);
16.1288 + }
16.1289 + if (ctx->hp != save_hp)
16.1290 + {
16.1291 + const char *name;
16.1292 + ctx->context->GetDebugInfo()->LookupFunction(code_addr, &name);
16.1293 + g_Logger.LogError("Heap leak detected: hp:%d should be %d on function %s", ctx->hp, save_hp, name);
16.1294 + }
16.1295 + //assert(ctx->sp - pushcount * sizeof(cell_t) == save_sp);
16.1296 + //assert(ctx->hp == save_hp);
16.1297 + }
16.1298 +#endif
16.1299 + if (err != SP_ERROR_NONE)
16.1300 + {
16.1301 + ctx->sp = save_sp;
16.1302 + ctx->hp = save_hp;
16.1303 + }
16.1304 +
16.1305 + ctx->n_idx = n_idx;
16.1306 +
16.1307 + return err;
16.1308 +}
16.1309 +
16.1310 +void BaseContext::SetErrorMessage(const char *msg, va_list ap)
16.1311 +{
16.1312 + m_CustomMsg = true;
16.1313 +
16.1314 + vsnprintf(m_MsgCache, sizeof(m_MsgCache), msg, ap);
16.1315 +}
16.1316 +
16.1317 +cell_t BaseContext::ThrowNativeErrorEx(int error, const char *msg, ...)
16.1318 +{
16.1319 + if (!m_InExec)
16.1320 + {
16.1321 + return 0;
16.1322 + }
16.1323 +
16.1324 + ctx->n_err = error;
16.1325 +
16.1326 + if (msg)
16.1327 + {
16.1328 + va_list ap;
16.1329 + va_start(ap, msg);
16.1330 + SetErrorMessage(msg, ap);
16.1331 + va_end(ap);
16.1332 + }
16.1333 +
16.1334 + return 0;
16.1335 +}
16.1336 +
16.1337 +cell_t BaseContext::ThrowNativeError(const char *msg, ...)
16.1338 +{
16.1339 + if (!m_InExec)
16.1340 + {
16.1341 + return 0;
16.1342 + }
16.1343 +
16.1344 + ctx->n_err = SP_ERROR_NATIVE;
16.1345 +
16.1346 + if (msg)
16.1347 + {
16.1348 + va_list ap;
16.1349 + va_start(ap, msg);
16.1350 + SetErrorMessage(msg, ap);
16.1351 + va_end(ap);
16.1352 + }
16.1353 +
16.1354 + return 0;
16.1355 +}
16.1356 +
16.1357 +int BaseContext::HeapAlloc(unsigned int cells, cell_t *local_addr, cell_t **phys_addr)
16.1358 +{
16.1359 + cell_t *addr;
16.1360 + ucell_t realmem;
16.1361 +
16.1362 +#if 0
16.1363 + if (cells > CELLBOUNDMAX)
16.1364 + {
16.1365 + return SP_ERROR_ARAM;
16.1366 + }
16.1367 +#else
16.1368 + assert(cells < CELLBOUNDMAX);
16.1369 +#endif
16.1370 +
16.1371 + realmem = cells * sizeof(cell_t);
16.1372 +
16.1373 + /**
16.1374 + * Check if the space between the heap and stack is sufficient.
16.1375 + */
16.1376 + if ((cell_t)(ctx->sp - ctx->hp - realmem) < STACKMARGIN)
16.1377 + {
16.1378 + return SP_ERROR_HEAPLOW;
16.1379 + }
16.1380 +
16.1381 + addr = (cell_t *)(ctx->memory + ctx->hp);
16.1382 + /* store size of allocation in cells */
16.1383 + *addr = (cell_t)cells;
16.1384 + addr++;
16.1385 + ctx->hp += sizeof(cell_t);
16.1386 +
16.1387 + *local_addr = ctx->hp;
16.1388 +
16.1389 + if (phys_addr)
16.1390 + {
16.1391 + *phys_addr = addr;
16.1392 + }
16.1393 +
16.1394 + ctx->hp += realmem;
16.1395 +
16.1396 + return SP_ERROR_NONE;
16.1397 +}
16.1398 +
16.1399 +int BaseContext::HeapPop(cell_t local_addr)
16.1400 +{
16.1401 + cell_t cellcount;
16.1402 + cell_t *addr;
16.1403 +
16.1404 + /* check the bounds of this address */
16.1405 + local_addr -= sizeof(cell_t);
16.1406 + if (local_addr < ctx->heap_base || local_addr >= ctx->sp)
16.1407 + {
16.1408 + return SP_ERROR_INVALID_ADDRESS;
16.1409 + }
16.1410 +
16.1411 + addr = (cell_t *)(ctx->memory + local_addr);
16.1412 + cellcount = (*addr) * sizeof(cell_t);
16.1413 + /* check if this memory count looks valid */
16.1414 + if ((signed)(ctx->hp - cellcount - sizeof(cell_t)) != local_addr)
16.1415 + {
16.1416 + return SP_ERROR_INVALID_ADDRESS;
16.1417 + }
16.1418 +
16.1419 + ctx->hp = local_addr;
16.1420 +
16.1421 + return SP_ERROR_NONE;
16.1422 +}
16.1423 +
16.1424 +
16.1425 +int BaseContext::HeapRelease(cell_t local_addr)
16.1426 +{
16.1427 + if (local_addr < ctx->heap_base)
16.1428 + {
16.1429 + return SP_ERROR_INVALID_ADDRESS;
16.1430 + }
16.1431 +
16.1432 + ctx->hp = local_addr - sizeof(cell_t);
16.1433 +
16.1434 + return SP_ERROR_NONE;
16.1435 +}
16.1436 +
16.1437 +int BaseContext::FindNativeByName(const char *name, uint32_t *index)
16.1438 +{
16.1439 + int high;
16.1440 +
16.1441 + high = ctx->plugin->info.natives_num - 1;
16.1442 +
16.1443 + for (uint32_t i=0; i<ctx->plugin->info.natives_num; i++)
16.1444 + {
16.1445 + if (strcmp(ctx->natives[i].name, name) == 0)
16.1446 + {
16.1447 + if (index)
16.1448 + {
16.1449 + *index = i;
16.1450 + }
16.1451 + return SP_ERROR_NONE;
16.1452 + }
16.1453 + }
16.1454 +
16.1455 + return SP_ERROR_NOT_FOUND;
16.1456 +}
16.1457 +
16.1458 +int BaseContext::GetNativeByIndex(uint32_t index, sp_native_t **native)
16.1459 +{
16.1460 + if (index >= ctx->plugin->info.natives_num)
16.1461 + {
16.1462 + return SP_ERROR_INDEX;
16.1463 + }
16.1464 +
16.1465 + if (native)
16.1466 + {
16.1467 + *native = &(ctx->natives[index]);
16.1468 + }
16.1469 +
16.1470 + return SP_ERROR_NONE;
16.1471 +}
16.1472 +
16.1473 +
16.1474 +uint32_t BaseContext::GetNativesNum()
16.1475 +{
16.1476 + return ctx->plugin->info.natives_num;
16.1477 +}
16.1478 +
16.1479 +int BaseContext::FindPublicByName(const char *name, uint32_t *index)
16.1480 +{
16.1481 + int diff, high, low;
16.1482 + uint32_t mid;
16.1483 +
16.1484 + high = ctx->plugin->info.publics_num - 1;
16.1485 + low = 0;
16.1486 +
16.1487 + while (low <= high)
16.1488 + {
16.1489 + mid = (low + high) / 2;
16.1490 + diff = strcmp(ctx->publics[mid].name, name);
16.1491 + if (diff == 0)
16.1492 + {
16.1493 + if (index)
16.1494 + {
16.1495 + *index = mid;
16.1496 + }
16.1497 + return SP_ERROR_NONE;
16.1498 + } else if (diff < 0) {
16.1499 + low = mid + 1;
16.1500 + } else {
16.1501 + high = mid - 1;
16.1502 + }
16.1503 + }
16.1504 +
16.1505 + return SP_ERROR_NOT_FOUND;
16.1506 +}
16.1507 +
16.1508 +int BaseContext::GetPublicByIndex(uint32_t index, sp_public_t **pblic)
16.1509 +{
16.1510 + if (index >= ctx->plugin->info.publics_num)
16.1511 + {
16.1512 + return SP_ERROR_INDEX;
16.1513 + }
16.1514 +
16.1515 + if (pblic)
16.1516 + {
16.1517 + *pblic = &(ctx->publics[index]);
16.1518 + }
16.1519 +
16.1520 + return SP_ERROR_NONE;
16.1521 +}
16.1522 +
16.1523 +uint32_t BaseContext::GetPublicsNum()
16.1524 +{
16.1525 + return ctx->plugin->info.publics_num;
16.1526 +}
16.1527 +
16.1528 +int BaseContext::GetPubvarByIndex(uint32_t index, sp_pubvar_t **pubvar)
16.1529 +{
16.1530 + if (index >= ctx->plugin->info.pubvars_num)
16.1531 + {
16.1532 + return SP_ERROR_INDEX;
16.1533 + }
16.1534 +
16.1535 + if (pubvar)
16.1536 + {
16.1537 + *pubvar = &(ctx->pubvars[index]);
16.1538 + }
16.1539 +
16.1540 + return SP_ERROR_NONE;
16.1541 +}
16.1542 +
16.1543 +int BaseContext::FindPubvarByName(const char *name, uint32_t *index)
16.1544 +{
16.1545 + int diff, high, low;
16.1546 + uint32_t mid;
16.1547 +
16.1548 + high = ctx->plugin->info.pubvars_num - 1;
16.1549 + low = 0;
16.1550 +
16.1551 + while (low <= high)
16.1552 + {
16.1553 + mid = (low + high) / 2;
16.1554 + diff = strcmp(ctx->pubvars[mid].name, name);
16.1555 + if (diff == 0)
16.1556 + {
16.1557 + if (index)
16.1558 + {
16.1559 + *index = mid;
16.1560 + }
16.1561 + return SP_ERROR_NONE;
16.1562 + } else if (diff < 0) {
16.1563 + low = mid + 1;
16.1564 + } else {
16.1565 + high = mid - 1;
16.1566 + }
16.1567 + }
16.1568 +
16.1569 + return SP_ERROR_NOT_FOUND;
16.1570 +}
16.1571 +
16.1572 +int BaseContext::GetPubvarAddrs(uint32_t index, cell_t *local_addr, cell_t **phys_addr)
16.1573 +{
16.1574 + if (index >= ctx->plugin->info.pubvars_num)
16.1575 + {
16.1576 + return SP_ERROR_INDEX;
16.1577 + }
16.1578 +
16.1579 + *local_addr = ctx->plugin->info.pubvars[index].address;
16.1580 + *phys_addr = ctx->pubvars[index].offs;
16.1581 +
16.1582 + return SP_ERROR_NONE;
16.1583 +}
16.1584 +
16.1585 +uint32_t BaseContext::GetPubVarsNum()
16.1586 +{
16.1587 + return ctx->plugin->info.pubvars_num;
16.1588 +}
16.1589 +
16.1590 +int BaseContext::BindNatives(const sp_nativeinfo_t *natives, unsigned int num, int overwrite)
16.1591 +{
16.1592 + uint32_t i, j, max;
16.1593 +
16.1594 + max = ctx->plugin->info.natives_num;
16.1595 +
16.1596 + for (i=0; i<max; i++)
16.1597 + {
16.1598 + if ((ctx->natives[i].status == SP_NATIVE_BOUND) && !overwrite)
16.1599 + {
16.1600 + continue;
16.1601 + }
16.1602 +
16.1603 + for (j=0; (natives[j].name) && (!num || j<num); j++)
16.1604 + {
16.1605 + if (!strcmp(ctx->natives[i].name, natives[j].name))
16.1606 + {
16.1607 + ctx->natives[i].pfn = natives[j].func;
16.1608 + ctx->natives[i].status = SP_NATIVE_BOUND;
16.1609 + }
16.1610 + }
16.1611 + }
16.1612 +
16.1613 + return SP_ERROR_NONE;
16.1614 +}
16.1615 +
16.1616 +int BaseContext::BindNative(const sp_nativeinfo_t *native)
16.1617 +{
16.1618 + uint32_t index;
16.1619 + int err;
16.1620 +
16.1621 + if ((err = FindNativeByName(native->name, &index)) != SP_ERROR_NONE)
16.1622 + {
16.1623 + return err;
16.1624 + }
16.1625 +
16.1626 + ctx->natives[index].pfn = native->func;
16.1627 + ctx->natives[index].status = SP_NATIVE_BOUND;
16.1628 +
16.1629 + return SP_ERROR_NONE;
16.1630 +}
16.1631 +
16.1632 +int BaseContext::BindNativeToAny(SPVM_NATIVE_FUNC native)
16.1633 +{
16.1634 + uint32_t nativesnum, i;
16.1635 +
16.1636 + nativesnum = ctx->plugin->info.natives_num;
16.1637 +
16.1638 + for (i=0; i<nativesnum; i++)
16.1639 + {
16.1640 + if (ctx->natives[i].status == SP_NATIVE_UNBOUND)
16.1641 + {
16.1642 + ctx->natives[i].pfn = native;
16.1643 + ctx->natives[i].status = SP_NATIVE_BOUND;
16.1644 + }
16.1645 + }
16.1646 +
16.1647 + return SP_ERROR_NONE;
16.1648 +}
16.1649 +
16.1650 +int BaseContext::LocalToPhysAddr(cell_t local_addr, cell_t **phys_addr)
16.1651 +{
16.1652 + if (((local_addr >= ctx->hp) && (local_addr < ctx->sp)) || (local_addr < 0) || ((ucell_t)local_addr >= ctx->mem_size))
16.1653 + {
16.1654 + return SP_ERROR_INVALID_ADDRESS;
16.1655 + }
16.1656 +
16.1657 + if (phys_addr)
16.1658 + {
16.1659 + *phys_addr = (cell_t *)(ctx->memory + local_addr);
16.1660 + }
16.1661 +
16.1662 + return SP_ERROR_NONE;
16.1663 +}
16.1664 +
16.1665 +int BaseContext::PushCell(cell_t value)
16.1666 +{
16.1667 + if ((ctx->hp + STACKMARGIN) > (cell_t)(ctx->sp - sizeof(cell_t)))
16.1668 + {
16.1669 + return SP_ERROR_STACKLOW;
16.1670 + }
16.1671 +
16.1672 + ctx->sp -= sizeof(cell_t);
16.1673 + *(cell_t *)(ctx->memory + ctx->sp) = value;
16.1674 + ctx->pushcount++;
16.1675 +
16.1676 + return SP_ERROR_NONE;
16.1677 +}
16.1678 +
16.1679 +int BaseContext::PushCellsFromArray(cell_t array[], unsigned int numcells)
16.1680 +{
16.1681 + unsigned int i;
16.1682 + int err;
16.1683 +
16.1684 + for (i=0; i<numcells; i++)
16.1685 + {
16.1686 + if ((err = PushCell(array[i])) != SP_ERROR_NONE)
16.1687 + {
16.1688 + ctx->sp += (cell_t)(i * sizeof(cell_t));
16.1689 + ctx->pushcount -= i;
16.1690 + return err;
16.1691 + }
16.1692 + }
16.1693 +
16.1694 + return SP_ERROR_NONE;
16.1695 +}
16.1696 +
16.1697 +int BaseContext::PushCellArray(cell_t *local_addr, cell_t **phys_addr, cell_t array[], unsigned int numcells)
16.1698 +{
16.1699 + cell_t *ph_addr;
16.1700 + int err;
16.1701 +
16.1702 + if ((err = HeapAlloc(numcells, local_addr, &ph_addr)) != SP_ERROR_NONE)
16.1703 + {
16.1704 + return err;
16.1705 + }
16.1706 +
16.1707 + memcpy(ph_addr, array, numcells * sizeof(cell_t));
16.1708 +
16.1709 + if ((err = PushCell(*local_addr)) != SP_ERROR_NONE)
16.1710 + {
16.1711 + HeapRelease(*local_addr);
16.1712 + return err;
16.1713 + }
16.1714 +
16.1715 + if (phys_addr)
16.1716 + {
16.1717 + *phys_addr = ph_addr;
16.1718 + }
16.1719 +
16.1720 + return SP_ERROR_NONE;
16.1721 +}
16.1722 +
16.1723 +int BaseContext::LocalToString(cell_t local_addr, char **addr)
16.1724 +{
16.1725 + if (((local_addr >= ctx->hp) && (local_addr < ctx->sp)) || (local_addr < 0) || ((ucell_t)local_addr >= ctx->mem_size))
16.1726 + {
16.1727 + return SP_ERROR_INVALID_ADDRESS;
16.1728 + }
16.1729 + *addr = (char *)(ctx->memory + local_addr);
16.1730 +
16.1731 + return SP_ERROR_NONE;
16.1732 +}
16.1733 +
16.1734 +int BaseContext::PushString(cell_t *local_addr, char **phys_addr, const char *string)
16.1735 +{
16.1736 + char *ph_addr;
16.1737 + int err;
16.1738 + unsigned int len, numcells = ((len=strlen(string)) + sizeof(cell_t)) / sizeof(cell_t);
16.1739 +
16.1740 + if ((err = HeapAlloc(numcells, local_addr, (cell_t **)&ph_addr)) != SP_ERROR_NONE)
16.1741 + {
16.1742 + return err;
16.1743 + }
16.1744 +
16.1745 + memcpy(ph_addr, string, len);
16.1746 + ph_addr[len] = '\0';
16.1747 +
16.1748 + if ((err = PushCell(*local_addr)) != SP_ERROR_NONE)
16.1749 + {
16.1750 + HeapRelease(*local_addr);
16.1751 + return err;
16.1752 + }
16.1753 +
16.1754 + if (phys_addr)
16.1755 + {
16.1756 + *phys_addr = ph_addr;
16.1757 + }
16.1758 +
16.1759 + return SP_ERROR_NONE;
16.1760 +}
16.1761 +
16.1762 +int BaseContext::StringToLocal(cell_t local_addr, size_t bytes, const char *source)
16.1763 +{
16.1764 + char *dest;
16.1765 + size_t len;
16.1766 +
16.1767 + if (((local_addr >= ctx->hp) && (local_addr < ctx->sp)) || (local_addr < 0) || ((ucell_t)local_addr >= ctx->mem_size))
16.1768 + {
16.1769 + return SP_ERROR_INVALID_ADDRESS;
16.1770 + }
16.1771 +
16.1772 + if (bytes == 0)
16.1773 + {
16.1774 + return SP_ERROR_NONE;
16.1775 + }
16.1776 +
16.1777 + len = strlen(source);
16.1778 + dest = (char *)(ctx->memory + local_addr);
16.1779 +
16.1780 + if (len >= bytes)
16.1781 + {
16.1782 + len = bytes - 1;
16.1783 + }
16.1784 +
16.1785 + memcpy(dest, source, len);
16.1786 + dest[len] = '\0';
16.1787 +
16.1788 + return SP_ERROR_NONE;
16.1789 +}
16.1790 +
16.1791 +inline int __CheckValidChar(char *c)
16.1792 +{
16.1793 + int count;
16.1794 + int bytecount = 0;
16.1795 +
16.1796 + for (count=1; (*c & 0xC0) == 0x80; count++)
16.1797 + {
16.1798 + c--;
16.1799 + }
16.1800 +
16.1801 + switch (*c & 0xF0)
16.1802 + {
16.1803 + case 0xC0:
16.1804 + case 0xD0:
16.1805 + {
16.1806 + bytecount = 2;
16.1807 + break;
16.1808 + }
16.1809 + case 0xE0:
16.1810 + {
16.1811 + bytecount = 3;
16.1812 + break;
16.1813 + }
16.1814 + case 0xF0:
16.1815 + {
16.1816 + bytecount = 4;
16.1817 + break;
16.1818 + }
16.1819 + }
16.1820 +
16.1821 + if (bytecount != count)
16.1822 + {
16.1823 + return count;
16.1824 + }
16.1825 +
16.1826 + return 0;
16.1827 +}
16.1828 +
16.1829 +int BaseContext::StringToLocalUTF8(cell_t local_addr, size_t maxbytes, const char *source, size_t *wrtnbytes)
16.1830 +{
16.1831 + char *dest;
16.1832 + size_t len;
16.1833 + bool needtocheck = false;
16.1834 +
16.1835 + if (((local_addr >= ctx->hp) && (local_addr < ctx->sp)) || (local_addr < 0) || ((ucell_t)local_addr >= ctx->mem_size))
16.1836 + {
16.1837 + return SP_ERROR_INVALID_ADDRESS;
16.1838 + }
16.1839 +
16.1840 + if (maxbytes == 0)
16.1841 + {
16.1842 + return SP_ERROR_NONE;
16.1843 + }
16.1844 +
16.1845 + len = strlen(source);
16.1846 + dest = (char *)(ctx->memory + local_addr);
16.1847 +
16.1848 + if ((size_t)len >= maxbytes)
16.1849 + {
16.1850 + len = maxbytes - 1;
16.1851 + needtocheck = true;
16.1852 + }
16.1853 +
16.1854 + memcpy(dest, source, len);
16.1855 + if ((dest[len-1] & 1<<7) && needtocheck)
16.1856 + {
16.1857 + len -= __CheckValidChar(dest+len-1);
16.1858 + }
16.1859 + dest[len] = '\0';
16.1860 +
16.1861 + if (wrtnbytes)
16.1862 + {
16.1863 + *wrtnbytes = len;
16.1864 + }
16.1865 +
16.1866 + return SP_ERROR_NONE;
16.1867 +}
16.1868 +
16.1869 +#define USHR(x) ((unsigned int)(x)>>1)
16.1870 +
16.1871 +int BaseContext::LookupFile(ucell_t addr, const char **filename)
16.1872 +{
16.1873 + int high, low, mid;
16.1874 +
16.1875 + high = ctx->plugin->debug.files_num;
16.1876 + low = -1;
16.1877 +
16.1878 + while (high - low > 1)
16.1879 + {
16.1880 + mid = USHR(low + high);
16.1881 + if (ctx->files[mid].addr <= addr)
16.1882 + {
16.1883 + low = mid;
16.1884 + } else {
16.1885 + high = mid;
16.1886 + }
16.1887 + }
16.1888 +
16.1889 + if (low == -1)
16.1890 + {
16.1891 + return SP_ERROR_NOT_FOUND;
16.1892 + }
16.1893 +
16.1894 + *filename = ctx->files[low].name;
16.1895 +
16.1896 + return SP_ERROR_NONE;
16.1897 +}
16.1898 +
16.1899 +int BaseContext::LookupFunction(ucell_t addr, const char **name)
16.1900 +{
16.1901 + uint32_t iter, max = ctx->plugin->debug.syms_num;
16.1902 +
16.1903 + for (iter=0; iter<max; iter++)
16.1904 + {
16.1905 + if ((ctx->symbols[iter].sym->ident == SP_SYM_FUNCTION)
16.1906 + && (ctx->symbols[iter].codestart <= addr)
16.1907 + && (ctx->symbols[iter].codeend > addr))
16.1908 + {
16.1909 + break;
16.1910 + }
16.1911 + }
16.1912 +
16.1913 + if (iter >= max)
16.1914 + {
16.1915 + return SP_ERROR_NOT_FOUND;
16.1916 + }
16.1917 +
16.1918 + *name = ctx->symbols[iter].name;
16.1919 +
16.1920 + return SP_ERROR_NONE;
16.1921 +}
16.1922 +
16.1923 +int BaseContext::LookupLine(ucell_t addr, uint32_t *line)
16.1924 +{
16.1925 + int high, low, mid;
16.1926 +
16.1927 + high = ctx->plugin->debug.lines_num;
16.1928 + low = -1;
16.1929 +
16.1930 + while (high - low > 1)
16.1931 + {
16.1932 + mid = USHR(low + high);
16.1933 + if (ctx->lines[mid].addr <= addr)
16.1934 + {
16.1935 + low = mid;
16.1936 + } else {
16.1937 + high = mid;
16.1938 + }
16.1939 + }
16.1940 +
16.1941 + if (low == -1)
16.1942 + {
16.1943 + return SP_ERROR_NOT_FOUND;
16.1944 + }
16.1945 +
16.1946 + /* Since the CIP occurs BEFORE the line, we have to add one */
16.1947 + *line = ctx->lines[low].line + 1;
16.1948 +
16.1949 + return SP_ERROR_NONE;
16.1950 +}
16.1951 +
16.1952 +IPluginFunction *BaseContext::GetFunctionById(funcid_t func_id)
16.1953 +{
16.1954 + CFunction *pFunc = NULL;
16.1955 +
16.1956 + if (func_id & 1)
16.1957 + {
16.1958 + func_id >>= 1;
16.1959 + if (func_id >= ctx->plugin->info.publics_num)
16.1960 + {
16.1961 + return NULL;
16.1962 + }
16.1963 + pFunc = m_pub_funcs[func_id];
16.1964 + if (!pFunc)
16.1965 + {
16.1966 + m_pub_funcs[func_id] = new CFunction(ctx->publics[func_id].code_offs, this);
16.1967 + pFunc = m_pub_funcs[func_id];
16.1968 + } else if (pFunc->IsInvalidated()) {
16.1969 + pFunc->Set(ctx->publics[func_id].code_offs, this);
16.1970 + }
16.1971 + } else {
16.1972 + /* :TODO: currently not used */
16.1973 +#if 0
16.1974 + func_id >>= 1;
16.1975 + unsigned int index;
16.1976 + if (!g_pVM->FunctionLookup(ctx, func_id, &index))
16.1977 + {
16.1978 + return NULL;
16.1979 + }
16.1980 + pFunc = m_priv_funcs[func_id];
16.1981 + if (!pFunc)
16.1982 + {
16.1983 + m_priv_funcs[func_id] = new CFunction(save, this);
16.1984 + pFunc = m_priv_funcs[func_id];
16.1985 + }
16.1986 +#endif
16.1987 + assert(false);
16.1988 + }
16.1989 +
16.1990 + return pFunc;
16.1991 +}
16.1992 +
16.1993 +IPluginFunction *BaseContext::GetFunctionByName(const char *public_name)
16.1994 +{
16.1995 + uint32_t index;
16.1996 +
16.1997 + if (FindPublicByName(public_name, &index) != SP_ERROR_NONE)
16.1998 + {
16.1999 + return NULL;
16.2000 + }
16.2001 +
16.2002 + CFunction *pFunc = m_pub_funcs[index];
16.2003 + if (!pFunc)
16.2004 + {
16.2005 + sp_public_t *pub = NULL;
16.2006 + GetPublicByIndex(index, &pub);
16.2007 + if (pub)
16.2008 + {
16.2009 + m_pub_funcs[index] = new CFunction(pub->code_offs, this);
16.2010 + }
16.2011 + pFunc = m_pub_funcs[index];
16.2012 + } else if (pFunc->IsInvalidated()) {
16.2013 + sp_public_t *pub = NULL;
16.2014 + GetPublicByIndex(index, &pub);
16.2015 + if (pub)
16.2016 + {
16.2017 + pFunc->Set(pub->code_offs, this);
16.2018 + } else {
16.2019 + pFunc = NULL;
16.2020 + }
16.2021 + }
16.2022 +
16.2023 + return pFunc;
16.2024 +}
16.2025 +
16.2026 +int BaseContext::LocalToStringNULL(cell_t local_addr, char **addr)
16.2027 +{
16.2028 + int err;
16.2029 + if ((err = LocalToString(local_addr, addr)) != SP_ERROR_NONE)
16.2030 + {
16.2031 + return err;
16.2032 + }
16.2033 +
16.2034 + if ((cell_t *)*addr == m_pNullString)
16.2035 + {
16.2036 + *addr = NULL;
16.2037 + }
16.2038 +
16.2039 + return SP_ERROR_NONE;
16.2040 +}
16.2041 +
16.2042 +#if defined SOURCEMOD_BUILD
16.2043 +SourceMod::IdentityToken_t *BaseContext::GetIdentity()
16.2044 +{
16.2045 + return m_pToken;
16.2046 +}
16.2047 +
16.2048 +void BaseContext::SetIdentity(SourceMod::IdentityToken_t *token)
16.2049 +{
16.2050 + m_pToken = token;
16.2051 +}
16.2052 +
16.2053 +cell_t *BaseContext::GetNullRef(SP_NULL_TYPE type)
16.2054 +{
16.2055 + if (type == SP_NULL_VECTOR)
16.2056 + {
16.2057 + return m_pNullVec;
16.2058 + }
16.2059 +
16.2060 + return NULL;
16.2061 +}
16.2062 +#endif
17.1 --- a/core/vm/sp_vm_engine.cpp Tue Aug 07 22:11:00 2007 +0000
17.2 +++ b/core/vm/sp_vm_engine.cpp Wed Aug 08 02:27:10 2007 +0000
17.3 @@ -81,6 +81,16 @@
17.4 "Call was aborted",
17.5 };
17.6
17.7 +const char *GetSourcePawnErrorMessage(int error)
17.8 +{
17.9 + if (error < 1 || error > ERROR_MESSAGE_MAX)
17.10 + {
17.11 + return NULL;
17.12 + }
17.13 +
17.14 + return g_ErrorMsgTable[error];
17.15 +}
17.16 +
17.17 SourcePawnEngine::SourcePawnEngine()
17.18 {
17.19 m_pDebugHook = NULL;
18.1 --- a/core/vm/sp_vm_engine.h Tue Aug 07 22:11:00 2007 +0000
18.2 +++ b/core/vm/sp_vm_engine.h Wed Aug 08 02:27:10 2007 +0000
18.3 @@ -45,7 +45,6 @@
18.4 unsigned int chain;
18.5 };
18.6
18.7 -
18.8 class CContextTrace : public IContextTrace
18.9 {
18.10 public:
19.1 --- a/plugins/include/menus.inc Tue Aug 07 22:11:00 2007 +0000
19.2 +++ b/plugins/include/menus.inc Wed Aug 08 02:27:10 2007 +0000
19.3 @@ -68,6 +68,11 @@
19.4 #define MENUFLAG_BUTTON_EXITBACK (1<<1) /**< Menu has an "exit back" button */
19.5 #define MENUFLAG_NO_SOUND (1<<2) /**< Menu will not have any select sounds */
19.6
19.7 +#define VOTEINFO_CLIENT_INDEX 0 /**< Client index */
19.8 +#define VOTEINFO_CLIENT_ITEM 1 /**< Item the client selected, or -1 for none */
19.9 +#define VOTEINFO_ITEM_INDEX 0 /**< Item index */
19.10 +#define VOTEINFO_ITEM_VOTES 1 /**< Number of votes for the item */
19.11 +
19.12 /**
19.13 * Reasons a menu can be cancelled.
19.14 */
19.15 @@ -351,12 +356,20 @@
19.16 native SetMenuOptionFlags(Handle:menu, flags);
19.17
19.18 /**
19.19 - * Returns whether a vote is in progress on the given menu.
19.20 + * Returns whether a vote is in progress.
19.21 *
19.22 - * @param menu Menu Handle.
19.23 + * @param menu Deprecated; no longer used.
19.24 * @return True if a vote is in progress, false otherwise.
19.25 */
19.26 -native bool:IsVoteInProgress(Handle:menu);
19.27 +native bool:IsVoteInProgress(Handle:menu=INVALID_HANDLE);
19.28 +
19.29 +/**
19.30 + * Cancels the vote in progress.
19.31 + *
19.32 + * @noreturn
19.33 + * @error If no vote is in progress.
19.34 + */
19.35 +native CancelVote();
19.36
19.37 /**
19.38 * Broadcasts a menu to a list of clients. The most selected item will be
19.39 @@ -372,7 +385,7 @@
19.40 * @param time Maximum time to leave menu on the screen.
19.41 * @return True on success, false if this menu already has a vote session
19.42 * in progress.
19.43 - * @error Invalid Handle.
19.44 + * @error Invalid Handle, or a vote is already in progress.
19.45 */
19.46 native bool:VoteMenu(Handle:menu, clients[], numClients, time);
19.47
19.48 @@ -402,6 +415,33 @@
19.49
19.50 return VoteMenu(menu, players, total, time);
19.51 }
19.52 +/**
19.53 + * Callback for when a vote has ended and results are available.
19.54 + *
19.55 + * @param num_votes Number of votes tallied in total.
19.56 + * @param num_clients Number of clients who could vote.
19.57 + * @param client_info Array of clients. Use VOTEINFO_CLIENT_ defines.
19.58 + * @param num_items Number of unique items that were selected.
19.59 + * @param item_info Array of items, sorted by count. Use VOTEINFO_ITEM
19.60 + * defines.
19.61 + * @noreturn
19.62 + */
19.63 +functag VoteHandler public(num_votes,
19.64 + num_clients,
19.65 + const client_info[][2],
19.66 + num_items,
19.67 + const item_info[][2]);
19.68 +
19.69 +/**
19.70 + * Sets an advanced vote handling callback. If this callback is set,
19.71 + * MenuAction_VoteEnd will not be called.
19.72 + *
19.73 + * @param menu Menu Handle.
19.74 + * @param callback Callback function.
19.75 + * @noreturn
19.76 + * @error Invalid Handle or callback.
19.77 + */
19.78 +native SetVoteResultCallback(Handle:menu, VoteHandler:callback);
19.79
19.80 /**
19.81 * Returns a style's global Handle.
20.1 --- a/public/IMenuManager.h Tue Aug 07 22:11:00 2007 +0000
20.2 +++ b/public/IMenuManager.h Wed Aug 08 02:27:10 2007 +0000
20.3 @@ -37,7 +37,7 @@
20.4 #include <IHandleSys.h>
20.5
20.6 #define SMINTERFACE_MENUMANAGER_NAME "IMenuManager"
20.7 -#define SMINTERFACE_MENUMANAGER_VERSION 8
20.8 +#define SMINTERFACE_MENUMANAGER_VERSION 9
20.9
20.10 /**
20.11 * @file IMenuManager.h
20.12 @@ -118,6 +118,27 @@
20.13 };
20.14
20.15 /**
20.16 + * @brief Contains information about a vote result.
20.17 + */
20.18 + struct menu_vote_result_t
20.19 + {
20.20 + unsigned int num_clients; /**< Number of clients the menu was displayed to */
20.21 + unsigned int num_votes; /**< Number of votes received */
20.22 + struct menu_client_vote_t
20.23 + {
20.24 + int client; /**< Client index */
20.25 + int item; /**< Item # (or -1 for none) */
20.26 + } *client_list; /**< Array of size num_clients */
20.27 + unsigned int num_items; /**< Number of items voted for */
20.28 + struct menu_item_vote_t
20.29 + {
20.30 + unsigned int item; /**< Item index */
20.31 + unsigned int count; /**< Number of votes */
20.32 + } *item_list; /**< Array of size num_items, sorted by count,
20.33 + descending */
20.34 + };
20.35 +
20.36 + /**
20.37 * @brief Reasons for a menu dying.
20.38 */
20.39 enum MenuCancelReason
20.40 @@ -141,6 +162,7 @@
20.41 MenuEnd_Cancelled = -3, /**< Menu was uncleanly cancelled */
20.42 MenuEnd_Exit = -4, /**< Menu was cleanly exited via "exit" */
20.43 MenuEnd_ExitBack = -5, /**< Menu was cleanly exited via "back" */
20.44 + MenuEnd_NoVotes = -6, /**< No votes received */
20.45 };
20.46
20.47
20.48 @@ -479,31 +501,14 @@
20.49 virtual IMenuPanel *CreatePanel() =0;
20.50
20.51 /**
20.52 - * @brief Returns whether or not the menu should have an "Exit" button for
20.53 - * paginated menus.
20.54 - *
20.55 - * @return True to have an exit button, false otherwise.
20.56 - */
20.57 - virtual bool GetExitButton() =0;
20.58 -
20.59 - /**
20.60 - * @brief Sets whether or not the menu should have an "Exit" button for
20.61 - * paginated menus.
20.62 - *
20.63 - * @param set True to enable, false to disable the exit button.
20.64 - * @return True on success, false if the exit button is
20.65 - * non-optional.
20.66 - */
20.67 - virtual bool SetExitButton(bool set) =0;
20.68 -
20.69 - /**
20.70 * @brief Sends the menu to a client.
20.71 *
20.72 * @param client Client index to display to.
20.73 * @param time Time to hold menu for.
20.74 + * @param alt_handler Alternate IMenuHandler.
20.75 * @return True on success, false otherwise.
20.76 */
20.77 - virtual bool Display(int client, unsigned int time) =0;
20.78 + virtual bool Display(int client, unsigned int time, IMenuHandler *alt_handler=NULL) =0;
20.79
20.80 /**
20.81 * @brief Destroys the menu and frees all associated resources.
20.82 @@ -531,46 +536,6 @@
20.83 * @return Handle_t handle value.
20.84 */
20.85 virtual Handle_t GetHandle() =0;
20.86 -
20.87 - /**
20.88 - * @brief Sends a menu to multiple clients as a vote menu. All callbacks
20.89 - * will be sent as normal, except two extras, OnMenuVoteStart and
20.90 - * OnMenuVoteEnd, will be called.
20.91 - *
20.92 - * @param clients Array of client indexes.
20.93 - * @param numClients Number of client indexes in the array.
20.94 - * @param maxTime Maximum amount of time to hold the vote.
20.95 - * @param flags Optional voting flags (currently unused).
20.96 - * @return True on success, false if a vote is already in
20.97 - * progress (the menu must be cancelled first).
20.98 - */
20.99 - virtual bool BroadcastVote(int clients[],
20.100 - unsigned int numClients,
20.101 - unsigned int maxTime,
20.102 - unsigned int flags=0) =0;
20.103 -
20.104 - /**
20.105 - * @brief Returns whether a vote menu is active.
20.106 - *
20.107 - * @return True if a vote menu is active, false otherwise.
20.108 - */
20.109 - virtual bool IsVoteInProgress() =0;
20.110 -
20.111 - /**
20.112 - * @brief Returns whether to draw a "Back" button on the first page.
20.113 - * ExitBack buttons are disabled by default.
20.114 - *
20.115 - * @return True if enabled, false otherwise.
20.116 - */
20.117 - virtual bool GetExitBackButton() =0;
20.118 -
20.119 - /**
20.120 - * @brief Sets whether to draw a "Back" button on the first page.
20.121 - * ExitBack buttons are disabled by default.
20.122 - *
20.123 - * @param set True to enable, false to disable.
20.124 - */
20.125 - virtual void SetExitBackButton(bool set) =0;
20.126
20.127 /**
20.128 * @brief Returns menu option flags.
20.129 @@ -585,6 +550,13 @@
20.130 * @param flags Menu option flags.
20.131 */
20.132 virtual void SetMenuOptionFlags(unsigned int flags) =0;
20.133 +
20.134 + /**
20.135 + * @brief Returns the menu's handler.
20.136 + *
20.137 + * @return IMenuHandler of the menu.
20.138 + */
20.139 + virtual IMenuHandler *GetHandler() =0;
20.140 };
20.141
20.142 /**
20.143 @@ -716,20 +688,15 @@
20.144 * while it is in this function.
20.145 *
20.146 * @param menu Menu pointer.
20.147 - * @param item Item position that was chosen by a majority.
20.148 - * @param winningVotes Number of votes from the winning item.
20.149 - * @param totalVotes Number of votes total.
20.150 + * @param results Menu vote results.
20.151 */
20.152 - virtual void OnMenuVoteEnd(IBaseMenu *menu,
20.153 - unsigned int item,
20.154 - unsigned int winningVotes,
20.155 - unsigned int totalVotes)
20.156 + virtual void OnMenuVoteResults(IBaseMenu *menu, const menu_vote_result_t *results)
20.157 {
20.158 }
20.159
20.160 /**
20.161 * @brief Called when a vote is cancelled. If this is called, then
20.162 - * OnMenuVoteEnd() will not be called. In both cases, OnMenuEnd will
20.163 + * OnMenuVoteResults() will not be called. In both cases, OnMenuEnd will
20.164 * always be called.
20.165 *
20.166 * @param menu Menu pointer.
20.167 @@ -737,43 +704,18 @@
20.168 virtual void OnMenuVoteCancel(IBaseMenu *menu)
20.169 {
20.170 }
20.171 - };
20.172 -
20.173 - /**
20.174 - * @brief Contains functions for managing a vote handler.
20.175 - */
20.176 - class IVoteMenuHandler : public IMenuHandler
20.177 - {
20.178 - public:
20.179 - /**
20.180 - * @brief Returns whether or not a vote is in progress.
20.181 - *
20.182 - * @return True if a vote is in progress, false otherwise.
20.183 - */
20.184 - virtual bool IsVoteInProgress() =0;
20.185
20.186 /**
20.187 - * @brief Use this to mark the vote as in progress (start).
20.188 + * @brief Call to set private handler stuff.
20.189 *
20.190 - * @param menu Menu pointer.
20.191 - */
20.192 - virtual void InitializeVoting(IBaseMenu *menu) =0;
20.193 -
20.194 - /**
20.195 - * @brief Use this to notify that all clients' displays have been
20.196 - * processed (i.e., there are no more clients to display to).
20.197 + * @param option Option name.
20.198 + * @param data Private data.
20.199 + * @return True if set, false if invalid or unrecognized.
20.200 */
20.201 - virtual void StartVoting() =0;
20.202 -
20.203 - /**
20.204 - * @brief Notifies the vote handler that the voting should be
20.205 - * cancelled.
20.206 - *
20.207 - * Cancellation is not immediate and will only occur once every menu
20.208 - * has been cancelled from clients. Thus this should only be called
20.209 - * from the beginning of IBaseMenu::Cancel.
20.210 - */
20.211 - virtual void CancelVoting() =0;
20.212 + virtual bool OnSetHandlerOption(const char *option, const void *data)
20.213 + {
20.214 + return false;
20.215 + }
20.216 };
20.217
20.218 /**
20.219 @@ -792,7 +734,7 @@
20.220 }
20.221 virtual bool IsVersionCompatible(unsigned int version)
20.222 {
20.223 - if (version < 7 || version > GetInterfaceVersion())
20.224 + if (version < 9 || version > GetInterfaceVersion())
20.225 {
20.226 return false;
20.227 }
20.228 @@ -815,7 +757,7 @@
20.229 virtual IMenuStyle *GetDefaultStyle() =0;
20.230
20.231 /**
20.232 - * @brief Given a set of menu states, converts it to an IDisplay object.
20.233 + * @brief Given a set of menu states, converts it to an IMenuPanel object.
20.234 *
20.235 * The state parameter is both INPUT and OUTPUT.
20.236 * INPUT: menu, mh, firstItem, lastItem
20.237 @@ -823,7 +765,7 @@
20.238 *
20.239 * @param client Client index.
20.240 * @param states Menu states.
20.241 - * @return IDisplay pointer, or NULL if no items could be
20.242 + * @return IMenuPanel pointer, or NULL if no items could be
20.243 * found in the IBaseMenu pointer, or NULL if any
20.244 * other error occurred. Any valid pointer must
20.245 * be freed using IMenuPanel::DeleteThis.
20.246 @@ -831,25 +773,40 @@
20.247 virtual IMenuPanel *RenderMenu(int client, menu_states_t &states, ItemOrder order) =0;
20.248
20.249 /**
20.250 - * @brief Creates a standard voting wrapper. The wrapper is not
20.251 - * re-entrant; a second menu cannot be displayed on the same handler
20.252 - * at the same time.
20.253 + * @brief Cancels a menu. Calls IBaseMenu::Cancel() after doing some preparatory
20.254 + * work. This should always be used instead of directly calling Cancel().
20.255 *
20.256 - * @param mh Menu handler to wrap around.
20.257 - * @return An IMenuHandler pointer that is a wrapper
20.258 - * around IMenuHandler callbacks to invoke
20.259 - * voting related callbacks.
20.260 + * @param menu IBaseMenu pointer.
20.261 */
20.262 - virtual IVoteMenuHandler *CreateVoteWrapper(IMenuHandler *mh) =0;
20.263 + virtual void CancelMenu(IBaseMenu *menu) =0;
20.264
20.265 /**
20.266 - * @brief Frees a standard voting wrapper.
20.267 + * @brief Displays a menu as a vote.
20.268 *
20.269 - * @param mh Menu handler pointer created by
20.270 - * CreateVoteWrapper(). NULL values will be
20.271 - * safely ignored.
20.272 + * @param menu IBaseMenu pointer.
20.273 + * @param num_clients Number of clients to display to.
20.274 + * @param clients Client index array.
20.275 + * @param max_time Maximum time to hold menu for.
20.276 + * @param flags Vote flags (currently unused).
20.277 + * @return True on success, false if a vote is in progress.
20.278 */
20.279 - virtual void ReleaseVoteWrapper(IVoteMenuHandler *mh) =0;
20.280 + virtual bool StartVote(IBaseMenu *menu,
20.281 + unsigned int num_clients,
20.282 + int clients[],
20.283 + unsigned int max_time,
20.284 + unsigned int flags=0) =0;
20.285 +
20.286 + /**
20.287 + * @brief Returns whether or not a vote is in progress.
20.288 + *
20.289 + * @return True if a vote is in progress, false otherwise.
20.290 + */
20.291 + virtual bool IsVoteInProgress() =0;
20.292 +
20.293 + /**
20.294 + * @brief Cancels the vote in progress. This calls IBaseMenu::Cancel().
20.295 + */
20.296 + virtual void CancelVoting() =0;
20.297 };
20.298 }
20.299