jit refactoring branch
--HG-- branch : refac-jit extra : convert_revision : svn%3A39bc706e-5318-0410-9160-8a85361fbb7c/branches/refac-jit%402369
This commit is contained in:
@@ -0,0 +1,96 @@
|
||||
/**
|
||||
* vim: set ts=4 :
|
||||
* =============================================================================
|
||||
* SourceMod
|
||||
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
|
||||
* =============================================================================
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, version 3.0, as published by the
|
||||
* Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* As a special exception, AlliedModders LLC gives you permission to link the
|
||||
* code of this program (as well as its derivative works) to "Half-Life 2," the
|
||||
* "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
|
||||
* by the Valve Corporation. You must obey the GNU General Public License in
|
||||
* all respects for all other code used. Additionally, AlliedModders LLC grants
|
||||
* this exception to all derivative works. AlliedModders LLC defines further
|
||||
* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
|
||||
* or <http://www.sourcemod.net/license.php>.
|
||||
*
|
||||
* Version: $Id$
|
||||
*/
|
||||
|
||||
#include "ADTFactory.h"
|
||||
#include "sm_globals.h"
|
||||
#include "ShareSys.h"
|
||||
|
||||
ADTFactory g_AdtFactory;
|
||||
|
||||
const char *ADTFactory::GetInterfaceName()
|
||||
{
|
||||
return SMINTERFACE_ADTFACTORY_NAME;
|
||||
}
|
||||
|
||||
unsigned int ADTFactory::GetInterfaceVersion()
|
||||
{
|
||||
return SMINTERFACE_ADTFACTORY_VERSION;
|
||||
}
|
||||
|
||||
void ADTFactory::OnSourceModAllInitialized()
|
||||
{
|
||||
g_ShareSys.AddInterface(NULL, this);
|
||||
}
|
||||
|
||||
IBasicTrie *ADTFactory::CreateBasicTrie()
|
||||
{
|
||||
return new BaseTrie();
|
||||
}
|
||||
|
||||
BaseTrie::BaseTrie()
|
||||
{
|
||||
m_pTrie = sm_trie_create();
|
||||
}
|
||||
|
||||
BaseTrie::~BaseTrie()
|
||||
{
|
||||
sm_trie_destroy(m_pTrie);
|
||||
}
|
||||
|
||||
bool BaseTrie::Insert(const char *key, void *value)
|
||||
{
|
||||
return sm_trie_insert(m_pTrie, key, value);
|
||||
}
|
||||
|
||||
bool BaseTrie::Retrieve(const char *key, void **value)
|
||||
{
|
||||
return sm_trie_retrieve(m_pTrie, key, value);
|
||||
}
|
||||
|
||||
bool BaseTrie::Delete(const char *key)
|
||||
{
|
||||
return sm_trie_delete(m_pTrie, key);
|
||||
}
|
||||
|
||||
void BaseTrie::Clear()
|
||||
{
|
||||
sm_trie_clear(m_pTrie);
|
||||
}
|
||||
|
||||
void BaseTrie::Destroy()
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
|
||||
bool BaseTrie::Replace(const char *key, void *value)
|
||||
{
|
||||
return sm_trie_replace(m_pTrie, key, value);
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
/**
|
||||
* vim: set ts=4 :
|
||||
* =============================================================================
|
||||
* SourceMod
|
||||
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
|
||||
* =============================================================================
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, version 3.0, as published by the
|
||||
* Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* As a special exception, AlliedModders LLC gives you permission to link the
|
||||
* code of this program (as well as its derivative works) to "Half-Life 2," the
|
||||
* "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
|
||||
* by the Valve Corporation. You must obey the GNU General Public License in
|
||||
* all respects for all other code used. Additionally, AlliedModders LLC grants
|
||||
* this exception to all derivative works. AlliedModders LLC defines further
|
||||
* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
|
||||
* or <http://www.sourcemod.net/license.php>.
|
||||
*
|
||||
* Version: $Id$
|
||||
*/
|
||||
|
||||
#ifndef _INCLUDE_SOURCEMOD_ADTFACTORY_H_
|
||||
#define _INCLUDE_SOURCEMOD_ADTFACTORY_H_
|
||||
|
||||
#include <IADTFactory.h>
|
||||
#include "sm_globals.h"
|
||||
#include "sm_trie.h"
|
||||
|
||||
using namespace SourceMod;
|
||||
|
||||
class BaseTrie : public IBasicTrie
|
||||
{
|
||||
public:
|
||||
BaseTrie();
|
||||
virtual ~BaseTrie();
|
||||
virtual bool Insert(const char *key, void *value);
|
||||
virtual bool Replace(const char *key, void *value);
|
||||
virtual bool Retrieve(const char *key, void **value);
|
||||
virtual bool Delete(const char *key);
|
||||
virtual void Clear();
|
||||
virtual void Destroy();
|
||||
private:
|
||||
Trie *m_pTrie;
|
||||
};
|
||||
|
||||
class ADTFactory :
|
||||
public SMGlobalClass,
|
||||
public IADTFactory
|
||||
{
|
||||
public: //SMInterface
|
||||
const char *GetInterfaceName();
|
||||
unsigned int GetInterfaceVersion();
|
||||
public: //SMGlobalClass
|
||||
void OnSourceModAllInitialized();
|
||||
public: //IADTFactory
|
||||
IBasicTrie *CreateBasicTrie();
|
||||
};
|
||||
|
||||
#endif //_INCLUDE_SOURCEMOD_ADTFACTORY_H_
|
||||
|
||||
+2081
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,208 @@
|
||||
/**
|
||||
* vim: set ts=4 :
|
||||
* =============================================================================
|
||||
* SourceMod
|
||||
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
|
||||
* =============================================================================
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, version 3.0, as published by the
|
||||
* Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* As a special exception, AlliedModders LLC gives you permission to link the
|
||||
* code of this program (as well as its derivative works) to "Half-Life 2," the
|
||||
* "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
|
||||
* by the Valve Corporation. You must obey the GNU General Public License in
|
||||
* all respects for all other code used. Additionally, AlliedModders LLC grants
|
||||
* this exception to all derivative works. AlliedModders LLC defines further
|
||||
* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
|
||||
* or <http://www.sourcemod.net/license.php>.
|
||||
*
|
||||
* Version: $Id$
|
||||
*/
|
||||
|
||||
#ifndef _INCLUDE_SOURCEMOD_ADMINCACHE_H_
|
||||
#define _INCLUDE_SOURCEMOD_ADMINCACHE_H_
|
||||
|
||||
#include "sm_memtable.h"
|
||||
#include <sm_trie.h>
|
||||
#include <sh_list.h>
|
||||
#include <sh_string.h>
|
||||
#include <IAdminSystem.h>
|
||||
#include "sm_globals.h"
|
||||
#include <IForwardSys.h>
|
||||
|
||||
using namespace SourceHook;
|
||||
|
||||
#define GRP_MAGIC_SET 0xDEADFADE
|
||||
#define GRP_MAGIC_UNSET 0xFACEFACE
|
||||
#define USR_MAGIC_SET 0xDEADFACE
|
||||
#define USR_MAGIC_UNSET 0xFADEDEAD
|
||||
|
||||
struct AdminGroup
|
||||
{
|
||||
uint32_t magic; /* Magic flag, for memory validation (ugh) */
|
||||
unsigned int immunity_level; /* Immunity level */
|
||||
/* Immune from target table (-1 = nonexistent)
|
||||
* [0] = number of entries
|
||||
* [1...N] = immune targets
|
||||
*/
|
||||
int immune_table;
|
||||
Trie *pCmdTable; /* Command override table (can be NULL) */
|
||||
Trie *pCmdGrpTable; /* Command group override table (can be NULL) */
|
||||
int next_grp; /* Next group in the chain */
|
||||
int prev_grp; /* Previous group in the chain */
|
||||
int nameidx; /* Name */
|
||||
FlagBits addflags; /* Additive flags */
|
||||
};
|
||||
|
||||
struct AuthMethod
|
||||
{
|
||||
String name;
|
||||
Trie *table;
|
||||
};
|
||||
|
||||
struct UserAuth
|
||||
{
|
||||
unsigned int index; /* Index into auth table */
|
||||
int identidx; /* Index into the string table */
|
||||
};
|
||||
|
||||
struct AdminUser
|
||||
{
|
||||
uint32_t magic; /* Magic flag, for memory validation */
|
||||
FlagBits flags; /* Flags */
|
||||
FlagBits eflags; /* Effective flags */
|
||||
int nameidx; /* Name index */
|
||||
int password; /* Password index */
|
||||
unsigned int grp_count; /* Number of groups */
|
||||
unsigned int grp_size; /* Size of groups table */
|
||||
int grp_table; /* Group table itself */
|
||||
int next_user; /* Next user in the list */
|
||||
int prev_user; /* Previous user in the list */
|
||||
UserAuth auth; /* Auth method for this user */
|
||||
unsigned int immunity_level; /* Immunity level */
|
||||
unsigned int serialchange; /* Serial # for changes */
|
||||
};
|
||||
|
||||
class AdminCache :
|
||||
public IAdminSystem,
|
||||
public SMGlobalClass
|
||||
{
|
||||
public:
|
||||
AdminCache();
|
||||
~AdminCache();
|
||||
public: //SMGlobalClass
|
||||
void OnSourceModStartup(bool late);
|
||||
void OnSourceModAllInitialized();
|
||||
void OnSourceModLevelChange(const char *mapName);
|
||||
void OnSourceModShutdown();
|
||||
void OnSourceModPluginsLoaded();
|
||||
public: //IAdminSystem
|
||||
/** Command cache stuff */
|
||||
void AddCommandOverride(const char *cmd, OverrideType type, FlagBits flags);
|
||||
bool GetCommandOverride(const char *cmd, OverrideType type, FlagBits *flags);
|
||||
void UnsetCommandOverride(const char *cmd, OverrideType type);
|
||||
/** Group cache stuff */
|
||||
GroupId AddGroup(const char *group_name);
|
||||
GroupId FindGroupByName(const char *group_name);
|
||||
void SetGroupAddFlag(GroupId id, AdminFlag flag, bool enabled);
|
||||
bool GetGroupAddFlag(GroupId id, AdminFlag flag);
|
||||
FlagBits GetGroupAddFlags(GroupId id);
|
||||
void SetGroupGenericImmunity(GroupId id, ImmunityType type, bool enabled);
|
||||
bool GetGroupGenericImmunity(GroupId id, ImmunityType type);
|
||||
void InvalidateGroup(GroupId id);
|
||||
void AddGroupImmunity(GroupId id, GroupId other_id);
|
||||
unsigned int GetGroupImmunityCount(GroupId id);
|
||||
GroupId GetGroupImmunity(GroupId id, unsigned int number);
|
||||
void AddGroupCommandOverride(GroupId id, const char *name, OverrideType type, OverrideRule rule);
|
||||
bool GetGroupCommandOverride(GroupId id, const char *name, OverrideType type, OverrideRule *pRule);
|
||||
void DumpAdminCache(AdminCachePart part, bool rebuild);
|
||||
void AddAdminListener(IAdminListener *pListener);
|
||||
void RemoveAdminListener(IAdminListener *pListener);
|
||||
/** User stuff */
|
||||
void RegisterAuthIdentType(const char *name);
|
||||
AdminId CreateAdmin(const char *name);
|
||||
const char *GetAdminName(AdminId id);
|
||||
bool BindAdminIdentity(AdminId id, const char *auth, const char *ident);
|
||||
virtual void SetAdminFlag(AdminId id, AdminFlag flag, bool enabled);
|
||||
bool GetAdminFlag(AdminId id, AdminFlag flag, AccessMode mode);
|
||||
FlagBits GetAdminFlags(AdminId id, AccessMode mode);
|
||||
bool AdminInheritGroup(AdminId id, GroupId gid);
|
||||
unsigned int GetAdminGroupCount(AdminId id);
|
||||
GroupId GetAdminGroup(AdminId id, unsigned int index, const char **name);
|
||||
void SetAdminPassword(AdminId id, const char *password);
|
||||
const char *GetAdminPassword(AdminId id);
|
||||
AdminId FindAdminByIdentity(const char *auth, const char *identity);
|
||||
bool InvalidateAdmin(AdminId id);
|
||||
unsigned int FlagBitsToBitArray(FlagBits bits, bool array[], unsigned int maxSize);
|
||||
FlagBits FlagBitArrayToBits(const bool array[], unsigned int maxSize);
|
||||
FlagBits FlagArrayToBits(const AdminFlag array[], unsigned int numFlags);
|
||||
unsigned int FlagBitsToArray(FlagBits bits, AdminFlag array[], unsigned int maxSize);
|
||||
bool CheckAdminFlags(AdminId id, FlagBits bits);
|
||||
bool CanAdminTarget(AdminId id, AdminId target);
|
||||
void SetAdminFlags(AdminId id, AccessMode mode, FlagBits bits);
|
||||
bool FindFlag(const char *str, AdminFlag *pFlag);
|
||||
bool FindFlag(char c, AdminFlag *pAdmFlag);
|
||||
FlagBits ReadFlagString(const char *flags, const char **end);
|
||||
size_t FillFlagString(FlagBits bits, char *buffer, size_t maxlen);
|
||||
unsigned int GetAdminSerialChange(AdminId id);
|
||||
bool CanAdminUseCommand(int client, const char *cmd);
|
||||
const char *GetGroupName(GroupId gid);
|
||||
unsigned int SetGroupImmunityLevel(GroupId gid, unsigned int level);
|
||||
unsigned int GetGroupImmunityLevel(GroupId gid);
|
||||
unsigned int SetAdminImmunityLevel(AdminId id, unsigned int level);
|
||||
unsigned int GetAdminImmunityLevel(AdminId id);
|
||||
bool CheckAccess(int client,
|
||||
const char *cmd,
|
||||
FlagBits flags,
|
||||
bool override_only);
|
||||
bool FindFlagChar(AdminFlag flag, char *c);
|
||||
public:
|
||||
bool IsValidAdmin(AdminId id);
|
||||
void DumpCache(FILE *fp);
|
||||
AdminGroup *GetGroup(GroupId gid);
|
||||
AdminUser *GetUser(AdminId id);
|
||||
const char *GetString(int idx);
|
||||
private:
|
||||
void _UnsetCommandOverride(const char *cmd);
|
||||
void _UnsetCommandGroupOverride(const char *group);
|
||||
void InvalidateGroupCache();
|
||||
void InvalidateAdminCache(bool unlink_admins);
|
||||
void DumpCommandOverrideCache(OverrideType type);
|
||||
Trie *GetMethodByIndex(unsigned int index);
|
||||
bool GetMethodIndex(const char *name, unsigned int *_index);
|
||||
const char *GetMethodName(unsigned int index);
|
||||
void NameFlag(const char *str, AdminFlag flag);
|
||||
public:
|
||||
BaseStringTable *m_pStrings;
|
||||
BaseMemTable *m_pMemory;
|
||||
Trie *m_pCmdOverrides;
|
||||
Trie *m_pCmdGrpOverrides;
|
||||
int m_FirstGroup;
|
||||
int m_LastGroup;
|
||||
int m_FreeGroupList;
|
||||
Trie *m_pGroups;
|
||||
List<IAdminListener *> m_hooks;
|
||||
List<AuthMethod> m_AuthMethods;
|
||||
Trie *m_pAuthTables;
|
||||
IForward *m_pCacheFwd;
|
||||
int m_FirstUser;
|
||||
int m_LastUser;
|
||||
int m_FreeUserList;
|
||||
bool m_InvalidatingAdmins;
|
||||
bool m_destroying;
|
||||
Trie *m_pLevelNames;
|
||||
};
|
||||
|
||||
extern AdminCache g_Admins;
|
||||
|
||||
#endif //_INCLUDE_SOURCEMOD_ADMINCACHE_H_
|
||||
@@ -0,0 +1,259 @@
|
||||
/**
|
||||
* vim: set ts=4 :
|
||||
* =============================================================================
|
||||
* SourceMod
|
||||
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
|
||||
* =============================================================================
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, version 3.0, as published by the
|
||||
* Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* As a special exception, AlliedModders LLC gives you permission to link the
|
||||
* code of this program (as well as its derivative works) to "Half-Life 2," the
|
||||
* "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
|
||||
* by the Valve Corporation. You must obey the GNU General Public License in
|
||||
* all respects for all other code used. Additionally, AlliedModders LLC grants
|
||||
* this exception to all derivative works. AlliedModders LLC defines further
|
||||
* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
|
||||
* or <http://www.sourcemod.net/license.php>.
|
||||
*
|
||||
* Version: $Id$
|
||||
*/
|
||||
|
||||
#include <malloc.h>
|
||||
#include <string.h>
|
||||
#include "CDataPack.h"
|
||||
|
||||
#define DATAPACK_INITIAL_SIZE 512
|
||||
|
||||
CDataPack::CDataPack()
|
||||
{
|
||||
m_pBase = (char *)malloc(DATAPACK_INITIAL_SIZE);
|
||||
m_capacity = DATAPACK_INITIAL_SIZE;
|
||||
Initialize();
|
||||
}
|
||||
|
||||
CDataPack::~CDataPack()
|
||||
{
|
||||
free(m_pBase);
|
||||
}
|
||||
|
||||
void CDataPack::Initialize()
|
||||
{
|
||||
m_curptr = m_pBase;
|
||||
m_size = 0;
|
||||
}
|
||||
|
||||
void CDataPack::CheckSize(size_t typesize)
|
||||
{
|
||||
if (m_curptr - m_pBase + typesize <= m_capacity)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
size_t pos = m_curptr - m_pBase;
|
||||
do
|
||||
{
|
||||
m_capacity *= 2;
|
||||
m_pBase = (char *)realloc(m_pBase, m_capacity);
|
||||
m_curptr = m_pBase + pos;
|
||||
} while (m_curptr - m_pBase + typesize > m_capacity);
|
||||
}
|
||||
|
||||
void CDataPack::ResetSize()
|
||||
{
|
||||
m_size = 0;
|
||||
}
|
||||
|
||||
size_t CDataPack::CreateMemory(size_t size, void **addr)
|
||||
{
|
||||
CheckSize(sizeof(size_t) + size);
|
||||
size_t pos = m_curptr - m_pBase;
|
||||
|
||||
*(size_t *)m_curptr = size;
|
||||
m_curptr += sizeof(size_t);
|
||||
|
||||
if (addr)
|
||||
{
|
||||
*addr = m_curptr;
|
||||
}
|
||||
|
||||
m_curptr += size;
|
||||
m_size += sizeof(size_t) + size;
|
||||
|
||||
return pos;
|
||||
}
|
||||
|
||||
void CDataPack::PackCell(cell_t cell)
|
||||
{
|
||||
CheckSize(sizeof(size_t) + sizeof(cell_t));
|
||||
|
||||
*(size_t *)m_curptr = sizeof(cell_t);
|
||||
m_curptr += sizeof(size_t);
|
||||
|
||||
*(cell_t *)m_curptr = cell;
|
||||
m_curptr += sizeof(cell_t);
|
||||
|
||||
m_size += sizeof(size_t) + sizeof(cell_t);
|
||||
}
|
||||
|
||||
void CDataPack::PackFloat(float val)
|
||||
{
|
||||
CheckSize(sizeof(size_t) + sizeof(float));
|
||||
|
||||
*(size_t *)m_curptr = sizeof(float);
|
||||
m_curptr += sizeof(size_t);
|
||||
|
||||
*(float *)m_curptr = val;
|
||||
m_curptr += sizeof(float);
|
||||
|
||||
m_size += sizeof(size_t) + sizeof(float);
|
||||
}
|
||||
|
||||
void CDataPack::PackString(const char *string)
|
||||
{
|
||||
size_t len = strlen(string);
|
||||
size_t maxsize = sizeof(size_t) + len + 1;
|
||||
CheckSize(maxsize);
|
||||
|
||||
// Pack the string length first for buffer overrun checking.
|
||||
*(size_t *)m_curptr = len;
|
||||
m_curptr += sizeof(size_t);
|
||||
|
||||
// Now pack the string.
|
||||
memcpy(m_curptr, string, len);
|
||||
m_curptr[len] = '\0';
|
||||
m_curptr += len + 1;
|
||||
|
||||
m_size += maxsize;
|
||||
}
|
||||
|
||||
void CDataPack::Reset() const
|
||||
{
|
||||
m_curptr = m_pBase;
|
||||
}
|
||||
|
||||
size_t CDataPack::GetPosition() const
|
||||
{
|
||||
return static_cast<size_t>(m_curptr - m_pBase);
|
||||
}
|
||||
|
||||
bool CDataPack::SetPosition(size_t pos) const
|
||||
{
|
||||
if (pos > m_size-1)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
m_curptr = m_pBase + pos;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
cell_t CDataPack::ReadCell() const
|
||||
{
|
||||
if (!IsReadable(sizeof(size_t) + sizeof(cell_t)))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
if (*reinterpret_cast<size_t *>(m_curptr) != sizeof(cell_t))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
m_curptr += sizeof(size_t);
|
||||
|
||||
cell_t val = *reinterpret_cast<cell_t *>(m_curptr);
|
||||
m_curptr += sizeof(cell_t);
|
||||
return val;
|
||||
}
|
||||
|
||||
float CDataPack::ReadFloat() const
|
||||
{
|
||||
if (!IsReadable(sizeof(size_t) + sizeof(float)))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
if (*reinterpret_cast<size_t *>(m_curptr) != sizeof(float))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
m_curptr += sizeof(size_t);
|
||||
|
||||
float val = *reinterpret_cast<float *>(m_curptr);
|
||||
m_curptr += sizeof(float);
|
||||
return val;
|
||||
}
|
||||
|
||||
bool CDataPack::IsReadable(size_t bytes) const
|
||||
{
|
||||
return (bytes + (m_curptr - m_pBase) > m_size) ? false : true;
|
||||
}
|
||||
|
||||
const char *CDataPack::ReadString(size_t *len) const
|
||||
{
|
||||
if (!IsReadable(sizeof(size_t)))
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
size_t real_len = *(size_t *)m_curptr;
|
||||
|
||||
m_curptr += sizeof(size_t);
|
||||
char *str = (char *)m_curptr;
|
||||
|
||||
if ((strlen(str) != real_len) || !(IsReadable(real_len+1)))
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (len)
|
||||
{
|
||||
*len = real_len;
|
||||
}
|
||||
|
||||
m_curptr += real_len + 1;
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
void *CDataPack::GetMemory() const
|
||||
{
|
||||
return m_curptr;
|
||||
}
|
||||
|
||||
void *CDataPack::ReadMemory(size_t *size) const
|
||||
{
|
||||
if (!IsReadable(sizeof(size_t)))
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
size_t bytecount = *(size_t *)m_curptr;
|
||||
m_curptr += sizeof(size_t);
|
||||
|
||||
if (!IsReadable(bytecount))
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void *ptr = m_curptr;
|
||||
|
||||
if (size)
|
||||
{
|
||||
*size = bytecount;
|
||||
}
|
||||
|
||||
m_curptr += bytecount;
|
||||
|
||||
return ptr;
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
/**
|
||||
* vim: set ts=4 :
|
||||
* =============================================================================
|
||||
* SourceMod
|
||||
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
|
||||
* =============================================================================
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, version 3.0, as published by the
|
||||
* Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* As a special exception, AlliedModders LLC gives you permission to link the
|
||||
* code of this program (as well as its derivative works) to "Half-Life 2," the
|
||||
* "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
|
||||
* by the Valve Corporation. You must obey the GNU General Public License in
|
||||
* all respects for all other code used. Additionally, AlliedModders LLC grants
|
||||
* this exception to all derivative works. AlliedModders LLC defines further
|
||||
* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
|
||||
* or <http://www.sourcemod.net/license.php>.
|
||||
*
|
||||
* Version: $Id$
|
||||
*/
|
||||
|
||||
#ifndef _INCLUDE_SOURCEMOD_CDATAPACK_H_
|
||||
#define _INCLUDE_SOURCEMOD_CDATAPACK_H_
|
||||
|
||||
#include <IDataPack.h>
|
||||
|
||||
using namespace SourceMod;
|
||||
|
||||
class CDataPack : public IDataPack
|
||||
{
|
||||
public:
|
||||
CDataPack();
|
||||
~CDataPack();
|
||||
public: //IDataReader
|
||||
void Reset() const;
|
||||
size_t GetPosition() const;
|
||||
bool SetPosition(size_t pos) const;
|
||||
cell_t ReadCell() const;
|
||||
float ReadFloat() const;
|
||||
bool IsReadable(size_t bytes) const;
|
||||
const char *ReadString(size_t *len) const;
|
||||
void *GetMemory() const;
|
||||
void *ReadMemory(size_t *size) const;
|
||||
public: //IDataPack
|
||||
void ResetSize();
|
||||
void PackCell(cell_t cell);
|
||||
void PackFloat(float val);
|
||||
void PackString(const char *string);
|
||||
size_t CreateMemory(size_t size, void **addr);
|
||||
public:
|
||||
void Initialize();
|
||||
private:
|
||||
void CheckSize(size_t sizetype);
|
||||
private:
|
||||
char *m_pBase;
|
||||
mutable char *m_curptr;
|
||||
size_t m_capacity;
|
||||
size_t m_size;
|
||||
};
|
||||
|
||||
#endif //_INCLUDE_SOURCEMOD_CDATAPACK_H_
|
||||
@@ -0,0 +1,206 @@
|
||||
/**
|
||||
* vim: set ts=4 :
|
||||
* =============================================================================
|
||||
* SourceMod
|
||||
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
|
||||
* =============================================================================
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, version 3.0, as published by the
|
||||
* Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* As a special exception, AlliedModders LLC gives you permission to link the
|
||||
* code of this program (as well as its derivative works) to "Half-Life 2," the
|
||||
* "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
|
||||
* by the Valve Corporation. You must obey the GNU General Public License in
|
||||
* all respects for all other code used. Additionally, AlliedModders LLC grants
|
||||
* this exception to all derivative works. AlliedModders LLC defines further
|
||||
* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
|
||||
* or <http://www.sourcemod.net/license.php>.
|
||||
*
|
||||
* Version: $Id$
|
||||
*/
|
||||
|
||||
#include <malloc.h>
|
||||
#include <string.h>
|
||||
|
||||
extern HandleType_t htCellArray;
|
||||
|
||||
class CellArray
|
||||
{
|
||||
public:
|
||||
CellArray(size_t blocksize) : m_Data(NULL), m_BlockSize(blocksize), m_AllocSize(0), m_Size(0)
|
||||
{
|
||||
}
|
||||
|
||||
~CellArray()
|
||||
{
|
||||
free(m_Data);
|
||||
}
|
||||
|
||||
size_t size() const
|
||||
{
|
||||
return m_Size;
|
||||
}
|
||||
|
||||
cell_t *push()
|
||||
{
|
||||
if (!GrowIfNeeded(1))
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
cell_t *arr = &m_Data[m_Size * m_BlockSize];
|
||||
m_Size++;
|
||||
return arr;
|
||||
}
|
||||
|
||||
cell_t *at(size_t b) const
|
||||
{
|
||||
return &m_Data[b * m_BlockSize];
|
||||
}
|
||||
|
||||
size_t blocksize() const
|
||||
{
|
||||
return m_BlockSize;
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
m_Size = 0;
|
||||
}
|
||||
|
||||
bool swap(size_t item1, size_t item2)
|
||||
{
|
||||
/* Make sure there is extra space available */
|
||||
if (!GrowIfNeeded(1))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
cell_t *pri = at(item1);
|
||||
cell_t *alt = at(item2);
|
||||
|
||||
/* Get our temporary array 1 after the limit */
|
||||
cell_t *temp = &m_Data[m_Size * m_BlockSize];
|
||||
|
||||
memcpy(temp, pri, sizeof(cell_t) * m_BlockSize);
|
||||
memcpy(pri, alt, sizeof(cell_t) * m_BlockSize);
|
||||
memcpy(alt, temp, sizeof(cell_t) * m_BlockSize);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void remove(size_t index)
|
||||
{
|
||||
/* If we're at the end, take the easy way out */
|
||||
if (index == m_Size - 1)
|
||||
{
|
||||
m_Size--;
|
||||
return;
|
||||
}
|
||||
|
||||
/* Otherwise, it's time to move stuff! */
|
||||
size_t remaining_indexes = (m_Size - 1) - index;
|
||||
cell_t *src = at(index + 1);
|
||||
cell_t *dest = at(index);
|
||||
memmove(dest, src, sizeof(cell_t) * m_BlockSize * remaining_indexes);
|
||||
|
||||
m_Size--;
|
||||
}
|
||||
|
||||
cell_t *insert_at(size_t index)
|
||||
{
|
||||
/* Make sure it'll fit */
|
||||
if (!GrowIfNeeded(1))
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* move everything up */
|
||||
cell_t *src = at(index);
|
||||
cell_t *dst = at(index + 1);
|
||||
memmove(dst, src, sizeof(cell_t) * m_BlockSize * (m_Size-index));
|
||||
|
||||
m_Size++;
|
||||
|
||||
return src;
|
||||
}
|
||||
|
||||
bool resize(size_t count)
|
||||
{
|
||||
if (count <= m_Size)
|
||||
{
|
||||
m_Size = count;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!GrowIfNeeded(count - m_Size))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
m_Size = count;
|
||||
return true;
|
||||
}
|
||||
|
||||
CellArray *clone()
|
||||
{
|
||||
CellArray *array = new CellArray(m_BlockSize);
|
||||
array->m_AllocSize = m_AllocSize;
|
||||
array->m_Size = m_Size;
|
||||
array->m_Data = (cell_t *)malloc(sizeof(cell_t) * m_BlockSize * m_AllocSize);
|
||||
memcpy(array->m_Data, m_Data, sizeof(cell_t) * m_BlockSize * m_Size);
|
||||
return array;
|
||||
}
|
||||
|
||||
cell_t *base()
|
||||
{
|
||||
return m_Data;
|
||||
}
|
||||
|
||||
size_t mem_usage()
|
||||
{
|
||||
return m_AllocSize * m_BlockSize * sizeof(cell_t);
|
||||
}
|
||||
|
||||
private:
|
||||
bool GrowIfNeeded(size_t count)
|
||||
{
|
||||
/* Shortcut out if we can store this */
|
||||
if (m_Size + count <= m_AllocSize)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
/* Set a base allocation size of 8 items */
|
||||
if (!m_AllocSize)
|
||||
{
|
||||
m_AllocSize = 8;
|
||||
}
|
||||
/* If it's not enough, keep doubling */
|
||||
while (m_Size + count > m_AllocSize)
|
||||
{
|
||||
m_AllocSize *= 2;
|
||||
}
|
||||
/* finally, allocate the new block */
|
||||
if (m_Data)
|
||||
{
|
||||
m_Data = (cell_t *)realloc(m_Data, sizeof(cell_t) * m_BlockSize * m_AllocSize);
|
||||
} else {
|
||||
m_Data = (cell_t *)malloc(sizeof(cell_t) * m_BlockSize * m_AllocSize);
|
||||
}
|
||||
return (m_Data != NULL);
|
||||
}
|
||||
private:
|
||||
cell_t *m_Data;
|
||||
size_t m_BlockSize;
|
||||
size_t m_AllocSize;
|
||||
size_t m_Size;
|
||||
};
|
||||
@@ -0,0 +1,107 @@
|
||||
/**
|
||||
* vim: set ts=4 :
|
||||
* =============================================================================
|
||||
* SourceMod
|
||||
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
|
||||
* =============================================================================
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, version 3.0, as published by the
|
||||
* Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* As a special exception, AlliedModders LLC gives you permission to link the
|
||||
* code of this program (as well as its derivative works) to "Half-Life 2," the
|
||||
* "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
|
||||
* by the Valve Corporation. You must obey the GNU General Public License in
|
||||
* all respects for all other code used. Additionally, AlliedModders LLC grants
|
||||
* this exception to all derivative works. AlliedModders LLC defines further
|
||||
* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
|
||||
* or <http://www.sourcemod.net/license.php>.
|
||||
*
|
||||
* Version: $Id$
|
||||
*/
|
||||
|
||||
#ifndef _INCLUDE_SOURCEMOD_CELLRECIPIENTFILTER_H_
|
||||
#define _INCLUDE_SOURCEMOD_CELLRECIPIENTFILTER_H_
|
||||
|
||||
#include <irecipientfilter.h>
|
||||
#include <sp_vm_types.h>
|
||||
|
||||
class CellRecipientFilter : public IRecipientFilter
|
||||
{
|
||||
public:
|
||||
CellRecipientFilter() : m_IsReliable(false), m_IsInitMessage(false), m_Size(0) {}
|
||||
~CellRecipientFilter() {}
|
||||
public: //IRecipientFilter
|
||||
bool IsReliable() const;
|
||||
bool IsInitMessage() const;
|
||||
int GetRecipientCount() const;
|
||||
int GetRecipientIndex(int slot) const;
|
||||
public:
|
||||
void Initialize(const cell_t *ptr, size_t count);
|
||||
void SetToReliable(bool isreliable);
|
||||
void SetToInit(bool isinitmsg);
|
||||
void Reset();
|
||||
private:
|
||||
cell_t m_Players[255];
|
||||
bool m_IsReliable;
|
||||
bool m_IsInitMessage;
|
||||
size_t m_Size;
|
||||
};
|
||||
|
||||
inline void CellRecipientFilter::Reset()
|
||||
{
|
||||
m_IsReliable = false;
|
||||
m_IsInitMessage = false;
|
||||
m_Size = 0;
|
||||
}
|
||||
|
||||
inline bool CellRecipientFilter::IsReliable() const
|
||||
{
|
||||
return m_IsReliable;
|
||||
}
|
||||
|
||||
inline bool CellRecipientFilter::IsInitMessage() const
|
||||
{
|
||||
return m_IsInitMessage;
|
||||
}
|
||||
|
||||
inline int CellRecipientFilter::GetRecipientCount() const
|
||||
{
|
||||
return m_Size;
|
||||
}
|
||||
|
||||
inline int CellRecipientFilter::GetRecipientIndex(int slot) const
|
||||
{
|
||||
if ((slot < 0) || (slot >= GetRecipientCount()))
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
return static_cast<int>(m_Players[slot]);
|
||||
}
|
||||
|
||||
inline void CellRecipientFilter::SetToInit(bool isinitmsg)
|
||||
{
|
||||
m_IsInitMessage = isinitmsg;
|
||||
}
|
||||
|
||||
inline void CellRecipientFilter::SetToReliable(bool isreliable)
|
||||
{
|
||||
m_IsReliable = isreliable;
|
||||
}
|
||||
|
||||
inline void CellRecipientFilter::Initialize(const cell_t *ptr, size_t count)
|
||||
{
|
||||
memcpy(m_Players, ptr, count * sizeof(cell_t));
|
||||
m_Size = count;
|
||||
}
|
||||
|
||||
#endif //_INCLUDE_SOURCEMOD_CELLRECIPIENTFILTER_H_
|
||||
@@ -0,0 +1,450 @@
|
||||
/**
|
||||
* vim: set ts=4 :
|
||||
* =============================================================================
|
||||
* SourceMod
|
||||
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
|
||||
* =============================================================================
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, version 3.0, as published by the
|
||||
* Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* As a special exception, AlliedModders LLC gives you permission to link the
|
||||
* code of this program (as well as its derivative works) to "Half-Life 2," the
|
||||
* "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
|
||||
* by the Valve Corporation. You must obey the GNU General Public License in
|
||||
* all respects for all other code used. Additionally, AlliedModders LLC grants
|
||||
* this exception to all derivative works. AlliedModders LLC defines further
|
||||
* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
|
||||
* or <http://www.sourcemod.net/license.php>.
|
||||
*
|
||||
* Version: $Id$
|
||||
*/
|
||||
|
||||
#include <ITextParsers.h>
|
||||
#include "ChatTriggers.h"
|
||||
#include "sm_stringutil.h"
|
||||
#include "ConCmdManager.h"
|
||||
#include "PlayerManager.h"
|
||||
#include "Translator.h"
|
||||
#include "HalfLife2.h"
|
||||
|
||||
/* :HACKHACK: We can't SH_DECL here because ConCmdManager.cpp does.
|
||||
* While the OB build only runs on MM:S 1.6.0+ (SH 5+), the older one
|
||||
* can technically be compiled against any MM:S version after 1.4.2.
|
||||
*/
|
||||
#if defined ORANGEBOX_BUILD
|
||||
extern bool __SourceHook_FHRemoveConCommandDispatch(void *, bool, class fastdelegate::FastDelegate1<const CCommand &, void>);
|
||||
extern int __SourceHook_FHAddConCommandDispatch(void *, ISourceHook::AddHookMode, bool, class fastdelegate::FastDelegate1<const CCommand &, void>);
|
||||
#else
|
||||
extern bool __SourceHook_FHRemoveConCommandDispatch(void *, bool, class fastdelegate::FastDelegate0<void>);
|
||||
#if SH_IMPL_VERSION >= 5
|
||||
extern int __SourceHook_FHAddConCommandDispatch(void *, ISourceHook::AddHookMode, bool, class fastdelegate::FastDelegate0<void>);
|
||||
#elif SH_IMPL_VERSION == 4
|
||||
extern int __SourceHook_FHAddConCommandDispatch(void *, bool, class fastdelegate::FastDelegate0<void>);
|
||||
#elif SH_IMPL_VERSION == 3
|
||||
extern bool __SourceHook_FHAddConCommandDispatch(void *, bool, class fastdelegate::FastDelegate0<void>);
|
||||
#endif //SH_IMPL_VERSION
|
||||
#endif //ORANGEBOX_BUILD
|
||||
|
||||
ChatTriggers g_ChatTriggers;
|
||||
bool g_bSupressSilentFails = false;
|
||||
CPhraseFile *g_pFloodPhrases = NULL;
|
||||
|
||||
ChatTriggers::ChatTriggers() : m_pSayCmd(NULL), m_bWillProcessInPost(false),
|
||||
m_bTriggerWasSilent(false), m_ReplyTo(SM_REPLY_CONSOLE)
|
||||
{
|
||||
m_PubTrigger = sm_strdup("!");
|
||||
m_PrivTrigger = sm_strdup("/");
|
||||
m_PubTriggerSize = 1;
|
||||
m_PrivTriggerSize = 1;
|
||||
m_bIsChatTrigger = false;
|
||||
}
|
||||
|
||||
ChatTriggers::~ChatTriggers()
|
||||
{
|
||||
delete [] m_PubTrigger;
|
||||
m_PubTrigger = NULL;
|
||||
delete [] m_PrivTrigger;
|
||||
m_PrivTrigger = NULL;
|
||||
}
|
||||
|
||||
ConfigResult ChatTriggers::OnSourceModConfigChanged(const char *key,
|
||||
const char *value,
|
||||
ConfigSource source,
|
||||
char *error,
|
||||
size_t maxlength)
|
||||
{
|
||||
if (strcmp(key, "PublicChatTrigger") == 0)
|
||||
{
|
||||
delete [] m_PubTrigger;
|
||||
m_PubTrigger = sm_strdup(value);
|
||||
m_PubTriggerSize = strlen(m_PubTrigger);
|
||||
return ConfigResult_Accept;
|
||||
}
|
||||
else if (strcmp(key, "SilentChatTrigger") == 0)
|
||||
{
|
||||
delete [] m_PrivTrigger;
|
||||
m_PrivTrigger = sm_strdup(value);
|
||||
m_PrivTriggerSize = strlen(m_PrivTrigger);
|
||||
return ConfigResult_Accept;
|
||||
}
|
||||
else if (strcmp(key, "SilentFailSuppress") == 0)
|
||||
{
|
||||
g_bSupressSilentFails = strcmp(value, "yes") == 0;
|
||||
return ConfigResult_Accept;
|
||||
}
|
||||
|
||||
return ConfigResult_Ignore;
|
||||
}
|
||||
|
||||
void ChatTriggers::OnSourceModAllInitialized()
|
||||
{
|
||||
m_pShouldFloodBlock = g_Forwards.CreateForward("OnClientFloodCheck", ET_Event, 1, NULL, Param_Cell);
|
||||
m_pDidFloodBlock = g_Forwards.CreateForward("OnClientFloodResult", ET_Event, 2, NULL, Param_Cell, Param_Cell);
|
||||
}
|
||||
|
||||
void ChatTriggers::OnSourceModAllInitialized_Post()
|
||||
{
|
||||
unsigned int file_id;
|
||||
|
||||
file_id = g_Translator.FindOrAddPhraseFile("antiflood.phrases.txt");
|
||||
g_pFloodPhrases = g_Translator.GetFileByIndex(file_id);
|
||||
}
|
||||
|
||||
void ChatTriggers::OnSourceModGameInitialized()
|
||||
{
|
||||
unsigned int total = 2;
|
||||
ConCommandBase *pCmd = icvar->GetCommands();
|
||||
const char *name;
|
||||
while (pCmd)
|
||||
{
|
||||
if (pCmd->IsCommand())
|
||||
{
|
||||
name = pCmd->GetName();
|
||||
if (!m_pSayCmd && strcmp(name, "say") == 0)
|
||||
{
|
||||
m_pSayCmd = (ConCommand *)pCmd;
|
||||
if (--total == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
} else if (!m_pSayTeamCmd && strcmp(name, "say_team") == 0) {
|
||||
m_pSayTeamCmd = (ConCommand *)pCmd;
|
||||
if (--total == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
pCmd = const_cast<ConCommandBase *>(pCmd->GetNext());
|
||||
}
|
||||
|
||||
if (m_pSayCmd)
|
||||
{
|
||||
SH_ADD_HOOK_MEMFUNC(ConCommand, Dispatch, m_pSayCmd, this, &ChatTriggers::OnSayCommand_Pre, false);
|
||||
SH_ADD_HOOK_MEMFUNC(ConCommand, Dispatch, m_pSayCmd, this, &ChatTriggers::OnSayCommand_Post, true);
|
||||
}
|
||||
if (m_pSayTeamCmd)
|
||||
{
|
||||
SH_ADD_HOOK_MEMFUNC(ConCommand, Dispatch, m_pSayTeamCmd, this, &ChatTriggers::OnSayCommand_Pre, false);
|
||||
SH_ADD_HOOK_MEMFUNC(ConCommand, Dispatch, m_pSayTeamCmd, this, &ChatTriggers::OnSayCommand_Post, true);
|
||||
}
|
||||
}
|
||||
|
||||
void ChatTriggers::OnSourceModShutdown()
|
||||
{
|
||||
if (m_pSayTeamCmd)
|
||||
{
|
||||
SH_REMOVE_HOOK_MEMFUNC(ConCommand, Dispatch, m_pSayTeamCmd, this, &ChatTriggers::OnSayCommand_Post, true);
|
||||
SH_REMOVE_HOOK_MEMFUNC(ConCommand, Dispatch, m_pSayTeamCmd, this, &ChatTriggers::OnSayCommand_Pre, false);
|
||||
}
|
||||
if (m_pSayCmd)
|
||||
{
|
||||
SH_REMOVE_HOOK_MEMFUNC(ConCommand, Dispatch, m_pSayCmd, this, &ChatTriggers::OnSayCommand_Post, true);
|
||||
SH_REMOVE_HOOK_MEMFUNC(ConCommand, Dispatch, m_pSayCmd, this, &ChatTriggers::OnSayCommand_Pre, false);
|
||||
}
|
||||
|
||||
g_Forwards.ReleaseForward(m_pShouldFloodBlock);
|
||||
g_Forwards.ReleaseForward(m_pDidFloodBlock);
|
||||
}
|
||||
|
||||
#if defined ORANGEBOX_BUILD
|
||||
void ChatTriggers::OnSayCommand_Pre(const CCommand &command)
|
||||
{
|
||||
#else
|
||||
void ChatTriggers::OnSayCommand_Pre()
|
||||
{
|
||||
CCommand command;
|
||||
#endif
|
||||
int client;
|
||||
CPlayer *pPlayer;
|
||||
|
||||
client = g_ConCmds.GetCommandClient();
|
||||
m_bIsChatTrigger = false;
|
||||
m_bWasFloodedMessage = false;
|
||||
|
||||
/* The server console cannot do this */
|
||||
if (client == 0 || (pPlayer = g_Players.GetPlayerByIndex(client)) == NULL)
|
||||
{
|
||||
RETURN_META(MRES_IGNORED);
|
||||
}
|
||||
|
||||
/* We guarantee the client is connected */
|
||||
if (!pPlayer->IsConnected())
|
||||
{
|
||||
RETURN_META(MRES_IGNORED);
|
||||
}
|
||||
|
||||
const char *args = command.ArgS();
|
||||
|
||||
if (!args)
|
||||
{
|
||||
RETURN_META(MRES_IGNORED);
|
||||
}
|
||||
|
||||
/* Check if we need to block this message from being sent */
|
||||
if (ClientIsFlooding(client))
|
||||
{
|
||||
char buffer[128];
|
||||
|
||||
if (!CoreTranslate(
|
||||
buffer,
|
||||
sizeof(buffer),
|
||||
"%T",
|
||||
2,
|
||||
NULL,
|
||||
"Flooding the server",
|
||||
&client))
|
||||
{
|
||||
UTIL_Format(buffer, sizeof(buffer), "You are flooding the server!");
|
||||
}
|
||||
|
||||
/* :TODO: we should probably kick people who spam too much. */
|
||||
|
||||
char fullbuffer[192];
|
||||
UTIL_Format(fullbuffer, sizeof(fullbuffer), "[SM] %s", buffer);
|
||||
g_HL2.TextMsg(client, HUD_PRINTTALK, fullbuffer);
|
||||
|
||||
m_bWasFloodedMessage = true;
|
||||
|
||||
RETURN_META(MRES_SUPERCEDE);
|
||||
}
|
||||
|
||||
/* Handle quoted string sets */
|
||||
bool is_quoted = false;
|
||||
if (args[0] == '"')
|
||||
{
|
||||
args++;
|
||||
is_quoted = true;
|
||||
}
|
||||
|
||||
bool is_trigger = false;
|
||||
bool is_silent = false;
|
||||
|
||||
/* Check for either trigger */
|
||||
if (m_PubTriggerSize && strncmp(args, m_PubTrigger, m_PubTriggerSize) == 0)
|
||||
{
|
||||
is_trigger = true;
|
||||
args = &args[m_PubTriggerSize];
|
||||
}
|
||||
else if (m_PrivTriggerSize && strncmp(args, m_PrivTrigger, m_PrivTriggerSize) == 0)
|
||||
{
|
||||
is_trigger = true;
|
||||
is_silent = true;
|
||||
args = &args[m_PrivTriggerSize];
|
||||
}
|
||||
|
||||
if (!is_trigger)
|
||||
{
|
||||
RETURN_META(MRES_IGNORED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if this is actually a command!
|
||||
*/
|
||||
if (!PreProcessTrigger(engine->PEntityOfEntIndex(client), args, is_quoted))
|
||||
{
|
||||
CPlayer *pPlayer;
|
||||
if (is_silent
|
||||
&& g_bSupressSilentFails
|
||||
&& client != 0
|
||||
&& (pPlayer = g_Players.GetPlayerByIndex(client)) != NULL
|
||||
&& pPlayer->GetAdminId() != INVALID_ADMIN_ID)
|
||||
{
|
||||
RETURN_META(MRES_SUPERCEDE);
|
||||
}
|
||||
RETURN_META(MRES_IGNORED);
|
||||
}
|
||||
|
||||
m_bIsChatTrigger = true;
|
||||
|
||||
/**
|
||||
* We'll execute it in post.
|
||||
*/
|
||||
m_bWillProcessInPost = true;
|
||||
m_bTriggerWasSilent = is_silent;
|
||||
|
||||
/* If we're silent, block */
|
||||
if (is_silent)
|
||||
{
|
||||
RETURN_META(MRES_SUPERCEDE);
|
||||
}
|
||||
|
||||
/* Otherwise, let the command continue */
|
||||
RETURN_META(MRES_IGNORED);
|
||||
}
|
||||
|
||||
#if defined ORANGEBOX_BUILD
|
||||
void ChatTriggers::OnSayCommand_Post(const CCommand &command)
|
||||
#else
|
||||
void ChatTriggers::OnSayCommand_Post()
|
||||
#endif
|
||||
{
|
||||
m_bIsChatTrigger = false;
|
||||
m_bWasFloodedMessage = false;
|
||||
if (m_bWillProcessInPost)
|
||||
{
|
||||
/* Reset this for re-entrancy */
|
||||
m_bWillProcessInPost = false;
|
||||
|
||||
/* Execute the cached command */
|
||||
int client = g_ConCmds.GetCommandClient();
|
||||
unsigned int old = SetReplyTo(SM_REPLY_CHAT);
|
||||
serverpluginhelpers->ClientCommand(engine->PEntityOfEntIndex(client), m_ToExecute);
|
||||
SetReplyTo(old);
|
||||
}
|
||||
}
|
||||
|
||||
bool ChatTriggers::PreProcessTrigger(edict_t *pEdict, const char *args, bool is_quoted)
|
||||
{
|
||||
/* Extract a command. This is kind of sloppy. */
|
||||
char cmd_buf[64];
|
||||
size_t cmd_len = 0;
|
||||
const char *inptr = args;
|
||||
while (*inptr != '\0'
|
||||
&& !textparsers->IsWhitespace(inptr)
|
||||
&& *inptr != '"'
|
||||
&& cmd_len < sizeof(cmd_buf) - 1)
|
||||
{
|
||||
cmd_buf[cmd_len++] = *inptr++;
|
||||
}
|
||||
cmd_buf[cmd_len] = '\0';
|
||||
|
||||
if (cmd_len == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/* See if we have this registered */
|
||||
bool prepended = false;
|
||||
if (!g_ConCmds.LookForSourceModCommand(cmd_buf))
|
||||
{
|
||||
/* Check if we had an "sm_" prefix */
|
||||
if (strncmp(cmd_buf, "sm_", 3) == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Now, prepend. Don't worry about the buffers. This will
|
||||
* work because the sizes are limited from earlier.
|
||||
*/
|
||||
char new_buf[80];
|
||||
strcpy(new_buf, "sm_");
|
||||
strncopy(&new_buf[3], cmd_buf, sizeof(new_buf)-3);
|
||||
|
||||
/* Recheck */
|
||||
if (!g_ConCmds.LookForSourceModCommand(new_buf))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
prepended = true;
|
||||
}
|
||||
|
||||
/* See if we need to do extra string manipulation */
|
||||
if (is_quoted || prepended)
|
||||
{
|
||||
size_t len;
|
||||
|
||||
/* Check if we need to prepend sm_ */
|
||||
if (prepended)
|
||||
{
|
||||
len = UTIL_Format(m_ToExecute, sizeof(m_ToExecute), "sm_%s", args);
|
||||
} else {
|
||||
len = strncopy(m_ToExecute, args, sizeof(m_ToExecute));
|
||||
}
|
||||
|
||||
/* Check if we need to strip a quote */
|
||||
if (is_quoted)
|
||||
{
|
||||
if (m_ToExecute[len-1] == '"')
|
||||
{
|
||||
m_ToExecute[--len] = '\0';
|
||||
}
|
||||
}
|
||||
} else {
|
||||
strncopy(m_ToExecute, args, sizeof(m_ToExecute));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
unsigned int ChatTriggers::SetReplyTo(unsigned int reply)
|
||||
{
|
||||
unsigned int old = m_ReplyTo;
|
||||
|
||||
m_ReplyTo = reply;
|
||||
|
||||
return old;
|
||||
}
|
||||
|
||||
unsigned int ChatTriggers::GetReplyTo()
|
||||
{
|
||||
return m_ReplyTo;
|
||||
}
|
||||
|
||||
bool ChatTriggers::IsChatTrigger()
|
||||
{
|
||||
return m_bIsChatTrigger;
|
||||
}
|
||||
|
||||
bool ChatTriggers::ClientIsFlooding(int client)
|
||||
{
|
||||
bool is_flooding = false;
|
||||
|
||||
if (m_pShouldFloodBlock->GetFunctionCount() != 0)
|
||||
{
|
||||
cell_t res = 0;
|
||||
|
||||
m_pShouldFloodBlock->PushCell(client);
|
||||
m_pShouldFloodBlock->Execute(&res);
|
||||
|
||||
if (res != 0)
|
||||
{
|
||||
is_flooding = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_pDidFloodBlock->GetFunctionCount() != 0)
|
||||
{
|
||||
m_pDidFloodBlock->PushCell(client);
|
||||
m_pDidFloodBlock->PushCell(is_flooding ? 1 : 0);
|
||||
m_pDidFloodBlock->Execute(NULL);
|
||||
}
|
||||
|
||||
return is_flooding;
|
||||
}
|
||||
|
||||
bool ChatTriggers::WasFloodedMessage()
|
||||
{
|
||||
return m_bWasFloodedMessage;
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
/**
|
||||
* vim: set ts=4 :
|
||||
* =============================================================================
|
||||
* SourceMod
|
||||
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
|
||||
* =============================================================================
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, version 3.0, as published by the
|
||||
* Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* As a special exception, AlliedModders LLC gives you permission to link the
|
||||
* code of this program (as well as its derivative works) to "Half-Life 2," the
|
||||
* "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
|
||||
* by the Valve Corporation. You must obey the GNU General Public License in
|
||||
* all respects for all other code used. Additionally, AlliedModders LLC grants
|
||||
* this exception to all derivative works. AlliedModders LLC defines further
|
||||
* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
|
||||
* or <http://www.sourcemod.net/license.php>.
|
||||
*
|
||||
* Version: $Id$
|
||||
*/
|
||||
|
||||
#ifndef _INCLUDE_SOURCEMOD_CHAT_TRIGGERS_H_
|
||||
#define _INCLUDE_SOURCEMOD_CHAT_TRIGGERS_H_
|
||||
|
||||
#include "sm_globals.h"
|
||||
#include "sourcemm_api.h"
|
||||
#include <IGameHelpers.h>
|
||||
#include <compat_wrappers.h>
|
||||
#include <IForwardSys.h>
|
||||
|
||||
class ChatTriggers : public SMGlobalClass
|
||||
{
|
||||
public:
|
||||
ChatTriggers();
|
||||
~ChatTriggers();
|
||||
public: //SMGlobalClass
|
||||
void OnSourceModAllInitialized();
|
||||
void OnSourceModAllInitialized_Post();
|
||||
void OnSourceModGameInitialized();
|
||||
void OnSourceModShutdown();
|
||||
ConfigResult OnSourceModConfigChanged(const char *key,
|
||||
const char *value,
|
||||
ConfigSource source,
|
||||
char *error,
|
||||
size_t maxlength);
|
||||
private: //ConCommand
|
||||
#if defined ORANGEBOX_BUILD
|
||||
void OnSayCommand_Pre(const CCommand &command);
|
||||
void OnSayCommand_Post(const CCommand &command);
|
||||
#else
|
||||
void OnSayCommand_Pre();
|
||||
void OnSayCommand_Post();
|
||||
#endif
|
||||
public:
|
||||
unsigned int GetReplyTo();
|
||||
unsigned int SetReplyTo(unsigned int reply);
|
||||
bool IsChatTrigger();
|
||||
bool WasFloodedMessage();
|
||||
private:
|
||||
bool PreProcessTrigger(edict_t *pEdict, const char *args, bool is_quoted);
|
||||
bool ClientIsFlooding(int client);
|
||||
private:
|
||||
ConCommand *m_pSayCmd;
|
||||
ConCommand *m_pSayTeamCmd;
|
||||
char *m_PubTrigger;
|
||||
size_t m_PubTriggerSize;
|
||||
char *m_PrivTrigger;
|
||||
size_t m_PrivTriggerSize;
|
||||
bool m_bWillProcessInPost;
|
||||
bool m_bTriggerWasSilent;
|
||||
bool m_bIsChatTrigger;
|
||||
bool m_bWasFloodedMessage;
|
||||
unsigned int m_ReplyTo;
|
||||
char m_ToExecute[300];
|
||||
IForward *m_pShouldFloodBlock;
|
||||
IForward *m_pDidFloodBlock;
|
||||
};
|
||||
|
||||
extern ChatTriggers g_ChatTriggers;
|
||||
|
||||
#endif //_INCLUDE_SOURCEMOD_CHAT_TRIGGERS_H_
|
||||
@@ -0,0 +1,991 @@
|
||||
/**
|
||||
* vim: set ts=4 :
|
||||
* =============================================================================
|
||||
* SourceMod
|
||||
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
|
||||
* =============================================================================
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, version 3.0, as published by the
|
||||
* Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* As a special exception, AlliedModders LLC gives you permission to link the
|
||||
* code of this program (as well as its derivative works) to "Half-Life 2," the
|
||||
* "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
|
||||
* by the Valve Corporation. You must obey the GNU General Public License in
|
||||
* all respects for all other code used. Additionally, AlliedModders LLC grants
|
||||
* this exception to all derivative works. AlliedModders LLC defines further
|
||||
* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
|
||||
* or <http://www.sourcemod.net/license.php>.
|
||||
*
|
||||
* Version: $Id$
|
||||
*/
|
||||
|
||||
#include "ConCmdManager.h"
|
||||
#include "sm_srvcmds.h"
|
||||
#include "AdminCache.h"
|
||||
#include "sm_stringutil.h"
|
||||
#include "PlayerManager.h"
|
||||
#include "Translator.h"
|
||||
#include "HalfLife2.h"
|
||||
#include "ChatTriggers.h"
|
||||
|
||||
ConCmdManager g_ConCmds;
|
||||
|
||||
#if defined ORANGEBOX_BUILD
|
||||
SH_DECL_HOOK1_void(ConCommand, Dispatch, SH_NOATTRIB, false, const CCommand &);
|
||||
#else
|
||||
SH_DECL_HOOK0_void(ConCommand, Dispatch, SH_NOATTRIB, false);
|
||||
#endif
|
||||
|
||||
SH_DECL_HOOK1_void(IServerGameClients, SetCommandClient, SH_NOATTRIB, false, int);
|
||||
|
||||
struct PlCmdInfo
|
||||
{
|
||||
ConCmdInfo *pInfo;
|
||||
CmdHook *pHook;
|
||||
CmdType type;
|
||||
};
|
||||
typedef List<PlCmdInfo> CmdList;
|
||||
|
||||
void AddToPlCmdList(CmdList *pList, const PlCmdInfo &info);
|
||||
|
||||
ConCmdManager::ConCmdManager() : m_Strings(1024)
|
||||
{
|
||||
m_pCmds = sm_trie_create();
|
||||
m_pCmdGrps = sm_trie_create();
|
||||
m_CmdClient = 0;
|
||||
}
|
||||
|
||||
ConCmdManager::~ConCmdManager()
|
||||
{
|
||||
sm_trie_destroy(m_pCmds);
|
||||
sm_trie_destroy(m_pCmdGrps);
|
||||
}
|
||||
|
||||
void ConCmdManager::OnSourceModAllInitialized()
|
||||
{
|
||||
g_PluginSys.AddPluginsListener(this);
|
||||
g_RootMenu.AddRootConsoleCommand("cmds", "List console commands", this);
|
||||
SH_ADD_HOOK_MEMFUNC(IServerGameClients, SetCommandClient, serverClients, this, &ConCmdManager::SetCommandClient, false);
|
||||
}
|
||||
|
||||
void ConCmdManager::OnSourceModShutdown()
|
||||
{
|
||||
/* All commands should already be removed by the time we're done */
|
||||
SH_REMOVE_HOOK_MEMFUNC(IServerGameClients, SetCommandClient, serverClients, this, &ConCmdManager::SetCommandClient, false);
|
||||
g_RootMenu.RemoveRootConsoleCommand("cmds", this);
|
||||
}
|
||||
|
||||
void ConCmdManager::OnUnlinkConCommandBase(ConCommandBase *pBase, const char *name, bool is_read_safe)
|
||||
{
|
||||
/* Whoa, first get its information struct */
|
||||
ConCmdInfo *pInfo;
|
||||
|
||||
if (!sm_trie_retrieve(m_pCmds, name, (void **)&pInfo))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
RemoveConCmds(pInfo->srvhooks);
|
||||
RemoveConCmds(pInfo->conhooks);
|
||||
|
||||
RemoveConCmd(pInfo, name, is_read_safe, false);
|
||||
}
|
||||
|
||||
void ConCmdManager::RemoveConCmds(List<CmdHook *> &cmdlist)
|
||||
{
|
||||
List<CmdHook *>::iterator iter = cmdlist.begin();
|
||||
|
||||
while (iter != cmdlist.end())
|
||||
{
|
||||
CmdHook *pHook = (*iter);
|
||||
IPluginContext *pContext = pHook->pf->GetParentContext();
|
||||
IPlugin *pPlugin = g_PluginSys.GetPluginByCtx(pContext->GetContext());
|
||||
CmdList *pList = NULL;
|
||||
|
||||
//gaben
|
||||
if (!pPlugin->GetProperty("CommandList", (void **)&pList, false) || !pList)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
CmdList::iterator p_iter = pList->begin();
|
||||
while (p_iter != pList->end())
|
||||
{
|
||||
PlCmdInfo &cmd = (*p_iter);
|
||||
if (cmd.pHook == pHook)
|
||||
{
|
||||
p_iter = pList->erase(p_iter);
|
||||
}
|
||||
else
|
||||
{
|
||||
p_iter++;
|
||||
}
|
||||
}
|
||||
|
||||
delete pHook->pAdmin;
|
||||
delete pHook;
|
||||
|
||||
iter = cmdlist.erase(iter);
|
||||
}
|
||||
}
|
||||
|
||||
void ConCmdManager::RemoveConCmds(List<CmdHook *> &cmdlist, IPluginContext *pContext)
|
||||
{
|
||||
List<CmdHook *>::iterator iter = cmdlist.begin();
|
||||
CmdHook *pHook;
|
||||
|
||||
while (iter != cmdlist.end())
|
||||
{
|
||||
pHook = (*iter);
|
||||
if (pHook->pf->GetParentContext() == pContext)
|
||||
{
|
||||
delete pHook->pAdmin;
|
||||
delete pHook;
|
||||
iter = cmdlist.erase(iter);
|
||||
}
|
||||
else
|
||||
{
|
||||
iter++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ConCmdManager::OnPluginDestroyed(IPlugin *plugin)
|
||||
{
|
||||
CmdList *pList;
|
||||
List<ConCmdInfo *> removed;
|
||||
if (plugin->GetProperty("CommandList", (void **)&pList, true))
|
||||
{
|
||||
IPluginContext *pContext = plugin->GetBaseContext();
|
||||
CmdList::iterator iter;
|
||||
/* First pass!
|
||||
* Only bother if there's an actual command list on this plugin...
|
||||
*/
|
||||
for (iter=pList->begin();
|
||||
iter!=pList->end();
|
||||
iter++)
|
||||
{
|
||||
PlCmdInfo &cmd = (*iter);
|
||||
ConCmdInfo *pInfo = cmd.pInfo;
|
||||
|
||||
/* Has this chain already been fully cleaned/removed? */
|
||||
if (removed.find(pInfo) != removed.end())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Remove any hooks from us on this command */
|
||||
RemoveConCmds(pInfo->conhooks, pContext);
|
||||
RemoveConCmds(pInfo->srvhooks, pContext);
|
||||
|
||||
/* See if there are still hooks */
|
||||
if (pInfo->srvhooks.size())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (pInfo->conhooks.size())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Remove the command, it should be safe now */
|
||||
RemoveConCmd(pInfo, pInfo->pCmd->GetName(), true, true);
|
||||
removed.push_back(pInfo);
|
||||
}
|
||||
delete pList;
|
||||
}
|
||||
}
|
||||
#if defined ORANGEBOX_BUILD
|
||||
void CommandCallback(const CCommand &command)
|
||||
{
|
||||
#else
|
||||
void CommandCallback()
|
||||
{
|
||||
CCommand command;
|
||||
#endif
|
||||
|
||||
g_HL2.PushCommandStack(&command);
|
||||
|
||||
g_ConCmds.InternalDispatch(command);
|
||||
|
||||
g_HL2.PopCommandStack();
|
||||
}
|
||||
|
||||
void ConCmdManager::SetCommandClient(int client)
|
||||
{
|
||||
m_CmdClient = client + 1;
|
||||
}
|
||||
|
||||
ResultType ConCmdManager::DispatchClientCommand(int client, const char *cmd, int args, ResultType type)
|
||||
{
|
||||
ConCmdInfo *pInfo;
|
||||
if (sm_trie_retrieve(m_pCmds, cmd, (void **)&pInfo))
|
||||
{
|
||||
cell_t result = type;
|
||||
cell_t tempres = result;
|
||||
List<CmdHook *>::iterator iter;
|
||||
CmdHook *pHook;
|
||||
for (iter=pInfo->conhooks.begin();
|
||||
iter!=pInfo->conhooks.end();
|
||||
iter++)
|
||||
{
|
||||
pHook = (*iter);
|
||||
if (!pHook->pf->IsRunnable())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (pHook->pAdmin && !CheckAccess(client, cmd, pHook->pAdmin))
|
||||
{
|
||||
if (result < Pl_Handled)
|
||||
{
|
||||
result = Pl_Handled;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
pHook->pf->PushCell(client);
|
||||
pHook->pf->PushCell(args);
|
||||
if (pHook->pf->Execute(&tempres) == SP_ERROR_NONE)
|
||||
{
|
||||
if (tempres > result)
|
||||
{
|
||||
result = tempres;
|
||||
}
|
||||
if (result == Pl_Stop)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
type = (ResultType)result;
|
||||
}
|
||||
|
||||
return type;
|
||||
}
|
||||
|
||||
void ConCmdManager::InternalDispatch(const CCommand &command)
|
||||
{
|
||||
int client = m_CmdClient;
|
||||
|
||||
if (client)
|
||||
{
|
||||
CPlayer *pPlayer = g_Players.GetPlayerByIndex(client);
|
||||
if (!pPlayer || !pPlayer->IsConnected())
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Note: Console commands will EITHER go through IServerGameDLL::ClientCommand,
|
||||
* OR this dispatch. They will NEVER go through both.
|
||||
* --
|
||||
* Whether or not it goes through the callback is determined by FCVAR_GAMEDLL
|
||||
*/
|
||||
const char *cmd = g_HL2.CurrentCommandName();
|
||||
|
||||
ConCmdInfo *pInfo;
|
||||
if (!sm_trie_retrieve(m_pCmds, cmd, (void **)&pInfo))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* This is a hack to prevent say triggers from firing on messages that were
|
||||
* blocked because of flooding. We won't remove this, but the hack will get
|
||||
* "nicer" when we expose explicit say hooks.
|
||||
*/
|
||||
if (g_ChatTriggers.WasFloodedMessage())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
cell_t result = Pl_Continue;
|
||||
int args = command.ArgC() - 1;
|
||||
|
||||
List<CmdHook *>::iterator iter;
|
||||
CmdHook *pHook;
|
||||
|
||||
/* Execute server-only commands if viable */
|
||||
if (client == 0 && pInfo->srvhooks.size())
|
||||
{
|
||||
cell_t tempres = result;
|
||||
for (iter=pInfo->srvhooks.begin();
|
||||
iter!=pInfo->srvhooks.end();
|
||||
iter++)
|
||||
{
|
||||
pHook = (*iter);
|
||||
if (!pHook->pf->IsRunnable())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
pHook->pf->PushCell(args);
|
||||
if (pHook->pf->Execute(&tempres) == SP_ERROR_NONE)
|
||||
{
|
||||
if (tempres > result)
|
||||
{
|
||||
result = tempres;
|
||||
}
|
||||
if (result == Pl_Stop)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Check if there's an early stop */
|
||||
if (result >= Pl_Stop)
|
||||
{
|
||||
if (!pInfo->sourceMod)
|
||||
{
|
||||
RETURN_META(MRES_SUPERCEDE);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* Execute console commands */
|
||||
if (pInfo->conhooks.size())
|
||||
{
|
||||
cell_t tempres = result;
|
||||
for (iter=pInfo->conhooks.begin();
|
||||
iter!=pInfo->conhooks.end();
|
||||
iter++)
|
||||
{
|
||||
pHook = (*iter);
|
||||
if (!pHook->pf->IsRunnable())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (client
|
||||
&& pHook->pAdmin
|
||||
&& !CheckAccess(client, cmd, pHook->pAdmin))
|
||||
{
|
||||
if (result < Pl_Handled)
|
||||
{
|
||||
result = Pl_Handled;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
/* On a listen server, sometimes the server host's client index can be set as 0.
|
||||
* So index 1 is passed to the command callback to correct this potential problem.
|
||||
*/
|
||||
if (!engine->IsDedicatedServer())
|
||||
{
|
||||
client = g_Players.ListenClient();
|
||||
}
|
||||
|
||||
pHook->pf->PushCell(client);
|
||||
pHook->pf->PushCell(args);
|
||||
|
||||
if (pHook->pf->Execute(&tempres) == SP_ERROR_NONE)
|
||||
{
|
||||
if (tempres > result)
|
||||
{
|
||||
result = tempres;
|
||||
}
|
||||
if (result == Pl_Stop)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (result >= Pl_Handled)
|
||||
{
|
||||
if (!pInfo->sourceMod)
|
||||
{
|
||||
RETURN_META(MRES_SUPERCEDE);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
bool ConCmdManager::CheckCommandAccess(int client, const char *cmd, FlagBits cmdflags)
|
||||
{
|
||||
if (cmdflags == 0 || client == 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/* If running listen server, then client 1 is the server host and should have 'root' access */
|
||||
if (client == 1 && !engine->IsDedicatedServer())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
CPlayer *player = g_Players.GetPlayerByIndex(client);
|
||||
if (!player
|
||||
|| player->GetEdict() == NULL
|
||||
|| player->IsFakeClient())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
AdminId adm = player->GetAdminId();
|
||||
if (adm != INVALID_ADMIN_ID)
|
||||
{
|
||||
FlagBits bits = g_Admins.GetAdminFlags(adm, Access_Effective);
|
||||
|
||||
/* root knows all, WHOA */
|
||||
if ((bits & ADMFLAG_ROOT) == ADMFLAG_ROOT)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Check for overrides
|
||||
* :TODO: is it worth optimizing this?
|
||||
*/
|
||||
unsigned int groups = g_Admins.GetAdminGroupCount(adm);
|
||||
GroupId gid;
|
||||
OverrideRule rule;
|
||||
bool override = false;
|
||||
for (unsigned int i=0; i<groups; i++)
|
||||
{
|
||||
gid = g_Admins.GetAdminGroup(adm, i, NULL);
|
||||
/* First get group-level override */
|
||||
override = g_Admins.GetGroupCommandOverride(gid, cmd, Override_CommandGroup, &rule);
|
||||
/* Now get the specific command override */
|
||||
if (g_Admins.GetGroupCommandOverride(gid, cmd, Override_Command, &rule))
|
||||
{
|
||||
override = true;
|
||||
}
|
||||
if (override)
|
||||
{
|
||||
if (rule == Command_Allow)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else if (rule == Command_Deny)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* See if our other flags match */
|
||||
if ((bits & cmdflags) == cmdflags)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ConCmdManager::CheckAccess(int client, const char *cmd, AdminCmdInfo *pAdmin)
|
||||
{
|
||||
if (CheckCommandAccess(client, cmd, pAdmin->eflags))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
edict_t *pEdict = engine->PEntityOfEntIndex(client);
|
||||
|
||||
/* If we got here, the command failed... */
|
||||
char buffer[128];
|
||||
if (!CoreTranslate(buffer, sizeof(buffer), "%T", 2, NULL, "No Access", &client))
|
||||
{
|
||||
UTIL_Format(buffer, sizeof(buffer), "You do not have access to this command");
|
||||
}
|
||||
|
||||
unsigned int replyto = g_ChatTriggers.GetReplyTo();
|
||||
if (replyto == SM_REPLY_CONSOLE)
|
||||
{
|
||||
char fullbuffer[192];
|
||||
UTIL_Format(fullbuffer, sizeof(fullbuffer), "[SM] %s.\n", buffer);
|
||||
engine->ClientPrintf(pEdict, fullbuffer);
|
||||
}
|
||||
else if (replyto == SM_REPLY_CHAT)
|
||||
{
|
||||
char fullbuffer[192];
|
||||
UTIL_Format(fullbuffer, sizeof(fullbuffer), "[SM] %s.", buffer);
|
||||
g_HL2.TextMsg(client, HUD_PRINTTALK, fullbuffer);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ConCmdManager::AddConsoleCommand(IPluginFunction *pFunction,
|
||||
const char *name,
|
||||
const char *description,
|
||||
int flags)
|
||||
{
|
||||
ConCmdInfo *pInfo = AddOrFindCommand(name, description, flags);
|
||||
|
||||
if (!pInfo)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
CmdHook *pHook = new CmdHook();
|
||||
|
||||
pHook->pf = pFunction;
|
||||
if (description && description[0])
|
||||
{
|
||||
pHook->helptext.assign(description);
|
||||
}
|
||||
pInfo->conhooks.push_back(pHook);
|
||||
|
||||
/* Add to the plugin */
|
||||
CmdList *pList;
|
||||
IPlugin *pPlugin = g_PluginSys.GetPluginByCtx(pFunction->GetParentContext()->GetContext());
|
||||
if (!pPlugin->GetProperty("CommandList", (void **)&pList))
|
||||
{
|
||||
pList = new CmdList();
|
||||
pPlugin->SetProperty("CommandList", pList);
|
||||
}
|
||||
PlCmdInfo info;
|
||||
info.pInfo = pInfo;
|
||||
info.type = Cmd_Console;
|
||||
info.pHook = pHook;
|
||||
AddToPlCmdList(pList, info);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ConCmdManager::AddAdminCommand(IPluginFunction *pFunction,
|
||||
const char *name,
|
||||
const char *group,
|
||||
int adminflags,
|
||||
const char *description,
|
||||
int flags)
|
||||
{
|
||||
ConCmdInfo *pInfo = AddOrFindCommand(name, description, flags);
|
||||
|
||||
if (!pInfo)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
CmdHook *pHook = new CmdHook();
|
||||
AdminCmdInfo *pAdmin = new AdminCmdInfo();
|
||||
|
||||
pHook->pf = pFunction;
|
||||
if (description && description[0])
|
||||
{
|
||||
pHook->helptext.assign(description);
|
||||
}
|
||||
pHook->pAdmin = pAdmin;
|
||||
|
||||
void *object;
|
||||
int grpid;
|
||||
if (!sm_trie_retrieve(m_pCmdGrps, group, (void **)&object))
|
||||
{
|
||||
grpid = m_Strings.AddString(group);
|
||||
sm_trie_insert(m_pCmdGrps, group, (void *)grpid);
|
||||
}
|
||||
else
|
||||
{
|
||||
grpid = (int)object;
|
||||
}
|
||||
|
||||
pAdmin->cmdGrpId = grpid;
|
||||
pAdmin->flags = adminflags;
|
||||
|
||||
/* First get the command group override, if any */
|
||||
bool override = g_Admins.GetCommandOverride(group,
|
||||
Override_CommandGroup,
|
||||
&(pAdmin->eflags));
|
||||
|
||||
/* Next get the command override, if any */
|
||||
if (g_Admins.GetCommandOverride(name,
|
||||
Override_Command,
|
||||
&(pAdmin->eflags)))
|
||||
{
|
||||
override = true;
|
||||
}
|
||||
|
||||
/* Assign normal flags if there were no overrides */
|
||||
if (!override)
|
||||
{
|
||||
pAdmin->eflags = pAdmin->flags;
|
||||
}
|
||||
|
||||
/* Finally, add the hook */
|
||||
pInfo->conhooks.push_back(pHook);
|
||||
pInfo->admin = *(pHook->pAdmin);
|
||||
pInfo->is_admin_set = true;
|
||||
|
||||
/* Now add to the plugin */
|
||||
CmdList *pList;
|
||||
IPlugin *pPlugin = g_PluginSys.GetPluginByCtx(pFunction->GetParentContext()->GetContext());
|
||||
if (!pPlugin->GetProperty("CommandList", (void **)&pList))
|
||||
{
|
||||
pList = new CmdList();
|
||||
pPlugin->SetProperty("CommandList", pList);
|
||||
}
|
||||
PlCmdInfo info;
|
||||
info.pInfo = pInfo;
|
||||
info.type = Cmd_Admin;
|
||||
info.pHook = pHook;
|
||||
AddToPlCmdList(pList, info);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ConCmdManager::AddServerCommand(IPluginFunction *pFunction,
|
||||
const char *name,
|
||||
const char *description,
|
||||
int flags)
|
||||
|
||||
{
|
||||
ConCmdInfo *pInfo = AddOrFindCommand(name, description, flags);
|
||||
|
||||
if (!pInfo)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
CmdHook *pHook = new CmdHook();
|
||||
|
||||
pHook->pf = pFunction;
|
||||
if (description && description[0])
|
||||
{
|
||||
pHook->helptext.assign(description);
|
||||
}
|
||||
|
||||
pInfo->srvhooks.push_back(pHook);
|
||||
|
||||
/* Add to the plugin */
|
||||
CmdList *pList;
|
||||
IPlugin *pPlugin = g_PluginSys.GetPluginByCtx(pFunction->GetParentContext()->GetContext());
|
||||
if (!pPlugin->GetProperty("CommandList", (void **)&pList))
|
||||
{
|
||||
pList = new CmdList();
|
||||
pPlugin->SetProperty("CommandList", pList);
|
||||
}
|
||||
PlCmdInfo info;
|
||||
info.pInfo = pInfo;
|
||||
info.type = Cmd_Server;
|
||||
info.pHook = pHook;
|
||||
AddToPlCmdList(pList, info);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void AddToPlCmdList(CmdList *pList, const PlCmdInfo &info)
|
||||
{
|
||||
CmdList::iterator iter = pList->begin();
|
||||
bool inserted = false;
|
||||
const char *orig = NULL;
|
||||
|
||||
orig = info.pInfo->pCmd->GetName();
|
||||
|
||||
/* Insert this into the help list, SORTED alphabetically. */
|
||||
while (iter != pList->end())
|
||||
{
|
||||
PlCmdInfo &obj = (*iter);
|
||||
const char *cmd = obj.pInfo->pCmd->GetName();
|
||||
if (strcmp(orig, cmd) < 0)
|
||||
{
|
||||
pList->insert(iter, info);
|
||||
inserted = true;
|
||||
break;
|
||||
}
|
||||
iter++;
|
||||
}
|
||||
|
||||
if (!inserted)
|
||||
{
|
||||
pList->push_back(info);
|
||||
}
|
||||
}
|
||||
|
||||
void ConCmdManager::AddToCmdList(ConCmdInfo *info)
|
||||
{
|
||||
List<ConCmdInfo *>::iterator iter = m_CmdList.begin();
|
||||
ConCmdInfo *pInfo;
|
||||
bool inserted = false;
|
||||
const char *orig = NULL;
|
||||
|
||||
orig = info->pCmd->GetName();
|
||||
|
||||
/* Insert this into the help list, SORTED alphabetically. */
|
||||
while (iter != m_CmdList.end())
|
||||
{
|
||||
const char *cmd = NULL;
|
||||
pInfo = (*iter);
|
||||
cmd = pInfo->pCmd->GetName();
|
||||
if (strcmp(orig, cmd) < 0)
|
||||
{
|
||||
m_CmdList.insert(iter, info);
|
||||
inserted = true;
|
||||
break;
|
||||
}
|
||||
iter++;
|
||||
}
|
||||
|
||||
if (!inserted)
|
||||
{
|
||||
m_CmdList.push_back(info);
|
||||
}
|
||||
}
|
||||
|
||||
void ConCmdManager::UpdateAdminCmdFlags(const char *cmd, OverrideType type, FlagBits bits, bool remove)
|
||||
{
|
||||
ConCmdInfo *pInfo;
|
||||
|
||||
if (type == Override_Command)
|
||||
{
|
||||
if (!sm_trie_retrieve(m_pCmds, cmd, (void **)&pInfo))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
List<CmdHook *>::iterator iter;
|
||||
CmdHook *pHook;
|
||||
|
||||
for (iter=pInfo->conhooks.begin(); iter!=pInfo->conhooks.end(); iter++)
|
||||
{
|
||||
pHook = (*iter);
|
||||
if (pHook->pAdmin)
|
||||
{
|
||||
if (!remove)
|
||||
{
|
||||
pHook->pAdmin->eflags = bits;
|
||||
} else {
|
||||
pHook->pAdmin->eflags = pHook->pAdmin->flags;
|
||||
}
|
||||
pInfo->admin = *(pHook->pAdmin);
|
||||
}
|
||||
}
|
||||
pInfo->is_admin_set = true;
|
||||
}
|
||||
else if (type == Override_CommandGroup)
|
||||
{
|
||||
void *object;
|
||||
if (!sm_trie_retrieve(m_pCmdGrps, cmd, &object))
|
||||
{
|
||||
return;
|
||||
}
|
||||
int grpid = (int)object;
|
||||
|
||||
/* This is bad :( loop through all commands */
|
||||
List<ConCmdInfo *>::iterator iter;
|
||||
CmdHook *pHook;
|
||||
for (iter=m_CmdList.begin(); iter!=m_CmdList.end(); iter++)
|
||||
{
|
||||
pInfo = (*iter);
|
||||
for (List<CmdHook *>::iterator citer=pInfo->conhooks.begin();
|
||||
citer!=pInfo->conhooks.end();
|
||||
citer++)
|
||||
{
|
||||
pHook = (*citer);
|
||||
if (pHook->pAdmin && pHook->pAdmin->cmdGrpId == grpid)
|
||||
{
|
||||
if (remove)
|
||||
{
|
||||
pHook->pAdmin->eflags = bits;
|
||||
} else {
|
||||
pHook->pAdmin->eflags = pHook->pAdmin->flags;
|
||||
}
|
||||
pInfo->admin = *(pHook->pAdmin);
|
||||
}
|
||||
}
|
||||
}
|
||||
pInfo->is_admin_set = true;
|
||||
}
|
||||
}
|
||||
|
||||
void ConCmdManager::RemoveConCmd(ConCmdInfo *info, const char *name, bool is_read_safe, bool untrack)
|
||||
{
|
||||
/* Remove from the trie */
|
||||
sm_trie_delete(m_pCmds, name);
|
||||
|
||||
/* Remove console-specific information
|
||||
* This should always be true as of right now
|
||||
*/
|
||||
if (info->pCmd)
|
||||
{
|
||||
if (info->sourceMod)
|
||||
{
|
||||
/* Unlink from SourceMM */
|
||||
g_SMAPI->UnregisterConCommandBase(g_PLAPI, info->pCmd);
|
||||
/* Delete the command's memory */
|
||||
char *new_help = const_cast<char *>(info->pCmd->GetHelpText());
|
||||
char *new_name = const_cast<char *>(info->pCmd->GetName());
|
||||
delete [] new_help;
|
||||
delete [] new_name;
|
||||
delete info->pCmd;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (is_read_safe)
|
||||
{
|
||||
/* Remove the external hook */
|
||||
SH_REMOVE_HOOK_STATICFUNC(ConCommand, Dispatch, info->pCmd, CommandCallback, false);
|
||||
}
|
||||
if (untrack)
|
||||
{
|
||||
UntrackConCommandBase(info->pCmd, this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Remove from list */
|
||||
m_CmdList.remove(info);
|
||||
|
||||
delete info;
|
||||
}
|
||||
|
||||
bool ConCmdManager::LookForSourceModCommand(const char *cmd)
|
||||
{
|
||||
ConCmdInfo *pInfo;
|
||||
|
||||
if (!sm_trie_retrieve(m_pCmds, cmd, (void **)&pInfo))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return pInfo->sourceMod && (pInfo->conhooks.size() > 0);
|
||||
}
|
||||
|
||||
bool ConCmdManager::LookForCommandAdminFlags(const char *cmd, FlagBits *pFlags)
|
||||
{
|
||||
ConCmdInfo *pInfo;
|
||||
|
||||
if (!sm_trie_retrieve(m_pCmds, cmd, (void **)&pInfo))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
*pFlags = pInfo->admin.eflags;
|
||||
|
||||
return pInfo->is_admin_set;
|
||||
}
|
||||
|
||||
ConCmdInfo *ConCmdManager::AddOrFindCommand(const char *name, const char *description, int flags)
|
||||
{
|
||||
ConCmdInfo *pInfo;
|
||||
if (!sm_trie_retrieve(m_pCmds, name, (void **)&pInfo))
|
||||
{
|
||||
pInfo = new ConCmdInfo();
|
||||
/* Find the commandopan */
|
||||
ConCommandBase *pBase = icvar->GetCommands();
|
||||
ConCommand *pCmd = NULL;
|
||||
while (pBase)
|
||||
{
|
||||
if (strcmp(pBase->GetName(), name) == 0)
|
||||
{
|
||||
/* Don't want to return convar with same name */
|
||||
if (!pBase->IsCommand())
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pCmd = (ConCommand *)pBase;
|
||||
break;
|
||||
}
|
||||
pBase = const_cast<ConCommandBase *>(pBase->GetNext());
|
||||
}
|
||||
|
||||
if (!pCmd)
|
||||
{
|
||||
/* Note that we have to duplicate because the source might not be
|
||||
* a static string, and these expect static memory.
|
||||
*/
|
||||
if (!description)
|
||||
{
|
||||
description = "";
|
||||
}
|
||||
char *new_name = sm_strdup(name);
|
||||
char *new_help = sm_strdup(description);
|
||||
pCmd = new ConCommand(new_name, CommandCallback, new_help, flags);
|
||||
pInfo->sourceMod = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
TrackConCommandBase(pCmd, this);
|
||||
SH_ADD_HOOK_STATICFUNC(ConCommand, Dispatch, pCmd, CommandCallback, false);
|
||||
}
|
||||
|
||||
pInfo->pCmd = pCmd;
|
||||
pInfo->is_admin_set = false;
|
||||
|
||||
sm_trie_insert(m_pCmds, name, pInfo);
|
||||
AddToCmdList(pInfo);
|
||||
}
|
||||
|
||||
return pInfo;
|
||||
}
|
||||
|
||||
void ConCmdManager::OnRootConsoleCommand(const char *cmdname, const CCommand &command)
|
||||
{
|
||||
if (command.ArgC() >= 3)
|
||||
{
|
||||
const char *text = command.Arg(2);
|
||||
|
||||
CPlugin *pPlugin = g_PluginSys.FindPluginByConsoleArg(text);
|
||||
|
||||
if (!pPlugin)
|
||||
{
|
||||
g_RootMenu.ConsolePrint("[SM] Plugin \"%s\" was not found.", text);
|
||||
return;
|
||||
}
|
||||
|
||||
const sm_plugininfo_t *plinfo = pPlugin->GetPublicInfo();
|
||||
const char *plname = IS_STR_FILLED(plinfo->name) ? plinfo->name : pPlugin->GetFilename();
|
||||
|
||||
CmdList *pList;
|
||||
if (!pPlugin->GetProperty("CommandList", (void **)&pList))
|
||||
{
|
||||
g_RootMenu.ConsolePrint("[SM] No commands found for: %s", plname);
|
||||
return;
|
||||
}
|
||||
if (!pList->size())
|
||||
{
|
||||
g_RootMenu.ConsolePrint("[SM] No commands found for: %s", plname);
|
||||
return;
|
||||
}
|
||||
|
||||
CmdList::iterator iter;
|
||||
const char *type = NULL;
|
||||
const char *name;
|
||||
const char *help;
|
||||
g_RootMenu.ConsolePrint("[SM] Listing %d commands for: %s", pList->size(), plname);
|
||||
g_RootMenu.ConsolePrint(" %-17.16s %-8.7s %s", "[Name]", "[Type]", "[Help]");
|
||||
for (iter=pList->begin();
|
||||
iter!=pList->end();
|
||||
iter++)
|
||||
{
|
||||
PlCmdInfo &cmd = (*iter);
|
||||
if (cmd.type == Cmd_Server)
|
||||
{
|
||||
type = "server";
|
||||
}
|
||||
else if (cmd.type == Cmd_Console)
|
||||
{
|
||||
type = "console";
|
||||
}
|
||||
else if (cmd.type == Cmd_Admin)
|
||||
{
|
||||
type = "admin";
|
||||
}
|
||||
name = cmd.pInfo->pCmd->GetName();
|
||||
if (cmd.pHook->helptext.size())
|
||||
{
|
||||
help = cmd.pHook->helptext.c_str();
|
||||
}
|
||||
else
|
||||
{
|
||||
help = cmd.pInfo->pCmd->GetHelpText();
|
||||
}
|
||||
g_RootMenu.ConsolePrint(" %-17.16s %-12.11s %s", name, type, help);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
g_RootMenu.ConsolePrint("[SM] Usage: sm cmds <plugin #>");
|
||||
}
|
||||
@@ -0,0 +1,161 @@
|
||||
/**
|
||||
* vim: set ts=4 :
|
||||
* =============================================================================
|
||||
* SourceMod
|
||||
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
|
||||
* =============================================================================
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, version 3.0, as published by the
|
||||
* Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* As a special exception, AlliedModders LLC gives you permission to link the
|
||||
* code of this program (as well as its derivative works) to "Half-Life 2," the
|
||||
* "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
|
||||
* by the Valve Corporation. You must obey the GNU General Public License in
|
||||
* all respects for all other code used. Additionally, AlliedModders LLC grants
|
||||
* this exception to all derivative works. AlliedModders LLC defines further
|
||||
* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
|
||||
* or <http://www.sourcemod.net/license.php>.
|
||||
*
|
||||
* Version: $Id$
|
||||
*/
|
||||
|
||||
#ifndef _INCLUDE_SOURCEMOD_CONCMDMANAGER_H_
|
||||
#define _INCLUDE_SOURCEMOD_CONCMDMANAGER_H_
|
||||
|
||||
#include "sm_globals.h"
|
||||
#include "sourcemm_api.h"
|
||||
#include "ForwardSys.h"
|
||||
#include "sm_trie.h"
|
||||
#include "sm_memtable.h"
|
||||
#include <sh_list.h>
|
||||
#include <sh_string.h>
|
||||
#include <IRootConsoleMenu.h>
|
||||
#include <IAdminSystem.h>
|
||||
#include "concmd_cleaner.h"
|
||||
|
||||
using namespace SourceHook;
|
||||
|
||||
enum CmdType
|
||||
{
|
||||
Cmd_Server,
|
||||
Cmd_Console,
|
||||
Cmd_Admin,
|
||||
};
|
||||
|
||||
struct AdminCmdInfo
|
||||
{
|
||||
AdminCmdInfo()
|
||||
{
|
||||
cmdGrpId = -1;
|
||||
flags = 0;
|
||||
eflags = 0;
|
||||
}
|
||||
int cmdGrpId; /* index into cmdgroup string table */
|
||||
FlagBits flags; /* default flags */
|
||||
FlagBits eflags; /* effective flags */
|
||||
};
|
||||
|
||||
struct CmdHook
|
||||
{
|
||||
CmdHook()
|
||||
{
|
||||
pf = NULL;
|
||||
pAdmin = NULL;
|
||||
}
|
||||
IPluginFunction *pf; /* function hook */
|
||||
String helptext; /* help text */
|
||||
AdminCmdInfo *pAdmin; /* admin requirements, if any */
|
||||
};
|
||||
|
||||
struct ConCmdInfo
|
||||
{
|
||||
ConCmdInfo()
|
||||
{
|
||||
sourceMod = false;
|
||||
pCmd = NULL;
|
||||
}
|
||||
bool sourceMod; /**< Determines whether or not concmd was created by a SourceMod plugin */
|
||||
ConCommand *pCmd; /**< Pointer to the command itself */
|
||||
List<CmdHook *> srvhooks; /**< Hooks as a server command */
|
||||
List<CmdHook *> conhooks; /**< Hooks as a console command */
|
||||
AdminCmdInfo admin; /**< Admin info, if any */
|
||||
bool is_admin_set; /**< Whether or not admin info is set */
|
||||
};
|
||||
|
||||
class ConCmdManager :
|
||||
public SMGlobalClass,
|
||||
public IRootConsoleCommand,
|
||||
public IPluginsListener,
|
||||
public IConCommandTracker
|
||||
{
|
||||
#if defined ORANGEBOX_BUILD
|
||||
friend void CommandCallback(const CCommand &command);
|
||||
#else
|
||||
friend void CommandCallback();
|
||||
#endif
|
||||
public:
|
||||
ConCmdManager();
|
||||
~ConCmdManager();
|
||||
public: //SMGlobalClass
|
||||
void OnSourceModAllInitialized();
|
||||
void OnSourceModShutdown();
|
||||
public: //IPluginsListener
|
||||
void OnPluginDestroyed(IPlugin *plugin);
|
||||
public: //IRootConsoleCommand
|
||||
void OnRootConsoleCommand(const char *cmdname, const CCommand &command);
|
||||
public: //IConCommandTracker
|
||||
void OnUnlinkConCommandBase(ConCommandBase *pBase, const char *name, bool is_read_safe);
|
||||
public:
|
||||
bool AddServerCommand(IPluginFunction *pFunction, const char *name, const char *description, int flags);
|
||||
bool AddConsoleCommand(IPluginFunction *pFunction, const char *name, const char *description, int flags);
|
||||
bool AddAdminCommand(IPluginFunction *pFunction,
|
||||
const char *name,
|
||||
const char *group,
|
||||
int adminflags,
|
||||
const char *description,
|
||||
int flags);
|
||||
ResultType DispatchClientCommand(int client, const char *cmd, int args, ResultType type);
|
||||
void UpdateAdminCmdFlags(const char *cmd, OverrideType type, FlagBits bits, bool remove);
|
||||
bool LookForSourceModCommand(const char *cmd);
|
||||
bool LookForCommandAdminFlags(const char *cmd, FlagBits *pFlags);
|
||||
bool CheckCommandAccess(int client, const char *cmd, FlagBits flags);
|
||||
private:
|
||||
void InternalDispatch(const CCommand &command);
|
||||
ResultType RunAdminCommand(ConCmdInfo *pInfo, int client, int args);
|
||||
ConCmdInfo *AddOrFindCommand(const char *name, const char *description, int flags);
|
||||
void SetCommandClient(int client);
|
||||
void AddToCmdList(ConCmdInfo *info);
|
||||
void RemoveConCmd(ConCmdInfo *info, const char *cmd, bool is_read_safe, bool untrack);
|
||||
void RemoveConCmds(List<CmdHook *> &cmdlist);
|
||||
void RemoveConCmds(List<CmdHook *> &cmdlist, IPluginContext *pContext);
|
||||
bool CheckAccess(int client, const char *cmd, AdminCmdInfo *pAdmin);
|
||||
public:
|
||||
inline int GetCommandClient()
|
||||
{
|
||||
return m_CmdClient;
|
||||
}
|
||||
inline const List<ConCmdInfo *> & GetCommandList()
|
||||
{
|
||||
return m_CmdList;
|
||||
}
|
||||
private:
|
||||
Trie *m_pCmds; /* command lookup */
|
||||
Trie *m_pCmdGrps; /* command group lookup */
|
||||
List<ConCmdInfo *> m_CmdList; /* command list */
|
||||
int m_CmdClient; /* current client */
|
||||
BaseStringTable m_Strings; /* string table */
|
||||
};
|
||||
|
||||
extern ConCmdManager g_ConCmds;
|
||||
|
||||
#endif // _INCLUDE_SOURCEMOD_CONCMDMANAGER_H_
|
||||
@@ -0,0 +1,707 @@
|
||||
/**
|
||||
* vim: set ts=4 :
|
||||
* =============================================================================
|
||||
* SourceMod
|
||||
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
|
||||
* =============================================================================
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, version 3.0, as published by the
|
||||
* Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* As a special exception, AlliedModders LLC gives you permission to link the
|
||||
* code of this program (as well as its derivative works) to "Half-Life 2," the
|
||||
* "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
|
||||
* by the Valve Corporation. You must obey the GNU General Public License in
|
||||
* all respects for all other code used. Additionally, AlliedModders LLC grants
|
||||
* this exception to all derivative works. AlliedModders LLC defines further
|
||||
* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
|
||||
* or <http://www.sourcemod.net/license.php>.
|
||||
*
|
||||
* Version: $Id$
|
||||
*/
|
||||
|
||||
#include "ConVarManager.h"
|
||||
#include "HalfLife2.h"
|
||||
#include "PluginSys.h"
|
||||
#include "ForwardSys.h"
|
||||
#include "HandleSys.h"
|
||||
#include "sm_srvcmds.h"
|
||||
#include "sm_stringutil.h"
|
||||
#include <sh_vector.h>
|
||||
#include <sm_trie_tpl.h>
|
||||
|
||||
ConVarManager g_ConVarManager;
|
||||
|
||||
#if !defined ORANGEBOX_BUILD
|
||||
#define CallGlobalChangeCallbacks CallGlobalChangeCallback
|
||||
#endif
|
||||
|
||||
#if defined ORANGEBOX_BUILD
|
||||
SH_DECL_HOOK3_void(ICvar, CallGlobalChangeCallbacks, SH_NOATTRIB, false, ConVar *, const char *, float);
|
||||
#else
|
||||
SH_DECL_HOOK2_void(ICvar, CallGlobalChangeCallbacks, SH_NOATTRIB, false, ConVar *, const char *);
|
||||
#endif
|
||||
|
||||
SH_DECL_HOOK5_void(IServerGameDLL, OnQueryCvarValueFinished, SH_NOATTRIB, 0, QueryCvarCookie_t, edict_t *, EQueryCvarValueStatus, const char *, const char *);
|
||||
SH_DECL_HOOK5_void(IServerPluginCallbacks, OnQueryCvarValueFinished, SH_NOATTRIB, 0, QueryCvarCookie_t, edict_t *, EQueryCvarValueStatus, const char *, const char *);
|
||||
|
||||
const ParamType CONVARCHANGE_PARAMS[] = {Param_Cell, Param_String, Param_String};
|
||||
typedef List<const ConVar *> ConVarList;
|
||||
KTrie<ConVarInfo *> convar_cache;
|
||||
|
||||
ConVarManager::ConVarManager() : m_ConVarType(0), m_bIsDLLQueryHooked(false), m_bIsVSPQueryHooked(false)
|
||||
{
|
||||
}
|
||||
|
||||
ConVarManager::~ConVarManager()
|
||||
{
|
||||
}
|
||||
|
||||
void ConVarManager::OnSourceModStartup(bool late)
|
||||
{
|
||||
HandleAccess sec;
|
||||
|
||||
/* Set up access rights for the 'ConVar' handle type */
|
||||
sec.access[HandleAccess_Read] = 0;
|
||||
sec.access[HandleAccess_Delete] = HANDLE_RESTRICT_IDENTITY | HANDLE_RESTRICT_OWNER;
|
||||
sec.access[HandleAccess_Clone] = HANDLE_RESTRICT_IDENTITY | HANDLE_RESTRICT_OWNER;
|
||||
|
||||
/* Create the 'ConVar' handle type */
|
||||
m_ConVarType = g_HandleSys.CreateType("ConVar", this, 0, NULL, &sec, g_pCoreIdent, NULL);
|
||||
}
|
||||
|
||||
void ConVarManager::OnSourceModAllInitialized()
|
||||
{
|
||||
/**
|
||||
* Episode 2 has this function by default, but the older versions do not.
|
||||
*/
|
||||
#if !defined ORANGEBOX_BUILD
|
||||
if (g_SMAPI->GetGameDLLVersion() >= 6)
|
||||
{
|
||||
SH_ADD_HOOK_MEMFUNC(IServerGameDLL, OnQueryCvarValueFinished, gamedll, this, &ConVarManager::OnQueryCvarValueFinished, false);
|
||||
m_bIsDLLQueryHooked = true;
|
||||
}
|
||||
#endif
|
||||
|
||||
SH_ADD_HOOK_STATICFUNC(ICvar, CallGlobalChangeCallbacks, icvar, OnConVarChanged, false);
|
||||
|
||||
/* Add the 'convars' option to the 'sm' console command */
|
||||
g_RootMenu.AddRootConsoleCommand("cvars", "View convars created by a plugin", this);
|
||||
}
|
||||
|
||||
void ConVarManager::OnSourceModShutdown()
|
||||
{
|
||||
List<ConVarInfo *>::iterator iter = m_ConVars.begin();
|
||||
HandleSecurity sec(NULL, g_pCoreIdent);
|
||||
|
||||
/* Iterate list of ConVarInfo structures, remove every one of them */
|
||||
while (iter != m_ConVars.end())
|
||||
{
|
||||
ConVarInfo *pInfo = (*iter);
|
||||
|
||||
iter = m_ConVars.erase(iter);
|
||||
|
||||
g_HandleSys.FreeHandle(pInfo->handle, &sec);
|
||||
if (pInfo->pChangeForward != NULL)
|
||||
{
|
||||
g_Forwards.ReleaseForward(pInfo->pChangeForward);
|
||||
}
|
||||
if (pInfo->sourceMod)
|
||||
{
|
||||
/* If we created it, we won't be tracking it, therefore it is
|
||||
* safe to remove everything in one go.
|
||||
*/
|
||||
META_UNREGCVAR(pInfo->pVar);
|
||||
delete [] pInfo->pVar->GetName();
|
||||
delete [] pInfo->pVar->GetHelpText();
|
||||
delete [] pInfo->pVar->GetDefault();
|
||||
delete pInfo->pVar;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* If we didn't create it, we might be tracking it. Also,
|
||||
* it could be unreadable.
|
||||
*/
|
||||
UntrackConCommandBase(pInfo->pVar, this);
|
||||
}
|
||||
|
||||
/* It's not safe to read the name here, so we simply delete the
|
||||
* the info struct and clear the lookup cache at the end.
|
||||
*/
|
||||
delete pInfo;
|
||||
}
|
||||
convar_cache.clear();
|
||||
|
||||
/* Unhook things */
|
||||
if (m_bIsDLLQueryHooked)
|
||||
{
|
||||
SH_REMOVE_HOOK_MEMFUNC(IServerGameDLL, OnQueryCvarValueFinished, gamedll, this, &ConVarManager::OnQueryCvarValueFinished, false);
|
||||
m_bIsDLLQueryHooked = false;
|
||||
}
|
||||
else if (m_bIsVSPQueryHooked)
|
||||
{
|
||||
SH_REMOVE_HOOK_MEMFUNC(IServerPluginCallbacks, OnQueryCvarValueFinished, vsp_interface, this, &ConVarManager::OnQueryCvarValueFinished, false);
|
||||
m_bIsVSPQueryHooked = false;
|
||||
}
|
||||
|
||||
SH_REMOVE_HOOK_STATICFUNC(ICvar, CallGlobalChangeCallbacks, icvar, OnConVarChanged, false);
|
||||
|
||||
/* Remove the 'convars' option from the 'sm' console command */
|
||||
g_RootMenu.RemoveRootConsoleCommand("cvars", this);
|
||||
|
||||
/* Remove the 'ConVar' handle type */
|
||||
g_HandleSys.RemoveType(m_ConVarType, g_pCoreIdent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Orange Box will never use this.
|
||||
*/
|
||||
void ConVarManager::OnSourceModVSPReceived()
|
||||
{
|
||||
/**
|
||||
* Don't bother if the DLL is already hooked.
|
||||
*/
|
||||
if (m_bIsDLLQueryHooked)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* For later MM:S versions, use the updated API, since it's cleaner. */
|
||||
#if defined METAMOD_PLAPI_VERSION
|
||||
int engine = g_SMAPI->GetSourceEngineBuild();
|
||||
if (engine == SOURCE_ENGINE_ORIGINAL || vsp_version < 2)
|
||||
{
|
||||
return;
|
||||
}
|
||||
#else
|
||||
if (g_HL2.IsOriginalEngine() || vsp_version < 2)
|
||||
{
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
SH_ADD_HOOK_MEMFUNC(IServerPluginCallbacks, OnQueryCvarValueFinished, vsp_interface, this, &ConVarManager::OnQueryCvarValueFinished, false);
|
||||
m_bIsVSPQueryHooked = true;
|
||||
}
|
||||
|
||||
bool convar_cache_lookup(const char *name, ConVarInfo **pVar)
|
||||
{
|
||||
ConVarInfo **pLookup = convar_cache.retrieve(name);
|
||||
if (pLookup != NULL)
|
||||
{
|
||||
*pVar = *pLookup;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void ConVarManager::OnUnlinkConCommandBase(ConCommandBase *pBase, const char *name, bool is_read_safe)
|
||||
{
|
||||
/* Only check convars that have not been created by SourceMod's core */
|
||||
ConVarInfo *pInfo;
|
||||
if (!convar_cache_lookup(name, &pInfo))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
HandleSecurity sec(NULL, g_pCoreIdent);
|
||||
|
||||
/* Remove it from our cache */
|
||||
m_ConVars.remove(pInfo);
|
||||
convar_cache.remove(name);
|
||||
|
||||
/* Now make sure no plugins are referring to this pointer */
|
||||
IPluginIterator *pl_iter = g_PluginSys.GetPluginIterator();
|
||||
while (pl_iter->MorePlugins())
|
||||
{
|
||||
IPlugin *pl = pl_iter->GetPlugin();
|
||||
|
||||
ConVarList *pConVarList;
|
||||
if (pl->GetProperty("ConVarList", (void **)&pConVarList, true)
|
||||
&& pConVarList != NULL)
|
||||
{
|
||||
pConVarList->remove(pInfo->pVar);
|
||||
}
|
||||
|
||||
pl_iter->NextPlugin();
|
||||
}
|
||||
|
||||
/* Free resources */
|
||||
g_HandleSys.FreeHandle(pInfo->handle, &sec);
|
||||
delete pInfo;
|
||||
}
|
||||
|
||||
void ConVarManager::OnPluginUnloaded(IPlugin *plugin)
|
||||
{
|
||||
ConVarList *pConVarList;
|
||||
|
||||
/* If plugin has a convar list, free its memory */
|
||||
if (plugin->GetProperty("ConVarList", (void **)&pConVarList, true))
|
||||
{
|
||||
delete pConVarList;
|
||||
}
|
||||
}
|
||||
|
||||
void ConVarManager::OnHandleDestroy(HandleType_t type, void *object)
|
||||
{
|
||||
}
|
||||
|
||||
bool ConVarManager::GetHandleApproxSize(HandleType_t type, void *object, unsigned int *pSize)
|
||||
{
|
||||
*pSize = sizeof(ConVar) + sizeof(ConVarInfo);
|
||||
return true;
|
||||
}
|
||||
|
||||
void ConVarManager::OnRootConsoleCommand(const char *cmdname, const CCommand &command)
|
||||
{
|
||||
int argcount = command.ArgC();
|
||||
if (argcount >= 3)
|
||||
{
|
||||
/* Get plugin index that was passed */
|
||||
const char *arg = command.Arg(2);
|
||||
|
||||
/* Get plugin object */
|
||||
CPlugin *plugin = g_PluginSys.FindPluginByConsoleArg(arg);
|
||||
|
||||
if (!plugin)
|
||||
{
|
||||
g_RootMenu.ConsolePrint("[SM] Plugin \"%s\" was not found.", arg);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Get plugin name */
|
||||
const sm_plugininfo_t *plinfo = plugin->GetPublicInfo();
|
||||
const char *plname = IS_STR_FILLED(plinfo->name) ? plinfo->name : plugin->GetFilename();
|
||||
|
||||
ConVarList *pConVarList;
|
||||
ConVarList::iterator iter;
|
||||
|
||||
/* If no convar list... */
|
||||
if (!plugin->GetProperty("ConVarList", (void **)&pConVarList))
|
||||
{
|
||||
g_RootMenu.ConsolePrint("[SM] No convars found for: %s", plname);
|
||||
return;
|
||||
}
|
||||
|
||||
g_RootMenu.ConsolePrint("[SM] Listing %d convars for: %s", pConVarList->size(), plname);
|
||||
g_RootMenu.ConsolePrint(" %-32.31s %s", "[Name]", "[Value]");
|
||||
|
||||
/* Iterate convar list and display each one */
|
||||
for (iter = pConVarList->begin(); iter != pConVarList->end(); iter++)
|
||||
{
|
||||
const ConVar *pConVar = (*iter);
|
||||
g_RootMenu.ConsolePrint(" %-32.31s %s", pConVar->GetName(), pConVar->GetString());
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* Display usage of subcommand */
|
||||
g_RootMenu.ConsolePrint("[SM] Usage: sm convars <plugin #>");
|
||||
}
|
||||
|
||||
Handle_t ConVarManager::CreateConVar(IPluginContext *pContext, const char *name, const char *defaultVal, const char *description, int flags, bool hasMin, float min, bool hasMax, float max)
|
||||
{
|
||||
ConVar *pConVar = NULL;
|
||||
ConVarInfo *pInfo = NULL;
|
||||
Handle_t hndl = 0;
|
||||
|
||||
/* Find out if the convar exists already */
|
||||
pConVar = icvar->FindVar(name);
|
||||
|
||||
/* If the convar already exists... */
|
||||
if (pConVar)
|
||||
{
|
||||
/* Add convar to plugin's list */
|
||||
AddConVarToPluginList(pContext, pConVar);
|
||||
|
||||
/* First find out if we already have a handle to it */
|
||||
if (convar_cache_lookup(name, &pInfo))
|
||||
{
|
||||
return pInfo->handle;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Create and initialize ConVarInfo structure */
|
||||
pInfo = new ConVarInfo();
|
||||
pInfo->sourceMod = false;
|
||||
pInfo->pChangeForward = NULL;
|
||||
pInfo->pVar = pConVar;
|
||||
|
||||
/* If we don't, then create a new handle from the convar */
|
||||
hndl = g_HandleSys.CreateHandle(m_ConVarType, pInfo, NULL, g_pCoreIdent, NULL);
|
||||
if (hndl == BAD_HANDLE)
|
||||
{
|
||||
delete pInfo;
|
||||
return BAD_HANDLE;
|
||||
}
|
||||
|
||||
pInfo->handle = hndl;
|
||||
|
||||
/* Insert struct into caches */
|
||||
m_ConVars.push_back(pInfo);
|
||||
convar_cache.insert(name, pInfo);
|
||||
TrackConCommandBase(pConVar, this);
|
||||
|
||||
return hndl;
|
||||
}
|
||||
}
|
||||
|
||||
/* To prevent creating a convar that has the same name as a console command... ugh */
|
||||
ConCommandBase *pBase = icvar->GetCommands();
|
||||
|
||||
while (pBase)
|
||||
{
|
||||
if (pBase->IsCommand() && strcmp(pBase->GetName(), name) == 0)
|
||||
{
|
||||
return BAD_HANDLE;
|
||||
}
|
||||
|
||||
pBase = const_cast<ConCommandBase *>(pBase->GetNext());
|
||||
}
|
||||
|
||||
/* Create and initialize ConVarInfo structure */
|
||||
pInfo = new ConVarInfo();
|
||||
pInfo->handle = hndl;
|
||||
pInfo->sourceMod = true;
|
||||
pInfo->pChangeForward = NULL;
|
||||
|
||||
/* Create a handle from the new convar */
|
||||
hndl = g_HandleSys.CreateHandle(m_ConVarType, pInfo, NULL, g_pCoreIdent, NULL);
|
||||
if (hndl == BAD_HANDLE)
|
||||
{
|
||||
delete pInfo;
|
||||
return BAD_HANDLE;
|
||||
}
|
||||
|
||||
pInfo->handle = hndl;
|
||||
|
||||
/* Since an existing convar (or concmd with the same name) was not found , now we can finally create it */
|
||||
pConVar = new ConVar(sm_strdup(name), sm_strdup(defaultVal), flags, sm_strdup(description), hasMin, min, hasMax, max);
|
||||
pInfo->pVar = pConVar;
|
||||
|
||||
/* Add convar to plugin's list */
|
||||
AddConVarToPluginList(pContext, pConVar);
|
||||
|
||||
/* Insert struct into caches */
|
||||
m_ConVars.push_back(pInfo);
|
||||
convar_cache.insert(name, pInfo);
|
||||
|
||||
return hndl;
|
||||
}
|
||||
|
||||
Handle_t ConVarManager::FindConVar(const char *name)
|
||||
{
|
||||
ConVar *pConVar = NULL;
|
||||
ConVarInfo *pInfo;
|
||||
Handle_t hndl;
|
||||
|
||||
/* Search for convar */
|
||||
pConVar = icvar->FindVar(name);
|
||||
|
||||
/* If it doesn't exist, then return an invalid handle */
|
||||
if (!pConVar)
|
||||
{
|
||||
return BAD_HANDLE;
|
||||
}
|
||||
|
||||
/* At this point, the convar exists. So, find out if we already have a handle */
|
||||
if (convar_cache_lookup(name, &pInfo))
|
||||
{
|
||||
return pInfo->handle;
|
||||
}
|
||||
|
||||
/* Create and initialize ConVarInfo structure */
|
||||
pInfo = new ConVarInfo();
|
||||
pInfo->sourceMod = false;
|
||||
pInfo->pChangeForward = NULL;
|
||||
pInfo->pVar = pConVar;
|
||||
|
||||
/* If we don't have a handle, then create a new one */
|
||||
hndl = g_HandleSys.CreateHandle(m_ConVarType, pInfo, NULL, g_pCoreIdent, NULL);
|
||||
if (hndl == BAD_HANDLE)
|
||||
{
|
||||
delete pInfo;
|
||||
return BAD_HANDLE;
|
||||
}
|
||||
|
||||
pInfo->handle = hndl;
|
||||
|
||||
/* Insert struct into our caches */
|
||||
m_ConVars.push_back(pInfo);
|
||||
convar_cache.insert(name, pInfo);
|
||||
TrackConCommandBase(pConVar, this);
|
||||
|
||||
return hndl;
|
||||
}
|
||||
|
||||
void ConVarManager::AddConVarChangeListener(const char *name, IConVarChangeListener *pListener)
|
||||
{
|
||||
ConVarInfo *pInfo;
|
||||
|
||||
if (FindConVar(name) == BAD_HANDLE)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* Find the convar in the lookup trie */
|
||||
if (convar_cache_lookup(name, &pInfo))
|
||||
{
|
||||
pInfo->changeListeners.push_back(pListener);
|
||||
}
|
||||
}
|
||||
|
||||
void ConVarManager::RemoveConVarChangeListener(const char *name, IConVarChangeListener *pListener)
|
||||
{
|
||||
ConVarInfo *pInfo;
|
||||
|
||||
/* Find the convar in the lookup trie */
|
||||
if (convar_cache_lookup(name, &pInfo))
|
||||
{
|
||||
pInfo->changeListeners.remove(pListener);
|
||||
}
|
||||
}
|
||||
|
||||
void ConVarManager::HookConVarChange(ConVar *pConVar, IPluginFunction *pFunction)
|
||||
{
|
||||
ConVarInfo *pInfo;
|
||||
IChangeableForward *pForward;
|
||||
|
||||
/* Find the convar in the lookup trie */
|
||||
if (convar_cache_lookup(pConVar->GetName(), &pInfo))
|
||||
{
|
||||
/* Get the forward */
|
||||
pForward = pInfo->pChangeForward;
|
||||
|
||||
/* If forward does not exist, create it */
|
||||
if (!pForward)
|
||||
{
|
||||
pForward = g_Forwards.CreateForwardEx(NULL, ET_Ignore, 3, CONVARCHANGE_PARAMS);
|
||||
pInfo->pChangeForward = pForward;
|
||||
}
|
||||
|
||||
/* Add function to forward's list */
|
||||
pForward->AddFunction(pFunction);
|
||||
}
|
||||
}
|
||||
|
||||
void ConVarManager::UnhookConVarChange(ConVar *pConVar, IPluginFunction *pFunction)
|
||||
{
|
||||
ConVarInfo *pInfo;
|
||||
IChangeableForward *pForward;
|
||||
IPluginContext *pContext = pFunction->GetParentContext();
|
||||
|
||||
/* Find the convar in the lookup trie */
|
||||
if (convar_cache_lookup(pConVar->GetName(), &pInfo))
|
||||
{
|
||||
/* Get the forward */
|
||||
pForward = pInfo->pChangeForward;
|
||||
|
||||
/* If the forward doesn't exist, we can't unhook anything */
|
||||
if (!pForward)
|
||||
{
|
||||
pContext->ThrowNativeError("Convar \"%s\" has no active hook", pConVar->GetName());
|
||||
return;
|
||||
}
|
||||
|
||||
/* Remove the function from the forward's list */
|
||||
if (!pForward->RemoveFunction(pFunction))
|
||||
{
|
||||
pContext->ThrowNativeError("Invalid hook callback specified for convar \"%s\"", pConVar->GetName());
|
||||
return;
|
||||
}
|
||||
|
||||
/* If the forward now has 0 functions in it... */
|
||||
if (pForward->GetFunctionCount() == 0)
|
||||
{
|
||||
/* Free this forward */
|
||||
g_Forwards.ReleaseForward(pForward);
|
||||
pInfo->pChangeForward = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QueryCvarCookie_t ConVarManager::QueryClientConVar(edict_t *pPlayer, const char *name, IPluginFunction *pCallback, Handle_t hndl)
|
||||
{
|
||||
QueryCvarCookie_t cookie;
|
||||
|
||||
/* Call StartQueryCvarValue() in either the IVEngineServer or IServerPluginHelpers depending on situation */
|
||||
if (m_bIsDLLQueryHooked)
|
||||
{
|
||||
cookie = engine->StartQueryCvarValue(pPlayer, name);
|
||||
}
|
||||
else if (m_bIsVSPQueryHooked)
|
||||
{
|
||||
cookie = serverpluginhelpers->StartQueryCvarValue(pPlayer, name);
|
||||
}
|
||||
else
|
||||
{
|
||||
return InvalidQueryCvarCookie;
|
||||
}
|
||||
|
||||
ConVarQuery query = {cookie, pCallback, hndl};
|
||||
m_ConVarQueries.push_back(query);
|
||||
|
||||
return cookie;
|
||||
}
|
||||
|
||||
void ConVarManager::AddConVarToPluginList(IPluginContext *pContext, const ConVar *pConVar)
|
||||
{
|
||||
ConVarList *pConVarList;
|
||||
ConVarList::iterator iter;
|
||||
bool inserted = false;
|
||||
const char *orig = pConVar->GetName();
|
||||
|
||||
IPlugin *plugin = g_PluginSys.FindPluginByContext(pContext->GetContext());
|
||||
|
||||
/* Check plugin for an existing convar list */
|
||||
if (!plugin->GetProperty("ConVarList", (void **)&pConVarList))
|
||||
{
|
||||
pConVarList = new ConVarList();
|
||||
plugin->SetProperty("ConVarList", pConVarList);
|
||||
}
|
||||
else if (pConVarList->find(pConVar) != pConVarList->end())
|
||||
{
|
||||
/* If convar is already in list, then don't add it */
|
||||
return;
|
||||
}
|
||||
|
||||
/* Insert convar into list which is sorted alphabetically */
|
||||
for (iter = pConVarList->begin(); iter != pConVarList->end(); iter++)
|
||||
{
|
||||
if (strcmp(orig, (*iter)->GetName()) < 0)
|
||||
{
|
||||
pConVarList->insert(iter, pConVar);
|
||||
inserted = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!inserted)
|
||||
{
|
||||
pConVarList->push_back(pConVar);
|
||||
}
|
||||
}
|
||||
|
||||
#if defined ORANGEBOX_BUILD
|
||||
void ConVarManager::OnConVarChanged(ConVar *pConVar, const char *oldValue, float flOldValue)
|
||||
#else
|
||||
void ConVarManager::OnConVarChanged(ConVar *pConVar, const char *oldValue)
|
||||
#endif
|
||||
{
|
||||
/* If the values are the same, exit early in order to not trigger callbacks */
|
||||
if (strcmp(pConVar->GetString(), oldValue) == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ConVarInfo *pInfo;
|
||||
|
||||
/* Find the convar in the lookup trie */
|
||||
if (!convar_cache_lookup(pConVar->GetName(), &pInfo))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
IChangeableForward *pForward = pInfo->pChangeForward;
|
||||
|
||||
if (pInfo->changeListeners.size() != 0)
|
||||
{
|
||||
for (List<IConVarChangeListener *>::iterator i = pInfo->changeListeners.begin();
|
||||
i != pInfo->changeListeners.end();
|
||||
i++)
|
||||
{
|
||||
#if defined ORANGEBOX_BUILD
|
||||
(*i)->OnConVarChanged(pConVar, oldValue, flOldValue);
|
||||
#else
|
||||
(*i)->OnConVarChanged(pConVar, oldValue, atof(oldValue));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
if (pForward != NULL)
|
||||
{
|
||||
/* Now call forwards in plugins that have hooked this */
|
||||
pForward->PushCell(pInfo->handle);
|
||||
pForward->PushString(oldValue);
|
||||
pForward->PushString(pConVar->GetString());
|
||||
pForward->Execute(NULL);
|
||||
}
|
||||
}
|
||||
|
||||
bool ConVarManager::IsQueryingSupported()
|
||||
{
|
||||
return (m_bIsDLLQueryHooked || m_bIsVSPQueryHooked);
|
||||
}
|
||||
|
||||
void ConVarManager::OnQueryCvarValueFinished(QueryCvarCookie_t cookie, edict_t *pPlayer, EQueryCvarValueStatus result, const char *cvarName, const char *cvarValue)
|
||||
{
|
||||
IPluginFunction *pCallback = NULL;
|
||||
cell_t value = 0;
|
||||
List<ConVarQuery>::iterator iter;
|
||||
|
||||
for (iter = m_ConVarQueries.begin(); iter != m_ConVarQueries.end(); iter++)
|
||||
{
|
||||
ConVarQuery &query = (*iter);
|
||||
if (query.cookie == cookie)
|
||||
{
|
||||
pCallback = query.pCallback;
|
||||
value = query.value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (pCallback)
|
||||
{
|
||||
cell_t ret;
|
||||
|
||||
pCallback->PushCell(cookie);
|
||||
pCallback->PushCell(engine->IndexOfEdict(pPlayer));
|
||||
pCallback->PushCell(result);
|
||||
pCallback->PushString(cvarName);
|
||||
|
||||
if (result == eQueryCvarValueStatus_ValueIntact)
|
||||
{
|
||||
pCallback->PushString(cvarValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
pCallback->PushString("\0");
|
||||
}
|
||||
|
||||
pCallback->PushCell(value);
|
||||
pCallback->Execute(&ret);
|
||||
|
||||
m_ConVarQueries.erase(iter);
|
||||
}
|
||||
}
|
||||
|
||||
HandleError ConVarManager::ReadConVarHandle(Handle_t hndl, ConVar **pVar)
|
||||
{
|
||||
ConVarInfo *pInfo;
|
||||
HandleError error;
|
||||
|
||||
if ((error = g_HandleSys.ReadHandle(hndl, m_ConVarType, NULL, (void **)&pInfo)) != HandleError_None)
|
||||
{
|
||||
return error;
|
||||
}
|
||||
|
||||
if (pVar)
|
||||
{
|
||||
*pVar = pInfo->pVar;
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
@@ -0,0 +1,165 @@
|
||||
/**
|
||||
* vim: set ts=4 :
|
||||
* =============================================================================
|
||||
* SourceMod
|
||||
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
|
||||
* =============================================================================
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, version 3.0, as published by the
|
||||
* Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* As a special exception, AlliedModders LLC gives you permission to link the
|
||||
* code of this program (as well as its derivative works) to "Half-Life 2," the
|
||||
* "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
|
||||
* by the Valve Corporation. You must obey the GNU General Public License in
|
||||
* all respects for all other code used. Additionally, AlliedModders LLC grants
|
||||
* this exception to all derivative works. AlliedModders LLC defines further
|
||||
* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
|
||||
* or <http://www.sourcemod.net/license.php>.
|
||||
*
|
||||
* Version: $Id$
|
||||
*/
|
||||
|
||||
#ifndef _INCLUDE_SOURCEMOD_CONVARMANAGER_H_
|
||||
#define _INCLUDE_SOURCEMOD_CONVARMANAGER_H_
|
||||
|
||||
#include "sm_globals.h"
|
||||
#include "sourcemm_api.h"
|
||||
#include <sh_list.h>
|
||||
#include <IPluginSys.h>
|
||||
#include <IForwardSys.h>
|
||||
#include <IHandleSys.h>
|
||||
#include <IRootConsoleMenu.h>
|
||||
#include <compat_wrappers.h>
|
||||
#include "concmd_cleaner.h"
|
||||
|
||||
using namespace SourceHook;
|
||||
|
||||
class IConVarChangeListener
|
||||
{
|
||||
public:
|
||||
virtual void OnConVarChanged(ConVar *pConVar, const char *oldValue, float flOldValue) =0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Holds SourceMod-specific information about a convar
|
||||
*/
|
||||
struct ConVarInfo
|
||||
{
|
||||
Handle_t handle; /**< Handle to self */
|
||||
bool sourceMod; /**< Determines whether or not convar was created by a SourceMod plugin */
|
||||
IChangeableForward *pChangeForward; /**< Forward associated with convar */
|
||||
ConVar *pVar; /**< The actual convar */
|
||||
List<IConVarChangeListener *> changeListeners;
|
||||
};
|
||||
|
||||
/**
|
||||
* Holds information about a client convar query
|
||||
*/
|
||||
struct ConVarQuery
|
||||
{
|
||||
QueryCvarCookie_t cookie; /**< Cookie that identifies query */
|
||||
IPluginFunction *pCallback; /**< Function that will be called when query is finished */
|
||||
cell_t value; /**< Optional value passed to query function */
|
||||
};
|
||||
|
||||
class ConVarManager :
|
||||
public SMGlobalClass,
|
||||
public IHandleTypeDispatch,
|
||||
public IPluginsListener,
|
||||
public IRootConsoleCommand,
|
||||
public IConCommandTracker
|
||||
{
|
||||
public:
|
||||
ConVarManager();
|
||||
~ConVarManager();
|
||||
public: // SMGlobalClass
|
||||
void OnSourceModStartup(bool late);
|
||||
void OnSourceModAllInitialized();
|
||||
void OnSourceModShutdown();
|
||||
void OnSourceModVSPReceived();
|
||||
public: // IHandleTypeDispatch
|
||||
void OnHandleDestroy(HandleType_t type, void *object);
|
||||
bool GetHandleApproxSize(HandleType_t type, void *object, unsigned int *pSize);
|
||||
public: // IPluginsListener
|
||||
void OnPluginUnloaded(IPlugin *plugin);
|
||||
public: //IRootConsoleCommand
|
||||
void OnRootConsoleCommand(const char *cmdname, const CCommand &command);
|
||||
public: //IConCommandTracker
|
||||
void OnUnlinkConCommandBase(ConCommandBase *pBase, const char *name, bool is_read_safe);
|
||||
public:
|
||||
/**
|
||||
* Create a convar and return a handle to it.
|
||||
*/
|
||||
Handle_t CreateConVar(IPluginContext *pContext, const char *name, const char *defaultVal,
|
||||
const char *description, int flags, bool hasMin, float min, bool hasMax, float max);
|
||||
|
||||
/**
|
||||
* Searches for a convar and returns a handle to it
|
||||
*/
|
||||
Handle_t FindConVar(const char* name);
|
||||
|
||||
/**
|
||||
* Add a function to call when the specified convar changes.
|
||||
*/
|
||||
void HookConVarChange(ConVar *pConVar, IPluginFunction *pFunction);
|
||||
|
||||
/**
|
||||
* Remove a function from the forward that will be called when the specified convar changes.
|
||||
*/
|
||||
void UnhookConVarChange(ConVar *pConVar, IPluginFunction *pFunction);
|
||||
|
||||
void AddConVarChangeListener(const char *name, IConVarChangeListener *pListener);
|
||||
void RemoveConVarChangeListener(const char *name, IConVarChangeListener *pListener);
|
||||
|
||||
/**
|
||||
* Starts a query to find the value of a client convar.
|
||||
*/
|
||||
QueryCvarCookie_t QueryClientConVar(edict_t *pPlayer, const char *name, IPluginFunction *pCallback,
|
||||
Handle_t hndl);
|
||||
|
||||
bool IsQueryingSupported();
|
||||
|
||||
HandleError ReadConVarHandle(Handle_t hndl, ConVar **pVar);
|
||||
|
||||
private:
|
||||
/**
|
||||
* Adds a convar to a plugin's list.
|
||||
*/
|
||||
static void AddConVarToPluginList(IPluginContext *pContext, const ConVar *pConVar);
|
||||
|
||||
/**
|
||||
* Static callback that Valve's ConVar object executes when the convar's value changes.
|
||||
*/
|
||||
#if defined ORANGEBOX_BUILD
|
||||
static void OnConVarChanged(ConVar *pConVar, const char *oldValue, float flOldValue);
|
||||
#else
|
||||
static void OnConVarChanged(ConVar *pConVar, const char *oldValue);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Callback for when StartQueryCvarValue() has finished.
|
||||
*/
|
||||
void OnQueryCvarValueFinished(QueryCvarCookie_t cookie, edict_t *pPlayer, EQueryCvarValueStatus result,
|
||||
const char *cvarName, const char *cvarValue);
|
||||
private:
|
||||
HandleType_t m_ConVarType;
|
||||
List<ConVarInfo *> m_ConVars;
|
||||
List<ConVarQuery> m_ConVarQueries;
|
||||
bool m_bIsDLLQueryHooked;
|
||||
bool m_bIsVSPQueryHooked;
|
||||
};
|
||||
|
||||
extern ConVarManager g_ConVarManager;
|
||||
|
||||
#endif // _INCLUDE_SOURCEMOD_CONVARMANAGER_H_
|
||||
|
||||
@@ -0,0 +1,524 @@
|
||||
/**
|
||||
* vim: set ts=4 :
|
||||
* =============================================================================
|
||||
* SourceMod
|
||||
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
|
||||
* =============================================================================
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, version 3.0, as published by the
|
||||
* Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* As a special exception, AlliedModders LLC gives you permission to link the
|
||||
* code of this program (as well as its derivative works) to "Half-Life 2," the
|
||||
* "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
|
||||
* by the Valve Corporation. You must obey the GNU General Public License in
|
||||
* all respects for all other code used. Additionally, AlliedModders LLC grants
|
||||
* this exception to all derivative works. AlliedModders LLC defines further
|
||||
* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
|
||||
* or <http://www.sourcemod.net/license.php>.
|
||||
*
|
||||
* Version: $Id$
|
||||
*/
|
||||
|
||||
#include <ITextParsers.h>
|
||||
#include "CoreConfig.h"
|
||||
#include "sourcemod.h"
|
||||
#include "sourcemm_api.h"
|
||||
#include "sm_srvcmds.h"
|
||||
#include "sm_version.h"
|
||||
#include "sm_stringutil.h"
|
||||
#include "LibrarySys.h"
|
||||
#include "Logger.h"
|
||||
#include "PluginSys.h"
|
||||
#include "ForwardSys.h"
|
||||
|
||||
#ifdef PLATFORM_WINDOWS
|
||||
ConVar sm_corecfgfile("sm_corecfgfile", "addons\\sourcemod\\configs\\core.cfg", 0, "SourceMod core configuration file");
|
||||
#elif defined PLATFORM_LINUX
|
||||
ConVar sm_corecfgfile("sm_corecfgfile", "addons/sourcemod/configs/core.cfg", 0, "SourceMod core configuration file");
|
||||
#endif
|
||||
|
||||
IForward *g_pOnServerCfg = NULL;
|
||||
IForward *g_pOnConfigsExecuted = NULL;
|
||||
IForward *g_pOnAutoConfigsBuffered = NULL;
|
||||
CoreConfig g_CoreConfig;
|
||||
bool g_bConfigsExecd = false;
|
||||
bool g_bServerExecd = false;
|
||||
bool g_bGotServerStart = false;
|
||||
bool g_bGotTrigger = false;
|
||||
ConCommand *g_pExecPtr = NULL;
|
||||
ConVar *g_ServerCfgFile = NULL;
|
||||
|
||||
void CheckAndFinalizeConfigs();
|
||||
|
||||
#if defined ORANGEBOX_BUILD
|
||||
SH_DECL_EXTERN1_void(ConCommand, Dispatch, SH_NOATTRIB, false, const CCommand &);
|
||||
void Hook_ExecDispatchPre(const CCommand &cmd)
|
||||
#else
|
||||
extern bool __SourceHook_FHAddConCommandDispatch(void *,bool,class fastdelegate::FastDelegate0<void>);
|
||||
extern bool __SourceHook_FHRemoveConCommandDispatch(void *,bool,class fastdelegate::FastDelegate0<void>);
|
||||
void Hook_ExecDispatchPre()
|
||||
#endif
|
||||
{
|
||||
#if !defined ORANGEBOX_BUILD
|
||||
CCommand cmd;
|
||||
#endif
|
||||
|
||||
const char *arg = cmd.Arg(1);
|
||||
|
||||
if (!g_bServerExecd
|
||||
&& arg != NULL
|
||||
&& strcmp(arg, g_ServerCfgFile->GetString()) == 0)
|
||||
{
|
||||
g_bGotTrigger = true;
|
||||
}
|
||||
}
|
||||
|
||||
#if defined ORANGEBOX_BUILD
|
||||
void Hook_ExecDispatchPost(const CCommand &cmd)
|
||||
#else
|
||||
void Hook_ExecDispatchPost()
|
||||
#endif
|
||||
{
|
||||
if (g_bGotTrigger)
|
||||
{
|
||||
g_bGotTrigger = false;
|
||||
g_bServerExecd = true;
|
||||
CheckAndFinalizeConfigs();
|
||||
}
|
||||
}
|
||||
|
||||
void CheckAndFinalizeConfigs()
|
||||
{
|
||||
if ((g_bServerExecd || g_ServerCfgFile == NULL)
|
||||
&& g_bGotServerStart)
|
||||
{
|
||||
/* Order is important here. We need to buffer things before we send the command out. */
|
||||
g_pOnAutoConfigsBuffered->Execute(NULL);
|
||||
engine->ServerCommand("sm internal 1\n");
|
||||
}
|
||||
}
|
||||
|
||||
void CoreConfig::OnSourceModAllInitialized()
|
||||
{
|
||||
g_RootMenu.AddRootConsoleCommand("config", "Set core configuration options", this);
|
||||
g_pOnServerCfg = g_Forwards.CreateForward("OnServerCfg", ET_Ignore, 0, NULL);
|
||||
g_pOnConfigsExecuted = g_Forwards.CreateForward("OnConfigsExecuted", ET_Ignore, 0, NULL);
|
||||
g_pOnAutoConfigsBuffered = g_Forwards.CreateForward("OnAutoConfigsBuffered", ET_Ignore, 0, NULL);
|
||||
}
|
||||
|
||||
void CoreConfig::OnSourceModShutdown()
|
||||
{
|
||||
g_RootMenu.RemoveRootConsoleCommand("config", this);
|
||||
g_Forwards.ReleaseForward(g_pOnServerCfg);
|
||||
g_Forwards.ReleaseForward(g_pOnConfigsExecuted);
|
||||
g_Forwards.ReleaseForward(g_pOnAutoConfigsBuffered);
|
||||
|
||||
if (g_pExecPtr != NULL)
|
||||
{
|
||||
SH_REMOVE_HOOK_STATICFUNC(ConCommand, Dispatch, g_pExecPtr, Hook_ExecDispatchPre, false);
|
||||
SH_REMOVE_HOOK_STATICFUNC(ConCommand, Dispatch, g_pExecPtr, Hook_ExecDispatchPost, true);
|
||||
g_pExecPtr = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void CoreConfig::OnSourceModLevelChange(const char *mapName)
|
||||
{
|
||||
static bool already_checked = false;
|
||||
|
||||
if (!already_checked)
|
||||
{
|
||||
g_ServerCfgFile = icvar->FindVar("servercfgfile");
|
||||
if (g_ServerCfgFile != NULL)
|
||||
{
|
||||
ConCommandBase *pBase = icvar->GetCommands();
|
||||
while (pBase != NULL)
|
||||
{
|
||||
if (pBase->IsCommand() && strcmp(pBase->GetName(), "exec") == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
pBase = const_cast<ConCommandBase *>(pBase->GetNext());
|
||||
}
|
||||
|
||||
g_pExecPtr = (ConCommand *)pBase;
|
||||
if (g_pExecPtr != NULL)
|
||||
{
|
||||
SH_ADD_HOOK_STATICFUNC(ConCommand, Dispatch, g_pExecPtr, Hook_ExecDispatchPre, false);
|
||||
SH_ADD_HOOK_STATICFUNC(ConCommand, Dispatch, g_pExecPtr, Hook_ExecDispatchPost, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_ServerCfgFile = NULL;
|
||||
}
|
||||
}
|
||||
already_checked = true;
|
||||
}
|
||||
|
||||
g_bConfigsExecd = false;
|
||||
g_bServerExecd = false;
|
||||
g_bGotServerStart = false;
|
||||
g_bGotTrigger = false;
|
||||
}
|
||||
|
||||
void CoreConfig::OnRootConsoleCommand(const char *cmdname, const CCommand &command)
|
||||
{
|
||||
int argcount = command.ArgC();
|
||||
if (argcount >= 4)
|
||||
{
|
||||
const char *option = command.Arg(2);
|
||||
const char *value = command.Arg(3);
|
||||
|
||||
char error[255];
|
||||
|
||||
ConfigResult res = SetConfigOption(option, value, ConfigSource_Console, error, sizeof(error));
|
||||
|
||||
if (res == ConfigResult_Reject)
|
||||
{
|
||||
g_RootMenu.ConsolePrint("[SM] Could not set config option \"%s\" to \"%s\" (%s)", option, value, error);
|
||||
} else if (res == ConfigResult_Ignore) {
|
||||
g_RootMenu.ConsolePrint("[SM] No such config option \"%s\" exists.", option);
|
||||
} else {
|
||||
g_RootMenu.ConsolePrint("Config option \"%s\" successfully set to \"%s.\"", option, value);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
g_RootMenu.ConsolePrint("[SM] Usage: sm config <option> <value>");
|
||||
}
|
||||
|
||||
void CoreConfig::Initialize()
|
||||
{
|
||||
SMCError err;
|
||||
char filePath[PLATFORM_MAX_PATH];
|
||||
|
||||
/* Try to get command line value of core config convar */
|
||||
const char *corecfg = icvar->GetCommandLineValue("sm_corecfgfile");
|
||||
|
||||
/* If sm_corecfgfile not specified on command line, use default value */
|
||||
if (!corecfg)
|
||||
{
|
||||
corecfg = sm_corecfgfile.GetDefault();
|
||||
}
|
||||
|
||||
/* Format path to config file */
|
||||
g_LibSys.PathFormat(filePath, sizeof(filePath), "%s/%s", g_SourceMod.GetGamePath(), corecfg);
|
||||
|
||||
/* Parse config file */
|
||||
if ((err=textparsers->ParseFile_SMC(filePath, this, NULL)) != SMCError_Okay)
|
||||
{
|
||||
/* :TODO: This won't actually log or print anything :( - So fix that somehow */
|
||||
const char *error = textparsers->GetSMCErrorString(err);
|
||||
g_Logger.LogFatal("[SM] Error encountered parsing core config file: %s", error ? error : "");
|
||||
}
|
||||
}
|
||||
|
||||
SMCResult CoreConfig::ReadSMC_KeyValue(const SMCStates *states, const char *key, const char *value)
|
||||
{
|
||||
char error[255];
|
||||
ConfigResult err = SetConfigOption(key, value, ConfigSource_File, error, sizeof(error));
|
||||
|
||||
if (err == ConfigResult_Reject)
|
||||
{
|
||||
/* This is a fatal error */
|
||||
g_Logger.LogFatal("Config error (key: %s) (value: %s) %s", key, value, error);
|
||||
}
|
||||
|
||||
return SMCResult_Continue;
|
||||
}
|
||||
|
||||
ConfigResult CoreConfig::SetConfigOption(const char *option, const char *value, ConfigSource source, char *error, size_t maxlength)
|
||||
{
|
||||
ConfigResult result;
|
||||
|
||||
/* Notify! */
|
||||
SMGlobalClass *pBase = SMGlobalClass::head;
|
||||
while (pBase)
|
||||
{
|
||||
if ((result = pBase->OnSourceModConfigChanged(option, value, source, error, maxlength)) != ConfigResult_Ignore)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
pBase = pBase->m_pGlobalClassNext;
|
||||
}
|
||||
|
||||
return ConfigResult_Ignore;
|
||||
}
|
||||
|
||||
bool SM_AreConfigsExecuted()
|
||||
{
|
||||
return g_bConfigsExecd;
|
||||
}
|
||||
|
||||
inline bool IsPathSepChar(char c)
|
||||
{
|
||||
#if defined PLATFORM_WINDOWS
|
||||
return (c == '\\' || c == '/');
|
||||
#elif defined PLATFORM_LINUX
|
||||
return (c == '/');
|
||||
#endif
|
||||
}
|
||||
|
||||
bool SM_ExecuteConfig(CPlugin *pl, AutoConfig *cfg, bool can_create)
|
||||
{
|
||||
bool will_create = false;
|
||||
|
||||
/* See if we should be creating */
|
||||
if (can_create && cfg->create)
|
||||
{
|
||||
will_create = true;
|
||||
|
||||
/* If the folder does not exist, attempt to create it.
|
||||
* We're awfully nice.
|
||||
*/
|
||||
const char *folder = cfg->folder.c_str();
|
||||
char path[PLATFORM_MAX_PATH];
|
||||
char build[PLATFORM_MAX_PATH];
|
||||
|
||||
g_SourceMod.BuildPath(Path_Game, path, sizeof(path), "cfg/%s", folder);
|
||||
|
||||
if (!g_LibSys.IsPathDirectory(path))
|
||||
{
|
||||
char *cur_ptr = path;
|
||||
size_t len;
|
||||
|
||||
g_LibSys.PathFormat(path, sizeof(path), "%s", folder);
|
||||
len = g_SourceMod.BuildPath(Path_Game, build, sizeof(build), "cfg");
|
||||
|
||||
do
|
||||
{
|
||||
/* Find next suitable path */
|
||||
char *next_ptr = cur_ptr;
|
||||
while (*next_ptr != '\0')
|
||||
{
|
||||
if (IsPathSepChar(*next_ptr))
|
||||
{
|
||||
*next_ptr = '\0';
|
||||
next_ptr++;
|
||||
break;
|
||||
}
|
||||
next_ptr++;
|
||||
}
|
||||
if (*next_ptr == '\0')
|
||||
{
|
||||
next_ptr = NULL;
|
||||
}
|
||||
len += g_LibSys.PathFormat(&build[len],
|
||||
sizeof(build)-len,
|
||||
"/%s",
|
||||
cur_ptr);
|
||||
if (!g_LibSys.CreateFolder(build))
|
||||
{
|
||||
break;
|
||||
}
|
||||
cur_ptr = next_ptr;
|
||||
} while (cur_ptr);
|
||||
}
|
||||
}
|
||||
|
||||
/* Check if the file exists. */
|
||||
char file[PLATFORM_MAX_PATH];
|
||||
char local[PLATFORM_MAX_PATH];
|
||||
|
||||
if (cfg->folder.size())
|
||||
{
|
||||
g_LibSys.PathFormat(local,
|
||||
sizeof(local),
|
||||
"%s/%s.cfg",
|
||||
cfg->folder.c_str(),
|
||||
cfg->autocfg.c_str());
|
||||
} else {
|
||||
g_LibSys.PathFormat(local,
|
||||
sizeof(local),
|
||||
"%s.cfg",
|
||||
cfg->autocfg.c_str());
|
||||
}
|
||||
|
||||
g_SourceMod.BuildPath(Path_Game, file, sizeof(file), "cfg/%s", local);
|
||||
|
||||
bool file_exists = g_LibSys.IsPathFile(file);
|
||||
if (!file_exists && will_create)
|
||||
{
|
||||
List<const ConVar *> *convars = NULL;
|
||||
if (pl->GetProperty("ConVarList", (void **)&convars, false) && convars)
|
||||
{
|
||||
/* Attempt to create it */
|
||||
FILE *fp = fopen(file, "wt");
|
||||
if (fp)
|
||||
{
|
||||
fprintf(fp, "// This file was auto-generated by SourceMod (v%s)\n", SVN_FULL_VERSION);
|
||||
fprintf(fp, "// ConVars for plugin \"%s\"\n", pl->GetFilename());
|
||||
fprintf(fp, "\n\n");
|
||||
|
||||
List<const ConVar *>::iterator iter;
|
||||
float x;
|
||||
for (iter = convars->begin(); iter != convars->end(); iter++)
|
||||
{
|
||||
const ConVar *cvar = (*iter);
|
||||
|
||||
if ((cvar->GetFlags() & FCVAR_DONTRECORD) == FCVAR_DONTRECORD)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
char descr[255];
|
||||
char *dptr = descr;
|
||||
|
||||
/* Print comments until there is no more */
|
||||
strncopy(descr, cvar->GetHelpText(), sizeof(descr));
|
||||
while (*dptr != '\0')
|
||||
{
|
||||
/* Find the next line */
|
||||
char *next_ptr = dptr;
|
||||
while (*next_ptr != '\0')
|
||||
{
|
||||
if (*next_ptr == '\n')
|
||||
{
|
||||
*next_ptr = '\0';
|
||||
next_ptr++;
|
||||
break;
|
||||
}
|
||||
next_ptr++;
|
||||
}
|
||||
fprintf(fp, "// %s\n", dptr);
|
||||
dptr = next_ptr;
|
||||
}
|
||||
|
||||
fprintf(fp, "// -\n");
|
||||
fprintf(fp, "// Default: \"%s\"\n", cvar->GetDefault());
|
||||
if (cvar->GetMin(x))
|
||||
{
|
||||
fprintf(fp, "// Minimum: \"%02f\"\n", x);
|
||||
}
|
||||
if (cvar->GetMax(x))
|
||||
{
|
||||
fprintf(fp, "// Maximum: \"%02f\"\n", x);
|
||||
}
|
||||
fprintf(fp, "%s \"%s\"\n", cvar->GetName(), cvar->GetDefault());
|
||||
fprintf(fp, "\n");
|
||||
}
|
||||
fprintf(fp, "\n");
|
||||
|
||||
file_exists = true;
|
||||
can_create = false;
|
||||
fclose(fp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (file_exists)
|
||||
{
|
||||
char cmd[255];
|
||||
UTIL_Format(cmd, sizeof(cmd), "exec %s\n", local);
|
||||
engine->ServerCommand(cmd);
|
||||
}
|
||||
|
||||
return can_create;
|
||||
}
|
||||
|
||||
void SM_DoSingleExecFwds(IPluginContext *ctx)
|
||||
{
|
||||
IPluginFunction *pf;
|
||||
|
||||
if ((pf = ctx->GetFunctionByName("OnServerCfg")) != NULL)
|
||||
{
|
||||
pf->Execute(NULL);
|
||||
}
|
||||
|
||||
if ((pf = ctx->GetFunctionByName("OnConfigsExecuted")) != NULL)
|
||||
{
|
||||
pf->Execute(NULL);
|
||||
}
|
||||
}
|
||||
|
||||
void SM_ConfigsExecuted_Plugin(unsigned int serial)
|
||||
{
|
||||
IPluginIterator *iter = g_PluginSys.GetPluginIterator();
|
||||
while (iter->MorePlugins())
|
||||
{
|
||||
CPlugin *plugin = (CPlugin *)(iter->GetPlugin());
|
||||
if (plugin->GetSerial() == serial)
|
||||
{
|
||||
SM_DoSingleExecFwds(plugin->GetBaseContext());
|
||||
break;
|
||||
}
|
||||
iter->NextPlugin();
|
||||
}
|
||||
iter->Release();
|
||||
}
|
||||
|
||||
void SM_ExecuteForPlugin(IPluginContext *ctx)
|
||||
{
|
||||
CPlugin *plugin = (CPlugin *)g_PluginSys.GetPluginByCtx(ctx->GetContext());
|
||||
|
||||
unsigned int num = plugin->GetConfigCount();
|
||||
if (!num)
|
||||
{
|
||||
SM_DoSingleExecFwds(ctx);
|
||||
}
|
||||
else
|
||||
{
|
||||
bool can_create = true;
|
||||
for (unsigned int i=0; i<num; i++)
|
||||
{
|
||||
can_create = SM_ExecuteConfig(plugin, plugin->GetConfig(i), can_create);
|
||||
}
|
||||
char cmd[255];
|
||||
UTIL_Format(cmd, sizeof(cmd), "sm internal 2 %d\n", plugin->GetSerial());
|
||||
engine->ServerCommand(cmd);
|
||||
}
|
||||
}
|
||||
|
||||
void SM_ExecuteAllConfigs()
|
||||
{
|
||||
if (g_bGotServerStart)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
engine->ServerCommand("exec sourcemod/sourcemod.cfg\n");
|
||||
|
||||
IPluginIterator *iter = g_PluginSys.GetPluginIterator();
|
||||
while (iter->MorePlugins())
|
||||
{
|
||||
CPlugin *plugin = (CPlugin *)(iter->GetPlugin());
|
||||
unsigned int num = plugin->GetConfigCount();
|
||||
bool can_create = true;
|
||||
for (unsigned int i=0; i<num; i++)
|
||||
{
|
||||
can_create = SM_ExecuteConfig(plugin, plugin->GetConfig(i), can_create);
|
||||
}
|
||||
iter->NextPlugin();
|
||||
}
|
||||
iter->Release();
|
||||
|
||||
#if defined ORANGEBOX_BUILD
|
||||
engine->ServerExecute();
|
||||
#endif
|
||||
|
||||
g_bGotServerStart = true;
|
||||
CheckAndFinalizeConfigs();
|
||||
}
|
||||
|
||||
void SM_ConfigsExecuted_Global()
|
||||
{
|
||||
if (g_bConfigsExecd)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
g_bConfigsExecd = true;
|
||||
|
||||
g_pOnServerCfg->Execute(NULL);
|
||||
g_pOnConfigsExecuted->Execute(NULL);
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
/**
|
||||
* vim: set ts=4 :
|
||||
* =============================================================================
|
||||
* SourceMod
|
||||
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
|
||||
* =============================================================================
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, version 3.0, as published by the
|
||||
* Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* As a special exception, AlliedModders LLC gives you permission to link the
|
||||
* code of this program (as well as its derivative works) to "Half-Life 2," the
|
||||
* "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
|
||||
* by the Valve Corporation. You must obey the GNU General Public License in
|
||||
* all respects for all other code used. Additionally, AlliedModders LLC grants
|
||||
* this exception to all derivative works. AlliedModders LLC defines further
|
||||
* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
|
||||
* or <http://www.sourcemod.net/license.php>.
|
||||
*
|
||||
* Version: $Id$
|
||||
*/
|
||||
|
||||
#ifndef _INCLUDE_SOURCEMOD_CORECONFIG_H_
|
||||
#define _INCLUDE_SOURCEMOD_CORECONFIG_H_
|
||||
|
||||
#include "sm_globals.h"
|
||||
#include <ITextParsers.h>
|
||||
#include <IRootConsoleMenu.h>
|
||||
|
||||
using namespace SourceMod;
|
||||
|
||||
class CoreConfig :
|
||||
public SMGlobalClass,
|
||||
public ITextListener_SMC,
|
||||
public IRootConsoleCommand
|
||||
{
|
||||
public: // SMGlobalClass
|
||||
void OnSourceModAllInitialized();
|
||||
void OnSourceModShutdown();
|
||||
void OnSourceModLevelChange(const char *mapName);
|
||||
public: // ITextListener_SMC
|
||||
SMCResult ReadSMC_KeyValue(const SMCStates *states, const char *key, const char *value);
|
||||
public: // IRootConsoleCommand
|
||||
void OnRootConsoleCommand(const char *cmdname, const CCommand &command);
|
||||
public:
|
||||
/**
|
||||
* Initializes CoreConfig by reading from core.cfg file
|
||||
*/
|
||||
void Initialize();
|
||||
private:
|
||||
/**
|
||||
* Sets configuration option by notifying SourceMod components that rely on core.cfg
|
||||
*/
|
||||
ConfigResult SetConfigOption(const char *option, const char *value, ConfigSource, char *Error, size_t maxlength);
|
||||
};
|
||||
|
||||
extern bool SM_AreConfigsExecuted();
|
||||
extern void SM_ExecuteAllConfigs();
|
||||
extern void SM_ExecuteForPlugin(IPluginContext *ctx);
|
||||
extern void SM_ConfigsExecuted_Global();
|
||||
extern void SM_ConfigsExecuted_Plugin(unsigned int serial);
|
||||
|
||||
extern CoreConfig g_CoreConfig;
|
||||
|
||||
#endif // _INCLUDE_SOURCEMOD_CORECONFIG_H_
|
||||
@@ -0,0 +1,199 @@
|
||||
/**
|
||||
* vim: set ts=4 :
|
||||
* =============================================================================
|
||||
* SourceMod
|
||||
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
|
||||
* =============================================================================
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, version 3.0, as published by the
|
||||
* Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* As a special exception, AlliedModders LLC gives you permission to link the
|
||||
* code of this program (as well as its derivative works) to "Half-Life 2," the
|
||||
* "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
|
||||
* by the Valve Corporation. You must obey the GNU General Public License in
|
||||
* all respects for all other code used. Additionally, AlliedModders LLC grants
|
||||
* this exception to all derivative works. AlliedModders LLC defines further
|
||||
* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
|
||||
* or <http://www.sourcemod.net/license.php>.
|
||||
*
|
||||
* Version: $Id$
|
||||
*/
|
||||
|
||||
#include "sm_globals.h"
|
||||
#include "sourcemm_api.h"
|
||||
#include "Tlhelp32.h"
|
||||
#include "LibrarySys.h"
|
||||
#include "minidump.h"
|
||||
#include "sm_stringutil.h"
|
||||
|
||||
bool HookImportAddrTable(BYTE *base, const char *func, DWORD hookfunc, char *err, size_t maxlength)
|
||||
{
|
||||
IMAGE_DOS_HEADER *dos = (IMAGE_DOS_HEADER *)base;
|
||||
if (dos->e_magic != IMAGE_DOS_SIGNATURE)
|
||||
{
|
||||
UTIL_Format(err, maxlength, "%s", "Could not detect valid DOS signature");
|
||||
return false;
|
||||
}
|
||||
|
||||
IMAGE_NT_HEADERS *nt = (IMAGE_NT_HEADERS *)(base + dos->e_lfanew);
|
||||
if (nt->Signature != IMAGE_NT_SIGNATURE)
|
||||
{
|
||||
UTIL_Format(err, maxlength, "%s", "Could not detect valid NT signature");
|
||||
return false;
|
||||
}
|
||||
|
||||
IMAGE_IMPORT_DESCRIPTOR *desc =
|
||||
(IMAGE_IMPORT_DESCRIPTOR *)
|
||||
(base + nt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress);
|
||||
if (base == (BYTE *)desc)
|
||||
{
|
||||
UTIL_Format(err, maxlength, "%s", "Could not find IAT");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool entryFound = false;
|
||||
while (desc->Name)
|
||||
{
|
||||
if (desc->FirstThunk != 0)
|
||||
{
|
||||
IMAGE_THUNK_DATA *data = (IMAGE_THUNK_DATA *)(base + desc->OriginalFirstThunk);
|
||||
DWORD *iat = (DWORD *)(base + desc->FirstThunk);
|
||||
while (data->u1.Function)
|
||||
{
|
||||
if ((data->u1.Ordinal & IMAGE_ORDINAL_FLAG32) != IMAGE_ORDINAL_FLAG32)
|
||||
{
|
||||
IMAGE_IMPORT_BY_NAME *import = (IMAGE_IMPORT_BY_NAME *)(base + data->u1.AddressOfData);
|
||||
if (strcmp((char *)import->Name, func) == 0)
|
||||
{
|
||||
DWORD oldprot, oldprot2;
|
||||
VirtualProtect(iat, 4, PAGE_READWRITE, &oldprot);
|
||||
*iat = hookfunc;
|
||||
VirtualProtect(iat, 4, oldprot, &oldprot2);
|
||||
entryFound = true;
|
||||
}
|
||||
}
|
||||
data++;
|
||||
iat++;
|
||||
}
|
||||
}
|
||||
desc++;
|
||||
}
|
||||
|
||||
if (!entryFound)
|
||||
{
|
||||
UTIL_Format(err, maxlength, "Could not find IAT entry for %s", func);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
BOOL
|
||||
WINAPI
|
||||
MiniDumpWriteDump2(
|
||||
IN HANDLE hProcess,
|
||||
IN DWORD ProcessId,
|
||||
IN HANDLE hFile,
|
||||
IN MINIDUMP_TYPE DumpType,
|
||||
IN CONST PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam, OPTIONAL
|
||||
IN CONST PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam, OPTIONAL
|
||||
IN CONST PMINIDUMP_CALLBACK_INFORMATION CallbackParam OPTIONAL
|
||||
)
|
||||
{
|
||||
DumpType = (MINIDUMP_TYPE)((int)DumpType|MiniDumpWithFullMemory|MiniDumpWithHandleData);
|
||||
return MiniDumpWriteDump(hProcess, ProcessId, hFile, DumpType, ExceptionParam, UserStreamParam, CallbackParam);
|
||||
}
|
||||
|
||||
FARPROC WINAPI GetProcAddress2(HMODULE hModule, LPCSTR lpProcName)
|
||||
{
|
||||
if (strcmp(lpProcName, "MiniDumpWriteDump") == 0)
|
||||
{
|
||||
return (FARPROC)MiniDumpWriteDump2;
|
||||
}
|
||||
|
||||
return GetProcAddress(hModule, lpProcName);
|
||||
}
|
||||
|
||||
HMODULE WINAPI LoadLibraryEx2(LPCTSTR lpFileName, HANDLE hFile, DWORD dwFlags)
|
||||
{
|
||||
HMODULE lib = LoadLibraryEx(lpFileName, hFile, dwFlags);
|
||||
|
||||
/* Steam.dll seems to get loaded using an abs path, thus the use of stristr() */
|
||||
if (stristr(lpFileName, "steam.dll"))
|
||||
{
|
||||
char err[64];
|
||||
if (!HookImportAddrTable((BYTE *)lib, "GetProcAddress", (DWORD)GetProcAddress2, err, sizeof(err)))
|
||||
{
|
||||
Error("[SM] %s in steam.dll\n", err);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return lib;
|
||||
}
|
||||
|
||||
class CrazyWindowsDebugger : public SMGlobalClass
|
||||
{
|
||||
public:
|
||||
void OnSourceModAllInitialized()
|
||||
{
|
||||
HANDLE hModuleList = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, GetCurrentProcessId());
|
||||
MODULEENTRY32 me32;
|
||||
BYTE *engine = NULL;
|
||||
BYTE *steam = NULL;
|
||||
char err[64];
|
||||
|
||||
me32.dwSize = sizeof(MODULEENTRY32);
|
||||
|
||||
if (!Module32First(hModuleList, &me32))
|
||||
{
|
||||
Error("Could not initialize crazy debugger!");
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
if (strcasecmp(me32.szModule, "engine.dll") == 0)
|
||||
{
|
||||
engine = me32.modBaseAddr;
|
||||
}
|
||||
else if (strcasecmp(me32.szModule, "steam.dll") == 0)
|
||||
{
|
||||
steam = me32.modBaseAddr;
|
||||
}
|
||||
} while (Module32Next(hModuleList, &me32));
|
||||
|
||||
CloseHandle(hModuleList);
|
||||
|
||||
/* This really should not happen, but ... */
|
||||
if (!engine)
|
||||
{
|
||||
Error("[SM] Could not find engine.dll\n");
|
||||
}
|
||||
|
||||
/* Steam.dll isn't loaded yet */
|
||||
if (!steam)
|
||||
{
|
||||
if (!HookImportAddrTable(engine, "LoadLibraryExA", (DWORD)LoadLibraryEx2, err, sizeof(err)))
|
||||
{
|
||||
Error("[SM] %s in engine.dll)\n", err);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!HookImportAddrTable(steam, "GetProcAddress", (DWORD)GetProcAddress2, err, sizeof(err)))
|
||||
{
|
||||
Error("[SM] %s in steam.dll)\n", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
} s_CrazyDebugger;
|
||||
@@ -0,0 +1,725 @@
|
||||
/**
|
||||
* vim: set ts=4 :
|
||||
* =============================================================================
|
||||
* SourceMod
|
||||
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
|
||||
* =============================================================================
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, version 3.0, as published by the
|
||||
* Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* As a special exception, AlliedModders LLC gives you permission to link the
|
||||
* code of this program (as well as its derivative works) to "Half-Life 2," the
|
||||
* "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
|
||||
* by the Valve Corporation. You must obey the GNU General Public License in
|
||||
* all respects for all other code used. Additionally, AlliedModders LLC grants
|
||||
* this exception to all derivative works. AlliedModders LLC defines further
|
||||
* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
|
||||
* or <http://www.sourcemod.net/license.php>.
|
||||
*
|
||||
* Version: $Id$
|
||||
*/
|
||||
|
||||
#include "Database.h"
|
||||
#include "HandleSys.h"
|
||||
#include "ShareSys.h"
|
||||
#include "sourcemod.h"
|
||||
#include "sm_stringutil.h"
|
||||
#include "Logger.h"
|
||||
#include "ExtensionSys.h"
|
||||
#include <stdlib.h>
|
||||
#include "ThreadSupport.h"
|
||||
|
||||
#define DBPARSE_LEVEL_NONE 0
|
||||
#define DBPARSE_LEVEL_MAIN 1
|
||||
#define DBPARSE_LEVEL_DATABASE 2
|
||||
|
||||
DBManager g_DBMan;
|
||||
static bool s_OneTimeThreaderErrorMsg = false;
|
||||
|
||||
DBManager::DBManager()
|
||||
: m_StrTab(512), m_ParseLevel(0), m_ParseState(0), m_pDefault(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
void DBManager::OnSourceModAllInitialized()
|
||||
{
|
||||
HandleAccess sec;
|
||||
|
||||
g_HandleSys.InitAccessDefaults(NULL, &sec);
|
||||
sec.access[HandleAccess_Delete] |= HANDLE_RESTRICT_IDENTITY;
|
||||
sec.access[HandleAccess_Clone] |= HANDLE_RESTRICT_IDENTITY;
|
||||
|
||||
m_DriverType = g_HandleSys.CreateType("IDriver", this, 0, NULL, &sec, g_pCoreIdent, NULL);
|
||||
m_DatabaseType = g_HandleSys.CreateType("IDatabase", this, 0, NULL, NULL, g_pCoreIdent, NULL);
|
||||
|
||||
g_ShareSys.AddInterface(NULL, this);
|
||||
|
||||
g_SourceMod.BuildPath(Path_SM, m_Filename, sizeof(m_Filename), "configs/databases.cfg");
|
||||
|
||||
m_pConfigLock = g_pThreader->MakeMutex();
|
||||
m_pThinkLock = g_pThreader->MakeMutex();
|
||||
m_pQueueLock = g_pThreader->MakeMutex();
|
||||
|
||||
g_PluginSys.AddPluginsListener(this);
|
||||
}
|
||||
|
||||
void DBManager::OnSourceModLevelChange(const char *mapName)
|
||||
{
|
||||
SMCError err;
|
||||
SMCStates states = {0, 0};
|
||||
|
||||
/* We lock and don't give up the lock until we're done.
|
||||
* This way the thread's search won't be searching through a
|
||||
* potentially empty/corrupt list, which would be very bad.
|
||||
*/
|
||||
m_pConfigLock->Lock();
|
||||
if ((err = textparsers->ParseFile_SMC(m_Filename, this, &states)) != SMCError_Okay)
|
||||
{
|
||||
g_Logger.LogError("[SM] Detected parse error(s) in file \"%s\"", m_Filename);
|
||||
if (err != SMCError_Custom)
|
||||
{
|
||||
const char *txt = textparsers->GetSMCErrorString(err);
|
||||
g_Logger.LogError("[SM] Line %d: %s", states.line, txt);
|
||||
}
|
||||
}
|
||||
m_pConfigLock->Unlock();
|
||||
}
|
||||
|
||||
void DBManager::OnSourceModShutdown()
|
||||
{
|
||||
KillWorkerThread();
|
||||
g_PluginSys.RemovePluginsListener(this);
|
||||
m_pConfigLock->DestroyThis();
|
||||
m_pThinkLock->DestroyThis();
|
||||
m_pQueueLock->DestroyThis();
|
||||
g_HandleSys.RemoveType(m_DatabaseType, g_pCoreIdent);
|
||||
g_HandleSys.RemoveType(m_DriverType, g_pCoreIdent);
|
||||
}
|
||||
|
||||
unsigned int DBManager::GetInterfaceVersion()
|
||||
{
|
||||
return SMINTERFACE_DBI_VERSION;
|
||||
}
|
||||
|
||||
const char *DBManager::GetInterfaceName()
|
||||
{
|
||||
return SMINTERFACE_DBI_NAME;
|
||||
}
|
||||
|
||||
void DBManager::OnHandleDestroy(HandleType_t type, void *object)
|
||||
{
|
||||
if (type == m_DriverType)
|
||||
{
|
||||
/* Ignore */
|
||||
return;
|
||||
}
|
||||
|
||||
if (g_HandleSys.TypeCheck(type, m_DatabaseType))
|
||||
{
|
||||
IDatabase *pdb = (IDatabase *)object;
|
||||
pdb->Close();
|
||||
}
|
||||
}
|
||||
|
||||
void DBManager::ReadSMC_ParseStart()
|
||||
{
|
||||
m_confs.clear();
|
||||
m_ParseLevel = 0;
|
||||
m_ParseState = DBPARSE_LEVEL_NONE;
|
||||
m_StrTab.Reset();
|
||||
m_DefDriver.clear();
|
||||
}
|
||||
|
||||
ConfDbInfo s_CurInfo;
|
||||
SMCResult DBManager::ReadSMC_NewSection(const SMCStates *states, const char *name)
|
||||
{
|
||||
if (m_ParseLevel)
|
||||
{
|
||||
m_ParseLevel++;
|
||||
return SMCResult_Continue;
|
||||
}
|
||||
|
||||
if (m_ParseState == DBPARSE_LEVEL_NONE)
|
||||
{
|
||||
if (strcmp(name, "Databases") == 0)
|
||||
{
|
||||
m_ParseState = DBPARSE_LEVEL_MAIN;
|
||||
} else {
|
||||
m_ParseLevel++;
|
||||
}
|
||||
} else if (m_ParseState == DBPARSE_LEVEL_MAIN) {
|
||||
s_CurInfo = ConfDbInfo();
|
||||
s_CurInfo.name = m_StrTab.AddString(name);
|
||||
m_ParseState = DBPARSE_LEVEL_DATABASE;
|
||||
} else if (m_ParseState == DBPARSE_LEVEL_DATABASE) {
|
||||
m_ParseLevel++;
|
||||
}
|
||||
|
||||
return SMCResult_Continue;
|
||||
}
|
||||
|
||||
SMCResult DBManager::ReadSMC_KeyValue(const SMCStates *states, const char *key, const char *value)
|
||||
{
|
||||
if (m_ParseLevel)
|
||||
{
|
||||
return SMCResult_Continue;
|
||||
}
|
||||
|
||||
if (m_ParseState == DBPARSE_LEVEL_MAIN)
|
||||
{
|
||||
if (strcmp(key, "driver_default") == 0)
|
||||
{
|
||||
m_DefDriver.assign(value);
|
||||
}
|
||||
} else if (m_ParseState == DBPARSE_LEVEL_DATABASE) {
|
||||
if (strcmp(key, "driver") == 0)
|
||||
{
|
||||
if (strcmp(value, "default") != 0)
|
||||
{
|
||||
s_CurInfo.driver = m_StrTab.AddString(value);
|
||||
}
|
||||
} else if (strcmp(key, "database") == 0) {
|
||||
s_CurInfo.database = m_StrTab.AddString(value);
|
||||
} else if (strcmp(key, "host") == 0) {
|
||||
s_CurInfo.host = m_StrTab.AddString(value);
|
||||
} else if (strcmp(key, "user") == 0) {
|
||||
s_CurInfo.user = m_StrTab.AddString(value);
|
||||
} else if (strcmp(key, "pass") == 0) {
|
||||
s_CurInfo.pass = m_StrTab.AddString(value);
|
||||
} else if (strcmp(key, "timeout") == 0) {
|
||||
s_CurInfo.info.maxTimeout = atoi(value);
|
||||
} else if (strcmp(key, "port") == 0) {
|
||||
s_CurInfo.info.port = atoi(value);
|
||||
}
|
||||
}
|
||||
|
||||
return SMCResult_Continue;
|
||||
}
|
||||
|
||||
#define ASSIGN_VAR(var) \
|
||||
if (s_CurInfo.var == -1) { \
|
||||
s_CurInfo.info.var = ""; \
|
||||
} else { \
|
||||
s_CurInfo.info.var = m_StrTab.GetString(s_CurInfo.var); \
|
||||
}
|
||||
|
||||
SMCResult DBManager::ReadSMC_LeavingSection(const SMCStates *states)
|
||||
{
|
||||
if (m_ParseLevel)
|
||||
{
|
||||
m_ParseLevel--;
|
||||
return SMCResult_Continue;
|
||||
}
|
||||
|
||||
if (m_ParseState == DBPARSE_LEVEL_DATABASE)
|
||||
{
|
||||
/* Set all of the info members to either a blank string
|
||||
* or the string pointer from the string table.
|
||||
*/
|
||||
ASSIGN_VAR(driver);
|
||||
ASSIGN_VAR(database);
|
||||
ASSIGN_VAR(host);
|
||||
ASSIGN_VAR(user);
|
||||
ASSIGN_VAR(pass);
|
||||
|
||||
/* Save it.. */
|
||||
m_confs.push_back(s_CurInfo);
|
||||
|
||||
/* Go up one level */
|
||||
m_ParseState = DBPARSE_LEVEL_MAIN;
|
||||
} else if (m_ParseState == DBPARSE_LEVEL_MAIN) {
|
||||
m_ParseState = DBPARSE_LEVEL_NONE;
|
||||
return SMCResult_Halt;
|
||||
}
|
||||
|
||||
return SMCResult_Continue;
|
||||
}
|
||||
#undef ASSIGN_VAR
|
||||
|
||||
void DBManager::ReadSMC_ParseEnd(bool halted, bool failed)
|
||||
{
|
||||
}
|
||||
|
||||
bool DBManager::Connect(const char *name, IDBDriver **pdr, IDatabase **pdb, bool persistent, char *error, size_t maxlength)
|
||||
{
|
||||
ConfDbInfo *pInfo = GetDatabaseConf(name);
|
||||
|
||||
if (!pInfo)
|
||||
{
|
||||
if (pdr)
|
||||
{
|
||||
*pdr = NULL;
|
||||
}
|
||||
*pdb = NULL;
|
||||
UTIL_Format(error, maxlength, "Configuration \"%s\" not found", name);
|
||||
return false;
|
||||
}
|
||||
|
||||
const char *dname = pInfo->info.driver;
|
||||
if (!pInfo->realDriver)
|
||||
{
|
||||
/* Try to assign a real driver pointer */
|
||||
if (pInfo->info.driver[0] == '\0')
|
||||
{
|
||||
if (!m_pDefault && m_DefDriver.size() > 0)
|
||||
{
|
||||
m_pDefault = FindOrLoadDriver(m_DefDriver.c_str());
|
||||
}
|
||||
dname = m_DefDriver.size() ? m_DefDriver.c_str() : "default";
|
||||
pInfo->realDriver = m_pDefault;
|
||||
} else {
|
||||
pInfo->realDriver = FindOrLoadDriver(pInfo->info.driver);
|
||||
}
|
||||
}
|
||||
|
||||
if (pInfo->realDriver)
|
||||
{
|
||||
if (pdr)
|
||||
{
|
||||
*pdr = pInfo->realDriver;
|
||||
}
|
||||
*pdb = pInfo->realDriver->Connect(&pInfo->info, persistent, error, maxlength);
|
||||
return (*pdb != NULL);
|
||||
}
|
||||
|
||||
if (pdr)
|
||||
{
|
||||
*pdr = NULL;
|
||||
}
|
||||
*pdb = NULL;
|
||||
|
||||
UTIL_Format(error, maxlength, "Driver \"%s\" not found", dname);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void DBManager::AddDriver(IDBDriver *pDriver)
|
||||
{
|
||||
/* Let's kill the worker. Join the thread and let the queries flush.
|
||||
* This is kind of stupid but we just want to unload safely.
|
||||
* Rather than recreate the worker, we'll wait until someone throws
|
||||
* another query through.
|
||||
*/
|
||||
KillWorkerThread();
|
||||
|
||||
m_drivers.push_back(pDriver);
|
||||
}
|
||||
|
||||
void DBManager::RemoveDriver(IDBDriver *pDriver)
|
||||
{
|
||||
/* Again, we're forced to kill the worker. How rude!
|
||||
* Doing this flushes the queue, and thus we don't need to
|
||||
* clean anything else.
|
||||
*/
|
||||
KillWorkerThread();
|
||||
|
||||
for (size_t i=0; i<m_drivers.size(); i++)
|
||||
{
|
||||
if (m_drivers[i] == pDriver)
|
||||
{
|
||||
m_drivers.erase(m_drivers.iterAt(i));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Make sure NOTHING references this! */
|
||||
List<ConfDbInfo>::iterator iter;
|
||||
for (iter=m_confs.begin(); iter!=m_confs.end(); iter++)
|
||||
{
|
||||
ConfDbInfo &db = (*iter);
|
||||
if (db.realDriver == pDriver)
|
||||
{
|
||||
db.realDriver = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* Now that the driver is gone, we have to test the think queue.
|
||||
* Whatever happens therein is up to the db op!
|
||||
*/
|
||||
Queue<IDBThreadOperation *>::iterator qiter = m_ThinkQueue.begin();
|
||||
Queue<IDBThreadOperation *> templist;
|
||||
while (qiter != m_ThinkQueue.end())
|
||||
{
|
||||
IDBThreadOperation *op = (*qiter);
|
||||
if (op->GetDriver() == pDriver)
|
||||
{
|
||||
templist.push(op);
|
||||
qiter = m_ThinkQueue.erase(qiter);
|
||||
} else {
|
||||
qiter++;
|
||||
}
|
||||
}
|
||||
|
||||
for (qiter = templist.begin();
|
||||
qiter != templist.end();
|
||||
qiter++)
|
||||
{
|
||||
IDBThreadOperation *op = (*qiter);
|
||||
op->CancelThinkPart();
|
||||
op->Destroy();
|
||||
}
|
||||
}
|
||||
|
||||
IDBDriver *DBManager::GetDefaultDriver()
|
||||
{
|
||||
if (!m_pDefault && m_DefDriver.size() > 0)
|
||||
{
|
||||
m_pDefault = FindOrLoadDriver(m_DefDriver.c_str());
|
||||
}
|
||||
|
||||
return m_pDefault;
|
||||
}
|
||||
|
||||
Handle_t DBManager::CreateHandle(DBHandleType dtype, void *ptr, IdentityToken_t *pToken)
|
||||
{
|
||||
HandleType_t type = 0;
|
||||
|
||||
if (dtype == DBHandle_Driver)
|
||||
{
|
||||
type = m_DriverType;
|
||||
} else if (dtype == DBHandle_Database) {
|
||||
type = m_DatabaseType;
|
||||
} else {
|
||||
return BAD_HANDLE;
|
||||
}
|
||||
|
||||
return g_HandleSys.CreateHandle(type, ptr, pToken, g_pCoreIdent, NULL);
|
||||
}
|
||||
|
||||
HandleError DBManager::ReadHandle(Handle_t hndl, DBHandleType dtype, void **ptr)
|
||||
{
|
||||
HandleType_t type = 0;
|
||||
|
||||
if (dtype == DBHandle_Driver)
|
||||
{
|
||||
type = m_DriverType;
|
||||
} else if (dtype == DBHandle_Database) {
|
||||
type = m_DatabaseType;
|
||||
} else {
|
||||
return HandleError_Type;
|
||||
}
|
||||
|
||||
HandleSecurity sec(NULL, g_pCoreIdent);
|
||||
|
||||
return g_HandleSys.ReadHandle(hndl, type, &sec, ptr);
|
||||
}
|
||||
|
||||
HandleError DBManager::ReleaseHandle(Handle_t hndl, DBHandleType type, IdentityToken_t *token)
|
||||
{
|
||||
HandleSecurity sec(token, g_pCoreIdent);
|
||||
return g_HandleSys.FreeHandle(hndl, &sec);
|
||||
}
|
||||
|
||||
unsigned int DBManager::GetDriverCount()
|
||||
{
|
||||
return (unsigned int)m_drivers.size();
|
||||
}
|
||||
|
||||
IDBDriver *DBManager::GetDriver(unsigned int index)
|
||||
{
|
||||
if (index >= m_drivers.size())
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return m_drivers[index];
|
||||
}
|
||||
|
||||
const DatabaseInfo *DBManager::FindDatabaseConf(const char *name)
|
||||
{
|
||||
ConfDbInfo *info = GetDatabaseConf(name);
|
||||
if (!info)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return &info->info;
|
||||
}
|
||||
|
||||
ConfDbInfo *DBManager::GetDatabaseConf(const char *name)
|
||||
{
|
||||
List<ConfDbInfo>::iterator iter;
|
||||
|
||||
for (iter=m_confs.begin(); iter!=m_confs.end(); iter++)
|
||||
{
|
||||
ConfDbInfo &conf = (*iter);
|
||||
if (strcmp(m_StrTab.GetString(conf.name), name) == 0)
|
||||
{
|
||||
return &conf;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
IDBDriver *DBManager::FindOrLoadDriver(const char *name)
|
||||
{
|
||||
size_t last_size = m_drivers.size();
|
||||
for (size_t i=0; i<last_size; i++)
|
||||
{
|
||||
if (strcmp(m_drivers[i]->GetIdentifier(), name) == 0)
|
||||
{
|
||||
return m_drivers[i];
|
||||
}
|
||||
}
|
||||
|
||||
char filename[PLATFORM_MAX_PATH];
|
||||
UTIL_Format(filename, sizeof(filename), "dbi.%s.ext", name);
|
||||
|
||||
IExtension *pExt = g_Extensions.LoadAutoExtension(filename);
|
||||
if (!pExt || !pExt->IsLoaded() || m_drivers.size() <= last_size)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* last_size is now gauranteed to be a valid index.
|
||||
* The identifier must match the name.
|
||||
*/
|
||||
if (strcmp(m_drivers[last_size]->GetIdentifier(), name) == 0)
|
||||
{
|
||||
return m_drivers[last_size];
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void DBManager::KillWorkerThread()
|
||||
{
|
||||
if (m_pWorker)
|
||||
{
|
||||
m_pWorker->Stop(false);
|
||||
g_pThreader->DestroyWorker(m_pWorker);
|
||||
m_pWorker = NULL;
|
||||
s_OneTimeThreaderErrorMsg = false;
|
||||
}
|
||||
}
|
||||
|
||||
static IdentityToken_t *s_pAddBlock = NULL;
|
||||
|
||||
bool DBManager::AddToThreadQueue(IDBThreadOperation *op, PrioQueueLevel prio)
|
||||
{
|
||||
if (s_pAddBlock && op->GetOwner() == s_pAddBlock)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!m_pWorker)
|
||||
{
|
||||
m_pWorker = g_pThreader->MakeWorker(this, true);
|
||||
if (!m_pWorker)
|
||||
{
|
||||
if (!s_OneTimeThreaderErrorMsg)
|
||||
{
|
||||
g_Logger.LogError("[SM] Unable to create db threader (error unknown)");
|
||||
s_OneTimeThreaderErrorMsg = true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (!m_pWorker->Start())
|
||||
{
|
||||
if (!s_OneTimeThreaderErrorMsg)
|
||||
{
|
||||
g_Logger.LogError("[SM] Unable to start db threader (error unknown)");
|
||||
s_OneTimeThreaderErrorMsg = true;
|
||||
}
|
||||
g_pThreader->DestroyWorker(m_pWorker);
|
||||
m_pWorker = NULL;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/* Add to the queue */
|
||||
{
|
||||
m_pQueueLock->Lock();
|
||||
Queue<IDBThreadOperation *> &queue = m_OpQueue.GetQueue(prio);
|
||||
queue.push(op);
|
||||
m_pQueueLock->Unlock();
|
||||
}
|
||||
|
||||
/* Make the thread */
|
||||
m_pWorker->MakeThread(this);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void DBManager::OnWorkerStart(IThreadWorker *pWorker)
|
||||
{
|
||||
m_drSafety.clear();
|
||||
for (size_t i=0; i<m_drivers.size(); i++)
|
||||
{
|
||||
if (m_drivers[i]->IsThreadSafe())
|
||||
{
|
||||
m_drSafety.push_back(m_drivers[i]->InitializeThreadSafety());
|
||||
} else {
|
||||
m_drSafety.push_back(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DBManager::OnWorkerStop(IThreadWorker *pWorker)
|
||||
{
|
||||
for (size_t i=0; i<m_drivers.size(); i++)
|
||||
{
|
||||
if (m_drSafety[i])
|
||||
{
|
||||
m_drivers[i]->ShutdownThreadSafety();
|
||||
}
|
||||
}
|
||||
m_drSafety.clear();
|
||||
}
|
||||
|
||||
void DBManager::RunThread(IThreadHandle *pThread)
|
||||
{
|
||||
IDBThreadOperation *op = NULL;
|
||||
|
||||
/* Get something from the queue */
|
||||
{
|
||||
m_pQueueLock->Lock();
|
||||
Queue<IDBThreadOperation *> &queue = m_OpQueue.GetLikelyQueue();
|
||||
if (!queue.empty())
|
||||
{
|
||||
op = queue.first();
|
||||
queue.pop();
|
||||
}
|
||||
m_pQueueLock->Unlock();
|
||||
}
|
||||
|
||||
/* whoa. hi. did we get something? we should have. */
|
||||
if (!op)
|
||||
{
|
||||
/* wtf? */
|
||||
return;
|
||||
}
|
||||
|
||||
op->RunThreadPart();
|
||||
|
||||
m_pThinkLock->Lock();
|
||||
m_ThinkQueue.push(op);
|
||||
m_pThinkLock->Unlock();
|
||||
}
|
||||
|
||||
void DBManager::RunFrame()
|
||||
{
|
||||
/* Don't bother if we're empty */
|
||||
if (!m_ThinkQueue.size())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* Dump one thing per-frame so the server stays sane. */
|
||||
m_pThinkLock->Lock();
|
||||
IDBThreadOperation *op = m_ThinkQueue.first();
|
||||
m_ThinkQueue.pop();
|
||||
m_pThinkLock->Unlock();
|
||||
op->RunThinkPart();
|
||||
op->Destroy();
|
||||
}
|
||||
|
||||
void DBManager::OnTerminate(IThreadHandle *pThread, bool cancel)
|
||||
{
|
||||
/* Do nothing */
|
||||
}
|
||||
|
||||
void DBManager::OnSourceModIdentityDropped(IdentityToken_t *pToken)
|
||||
{
|
||||
s_pAddBlock = pToken;
|
||||
|
||||
/* Kill the thread so we can flush everything into the think queue... */
|
||||
KillWorkerThread();
|
||||
|
||||
/* Run all of the think operations.
|
||||
* Unlike the driver unloading example, we'll let these calls go through,
|
||||
* since a plugin unloading is far more normal.
|
||||
*/
|
||||
Queue<IDBThreadOperation *>::iterator iter = m_ThinkQueue.begin();
|
||||
Queue<IDBThreadOperation *> templist;
|
||||
while (iter != m_ThinkQueue.end())
|
||||
{
|
||||
IDBThreadOperation *op = (*iter);
|
||||
if (op->GetOwner() == pToken)
|
||||
{
|
||||
templist.push(op);
|
||||
iter = m_ThinkQueue.erase(iter);
|
||||
} else {
|
||||
iter++;
|
||||
}
|
||||
}
|
||||
|
||||
for (iter = templist.begin();
|
||||
iter != templist.end();
|
||||
iter++)
|
||||
{
|
||||
IDBThreadOperation *op = (*iter);
|
||||
op->RunThinkPart();
|
||||
op->Destroy();
|
||||
}
|
||||
|
||||
s_pAddBlock = NULL;
|
||||
}
|
||||
|
||||
void DBManager::OnPluginUnloaded(IPlugin *plugin)
|
||||
{
|
||||
/* Kill the thread so we can flush everything into the think queue... */
|
||||
KillWorkerThread();
|
||||
|
||||
/* Mark the plugin as being unloaded so future database calls will ignore threading... */
|
||||
plugin->SetProperty("DisallowDBThreads", NULL);
|
||||
|
||||
/* Run all of the think operations.
|
||||
* Unlike the driver unloading example, we'll let these calls go through,
|
||||
* since a plugin unloading is far more normal.
|
||||
*/
|
||||
Queue<IDBThreadOperation *>::iterator iter = m_ThinkQueue.begin();
|
||||
Queue<IDBThreadOperation *> templist;
|
||||
while (iter != m_ThinkQueue.end())
|
||||
{
|
||||
IDBThreadOperation *op = (*iter);
|
||||
if (op->GetOwner() == plugin->GetIdentity())
|
||||
{
|
||||
templist.push(op);
|
||||
iter = m_ThinkQueue.erase(iter);
|
||||
} else {
|
||||
iter++;
|
||||
}
|
||||
}
|
||||
|
||||
for (iter = templist.begin();
|
||||
iter != templist.end();
|
||||
iter++)
|
||||
{
|
||||
IDBThreadOperation *op = (*iter);
|
||||
op->RunThinkPart();
|
||||
op->Destroy();
|
||||
}
|
||||
}
|
||||
|
||||
void DBManager::LockConfig()
|
||||
{
|
||||
m_pConfigLock->Lock();
|
||||
}
|
||||
|
||||
void DBManager::UnlockConfig()
|
||||
{
|
||||
m_pConfigLock->Unlock();
|
||||
}
|
||||
|
||||
const char *DBManager::GetDefaultDriverName()
|
||||
{
|
||||
return m_DefDriver.c_str();
|
||||
}
|
||||
|
||||
void DBManager::AddDependency(IExtension *myself, IDBDriver *driver)
|
||||
{
|
||||
g_Extensions.AddRawDependency(myself, driver->GetIdentity(), driver);
|
||||
}
|
||||
|
||||
+150
@@ -0,0 +1,150 @@
|
||||
/**
|
||||
* vim: set ts=4 :
|
||||
* =============================================================================
|
||||
* SourceMod
|
||||
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
|
||||
* =============================================================================
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, version 3.0, as published by the
|
||||
* Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* As a special exception, AlliedModders LLC gives you permission to link the
|
||||
* code of this program (as well as its derivative works) to "Half-Life 2," the
|
||||
* "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
|
||||
* by the Valve Corporation. You must obey the GNU General Public License in
|
||||
* all respects for all other code used. Additionally, AlliedModders LLC grants
|
||||
* this exception to all derivative works. AlliedModders LLC defines further
|
||||
* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
|
||||
* or <http://www.sourcemod.net/license.php>.
|
||||
*
|
||||
* Version: $Id$
|
||||
*/
|
||||
|
||||
#ifndef _INCLUDE_DATABASE_MANAGER_H_
|
||||
#define _INCLUDE_DATABASE_MANAGER_H_
|
||||
|
||||
#include <IDBDriver.h>
|
||||
#include "sm_globals.h"
|
||||
#include <sh_vector.h>
|
||||
#include <sh_string.h>
|
||||
#include <sh_list.h>
|
||||
#include <ITextParsers.h>
|
||||
#include "sm_memtable.h"
|
||||
#include <IThreader.h>
|
||||
#include "sm_simple_prioqueue.h"
|
||||
#include "PluginSys.h"
|
||||
|
||||
using namespace SourceHook;
|
||||
|
||||
struct ConfDbInfo
|
||||
{
|
||||
ConfDbInfo() : name(-1), driver(-1), host(-1), user(-1), pass(-1),
|
||||
database(-1), realDriver(NULL)
|
||||
{
|
||||
}
|
||||
int name;
|
||||
int driver;
|
||||
int host;
|
||||
int user;
|
||||
int pass;
|
||||
int database;
|
||||
IDBDriver *realDriver;
|
||||
DatabaseInfo info;
|
||||
};
|
||||
|
||||
class DBManager :
|
||||
public IDBManager,
|
||||
public SMGlobalClass,
|
||||
public IHandleTypeDispatch,
|
||||
public ITextListener_SMC,
|
||||
public IThread,
|
||||
public IThreadWorkerCallbacks,
|
||||
public IPluginsListener
|
||||
{
|
||||
public:
|
||||
DBManager();
|
||||
public:
|
||||
const char *GetInterfaceName();
|
||||
unsigned int GetInterfaceVersion();
|
||||
public: //SMGlobalClass
|
||||
void OnSourceModAllInitialized();
|
||||
void OnSourceModLevelChange(const char *mapName);
|
||||
void OnSourceModIdentityDropped(IdentityToken_t *pToken);
|
||||
void OnSourceModShutdown();
|
||||
public: //IHandleTypeDispatch
|
||||
void OnHandleDestroy(HandleType_t type, void *object);
|
||||
public: //IDBManager
|
||||
void AddDriver(IDBDriver *pDrivera);
|
||||
void RemoveDriver(IDBDriver *pDriver);
|
||||
const DatabaseInfo *FindDatabaseConf(const char *name);
|
||||
bool Connect(const char *name, IDBDriver **pdr, IDatabase **pdb, bool persistent, char *error, size_t maxlength);
|
||||
unsigned int GetDriverCount();
|
||||
IDBDriver *GetDriver(unsigned int index);
|
||||
Handle_t CreateHandle(DBHandleType type, void *ptr, IdentityToken_t *pToken);
|
||||
HandleError ReadHandle(Handle_t hndl, DBHandleType type, void **ptr);
|
||||
HandleError ReleaseHandle(Handle_t hndl, DBHandleType type, IdentityToken_t *token);
|
||||
void AddDependency(IExtension *myself, IDBDriver *driver);
|
||||
public: //ITextListener_SMC
|
||||
void ReadSMC_ParseStart();
|
||||
SMCResult ReadSMC_NewSection(const SMCStates *states, const char *name);
|
||||
SMCResult ReadSMC_KeyValue(const SMCStates *states, const char *key, const char *value);
|
||||
SMCResult ReadSMC_LeavingSection(const SMCStates *states);
|
||||
void ReadSMC_ParseEnd(bool halted, bool failed);
|
||||
public: //IThread
|
||||
void RunThread(IThreadHandle *pThread);
|
||||
void OnTerminate(IThreadHandle *pThread, bool cancel);
|
||||
public: //IThreadWorkerCallbacks
|
||||
void OnWorkerStart(IThreadWorker *pWorker);
|
||||
void OnWorkerStop(IThreadWorker *pWorker);
|
||||
public: //IPluginsListener
|
||||
void OnPluginUnloaded(IPlugin *plugin);
|
||||
public:
|
||||
ConfDbInfo *GetDatabaseConf(const char *name);
|
||||
IDBDriver *FindOrLoadDriver(const char *name);
|
||||
IDBDriver *GetDefaultDriver();
|
||||
const char *GetDefaultDriverName();
|
||||
bool AddToThreadQueue(IDBThreadOperation *op, PrioQueueLevel prio);
|
||||
void LockConfig();
|
||||
void UnlockConfig();
|
||||
void RunFrame();
|
||||
inline HandleType_t GetDatabaseType()
|
||||
{
|
||||
return m_DatabaseType;
|
||||
}
|
||||
private:
|
||||
void KillWorkerThread();
|
||||
private:
|
||||
CVector<IDBDriver *> m_drivers;
|
||||
|
||||
/* Threading stuff */
|
||||
PrioQueue<IDBThreadOperation *> m_OpQueue;
|
||||
Queue<IDBThreadOperation *> m_ThinkQueue;
|
||||
CVector<bool> m_drSafety; /* which drivers are safe? */
|
||||
IThreadWorker *m_pWorker; /* Worker thread object */
|
||||
IMutex *m_pConfigLock; /* Configuration lock */
|
||||
IMutex *m_pQueueLock; /* Queue safety lock */
|
||||
IMutex *m_pThinkLock; /* Think-queue lock */
|
||||
|
||||
List<ConfDbInfo> m_confs;
|
||||
HandleType_t m_DriverType;
|
||||
HandleType_t m_DatabaseType;
|
||||
String m_DefDriver;
|
||||
BaseStringTable m_StrTab;
|
||||
char m_Filename[PLATFORM_MAX_PATH];
|
||||
unsigned int m_ParseLevel;
|
||||
unsigned int m_ParseState;
|
||||
IDBDriver *m_pDefault;
|
||||
};
|
||||
|
||||
extern DBManager g_DBMan;
|
||||
|
||||
#endif //_INCLUDE_DATABASE_MANAGER_H_
|
||||
@@ -0,0 +1,189 @@
|
||||
/**
|
||||
* vim: set ts=4 :
|
||||
* =============================================================================
|
||||
* SourceMod
|
||||
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
|
||||
* =============================================================================
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, version 3.0, as published by the
|
||||
* Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* As a special exception, AlliedModders LLC gives you permission to link the
|
||||
* code of this program (as well as its derivative works) to "Half-Life 2," the
|
||||
* "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
|
||||
* by the Valve Corporation. You must obey the GNU General Public License in
|
||||
* all respects for all other code used. Additionally, AlliedModders LLC grants
|
||||
* this exception to all derivative works. AlliedModders LLC defines further
|
||||
* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
|
||||
* or <http://www.sourcemod.net/license.php>.
|
||||
*
|
||||
* Version: $Id$
|
||||
*/
|
||||
|
||||
#include "DebugReporter.h"
|
||||
#include "Logger.h"
|
||||
#include "PluginSys.h"
|
||||
#include "sm_stringutil.h"
|
||||
|
||||
DebugReport g_DbgReporter;
|
||||
|
||||
/* I'm really lazy. This should probably be exported to ISourcePawnEngine someday,
|
||||
* but we need to make sure the JIT will deal with the version bump.
|
||||
*/
|
||||
extern const char *GetSourcePawnErrorMessage(int error);
|
||||
|
||||
void DebugReport::OnSourceModAllInitialized()
|
||||
{
|
||||
g_pSourcePawn->SetDebugListener(this);
|
||||
}
|
||||
|
||||
void DebugReport::GenerateError(IPluginContext *ctx, cell_t func_idx, int err, const char *message, ...)
|
||||
{
|
||||
va_list ap;
|
||||
char buffer[512];
|
||||
|
||||
va_start(ap, message);
|
||||
UTIL_FormatArgs(buffer, sizeof(buffer), message, ap);
|
||||
va_end(ap);
|
||||
|
||||
const char *plname = g_PluginSys.FindPluginByContext(ctx->GetContext())->GetFilename();
|
||||
const char *error = GetSourcePawnErrorMessage(err);
|
||||
|
||||
if (error)
|
||||
{
|
||||
g_Logger.LogError("[SM] Plugin \"%s\" encountered error %d: %s", plname, err, error);
|
||||
} else {
|
||||
g_Logger.LogError("[SM] Plugin \"%s\" encountered unknown error %d", plname, err);
|
||||
}
|
||||
|
||||
g_Logger.LogError("[SM] %s", buffer);
|
||||
|
||||
if (func_idx != -1)
|
||||
{
|
||||
if (func_idx & 1)
|
||||
{
|
||||
func_idx >>= 1;
|
||||
sp_public_t *function;
|
||||
if (ctx->GetPublicByIndex(func_idx, &function) == SP_ERROR_NONE)
|
||||
{
|
||||
g_Logger.LogError("[SM] Unable to call function \"%s\" due to above error(s).", function->name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DebugReport::GenerateCodeError(IPluginContext *pContext, uint32_t code_addr, int err, const char *message, ...)
|
||||
{
|
||||
va_list ap;
|
||||
char buffer[512];
|
||||
|
||||
va_start(ap, message);
|
||||
UTIL_FormatArgs(buffer, sizeof(buffer), message, ap);
|
||||
va_end(ap);
|
||||
|
||||
const char *plname = g_PluginSys.FindPluginByContext(pContext->GetContext())->GetFilename();
|
||||
const char *error = GetSourcePawnErrorMessage(err);
|
||||
|
||||
if (error)
|
||||
{
|
||||
g_Logger.LogError("[SM] Plugin \"%s\" encountered error %d: %s", plname, err, error);
|
||||
} else {
|
||||
g_Logger.LogError("[SM] Plugin \"%s\" encountered unknown error %d", plname, err);
|
||||
}
|
||||
|
||||
g_Logger.LogError("[SM] %s", buffer);
|
||||
|
||||
IPluginDebugInfo *pDebug;
|
||||
if ((pDebug = pContext->GetDebugInfo()) == NULL)
|
||||
{
|
||||
g_Logger.LogError("[SM] Debug mode is not enabled for \"%s\"", plname);
|
||||
g_Logger.LogError("[SM] To enable debug mode, edit plugin_settings.cfg, or type: sm plugins debug %d on",
|
||||
_GetPluginIndex(pContext));
|
||||
return;
|
||||
}
|
||||
|
||||
const char *name;
|
||||
if (pDebug->LookupFunction(code_addr, &name) == SP_ERROR_NONE)
|
||||
{
|
||||
g_Logger.LogError("[SM] Unable to call function \"%s\" due to above error(s).", name);
|
||||
} else {
|
||||
g_Logger.LogError("[SM] Unable to call function (name unknown, address \"%x\").", code_addr);
|
||||
}
|
||||
}
|
||||
|
||||
void DebugReport::OnContextExecuteError(IPluginContext *ctx, IContextTrace *error)
|
||||
{
|
||||
const char *lastname;
|
||||
const char *plname = g_PluginSys.FindPluginByContext(ctx->GetContext())->GetFilename();
|
||||
int n_err = error->GetErrorCode();
|
||||
|
||||
if (n_err != SP_ERROR_NATIVE)
|
||||
{
|
||||
g_Logger.LogError("[SM] Plugin encountered error %d: %s",
|
||||
n_err,
|
||||
error->GetErrorString());
|
||||
}
|
||||
|
||||
if ((lastname=error->GetLastNative(NULL)) != NULL)
|
||||
{
|
||||
const char *custerr;
|
||||
if ((custerr=error->GetCustomErrorString()) != NULL)
|
||||
{
|
||||
g_Logger.LogError("[SM] Native \"%s\" reported: %s", lastname, custerr);
|
||||
} else {
|
||||
g_Logger.LogError("[SM] Native \"%s\" encountered a generic error.", lastname);
|
||||
}
|
||||
}
|
||||
|
||||
if (!error->DebugInfoAvailable())
|
||||
{
|
||||
g_Logger.LogError("[SM] Debug mode is not enabled for \"%s\"", plname);
|
||||
g_Logger.LogError("[SM] To enable debug mode, edit plugin_settings.cfg, or type: sm plugins debug %d on",
|
||||
_GetPluginIndex(ctx));
|
||||
return;
|
||||
}
|
||||
|
||||
CallStackInfo stk_info;
|
||||
int i = 0;
|
||||
g_Logger.LogError("[SM] Displaying call stack trace for plugin \"%s\":", plname);
|
||||
while (error->GetTraceInfo(&stk_info))
|
||||
{
|
||||
g_Logger.LogError("[SM] [%d] Line %d, %s::%s()",
|
||||
i++,
|
||||
stk_info.line,
|
||||
stk_info.filename,
|
||||
stk_info.function);
|
||||
}
|
||||
}
|
||||
|
||||
int DebugReport::_GetPluginIndex(IPluginContext *ctx)
|
||||
{
|
||||
int id = 1;
|
||||
IPluginIterator *iter = g_PluginSys.GetPluginIterator();
|
||||
|
||||
for (; iter->MorePlugins(); iter->NextPlugin(), id++)
|
||||
{
|
||||
IPlugin *pl = iter->GetPlugin();
|
||||
if (pl->GetBaseContext() == ctx)
|
||||
{
|
||||
iter->Release();
|
||||
return id;
|
||||
}
|
||||
}
|
||||
|
||||
iter->Release();
|
||||
|
||||
/* If we don't know which plugin this is, it's one being loaded. Fake its index for now. */
|
||||
|
||||
return g_PluginSys.GetPluginCount() + 1;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
/**
|
||||
* vim: set ts=4 :
|
||||
* =============================================================================
|
||||
* SourceMod
|
||||
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
|
||||
* =============================================================================
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, version 3.0, as published by the
|
||||
* Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* As a special exception, AlliedModders LLC gives you permission to link the
|
||||
* code of this program (as well as its derivative works) to "Half-Life 2," the
|
||||
* "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
|
||||
* by the Valve Corporation. You must obey the GNU General Public License in
|
||||
* all respects for all other code used. Additionally, AlliedModders LLC grants
|
||||
* this exception to all derivative works. AlliedModders LLC defines further
|
||||
* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
|
||||
* or <http://www.sourcemod.net/license.php>.
|
||||
*
|
||||
* Version: $Id$
|
||||
*/
|
||||
|
||||
#ifndef _INCLUDE_SOURCEMOD_CDBGREPORTER_H_
|
||||
#define _INCLUDE_SOURCEMOD_CDBGREPORTER_H_
|
||||
|
||||
#include "sp_vm_api.h"
|
||||
#include "sm_globals.h"
|
||||
|
||||
class DebugReport :
|
||||
public SMGlobalClass,
|
||||
public IDebugListener
|
||||
{
|
||||
public: // SMGlobalClass
|
||||
void OnSourceModAllInitialized();
|
||||
public: // IDebugListener
|
||||
void OnContextExecuteError(IPluginContext *ctx, IContextTrace *error);
|
||||
void GenerateError(IPluginContext *ctx, cell_t func_idx, int err, const char *message, ...);
|
||||
void GenerateCodeError(IPluginContext *ctx, uint32_t code_addr, int err, const char *message, ...);
|
||||
private:
|
||||
int _GetPluginIndex(IPluginContext *ctx);
|
||||
};
|
||||
|
||||
extern DebugReport g_DbgReporter;
|
||||
|
||||
#endif // _INCLUDE_SOURCEMOD_CDBGREPORTER_H_
|
||||
|
||||
@@ -0,0 +1,461 @@
|
||||
/**
|
||||
* vim: set ts=4 :
|
||||
* =============================================================================
|
||||
* SourceMod
|
||||
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
|
||||
* =============================================================================
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, version 3.0, as published by the
|
||||
* Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* As a special exception, AlliedModders LLC gives you permission to link the
|
||||
* code of this program (as well as its derivative works) to "Half-Life 2," the
|
||||
* "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
|
||||
* by the Valve Corporation. You must obey the GNU General Public License in
|
||||
* all respects for all other code used. Additionally, AlliedModders LLC grants
|
||||
* this exception to all derivative works. AlliedModders LLC defines further
|
||||
* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
|
||||
* or <http://www.sourcemod.net/license.php>.
|
||||
*
|
||||
* Version: $Id$
|
||||
*/
|
||||
|
||||
#include "EventManager.h"
|
||||
#include "ForwardSys.h"
|
||||
#include "HandleSys.h"
|
||||
#include "PluginSys.h"
|
||||
#include "sm_stringutil.h"
|
||||
|
||||
EventManager g_EventManager;
|
||||
|
||||
SH_DECL_HOOK2(IGameEventManager2, FireEvent, SH_NOATTRIB, 0, bool, IGameEvent *, bool);
|
||||
|
||||
const ParamType GAMEEVENT_PARAMS[] = {Param_Cell, Param_String, Param_Cell};
|
||||
typedef List<EventHook *> EventHookList;
|
||||
|
||||
EventManager::EventManager() : m_EventType(0)
|
||||
{
|
||||
/* Create an event lookup trie */
|
||||
m_EventHooks = sm_trie_create();
|
||||
}
|
||||
|
||||
EventManager::~EventManager()
|
||||
{
|
||||
sm_trie_destroy(m_EventHooks);
|
||||
|
||||
/* Free memory used by EventInfo structs if any */
|
||||
CStack<EventInfo *>::iterator iter;
|
||||
for (iter = m_FreeEvents.begin(); iter != m_FreeEvents.end(); iter++)
|
||||
{
|
||||
delete (*iter);
|
||||
}
|
||||
|
||||
m_FreeEvents.popall();
|
||||
}
|
||||
|
||||
void EventManager::OnSourceModAllInitialized()
|
||||
{
|
||||
/* Add a hook for IGameEventManager2::FireEvent() */
|
||||
SH_ADD_HOOK_MEMFUNC(IGameEventManager2, FireEvent, gameevents, this, &EventManager::OnFireEvent, false);
|
||||
SH_ADD_HOOK_MEMFUNC(IGameEventManager2, FireEvent, gameevents, this, &EventManager::OnFireEvent_Post, true);
|
||||
|
||||
HandleAccess sec;
|
||||
|
||||
/* Handle access security for 'GameEvent' handle type */
|
||||
sec.access[HandleAccess_Read] = 0;
|
||||
sec.access[HandleAccess_Delete] = HANDLE_RESTRICT_IDENTITY | HANDLE_RESTRICT_OWNER;
|
||||
sec.access[HandleAccess_Clone] = HANDLE_RESTRICT_IDENTITY | HANDLE_RESTRICT_OWNER;
|
||||
|
||||
/* Create the 'GameEvent' handle type */
|
||||
m_EventType = g_HandleSys.CreateType("GameEvent", this, 0, NULL, &sec, g_pCoreIdent, NULL);
|
||||
}
|
||||
|
||||
void EventManager::OnSourceModShutdown()
|
||||
{
|
||||
/* Remove hook for IGameEventManager2::FireEvent() */
|
||||
SH_REMOVE_HOOK_MEMFUNC(IGameEventManager2, FireEvent, gameevents, this, &EventManager::OnFireEvent, false);
|
||||
SH_REMOVE_HOOK_MEMFUNC(IGameEventManager2, FireEvent, gameevents, this, &EventManager::OnFireEvent_Post, true);
|
||||
|
||||
/* Remove the 'GameEvent' handle type */
|
||||
g_HandleSys.RemoveType(m_EventType, g_pCoreIdent);
|
||||
|
||||
/* Remove ourselves as listener for events */
|
||||
gameevents->RemoveListener(this);
|
||||
}
|
||||
|
||||
void EventManager::OnHandleDestroy(HandleType_t type, void *object)
|
||||
{
|
||||
EventInfo *pInfo = static_cast<EventInfo *>(object);
|
||||
|
||||
/* Should only free event when created by a plugin */
|
||||
if (pInfo->pOwner)
|
||||
{
|
||||
/* Free IGameEvent */
|
||||
gameevents->FreeEvent(pInfo->pEvent);
|
||||
|
||||
/* Add EventInfo struct to free event stack */
|
||||
m_FreeEvents.push(pInfo);
|
||||
}
|
||||
}
|
||||
|
||||
void EventManager::OnPluginUnloaded(IPlugin *plugin)
|
||||
{
|
||||
EventHookList *pHookList;
|
||||
EventHookList::iterator iter;
|
||||
EventHook *pHook;
|
||||
|
||||
// If plugin has an event hook list...
|
||||
if (plugin->GetProperty("EventHooks", reinterpret_cast<void **>(&pHookList), true))
|
||||
{
|
||||
for (iter = pHookList->begin(); iter != pHookList->end(); iter++)
|
||||
{
|
||||
pHook = (*iter);
|
||||
|
||||
if (--pHook->refCount == 0)
|
||||
{
|
||||
if (pHook->pPreHook)
|
||||
{
|
||||
g_Forwards.ReleaseForward(pHook->pPreHook);
|
||||
}
|
||||
|
||||
if (pHook->pPostHook)
|
||||
{
|
||||
g_Forwards.ReleaseForward(pHook->pPostHook);
|
||||
}
|
||||
|
||||
delete pHook;
|
||||
}
|
||||
}
|
||||
|
||||
delete pHookList;
|
||||
}
|
||||
}
|
||||
|
||||
/* IGameEventListener2::FireGameEvent */
|
||||
void EventManager::FireGameEvent(IGameEvent *pEvent)
|
||||
{
|
||||
/* Not going to do anything here.
|
||||
Just need to add ourselves as a listener to make our hook on IGameEventManager2::FireEvent work */
|
||||
}
|
||||
|
||||
EventHookError EventManager::HookEvent(const char *name, IPluginFunction *pFunction, EventHookMode mode)
|
||||
{
|
||||
EventHook *pHook;
|
||||
|
||||
/* If we aren't listening to this event... */
|
||||
if (!gameevents->FindListener(this, name))
|
||||
{
|
||||
/* Then add ourselves */
|
||||
if (!gameevents->AddListener(this, name, true))
|
||||
{
|
||||
/* If event doesn't exist... */
|
||||
return EventHookErr_InvalidEvent;
|
||||
}
|
||||
}
|
||||
|
||||
/* If a hook structure does not exist... */
|
||||
if (!sm_trie_retrieve(m_EventHooks, name, (void **)&pHook))
|
||||
{
|
||||
EventHookList *pHookList;
|
||||
IPlugin *plugin = g_PluginSys.GetPluginByCtx(pFunction->GetParentContext()->GetContext());
|
||||
|
||||
/* Check plugin for an existing EventHook list */
|
||||
if (!plugin->GetProperty("EventHooks", (void **)&pHookList))
|
||||
{
|
||||
pHookList = new EventHookList();
|
||||
plugin->SetProperty("EventHooks", pHookList);
|
||||
}
|
||||
|
||||
/* Create new GameEventHook structure */
|
||||
pHook = new EventHook();
|
||||
|
||||
if (mode == EventHookMode_Pre)
|
||||
{
|
||||
/* Create forward for a pre hook */
|
||||
pHook->pPreHook = g_Forwards.CreateForwardEx(NULL, ET_Hook, 3, GAMEEVENT_PARAMS);
|
||||
/* Add to forward list */
|
||||
pHook->pPreHook->AddFunction(pFunction);
|
||||
} else {
|
||||
/* Create forward for a post hook */
|
||||
pHook->pPostHook = g_Forwards.CreateForwardEx(NULL, ET_Ignore, 3, GAMEEVENT_PARAMS);
|
||||
/* Should we copy data from a pre hook to the post hook? */
|
||||
pHook->postCopy = (mode == EventHookMode_Post);
|
||||
/* Add to forward list */
|
||||
pHook->pPostHook->AddFunction(pFunction);
|
||||
}
|
||||
|
||||
/* Increase reference count */
|
||||
pHook->refCount++;
|
||||
|
||||
/* Add hook structure to hook lists */
|
||||
pHookList->push_back(pHook);
|
||||
sm_trie_insert(m_EventHooks, name, pHook);
|
||||
|
||||
return EventHookErr_Okay;
|
||||
}
|
||||
|
||||
/* Hook structure already exists at this point */
|
||||
|
||||
if (mode == EventHookMode_Pre)
|
||||
{
|
||||
/* Create pre hook forward if necessary */
|
||||
if (!pHook->pPreHook)
|
||||
{
|
||||
pHook->pPreHook = g_Forwards.CreateForwardEx(NULL, ET_Event, 3, GAMEEVENT_PARAMS);
|
||||
}
|
||||
|
||||
/* Add plugin function to forward list */
|
||||
pHook->pPreHook->AddFunction(pFunction);
|
||||
} else {
|
||||
/* Create post hook forward if necessary */
|
||||
if (!pHook->pPostHook)
|
||||
{
|
||||
pHook->pPostHook = g_Forwards.CreateForwardEx(NULL, ET_Ignore, 3, GAMEEVENT_PARAMS);
|
||||
}
|
||||
|
||||
/* If postCopy is false, then we may want to set it to true */
|
||||
if (!pHook->postCopy)
|
||||
{
|
||||
pHook->postCopy = (mode == EventHookMode_Post);
|
||||
}
|
||||
|
||||
/* Add plugin function to forward list */
|
||||
pHook->pPostHook->AddFunction(pFunction);
|
||||
}
|
||||
|
||||
/* Increase reference count */
|
||||
pHook->refCount++;
|
||||
|
||||
return EventHookErr_Okay;
|
||||
}
|
||||
|
||||
EventHookError EventManager::UnhookEvent(const char *name, IPluginFunction *pFunction, EventHookMode mode)
|
||||
{
|
||||
EventHook *pHook;
|
||||
IChangeableForward **pEventForward;
|
||||
|
||||
/* If hook does not exist at all */
|
||||
if (!sm_trie_retrieve(m_EventHooks, name, (void **)&pHook))
|
||||
{
|
||||
return EventHookErr_NotActive;
|
||||
}
|
||||
|
||||
/* One forward to rule them all */
|
||||
if (mode == EventHookMode_Pre)
|
||||
{
|
||||
pEventForward = &pHook->pPreHook;
|
||||
} else {
|
||||
pEventForward = &pHook->pPostHook;
|
||||
}
|
||||
|
||||
/* Remove function from forward's list */
|
||||
if (*pEventForward == NULL || !(*pEventForward)->RemoveFunction(pFunction))
|
||||
{
|
||||
return EventHookErr_InvalidCallback;
|
||||
}
|
||||
|
||||
/* If forward's list contains 0 functions now, free it */
|
||||
if ((*pEventForward)->GetFunctionCount() == 0)
|
||||
{
|
||||
g_Forwards.ReleaseForward(*pEventForward);
|
||||
*pEventForward = NULL;
|
||||
}
|
||||
|
||||
/* Decrement reference count */
|
||||
if (--pHook->refCount == 0)
|
||||
{
|
||||
/* If reference count is now 0, free hook structure */
|
||||
|
||||
EventHookList *pHookList;
|
||||
IPlugin *plugin = g_PluginSys.GetPluginByCtx(pFunction->GetParentContext()->GetContext());
|
||||
|
||||
/* Get plugin's event hook list */
|
||||
if (!plugin->GetProperty("EventHooks", (void**)&pHookList))
|
||||
{
|
||||
return EventHookErr_NotActive;
|
||||
}
|
||||
|
||||
/* Make sure the event was actually being hooked */
|
||||
if (pHookList->find(pHook) == pHookList->end())
|
||||
{
|
||||
return EventHookErr_NotActive;
|
||||
}
|
||||
|
||||
/* Remove current structure from plugin's list */
|
||||
pHookList->remove(pHook);
|
||||
|
||||
/* Delete entry in trie */
|
||||
sm_trie_delete(m_EventHooks, name);
|
||||
|
||||
/* And finally free structure memory */
|
||||
delete pHook;
|
||||
}
|
||||
|
||||
return EventHookErr_Okay;
|
||||
}
|
||||
|
||||
EventInfo *EventManager::CreateEvent(IPluginContext *pContext, const char *name, bool force)
|
||||
{
|
||||
EventInfo *pInfo;
|
||||
IGameEvent *pEvent = gameevents->CreateEvent(name, force);
|
||||
|
||||
if (pEvent)
|
||||
{
|
||||
if (m_FreeEvents.empty())
|
||||
{
|
||||
pInfo = new EventInfo();
|
||||
} else {
|
||||
pInfo = m_FreeEvents.front();
|
||||
m_FreeEvents.pop();
|
||||
}
|
||||
|
||||
|
||||
pInfo->pEvent = pEvent;
|
||||
pInfo->pOwner = pContext->GetIdentity();
|
||||
|
||||
return pInfo;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void EventManager::FireEvent(EventInfo *pInfo, bool bDontBroadcast)
|
||||
{
|
||||
/* Actually fire event now */
|
||||
gameevents->FireEvent(pInfo->pEvent, bDontBroadcast);
|
||||
|
||||
/* IGameEvent is free at this point, so no one owns this */
|
||||
pInfo->pOwner = NULL;
|
||||
|
||||
/* Add EventInfo struct to free event stack */
|
||||
m_FreeEvents.push(pInfo);
|
||||
}
|
||||
|
||||
void EventManager::CancelCreatedEvent(EventInfo *pInfo)
|
||||
{
|
||||
/* Free event from IGameEventManager2 */
|
||||
gameevents->FreeEvent(pInfo->pEvent);
|
||||
|
||||
/* IGameEvent is free at this point, so no one owns this */
|
||||
pInfo->pOwner = NULL;
|
||||
|
||||
/* Add EventInfo struct to free event stack */
|
||||
m_FreeEvents.push(pInfo);
|
||||
}
|
||||
|
||||
/* IGameEventManager2::FireEvent hook */
|
||||
bool EventManager::OnFireEvent(IGameEvent *pEvent, bool bDontBroadcast)
|
||||
{
|
||||
EventHook *pHook;
|
||||
IChangeableForward *pForward;
|
||||
const char *name;
|
||||
cell_t res = Pl_Continue;
|
||||
|
||||
/* The engine accepts NULL without crashing, so to prevent a crash in SM we ignore these */
|
||||
if (!pEvent)
|
||||
{
|
||||
RETURN_META_VALUE(MRES_IGNORED, false);
|
||||
}
|
||||
|
||||
/* Get the event name, we're going to need this for passing to post hooks */
|
||||
name = pEvent->GetName();
|
||||
|
||||
m_EventNames.push(name);
|
||||
|
||||
if (sm_trie_retrieve(m_EventHooks, name, reinterpret_cast<void **>(&pHook)))
|
||||
{
|
||||
pForward = pHook->pPreHook;
|
||||
|
||||
if (pForward)
|
||||
{
|
||||
EventInfo info = { pEvent, NULL };
|
||||
|
||||
Handle_t hndl = g_HandleSys.CreateHandle(m_EventType, &info, NULL, g_pCoreIdent, NULL);
|
||||
pForward->PushCell(hndl);
|
||||
pForward->PushString(name);
|
||||
pForward->PushCell(bDontBroadcast);
|
||||
pForward->Execute(&res, NULL);
|
||||
|
||||
HandleSecurity sec(NULL, g_pCoreIdent);
|
||||
g_HandleSys.FreeHandle(hndl, &sec);
|
||||
}
|
||||
|
||||
if (pHook->postCopy)
|
||||
{
|
||||
pHook->pEventCopy = gameevents->DuplicateEvent(pEvent);
|
||||
}
|
||||
|
||||
if (res)
|
||||
{
|
||||
gameevents->FreeEvent(pEvent);
|
||||
RETURN_META_VALUE(MRES_SUPERCEDE, false);
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_META_VALUE(MRES_IGNORED, true);
|
||||
}
|
||||
|
||||
/* IGameEventManager2::FireEvent post hook */
|
||||
bool EventManager::OnFireEvent_Post(IGameEvent *pEvent, bool bDontBroadcast)
|
||||
{
|
||||
IGameEvent *pEventCopy = NULL;
|
||||
EventHook *pHook;
|
||||
IChangeableForward *pForward;
|
||||
const char *name;
|
||||
Handle_t hndl = 0;
|
||||
|
||||
/* The engine accepts NULL without crashing, so to prevent a crash in SM we ignore these */
|
||||
if (!pEvent)
|
||||
{
|
||||
RETURN_META_VALUE(MRES_IGNORED, false);
|
||||
}
|
||||
|
||||
name = m_EventNames.front();
|
||||
|
||||
if (sm_trie_retrieve(m_EventHooks, name, reinterpret_cast<void **>(&pHook)))
|
||||
{
|
||||
pForward = pHook->pPostHook;
|
||||
|
||||
if (pForward)
|
||||
{
|
||||
EventInfo info = { pHook->pEventCopy, NULL };
|
||||
|
||||
if (pHook->postCopy)
|
||||
{
|
||||
hndl = g_HandleSys.CreateHandle(m_EventType, &info, NULL, g_pCoreIdent, NULL);
|
||||
|
||||
pForward->PushCell(hndl);
|
||||
} else {
|
||||
pForward->PushCell(BAD_HANDLE);
|
||||
}
|
||||
|
||||
pForward->PushString(name);
|
||||
pForward->PushCell(bDontBroadcast);
|
||||
pForward->Execute(NULL);
|
||||
|
||||
if (pHook->postCopy)
|
||||
{
|
||||
/* Free handle */
|
||||
HandleSecurity sec(NULL, g_pCoreIdent);
|
||||
g_HandleSys.FreeHandle(hndl, &sec);
|
||||
|
||||
/* Free event structure */
|
||||
gameevents->FreeEvent(pEventCopy);
|
||||
pHook->pEventCopy = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_EventNames.pop();
|
||||
|
||||
RETURN_META_VALUE(MRES_IGNORED, true);
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
/**
|
||||
* vim: set ts=4 :
|
||||
* =============================================================================
|
||||
* SourceMod
|
||||
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
|
||||
* =============================================================================
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, version 3.0, as published by the
|
||||
* Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* As a special exception, AlliedModders LLC gives you permission to link the
|
||||
* code of this program (as well as its derivative works) to "Half-Life 2," the
|
||||
* "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
|
||||
* by the Valve Corporation. You must obey the GNU General Public License in
|
||||
* all respects for all other code used. Additionally, AlliedModders LLC grants
|
||||
* this exception to all derivative works. AlliedModders LLC defines further
|
||||
* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
|
||||
* or <http://www.sourcemod.net/license.php>.
|
||||
*
|
||||
* Version: $Id$
|
||||
*/
|
||||
|
||||
#ifndef _INCLUDE_SOURCEMOD_EVENTMANAGER_H_
|
||||
#define _INCLUDE_SOURCEMOD_EVENTMANAGER_H_
|
||||
|
||||
#include "sm_globals.h"
|
||||
#include "sourcemm_api.h"
|
||||
#include "sm_trie.h"
|
||||
#include <sh_list.h>
|
||||
#include <sh_stack.h>
|
||||
#include <IHandleSys.h>
|
||||
#include <IForwardSys.h>
|
||||
#include <IPluginSys.h>
|
||||
|
||||
using namespace SourceHook;
|
||||
|
||||
struct EventInfo
|
||||
{
|
||||
IGameEvent *pEvent;
|
||||
IdentityToken_t *pOwner;
|
||||
};
|
||||
|
||||
struct EventHook
|
||||
{
|
||||
EventHook()
|
||||
{
|
||||
pPreHook = NULL;
|
||||
pPostHook = NULL;
|
||||
postCopy = false;
|
||||
pEventCopy = NULL;
|
||||
refCount = 0;
|
||||
}
|
||||
IChangeableForward *pPreHook;
|
||||
IChangeableForward *pPostHook;
|
||||
bool postCopy;
|
||||
IGameEvent *pEventCopy;
|
||||
unsigned int refCount;
|
||||
};
|
||||
|
||||
enum EventHookMode
|
||||
{
|
||||
EventHookMode_Pre,
|
||||
EventHookMode_Post,
|
||||
EventHookMode_PostNoCopy
|
||||
};
|
||||
|
||||
enum EventHookError
|
||||
{
|
||||
EventHookErr_Okay = 0, /**< No error */
|
||||
EventHookErr_InvalidEvent, /**< Specified event does not exist */
|
||||
EventHookErr_NotActive, /**< Specified event has no active hook */
|
||||
EventHookErr_InvalidCallback, /**< Specified event does not fire specified callback */
|
||||
};
|
||||
|
||||
class EventManager :
|
||||
public SMGlobalClass,
|
||||
public IHandleTypeDispatch,
|
||||
public IPluginsListener,
|
||||
public IGameEventListener2
|
||||
{
|
||||
public:
|
||||
EventManager();
|
||||
~EventManager();
|
||||
public: // SMGlobalClass
|
||||
void OnSourceModAllInitialized();
|
||||
void OnSourceModShutdown();
|
||||
public: // IHandleTypeDispatch
|
||||
void OnHandleDestroy(HandleType_t type, void *object);
|
||||
public: // IPluginsListener
|
||||
void OnPluginUnloaded(IPlugin *plugin);
|
||||
public: // IGameEventListener2
|
||||
void FireGameEvent(IGameEvent *pEvent);
|
||||
public:
|
||||
/**
|
||||
* Get the 'GameEvent' handle type ID.
|
||||
*/
|
||||
inline HandleType_t GetHandleType()
|
||||
{
|
||||
return m_EventType;
|
||||
}
|
||||
public:
|
||||
EventHookError HookEvent(const char *name, IPluginFunction *pFunction, EventHookMode mode=EventHookMode_Post);
|
||||
EventHookError UnhookEvent(const char *name, IPluginFunction *pFunction, EventHookMode mode=EventHookMode_Post);
|
||||
EventInfo *CreateEvent(IPluginContext *pContext, const char *name, bool force=false);
|
||||
void FireEvent(EventInfo *pInfo, bool bDontBroadcast=false);
|
||||
void CancelCreatedEvent(EventInfo *pInfo);
|
||||
private: // IGameEventManager2 hooks
|
||||
bool OnFireEvent(IGameEvent *pEvent, bool bDontBroadcast);
|
||||
bool OnFireEvent_Post(IGameEvent *pEvent, bool bDontBroadcast);
|
||||
private:
|
||||
HandleType_t m_EventType;
|
||||
Trie *m_EventHooks;
|
||||
CStack<EventInfo *> m_FreeEvents;
|
||||
CStack<const char *> m_EventNames;
|
||||
};
|
||||
|
||||
extern EventManager g_EventManager;
|
||||
|
||||
#endif // _INCLUDE_SOURCEMOD_EVENTMANAGER_H_
|
||||
@@ -0,0 +1,694 @@
|
||||
/**
|
||||
* vim: set ts=4 :
|
||||
* =============================================================================
|
||||
* SourceMod
|
||||
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
|
||||
* =============================================================================
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, version 3.0, as published by the
|
||||
* Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* As a special exception, AlliedModders LLC gives you permission to link the
|
||||
* code of this program (as well as its derivative works) to "Half-Life 2," the
|
||||
* "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
|
||||
* by the Valve Corporation. You must obey the GNU General Public License in
|
||||
* all respects for all other code used. Additionally, AlliedModders LLC grants
|
||||
* this exception to all derivative works. AlliedModders LLC defines further
|
||||
* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
|
||||
* or <http://www.sourcemod.net/license.php>.
|
||||
*
|
||||
* Version: $Id$
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include "GameConfigs.h"
|
||||
#include "sm_stringutil.h"
|
||||
#include "sourcemod.h"
|
||||
#include "sourcemm_api.h"
|
||||
#include "HalfLife2.h"
|
||||
#include "Logger.h"
|
||||
#include "ShareSys.h"
|
||||
#include "MemoryUtils.h"
|
||||
#include "LibrarySys.h"
|
||||
#include "HandleSys.h"
|
||||
#include "sm_crc32.h"
|
||||
#include "GameDataFetcher.h"
|
||||
|
||||
#if defined PLATFORM_LINUX
|
||||
#include <dlfcn.h>
|
||||
#endif
|
||||
|
||||
GameConfigManager g_GameConfigs;
|
||||
IGameConfig *g_pGameConf = NULL;
|
||||
char g_Game[256];
|
||||
char g_GameDesc[256] = {'!', '\0'};
|
||||
char g_GameName[256] = {'$', '\0'};
|
||||
|
||||
#define PSTATE_NONE 0
|
||||
#define PSTATE_GAMES 1
|
||||
#define PSTATE_GAMEDEFS 2
|
||||
#define PSTATE_GAMEDEFS_OFFSETS 3
|
||||
#define PSTATE_GAMEDEFS_OFFSETS_OFFSET 4
|
||||
#define PSTATE_GAMEDEFS_KEYS 5
|
||||
#define PSTATE_GAMEDEFS_SUPPORTED 6
|
||||
#define PSTATE_GAMEDEFS_SIGNATURES 7
|
||||
#define PSTATE_GAMEDEFS_SIGNATURES_SIG 8
|
||||
#define PSTATE_GAMEDEFS_CRC 9
|
||||
#define PSTATE_GAMEDEFS_CRC_BINARY 10
|
||||
|
||||
#if defined PLATFORM_WINDOWS
|
||||
#define PLATFORM_NAME "windows"
|
||||
#define PLATFORM_SERVER_BINARY "server.dll"
|
||||
#elif defined PLATFORM_LINUX
|
||||
#define PLATFORM_NAME "linux"
|
||||
#define PLATFORM_SERVER_BINARY "server_i486.so"
|
||||
#endif
|
||||
|
||||
struct TempSigInfo
|
||||
{
|
||||
void Reset()
|
||||
{
|
||||
library[0] = '\0';
|
||||
sig[0] = '\0';
|
||||
}
|
||||
char sig[512];
|
||||
char library[64];
|
||||
} s_TempSig;
|
||||
unsigned int s_ServerBinCRC;
|
||||
bool s_ServerBinCRC_Ok = false;
|
||||
|
||||
CGameConfig::CGameConfig(const char *file)
|
||||
{
|
||||
m_pFile = sm_strdup(file);
|
||||
m_pOffsets = sm_trie_create();
|
||||
m_pProps = sm_trie_create();
|
||||
m_pKeys = sm_trie_create();
|
||||
m_pSigs = sm_trie_create();
|
||||
m_pStrings = new BaseStringTable(512);
|
||||
m_RefCount = 0;
|
||||
}
|
||||
|
||||
CGameConfig::~CGameConfig()
|
||||
{
|
||||
sm_trie_destroy(m_pOffsets);
|
||||
sm_trie_destroy(m_pProps);
|
||||
sm_trie_destroy(m_pKeys);
|
||||
sm_trie_destroy(m_pSigs);
|
||||
delete [] m_pFile;
|
||||
delete m_pStrings;
|
||||
}
|
||||
|
||||
SMCResult CGameConfig::ReadSMC_NewSection(const SMCStates *states, const char *name)
|
||||
{
|
||||
if (m_IgnoreLevel)
|
||||
{
|
||||
m_IgnoreLevel++;
|
||||
return SMCResult_Continue;
|
||||
}
|
||||
|
||||
switch (m_ParseState)
|
||||
{
|
||||
case PSTATE_NONE:
|
||||
{
|
||||
if (strcmp(name, "Games") == 0)
|
||||
{
|
||||
m_ParseState = PSTATE_GAMES;
|
||||
} else {
|
||||
m_IgnoreLevel++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case PSTATE_GAMES:
|
||||
{
|
||||
if ((strcmp(name, "*") == 0)
|
||||
|| (strcmp(name, "#default") == 0)
|
||||
|| (strcasecmp(name, g_Game) == 0)
|
||||
|| (strcasecmp(name, g_GameDesc) == 0)
|
||||
|| (strcasecmp(name, g_GameName) == 0))
|
||||
{
|
||||
bShouldBeReadingDefault = true;
|
||||
m_ParseState = PSTATE_GAMEDEFS;
|
||||
strncopy(m_Game, name, sizeof(m_Game));
|
||||
} else {
|
||||
m_IgnoreLevel++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case PSTATE_GAMEDEFS:
|
||||
{
|
||||
if (strcmp(name, "Offsets") == 0)
|
||||
{
|
||||
m_ParseState = PSTATE_GAMEDEFS_OFFSETS;
|
||||
}
|
||||
else if (strcmp(name, "Keys") == 0)
|
||||
{
|
||||
m_ParseState = PSTATE_GAMEDEFS_KEYS;
|
||||
}
|
||||
else if ((strcmp(name, "#supported") == 0)
|
||||
&& (strcmp(m_Game, "#default") == 0))
|
||||
{
|
||||
m_ParseState = PSTATE_GAMEDEFS_SUPPORTED;
|
||||
/* Ignore this section unless we get a game. */
|
||||
bShouldBeReadingDefault = false;
|
||||
}
|
||||
else if (strcmp(name, "Signatures") == 0)
|
||||
{
|
||||
m_ParseState = PSTATE_GAMEDEFS_SIGNATURES;
|
||||
}
|
||||
else if (strcmp(name, "CRC") == 0)
|
||||
{
|
||||
m_ParseState = PSTATE_GAMEDEFS_CRC;
|
||||
bShouldBeReadingDefault = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_IgnoreLevel++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case PSTATE_GAMEDEFS_OFFSETS:
|
||||
{
|
||||
m_Prop[0] = '\0';
|
||||
m_Class[0] = '\0';
|
||||
strncopy(m_offset, name, sizeof(m_offset));
|
||||
m_ParseState = PSTATE_GAMEDEFS_OFFSETS_OFFSET;
|
||||
break;
|
||||
}
|
||||
case PSTATE_GAMEDEFS_SIGNATURES:
|
||||
{
|
||||
strncopy(m_offset, name, sizeof(m_offset));
|
||||
s_TempSig.Reset();
|
||||
m_ParseState = PSTATE_GAMEDEFS_SIGNATURES_SIG;
|
||||
break;
|
||||
}
|
||||
case PSTATE_GAMEDEFS_CRC:
|
||||
{
|
||||
char error[255];
|
||||
error[0] = '\0';
|
||||
if (strcmp(name, "server") != 0)
|
||||
{
|
||||
UTIL_Format(error, sizeof(error), "Unrecognized library \"%s\"", name);
|
||||
}
|
||||
else if (!s_ServerBinCRC_Ok)
|
||||
{
|
||||
FILE *fp;
|
||||
char path[PLATFORM_MAX_PATH];
|
||||
|
||||
g_SourceMod.BuildPath(Path_Game, path, sizeof(path), "bin/" PLATFORM_SERVER_BINARY);
|
||||
if ((fp = fopen(path, "rb")) == NULL)
|
||||
{
|
||||
UTIL_Format(error, sizeof(error), "Could not open binary: %s", path);
|
||||
} else {
|
||||
size_t size;
|
||||
void *buffer;
|
||||
|
||||
fseek(fp, 0, SEEK_END);
|
||||
size = ftell(fp);
|
||||
fseek(fp, 0, SEEK_SET);
|
||||
|
||||
buffer = malloc(size);
|
||||
fread(buffer, size, 1, fp);
|
||||
s_ServerBinCRC = UTIL_CRC32(buffer, size);
|
||||
free(buffer);
|
||||
s_ServerBinCRC_Ok = true;
|
||||
}
|
||||
}
|
||||
if (error[0] != '\0')
|
||||
{
|
||||
m_IgnoreLevel = 1;
|
||||
g_Logger.LogError("[SM] Error while parsing CRC section for \"%s\" (%s):", m_Game, m_pFile);
|
||||
g_Logger.LogError("[SM] %s", error);
|
||||
} else {
|
||||
m_ParseState = PSTATE_GAMEDEFS_CRC_BINARY;
|
||||
}
|
||||
break;
|
||||
}
|
||||
/* No sub-sections allowed:
|
||||
case PSTATE_GAMEDEFS_OFFSETS_OFFSET:
|
||||
case PSTATE_GAMEDEFS_KEYS:
|
||||
case PSTATE_GAMEDEFS_SUPPORTED:
|
||||
case PSTATE_GAMEDEFS_SIGNATURES_SIG:
|
||||
case PSTATE_GAMEDEFS_CRC_BINARY:
|
||||
*/
|
||||
default:
|
||||
{
|
||||
/* If we don't know what we got, start ignoring */
|
||||
m_IgnoreLevel++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return SMCResult_Continue;
|
||||
}
|
||||
|
||||
SMCResult CGameConfig::ReadSMC_KeyValue(const SMCStates *states, const char *key, const char *value)
|
||||
{
|
||||
if (m_IgnoreLevel)
|
||||
{
|
||||
return SMCResult_Continue;
|
||||
}
|
||||
|
||||
if (m_ParseState == PSTATE_GAMEDEFS_OFFSETS_OFFSET)
|
||||
{
|
||||
if (strcmp(key, "class") == 0)
|
||||
{
|
||||
strncopy(m_Class, value, sizeof(m_Class));
|
||||
} else if (strcmp(key, "prop") == 0) {
|
||||
strncopy(m_Prop, value, sizeof(m_Prop));
|
||||
} else if (strcmp(key, PLATFORM_NAME) == 0) {
|
||||
int val = atoi(value);
|
||||
sm_trie_replace(m_pOffsets, m_offset, (void *)val);
|
||||
}
|
||||
} else if (m_ParseState == PSTATE_GAMEDEFS_KEYS) {
|
||||
int id = m_pStrings->AddString(value);
|
||||
sm_trie_replace(m_pKeys, key, (void *)id);
|
||||
} else if (m_ParseState == PSTATE_GAMEDEFS_SUPPORTED) {
|
||||
if (strcmp(key, "game") == 0
|
||||
&& (strcasecmp(value, g_Game) == 0
|
||||
|| strcasecmp(value, g_GameDesc) == 0
|
||||
|| strcasecmp(value, g_GameName) == 0)
|
||||
)
|
||||
{
|
||||
bShouldBeReadingDefault = true;
|
||||
}
|
||||
} else if (m_ParseState == PSTATE_GAMEDEFS_SIGNATURES_SIG) {
|
||||
if (strcmp(key, PLATFORM_NAME) == 0)
|
||||
{
|
||||
strncopy(s_TempSig.sig, value, sizeof(s_TempSig.sig));
|
||||
} else if (strcmp(key, "library") == 0) {
|
||||
strncopy(s_TempSig.library, value, sizeof(s_TempSig.library));
|
||||
}
|
||||
} else if (m_ParseState == PSTATE_GAMEDEFS_CRC_BINARY) {
|
||||
if (strcmp(key, PLATFORM_NAME) == 0
|
||||
&& s_ServerBinCRC_Ok
|
||||
&& !bShouldBeReadingDefault)
|
||||
{
|
||||
unsigned int crc = 0;
|
||||
sscanf(value, "%08X", &crc);
|
||||
if (s_ServerBinCRC == crc)
|
||||
{
|
||||
bShouldBeReadingDefault = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return SMCResult_Continue;
|
||||
}
|
||||
|
||||
SMCResult CGameConfig::ReadSMC_LeavingSection(const SMCStates *states)
|
||||
{
|
||||
if (m_IgnoreLevel)
|
||||
{
|
||||
m_IgnoreLevel--;
|
||||
return SMCResult_Continue;
|
||||
}
|
||||
|
||||
switch (m_ParseState)
|
||||
{
|
||||
case PSTATE_GAMES:
|
||||
{
|
||||
m_ParseState = PSTATE_NONE;
|
||||
break;
|
||||
}
|
||||
case PSTATE_GAMEDEFS:
|
||||
{
|
||||
m_ParseState = PSTATE_GAMES;
|
||||
break;
|
||||
}
|
||||
case PSTATE_GAMEDEFS_KEYS:
|
||||
case PSTATE_GAMEDEFS_OFFSETS:
|
||||
{
|
||||
m_ParseState = PSTATE_GAMEDEFS;
|
||||
break;
|
||||
}
|
||||
case PSTATE_GAMEDEFS_OFFSETS_OFFSET:
|
||||
{
|
||||
/* Parse the offset... */
|
||||
if (m_Class[0] != '\0'
|
||||
&& m_Prop[0] != '\0')
|
||||
{
|
||||
SendProp *pProp = g_HL2.FindInSendTable(m_Class, m_Prop);
|
||||
if (pProp)
|
||||
{
|
||||
int val = pProp->GetOffset();
|
||||
sm_trie_replace(m_pOffsets, m_offset, (void *)val);
|
||||
sm_trie_replace(m_pProps, m_offset, pProp);
|
||||
} else {
|
||||
/* Check if it's a non-default game and no offsets exist */
|
||||
if (((strcmp(m_Game, "*") != 0) && strcmp(m_Game, "#default") != 0)
|
||||
&& (!sm_trie_retrieve(m_pOffsets, m_offset, NULL)))
|
||||
{
|
||||
g_Logger.LogError("[SM] Unable to find property %s.%s (file \"%s\") (mod \"%s\")",
|
||||
m_Class,
|
||||
m_Prop,
|
||||
m_pFile,
|
||||
m_Game);
|
||||
}
|
||||
}
|
||||
}
|
||||
m_ParseState = PSTATE_GAMEDEFS_OFFSETS;
|
||||
break;
|
||||
}
|
||||
case PSTATE_GAMEDEFS_CRC:
|
||||
case PSTATE_GAMEDEFS_SUPPORTED:
|
||||
{
|
||||
if (!bShouldBeReadingDefault)
|
||||
{
|
||||
/* If we shouldn't read the rest of this section, set the ignore level. */
|
||||
m_IgnoreLevel = 1;
|
||||
m_ParseState = PSTATE_GAMES;
|
||||
} else {
|
||||
m_ParseState = PSTATE_GAMEDEFS;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case PSTATE_GAMEDEFS_CRC_BINARY:
|
||||
{
|
||||
m_ParseState = PSTATE_GAMEDEFS_CRC;
|
||||
break;
|
||||
}
|
||||
case PSTATE_GAMEDEFS_SIGNATURES:
|
||||
{
|
||||
m_ParseState = PSTATE_GAMEDEFS;
|
||||
break;
|
||||
}
|
||||
case PSTATE_GAMEDEFS_SIGNATURES_SIG:
|
||||
{
|
||||
if (s_TempSig.library[0] == '\0')
|
||||
{
|
||||
/* assume server */
|
||||
strncopy(s_TempSig.library, "server", sizeof(s_TempSig.library));
|
||||
}
|
||||
void *addrInBase = NULL;
|
||||
if (strcmp(s_TempSig.library, "server") == 0)
|
||||
{
|
||||
addrInBase = (void *)g_SMAPI->GetServerFactory(false);
|
||||
} else if (strcmp(s_TempSig.library, "engine") == 0) {
|
||||
addrInBase = (void *)g_SMAPI->GetEngineFactory(false);
|
||||
}
|
||||
void *final_addr = NULL;
|
||||
if (addrInBase == NULL)
|
||||
{
|
||||
g_Logger.LogError("[SM] Unrecognized library \"%s\" (gameconf \"%s\")",
|
||||
s_TempSig.library,
|
||||
m_pFile);
|
||||
} else {
|
||||
#if defined PLATFORM_LINUX
|
||||
if (s_TempSig.sig[0] == '@')
|
||||
{
|
||||
Dl_info info;
|
||||
/* GNU only: returns 0 on error, inconsistent! >:[ */
|
||||
if (dladdr(addrInBase, &info) != 0)
|
||||
{
|
||||
void *handle = dlopen(info.dli_fname, RTLD_NOW);
|
||||
if (handle)
|
||||
{
|
||||
final_addr = dlsym(handle, &s_TempSig.sig[1]);
|
||||
dlclose(handle);
|
||||
} else {
|
||||
g_Logger.LogError("[SM] Unable to load library \"%s\" (gameconf \"%s\")",
|
||||
s_TempSig.library,
|
||||
m_pFile);
|
||||
}
|
||||
} else {
|
||||
g_Logger.LogError("[SM] Unable to find library \"%s\" in memory (gameconf \"%s\")",
|
||||
s_TempSig.library,
|
||||
m_pFile);
|
||||
}
|
||||
}
|
||||
if (final_addr)
|
||||
{
|
||||
goto skip_find;
|
||||
}
|
||||
#endif
|
||||
/* First, preprocess the signature */
|
||||
char real_sig[511];
|
||||
size_t real_bytes;
|
||||
size_t length;
|
||||
|
||||
real_bytes = 0;
|
||||
length = strlen(s_TempSig.sig);
|
||||
|
||||
for (size_t i=0; i<length; i++)
|
||||
{
|
||||
if (real_bytes >= sizeof(real_sig))
|
||||
{
|
||||
break;
|
||||
}
|
||||
real_sig[real_bytes++] = s_TempSig.sig[i];
|
||||
if (s_TempSig.sig[i] == '\\'
|
||||
&& s_TempSig.sig[i+1] == 'x')
|
||||
{
|
||||
if (i + 3 >= length)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
/* Get the hex part */
|
||||
char s_byte[3];
|
||||
int r_byte;
|
||||
s_byte[0] = s_TempSig.sig[i+2];
|
||||
s_byte[1] = s_TempSig.sig[i+3];
|
||||
s_byte[2] = '\0';
|
||||
/* Read it as an integer */
|
||||
sscanf(s_byte, "%x", &r_byte);
|
||||
/* Save the value */
|
||||
real_sig[real_bytes-1] = r_byte;
|
||||
/* Adjust index */
|
||||
i += 3;
|
||||
}
|
||||
}
|
||||
|
||||
if (real_bytes >= 1)
|
||||
{
|
||||
final_addr = g_MemUtils.FindPattern(addrInBase, real_sig, real_bytes);
|
||||
}
|
||||
}
|
||||
|
||||
#if defined PLATFORM_LINUX
|
||||
skip_find:
|
||||
#endif
|
||||
sm_trie_replace(m_pSigs, m_offset, final_addr);
|
||||
m_ParseState = PSTATE_GAMEDEFS_SIGNATURES;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return SMCResult_Continue;
|
||||
}
|
||||
|
||||
bool CGameConfig::Reparse(char *error, size_t maxlength)
|
||||
{
|
||||
SMCError err;
|
||||
SMCStates state = {0, 0};
|
||||
|
||||
char path[PLATFORM_MAX_PATH];
|
||||
g_SourceMod.BuildPath(Path_SM, path, sizeof(path), "gamedata/%s.txt", m_pFile);
|
||||
/* Backwards compatibility */
|
||||
if (!g_LibSys.PathExists(path))
|
||||
{
|
||||
g_SourceMod.BuildPath(Path_SM, path, sizeof(path), "configs/gamedata/%s.txt", m_pFile);
|
||||
}
|
||||
|
||||
/* Initialize parse states */
|
||||
m_IgnoreLevel = 0;
|
||||
bShouldBeReadingDefault = true;
|
||||
m_ParseState = PSTATE_NONE;
|
||||
/* Reset cached data */
|
||||
m_pStrings->Reset();
|
||||
sm_trie_clear(m_pOffsets);
|
||||
sm_trie_clear(m_pProps);
|
||||
sm_trie_clear(m_pKeys);
|
||||
|
||||
if ((err=textparsers->ParseSMCFile(path, this, &state, error, maxlength))
|
||||
!= SMCError_Okay)
|
||||
{
|
||||
const char *msg;
|
||||
|
||||
msg = textparsers->GetSMCErrorString(err);
|
||||
|
||||
g_Logger.LogError("[SM] Error parsing gameconfig file \"%s\":", path);
|
||||
g_Logger.LogError("[SM] Error %d on line %d, col %d: %s",
|
||||
err,
|
||||
state.line,
|
||||
state.col,
|
||||
msg ? msg : "Unknown error");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CGameConfig::GetOffset(const char *key, int *value)
|
||||
{
|
||||
void *obj;
|
||||
|
||||
if (!sm_trie_retrieve(m_pOffsets, key, &obj))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
*value = (int)obj;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
const char *CGameConfig::GetKeyValue(const char *key)
|
||||
{
|
||||
void *obj;
|
||||
if (!sm_trie_retrieve(m_pKeys, key, &obj))
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
return m_pStrings->GetString((int)obj);
|
||||
}
|
||||
|
||||
SendProp *CGameConfig::GetSendProp(const char *key)
|
||||
{
|
||||
SendProp *pProp;
|
||||
|
||||
if (!sm_trie_retrieve(m_pProps, key, (void **)&pProp))
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return pProp;
|
||||
}
|
||||
|
||||
bool CGameConfig::GetMemSig(const char *key, void **addr)
|
||||
{
|
||||
return sm_trie_retrieve(m_pSigs, key, addr);
|
||||
}
|
||||
|
||||
void CGameConfig::IncRefCount()
|
||||
{
|
||||
m_RefCount++;
|
||||
}
|
||||
|
||||
unsigned int CGameConfig::DecRefCount()
|
||||
{
|
||||
m_RefCount--;
|
||||
return m_RefCount;
|
||||
}
|
||||
|
||||
GameConfigManager::GameConfigManager()
|
||||
{
|
||||
m_pLookup = sm_trie_create();
|
||||
}
|
||||
|
||||
GameConfigManager::~GameConfigManager()
|
||||
{
|
||||
sm_trie_destroy(m_pLookup);
|
||||
}
|
||||
|
||||
void GameConfigManager::OnSourceModStartup(bool late)
|
||||
{
|
||||
LoadGameConfigFile("core.games", &g_pGameConf, NULL, 0);
|
||||
|
||||
strncopy(g_Game, g_SourceMod.GetGameFolderName(), sizeof(g_Game));
|
||||
strncopy(g_GameDesc + 1, SERVER_CALL(GetGameDescription)(), sizeof(g_GameDesc) - 1);
|
||||
|
||||
KeyValues *pGameInfo = new KeyValues("GameInfo");
|
||||
if (g_HL2.KVLoadFromFile(pGameInfo, basefilesystem, "gameinfo.txt"))
|
||||
{
|
||||
const char *str;
|
||||
if ((str = pGameInfo->GetString("game", NULL)) != NULL)
|
||||
{
|
||||
strncopy(g_GameName + 1, str, sizeof(g_GameName) - 1);
|
||||
}
|
||||
}
|
||||
pGameInfo->deleteThis();
|
||||
}
|
||||
|
||||
void GameConfigManager::OnSourceModAllInitialized()
|
||||
{
|
||||
/* NOW initialize the game file */
|
||||
CGameConfig *pGameConf = (CGameConfig *)g_pGameConf;
|
||||
|
||||
char error[255];
|
||||
if (!pGameConf->Reparse(error, sizeof(error)))
|
||||
{
|
||||
/* :TODO: log */
|
||||
}
|
||||
|
||||
g_ShareSys.AddInterface(NULL, this);
|
||||
}
|
||||
|
||||
void GameConfigManager::OnSourceModAllShutdown()
|
||||
{
|
||||
CloseGameConfigFile(g_pGameConf);
|
||||
}
|
||||
|
||||
bool GameConfigManager::LoadGameConfigFile(const char *file, IGameConfig **_pConfig, char *error, size_t maxlength)
|
||||
{
|
||||
/* A crash was detected during last load - We block the gamedata loading so it hopefully won't happen again */
|
||||
if (g_blockGameDataLoad)
|
||||
{
|
||||
UTIL_Format(error, maxlength, "GameData loaded blocked due to detected crash");
|
||||
return false;
|
||||
}
|
||||
|
||||
CGameConfig *pConfig;
|
||||
|
||||
if (sm_trie_retrieve(m_pLookup, file, (void **)&pConfig))
|
||||
{
|
||||
pConfig->IncRefCount();
|
||||
*_pConfig = pConfig;
|
||||
return true;
|
||||
}
|
||||
|
||||
pConfig = new CGameConfig(file);
|
||||
|
||||
/* :HACKHACK: Don't parse the main config file */
|
||||
bool retval = true;
|
||||
if (_pConfig != &g_pGameConf)
|
||||
{
|
||||
retval = pConfig->Reparse(error, maxlength);
|
||||
}
|
||||
|
||||
m_cfgs.push_back(pConfig);
|
||||
sm_trie_insert(m_pLookup, file, pConfig);
|
||||
|
||||
pConfig->IncRefCount();
|
||||
|
||||
*_pConfig = pConfig;
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
void GameConfigManager::CloseGameConfigFile(IGameConfig *cfg)
|
||||
{
|
||||
CGameConfig *pConfig = (CGameConfig *)cfg;
|
||||
|
||||
if (pConfig->DecRefCount() == 0)
|
||||
{
|
||||
sm_trie_delete(m_pLookup, pConfig->m_pFile);
|
||||
delete pConfig;
|
||||
}
|
||||
}
|
||||
|
||||
extern HandleType_t g_GameConfigsType;
|
||||
|
||||
IGameConfig *GameConfigManager::ReadHandle(Handle_t hndl, IdentityToken_t *ident, HandleError *err)
|
||||
{
|
||||
HandleSecurity sec(ident, g_pCoreIdent);
|
||||
IGameConfig *conf = NULL;
|
||||
HandleError _err = g_HandleSys.ReadHandle(hndl, g_GameConfigsType, &sec, (void **)&conf);
|
||||
|
||||
if (err)
|
||||
{
|
||||
*err = _err;
|
||||
}
|
||||
|
||||
return conf;
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
/**
|
||||
* vim: set ts=4 :
|
||||
* =============================================================================
|
||||
* SourceMod
|
||||
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
|
||||
* =============================================================================
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, version 3.0, as published by the
|
||||
* Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* As a special exception, AlliedModders LLC gives you permission to link the
|
||||
* code of this program (as well as its derivative works) to "Half-Life 2," the
|
||||
* "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
|
||||
* by the Valve Corporation. You must obey the GNU General Public License in
|
||||
* all respects for all other code used. Additionally, AlliedModders LLC grants
|
||||
* this exception to all derivative works. AlliedModders LLC defines further
|
||||
* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
|
||||
* or <http://www.sourcemod.net/license.php>.
|
||||
*
|
||||
* Version: $Id$
|
||||
*/
|
||||
|
||||
#ifndef _INCLUDE_SOURCEMOD_CGAMECONFIGS_H_
|
||||
#define _INCLUDE_SOURCEMOD_CGAMECONFIGS_H_
|
||||
|
||||
#include <IGameConfigs.h>
|
||||
#include <ITextParsers.h>
|
||||
#include <sh_list.h>
|
||||
#include "sm_trie.h"
|
||||
#include "sm_globals.h"
|
||||
#include "sm_memtable.h"
|
||||
|
||||
using namespace SourceMod;
|
||||
using namespace SourceHook;
|
||||
|
||||
class SendProp;
|
||||
|
||||
class CGameConfig :
|
||||
public ITextListener_SMC,
|
||||
public IGameConfig
|
||||
{
|
||||
friend class GameConfigManager;
|
||||
public:
|
||||
CGameConfig(const char *file);
|
||||
~CGameConfig();
|
||||
public:
|
||||
bool Reparse(char *error, size_t maxlength);
|
||||
public: //ITextListener_SMC
|
||||
SMCResult ReadSMC_NewSection(const SMCStates *states, const char *name);
|
||||
SMCResult ReadSMC_KeyValue(const SMCStates *states, const char *key, const char *value);
|
||||
SMCResult ReadSMC_LeavingSection(const SMCStates *states);
|
||||
public: //IGameConfig
|
||||
const char *GetKeyValue(const char *key);
|
||||
bool GetOffset(const char *key, int *value);
|
||||
SendProp *GetSendProp(const char *key);
|
||||
bool GetMemSig(const char *key, void **addr);
|
||||
public:
|
||||
void IncRefCount();
|
||||
unsigned int DecRefCount();
|
||||
private:
|
||||
BaseStringTable *m_pStrings;
|
||||
char *m_pFile;
|
||||
Trie *m_pOffsets;
|
||||
Trie *m_pProps;
|
||||
Trie *m_pKeys;
|
||||
Trie *m_pSigs;
|
||||
unsigned int m_RefCount;
|
||||
/* Parse states */
|
||||
int m_ParseState;
|
||||
unsigned int m_IgnoreLevel;
|
||||
char m_Class[64];
|
||||
char m_Prop[64];
|
||||
char m_offset[64];
|
||||
char m_Game[256];
|
||||
bool bShouldBeReadingDefault;
|
||||
};
|
||||
|
||||
class GameConfigManager :
|
||||
public IGameConfigManager,
|
||||
public SMGlobalClass
|
||||
{
|
||||
public:
|
||||
GameConfigManager();
|
||||
~GameConfigManager();
|
||||
public: //IGameConfigManager
|
||||
bool LoadGameConfigFile(const char *file, IGameConfig **pConfig, char *error, size_t maxlength);
|
||||
void CloseGameConfigFile(IGameConfig *cfg);
|
||||
IGameConfig *ReadHandle(Handle_t hndl,
|
||||
IdentityToken_t *ident,
|
||||
HandleError *err);
|
||||
public: //SMGlobalClass
|
||||
void OnSourceModStartup(bool late);
|
||||
void OnSourceModAllInitialized();
|
||||
void OnSourceModAllShutdown();
|
||||
private:
|
||||
List<CGameConfig *> m_cfgs;
|
||||
Trie *m_pLookup;
|
||||
};
|
||||
|
||||
extern GameConfigManager g_GameConfigs;
|
||||
extern IGameConfig *g_pGameConf;
|
||||
|
||||
#endif //_INCLUDE_SOURCEMOD_CGAMECONFIGS_H_
|
||||
@@ -0,0 +1,693 @@
|
||||
/**
|
||||
* vim: set ts=4 :
|
||||
* =============================================================================
|
||||
* SourceMod
|
||||
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
|
||||
* =============================================================================
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, version 3.0, as published by the
|
||||
* Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* As a special exception, AlliedModders LLC gives you permission to link the
|
||||
* code of this program (as well as its derivative works) to "Half-Life 2," the
|
||||
* "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
|
||||
* by the Valve Corporation. You must obey the GNU General Public License in
|
||||
* all respects for all other code used. Additionally, AlliedModders LLC grants
|
||||
* this exception to all derivative works. AlliedModders LLC defines further
|
||||
* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
|
||||
* or <http://www.sourcemod.net/license.php>.
|
||||
*
|
||||
* Version: $Id$
|
||||
*/
|
||||
|
||||
#include "GameDataFetcher.h"
|
||||
#include "bitbuf.h"
|
||||
|
||||
#ifdef PLATFORM_WINDOWS
|
||||
#include <winsock2.h>
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netdb.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#define INVALID_SOCKET -1
|
||||
#define closesocket close
|
||||
#endif
|
||||
|
||||
#include "sh_vector.h"
|
||||
#include "sh_string.h"
|
||||
#include "sm_version.h"
|
||||
|
||||
#ifdef ORANGEBOX_BUILD
|
||||
#include "convar_sm_ob.h"
|
||||
#else
|
||||
#include "convar_sm.h"
|
||||
#endif
|
||||
|
||||
#include "sourcemm_api.h"
|
||||
#include "time.h"
|
||||
#include "TimerSys.h"
|
||||
#include "compat_wrappers.h"
|
||||
|
||||
#define QUERY_MAX_LENGTH 1024
|
||||
|
||||
BuildMD5ableBuffer g_MD5Builder;
|
||||
FetcherThread g_FetchThread;
|
||||
|
||||
FILE *logfile = NULL;
|
||||
|
||||
bool g_disableGameDataUpdate = false;
|
||||
bool g_restartAfterUpdate = false;
|
||||
|
||||
void FetcherThread::RunThread( IThreadHandle *pHandle )
|
||||
{
|
||||
char lock_path[PLATFORM_MAX_PATH];
|
||||
g_SourceMod.BuildPath(Path_SM, lock_path, sizeof(lock_path), "data/temp");
|
||||
g_LibSys.CreateFolder(lock_path);
|
||||
|
||||
g_SourceMod.BuildPath(Path_SM, lock_path, sizeof(lock_path), "data/temp/gamedata.lock");
|
||||
|
||||
g_Logger.LogMessage("Starting experimental gamedata update fetcher... please report problems to bugs.alliedmods.net");
|
||||
|
||||
char log_path[PLATFORM_MAX_PATH];
|
||||
g_SourceMod.BuildPath(Path_SM, log_path, sizeof(log_path), "logs/gamedata");
|
||||
|
||||
g_LibSys.CreateFolder(log_path);
|
||||
|
||||
time_t t;
|
||||
GetAdjustedTime(&t);
|
||||
tm *curtime = localtime(&t);
|
||||
|
||||
g_SourceMod.BuildPath(Path_SM, log_path, sizeof(log_path), "logs/gamedata/L%02d%02d.log", curtime->tm_mon + 1, curtime->tm_mday);
|
||||
|
||||
logfile = fopen(log_path, "a");
|
||||
|
||||
if (!logfile)
|
||||
{
|
||||
g_Logger.LogError("Failed to create GameData log file");
|
||||
return;
|
||||
}
|
||||
|
||||
//Create a blank lock file
|
||||
FILE *fp = fopen(lock_path, "w");
|
||||
if (fp)
|
||||
{
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
/* Create a new socket for this connection */
|
||||
int socketDescriptor = ConnectSocket();
|
||||
|
||||
if (socketDescriptor == INVALID_SOCKET)
|
||||
{
|
||||
fclose(logfile);
|
||||
unlink(lock_path);
|
||||
return;
|
||||
}
|
||||
|
||||
char query[QUERY_MAX_LENGTH];
|
||||
|
||||
/* Check for updated gamedata files */
|
||||
int len = BuildGameDataQuery(query, QUERY_MAX_LENGTH);
|
||||
|
||||
if (len == 0)
|
||||
{
|
||||
g_Logger.LogToOpenFile(logfile, "Query Writing failed");
|
||||
|
||||
closesocket(socketDescriptor);
|
||||
fclose(logfile);
|
||||
unlink(lock_path);
|
||||
return;
|
||||
}
|
||||
|
||||
if (g_disableGameDataUpdate)
|
||||
{
|
||||
g_Logger.LogMessage("Skipping GameData Query due to DisableAutoUpdate being set to true");
|
||||
|
||||
closesocket(socketDescriptor);
|
||||
fclose(logfile);
|
||||
unlink(lock_path);
|
||||
return;
|
||||
}
|
||||
|
||||
int sent = NonBlockingSend(socketDescriptor, query, len);
|
||||
|
||||
g_Logger.LogToOpenFile(logfile, "Sent Query!");
|
||||
|
||||
if (sent == 0)
|
||||
{
|
||||
g_Logger.LogToOpenFile(logfile, "Failed to send data");
|
||||
|
||||
closesocket(socketDescriptor);
|
||||
fclose(logfile);
|
||||
unlink(lock_path);
|
||||
return;
|
||||
}
|
||||
|
||||
ProcessGameDataQuery(socketDescriptor);
|
||||
|
||||
/* And we're done! */
|
||||
closesocket(socketDescriptor);
|
||||
fclose(logfile);
|
||||
unlink(lock_path);
|
||||
}
|
||||
|
||||
void FetcherThread::OnTerminate( IThreadHandle *pHandle, bool cancel )
|
||||
{
|
||||
//delete this;
|
||||
}
|
||||
|
||||
int FetcherThread::BuildGameDataQuery( char *buffer, int maxlen )
|
||||
{
|
||||
char gamedata_path[PLATFORM_MAX_PATH];
|
||||
g_SourceMod.BuildPath(Path_SM, gamedata_path, sizeof(gamedata_path), "gamedata");
|
||||
|
||||
IDirectory *dir = g_LibSys.OpenDirectory(gamedata_path);
|
||||
|
||||
if (dir == NULL)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
bf_write Writer = bf_write("GameDataQuery", buffer, maxlen);
|
||||
|
||||
Writer.WriteByte('A'); //Generic Header char
|
||||
Writer.WriteByte('G'); //G for gamedata query, or green, like my hat.
|
||||
|
||||
short build[4] = { SVN_FILE_VERSION };
|
||||
|
||||
Writer.WriteBytes(&build[0], 8);
|
||||
|
||||
Writer.WriteByte(0); // Initialize the file counter - Index 10
|
||||
|
||||
while (dir->MoreFiles())
|
||||
{
|
||||
if (dir->IsEntryFile())
|
||||
{
|
||||
const char *name = dir->GetEntryName();
|
||||
size_t len = strlen(name);
|
||||
if (len >= 4
|
||||
&& strcmp(&name[len-4], ".txt") == 0)
|
||||
{
|
||||
char file[PLATFORM_MAX_PATH];
|
||||
|
||||
g_LibSys.PathFormat(file, sizeof(file), "%s/%s", gamedata_path, name);
|
||||
|
||||
SMCStates states;
|
||||
if (g_TextParser.ParseFile_SMC(file, &g_MD5Builder, &states) == SMCError_Okay)
|
||||
{
|
||||
unsigned char *md5 = g_MD5Builder.GetMD5();
|
||||
if (md5 != NULL)
|
||||
{
|
||||
(uint8_t)buffer[10]++; //Increment the file counter
|
||||
Writer.WriteBytes(md5, 16);
|
||||
|
||||
g_Logger.LogToOpenFile(logfile, "%s - \"%s\"", file, g_MD5Builder.GetMD5String());
|
||||
|
||||
FileData *data = new FileData();
|
||||
data->filename = new SourceHook::String(file);
|
||||
memcpy(data->checksum, g_MD5Builder.GetMD5String(), 33);
|
||||
filenames.push_back(data);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_Logger.LogToOpenFile(logfile, "%s no md5?", file);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
g_Logger.LogToOpenFile(logfile, "%s failed!", file);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dir->NextEntry();
|
||||
}
|
||||
|
||||
return Writer.GetNumBytesWritten();
|
||||
}
|
||||
|
||||
int FetcherThread::ConnectSocket()
|
||||
{
|
||||
struct protoent *ptrp;
|
||||
ptrp = getprotobyname("tcp");
|
||||
|
||||
#ifdef WIN32
|
||||
WSADATA wsaData;
|
||||
WSAStartup(0x0101, &wsaData);
|
||||
#endif
|
||||
|
||||
if (ptrp == NULL)
|
||||
{
|
||||
g_Logger.LogToOpenFile(logfile, "Failed to find TCP");
|
||||
return INVALID_SOCKET;
|
||||
}
|
||||
|
||||
int socketDescriptor = socket(AF_INET, SOCK_STREAM, ptrp->p_proto);
|
||||
|
||||
if (socketDescriptor == INVALID_SOCKET)
|
||||
{
|
||||
//bugger aye?
|
||||
g_Logger.LogToOpenFile(logfile, "Failed to create a new socket");
|
||||
closesocket(socketDescriptor);
|
||||
return INVALID_SOCKET;
|
||||
}
|
||||
|
||||
struct hostent *he;
|
||||
struct sockaddr_in local_addr;
|
||||
|
||||
local_addr.sin_family = AF_INET;
|
||||
local_addr.sin_port = htons((u_short)6500);
|
||||
|
||||
he = gethostbyname("hayate.alliedmods.net");
|
||||
|
||||
if (!he)
|
||||
{
|
||||
if ((local_addr.sin_addr.s_addr = inet_addr("hayate.alliedmods.net")) == INADDR_NONE)
|
||||
{
|
||||
g_Logger.LogToOpenFile(logfile, "Couldnt locate address");
|
||||
closesocket(socketDescriptor);
|
||||
return INVALID_SOCKET;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
memcpy(&local_addr.sin_addr, (struct in_addr *)he->h_addr, he->h_length);
|
||||
}
|
||||
|
||||
if (connect(socketDescriptor, (struct sockaddr *) &local_addr, sizeof(local_addr)) < 0)
|
||||
{
|
||||
g_Logger.LogToOpenFile(logfile, "Couldn't connect");
|
||||
closesocket(socketDescriptor);
|
||||
return INVALID_SOCKET;
|
||||
}
|
||||
|
||||
return socketDescriptor;
|
||||
}
|
||||
|
||||
void FetcherThread::ProcessGameDataQuery(int socketDescriptor)
|
||||
{
|
||||
char buffer[50];
|
||||
|
||||
g_Logger.LogToOpenFile(logfile, "Waiting for reply!");
|
||||
|
||||
//Read in the header bytes
|
||||
int returnLen = NonBlockingRecv(socketDescriptor, buffer, 12);
|
||||
|
||||
g_Logger.LogToOpenFile(logfile, "Recv Completed");
|
||||
|
||||
if (returnLen == 0)
|
||||
{
|
||||
g_Logger.LogToOpenFile(logfile, ",but it failed.");
|
||||
/* Timeout or fail? */
|
||||
return;
|
||||
}
|
||||
|
||||
g_Logger.LogToOpenFile(logfile, "Received Header!");
|
||||
|
||||
bf_read Reader = bf_read("GameDataQuery", buffer, 12);
|
||||
|
||||
if (Reader.ReadByte() != 'A' || Reader.ReadByte() != 'G')
|
||||
{
|
||||
g_Logger.LogToOpenFile(logfile, "Invalid Query to handle");
|
||||
return;
|
||||
}
|
||||
|
||||
UpdateStatus updateStatus = (UpdateStatus)Reader.ReadByte();
|
||||
|
||||
short build[4] = {0,0,0,0};
|
||||
|
||||
build[0] = Reader.ReadShort();
|
||||
build[1] = Reader.ReadShort();
|
||||
build[2] = Reader.ReadShort();
|
||||
build[3] = Reader.ReadShort();
|
||||
|
||||
g_Logger.LogToOpenFile(logfile, "Update Status: %i - Latest %i.%i.%i.%i", updateStatus, build[0], build[1], build[2], build[3]);
|
||||
|
||||
HandleUpdateStatus(updateStatus, build);
|
||||
|
||||
int changedFiles = Reader.ReadByte();
|
||||
|
||||
g_Logger.LogToOpenFile(logfile, "Files to download: %i", changedFiles);
|
||||
|
||||
for (int i=0; i<changedFiles; i++)
|
||||
{
|
||||
//Read in the file index and byte count
|
||||
returnLen = NonBlockingRecv(socketDescriptor, buffer, 5);
|
||||
|
||||
if (returnLen == 0)
|
||||
{
|
||||
/* Timeout or fail? */
|
||||
return;
|
||||
}
|
||||
|
||||
Reader.StartReading(buffer, 5);
|
||||
|
||||
int index = Reader.ReadByte();
|
||||
int tempLen = Reader.ReadUBitLong(32);
|
||||
|
||||
g_Logger.LogToOpenFile(logfile, "File index %i and length %i", index, tempLen);
|
||||
|
||||
void *memPtr;
|
||||
memtable->CreateMem(tempLen+1, &memPtr);
|
||||
|
||||
//Read the contents of our file into the memtable
|
||||
returnLen = NonBlockingRecv(socketDescriptor, (char *)memPtr, tempLen);
|
||||
|
||||
g_Logger.LogToOpenFile(logfile, "Recieved %i bytes", returnLen);
|
||||
|
||||
if (returnLen == 0)
|
||||
{
|
||||
/* Timeout or fail? */
|
||||
return;
|
||||
}
|
||||
|
||||
((unsigned char *)memPtr)[tempLen] = '\0';
|
||||
|
||||
FileData *data = filenames.at(index);
|
||||
const char* filename;
|
||||
if (data != NULL)
|
||||
{
|
||||
filename = data->filename->c_str();
|
||||
|
||||
FILE *fp = fopen(filename, "w");
|
||||
|
||||
if (fp)
|
||||
{
|
||||
fprintf(fp, (const char *)memPtr);
|
||||
fclose(fp);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
filename = "";
|
||||
}
|
||||
|
||||
memtable->Reset();
|
||||
|
||||
g_Logger.LogToOpenFile(logfile, "Updated File %s", filename);
|
||||
}
|
||||
|
||||
g_Logger.LogToOpenFile(logfile, "File Downloads Completed!");
|
||||
|
||||
bool needsRestart = false;
|
||||
|
||||
if (changedFiles > 0)
|
||||
{
|
||||
needsRestart = true;
|
||||
g_Logger.LogMessage("New GameData Files have been downloaded to your gamedata directory. Please restart your server for these to take effect");
|
||||
}
|
||||
|
||||
//Read changed file count
|
||||
returnLen = NonBlockingRecv(socketDescriptor, buffer, 1);
|
||||
|
||||
if (returnLen == 0)
|
||||
{
|
||||
/* Timeout or fail? */
|
||||
g_Logger.LogToOpenFile(logfile, "Failed to receive unknown count");
|
||||
return;
|
||||
}
|
||||
|
||||
Reader.StartReading(buffer, 1);
|
||||
|
||||
changedFiles = Reader.ReadByte();
|
||||
|
||||
if (changedFiles == 0)
|
||||
{
|
||||
g_Logger.LogToOpenFile(logfile, "No unknown files. We're all done");
|
||||
return;
|
||||
}
|
||||
|
||||
char *changedFileIndexes = new char[changedFiles];
|
||||
|
||||
g_Logger.LogToOpenFile(logfile, "%i Files were unknown", changedFiles);
|
||||
|
||||
returnLen = NonBlockingRecv(socketDescriptor, changedFileIndexes, changedFiles);
|
||||
|
||||
if (returnLen == 0)
|
||||
{
|
||||
/* Timeout or fail? */
|
||||
g_Logger.LogToOpenFile(logfile, "Failed to receive unknown list");
|
||||
return;
|
||||
}
|
||||
|
||||
Reader.StartReading(changedFileIndexes, changedFiles);
|
||||
|
||||
for (int i=0; i<changedFiles; i++)
|
||||
{
|
||||
int index = Reader.ReadByte();
|
||||
char fileName[30];
|
||||
|
||||
FileData *data = filenames.at(index);
|
||||
const char* pathname;
|
||||
if (data != NULL)
|
||||
{
|
||||
pathname = data->filename->c_str();
|
||||
}
|
||||
else
|
||||
{
|
||||
pathname = "";
|
||||
}
|
||||
|
||||
g_LibSys.GetFileFromPath(fileName, sizeof(fileName), pathname);
|
||||
|
||||
g_Logger.LogToOpenFile(logfile, "Unknown File %i : %s", index, fileName);
|
||||
}
|
||||
|
||||
delete [] changedFileIndexes;
|
||||
|
||||
if (needsRestart && g_restartAfterUpdate)
|
||||
{
|
||||
g_Logger.LogMessage("Automatically restarting server after a successful gamedata update!");
|
||||
engine->ServerCommand("quit\n");
|
||||
}
|
||||
}
|
||||
|
||||
int FetcherThread::NonBlockingRecv( int socketDescriptor, char *buffer, int len )
|
||||
{
|
||||
fd_set fds;
|
||||
struct timeval tv;
|
||||
|
||||
/* Create a 10 Second Timeout */
|
||||
tv.tv_sec = 10;
|
||||
tv.tv_usec = 0;
|
||||
|
||||
/* Add our socket to a socket set */
|
||||
FD_ZERO(&fds);
|
||||
FD_SET(socketDescriptor, &fds);
|
||||
|
||||
/* Wait max of 10 seconds for recv to become available */
|
||||
select(socketDescriptor+1, &fds, NULL, NULL, &tv);
|
||||
|
||||
int bytesReceived = 0;
|
||||
|
||||
/* Is there a limit on how much we can receive? Some site said 1024 bytes, which will be well short of a file */
|
||||
if (FD_ISSET(socketDescriptor, &fds))
|
||||
{
|
||||
bytesReceived = recv(socketDescriptor, buffer, len, 0);
|
||||
}
|
||||
|
||||
if (bytesReceived == 0 || bytesReceived == -1)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (bytesReceived < len)
|
||||
{
|
||||
return bytesReceived + NonBlockingRecv(socketDescriptor, buffer+bytesReceived, len-bytesReceived);
|
||||
}
|
||||
|
||||
return bytesReceived;
|
||||
}
|
||||
|
||||
int FetcherThread::NonBlockingSend( int socketDescriptor, char *buffer, int len )
|
||||
{
|
||||
fd_set fds;
|
||||
struct timeval tv;
|
||||
|
||||
tv.tv_sec = 10;
|
||||
tv.tv_usec = 0;
|
||||
|
||||
FD_ZERO(&fds);
|
||||
FD_SET(socketDescriptor, &fds);
|
||||
|
||||
select(socketDescriptor+1, NULL, &fds, NULL, &tv);
|
||||
|
||||
int sentBytes = 0;
|
||||
|
||||
if (FD_ISSET(socketDescriptor, &fds))
|
||||
{
|
||||
sentBytes = send(socketDescriptor, buffer, len, 0);
|
||||
}
|
||||
|
||||
if (sentBytes == 0 || sentBytes == -1)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (sentBytes < len)
|
||||
{
|
||||
return sentBytes + NonBlockingSend(socketDescriptor, buffer+sentBytes, len-sentBytes);
|
||||
}
|
||||
|
||||
return sentBytes;
|
||||
}
|
||||
|
||||
void FetcherThread::HandleUpdateStatus( UpdateStatus status, short version[4] )
|
||||
{
|
||||
switch (status)
|
||||
{
|
||||
case Update_Unknown:
|
||||
case Update_Current:
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
case Update_NewBuild:
|
||||
{
|
||||
g_Logger.LogMessage("SourceMod Update: A new SVN build is available from sourcemod.net");
|
||||
g_Logger.LogMessage("Current Version: %i.%i.%i.%i Available: %i.%i.%i.%i", version[0], version[1], version[2], version[3], version[0], version[1], version[2], version[3]);
|
||||
break;
|
||||
}
|
||||
|
||||
case Update_MinorAvailable:
|
||||
{
|
||||
g_Logger.LogMessage("SourceMod Update: An incremental minor release of SourceMod is now available from sourcemod.net");
|
||||
g_Logger.LogMessage("Current Version: %i.%i.%i Available: %i.%i.%i", version[0], version[1], version[2], version[0], version[1], version[2]);
|
||||
break;
|
||||
}
|
||||
|
||||
case Update_MajorAvailable:
|
||||
{
|
||||
g_Logger.LogMessage("SourceMod Update: An major release of SourceMod is now available from sourcemod.net");
|
||||
g_Logger.LogMessage("Current Version: %i.%i.%i Available: %i.%i.%i", version[0], version[1], version[2], version[0], version[1], version[2]);
|
||||
break;
|
||||
}
|
||||
|
||||
case Update_CriticalAvailable:
|
||||
{
|
||||
g_Logger.LogError("SourceMod Update: A new critical release of SourceMod is now available from sourcemod.net. It is strongly recommended that you update");
|
||||
g_Logger.LogMessage("Current Version: %i.%i.%i.%i Available: %i.%i.%i.%i", version[0], version[1], version[2], version[3], version[0], version[1], version[2], version[3]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool g_blockGameDataLoad = false;
|
||||
|
||||
class InitFetch : public SMGlobalClass
|
||||
{
|
||||
public:
|
||||
void OnSourceModAllInitialized_Post()
|
||||
{
|
||||
char lock_path[PLATFORM_MAX_PATH];
|
||||
g_SourceMod.BuildPath(Path_SM, lock_path, sizeof(lock_path), "data/temp/gamedata.lock");
|
||||
|
||||
if (g_LibSys.IsPathFile(lock_path) && g_LibSys.PathExists(lock_path))
|
||||
{
|
||||
g_Logger.LogError("sourcemod/data/temp/gamedata.lock file detected. This is most likely due to a crash during GameData updating - Blocking GameData loading");
|
||||
g_Logger.LogError("If this error persists delete the file manually");
|
||||
g_blockGameDataLoad = true;
|
||||
}
|
||||
|
||||
ThreadParams fetchThreadParams = ThreadParams();
|
||||
fetchThreadParams.prio = ThreadPrio_Low;
|
||||
g_pThreader->MakeThread(&g_FetchThread, &fetchThreadParams);
|
||||
}
|
||||
|
||||
ConfigResult OnSourceModConfigChanged(const char *key,
|
||||
const char *value,
|
||||
ConfigSource source,
|
||||
char *error,
|
||||
size_t maxlength)
|
||||
{
|
||||
if (strcmp(key, "DisableAutoUpdate") == 0)
|
||||
{
|
||||
if (strcmp(value, "yes") == 0)
|
||||
{
|
||||
g_disableGameDataUpdate = true;
|
||||
return ConfigResult_Accept;
|
||||
}
|
||||
else if (strcmp(value, "no") == 0)
|
||||
{
|
||||
g_disableGameDataUpdate = false;
|
||||
return ConfigResult_Accept;
|
||||
}
|
||||
|
||||
return ConfigResult_Reject;
|
||||
}
|
||||
|
||||
if (strcmp(key, "ForceRestartAfterUpdate") == 0)
|
||||
{
|
||||
if (strcmp(value, "yes") == 0)
|
||||
{
|
||||
g_restartAfterUpdate = true;
|
||||
return ConfigResult_Accept;
|
||||
}
|
||||
else if (strcmp(value, "no") == 0)
|
||||
{
|
||||
g_restartAfterUpdate = false;
|
||||
return ConfigResult_Accept;
|
||||
}
|
||||
|
||||
return ConfigResult_Reject;
|
||||
}
|
||||
|
||||
return ConfigResult_Ignore;
|
||||
}
|
||||
} g_InitFetch;
|
||||
|
||||
CON_COMMAND(sm_gamedata_md5, "Checks the MD5 sum for a given gamedata file")
|
||||
{
|
||||
#if !defined ORANGEBOX_BUILD
|
||||
CCommand args;
|
||||
#endif
|
||||
|
||||
if (args.ArgC() < 2)
|
||||
{
|
||||
g_SMAPI->ConPrint("Usage: sm_gamedata_md5 <file>\n");
|
||||
return;
|
||||
}
|
||||
|
||||
const char *file = args.Arg(1);
|
||||
if (!file || file[0] == '\0')
|
||||
{
|
||||
g_SMAPI->ConPrint("Usage: sm_gamedata_md5 <file>\n");
|
||||
return;
|
||||
}
|
||||
|
||||
SourceHook::CVector<FileData *>::iterator iter = g_FetchThread.filenames.begin();
|
||||
|
||||
FileData *curData;
|
||||
|
||||
while (iter != g_FetchThread.filenames.end())
|
||||
{
|
||||
curData = (*iter);
|
||||
|
||||
char fileName[30];
|
||||
|
||||
g_LibSys.GetFileFromPath(fileName, sizeof(fileName), curData->filename->c_str());
|
||||
|
||||
if (strcmpi(fileName, file) == 0)
|
||||
{
|
||||
g_SMAPI->ConPrintf("MD5 Sum: %s\n", curData->checksum);
|
||||
return;
|
||||
}
|
||||
|
||||
iter++;
|
||||
}
|
||||
|
||||
g_SMAPI->ConPrint("File not found!\n");
|
||||
}
|
||||
@@ -0,0 +1,185 @@
|
||||
/**
|
||||
* vim: set ts=4 :
|
||||
* =============================================================================
|
||||
* SourceMod
|
||||
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
|
||||
* =============================================================================
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, version 3.0, as published by the
|
||||
* Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* As a special exception, AlliedModders LLC gives you permission to link the
|
||||
* code of this program (as well as its derivative works) to "Half-Life 2," the
|
||||
* "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
|
||||
* by the Valve Corporation. You must obey the GNU General Public License in
|
||||
* all respects for all other code used. Additionally, AlliedModders LLC grants
|
||||
* this exception to all derivative works. AlliedModders LLC defines further
|
||||
* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
|
||||
* or <http://www.sourcemod.net/license.php>.
|
||||
*
|
||||
* Version: $Id$
|
||||
*/
|
||||
|
||||
#ifndef _INCLUDE_SOURCEMOD_GAMEDATAFETCHER_H_
|
||||
#define _INCLUDE_SOURCEMOD_GAMEDATAFETCHER_H_
|
||||
|
||||
#include "sourcemod.h"
|
||||
#include "TextParsers.h"
|
||||
#include "IThreader.h"
|
||||
#include "Logger.h"
|
||||
#include "LibrarySys.h"
|
||||
#include "ThreadSupport.h"
|
||||
#include "md5.h"
|
||||
#include "sm_memtable.h"
|
||||
|
||||
enum UpdateStatus
|
||||
{
|
||||
Update_Unknown = 0, /* Version wasn't recognised or version querying is unsupported */
|
||||
Update_Current = 1, /* Server is running latest version */
|
||||
Update_NewBuild = 2, /* Server is on a svn release and a newer version is available */
|
||||
Update_MinorAvailable = 3, /* Server is on a release and a minor release has superceeded it */
|
||||
Update_MajorAvailable = 4, /* Server is on a release and a major release has superceeded it */
|
||||
Update_CriticalAvailable = 5, /* A critical update has been released (security fixes etc) */
|
||||
};
|
||||
|
||||
class BuildMD5ableBuffer : public ITextListener_SMC
|
||||
{
|
||||
public:
|
||||
|
||||
BuildMD5ableBuffer()
|
||||
{
|
||||
stringTable = new BaseStringTable(2048);
|
||||
md5[0] = 0;
|
||||
md5String[0] = 0;
|
||||
}
|
||||
|
||||
~BuildMD5ableBuffer()
|
||||
{
|
||||
delete stringTable;
|
||||
}
|
||||
|
||||
void ReadSMC_ParseStart()
|
||||
{
|
||||
checksum = MD5();
|
||||
}
|
||||
|
||||
SMCResult ReadSMC_KeyValue(const SMCStates *states, const char *key, const char *value)
|
||||
{
|
||||
stringTable->AddString(key);
|
||||
stringTable->AddString(value);
|
||||
|
||||
return SMCResult_Continue;
|
||||
}
|
||||
|
||||
SMCResult ReadSMC_NewSection(const SMCStates *states, const char *name)
|
||||
{
|
||||
stringTable->AddString(name);
|
||||
|
||||
return SMCResult_Continue;
|
||||
}
|
||||
|
||||
void ReadSMC_ParseEnd(bool halted, bool failed)
|
||||
{
|
||||
if (halted || failed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
void *data = stringTable->GetMemTable()->GetAddress(0);
|
||||
|
||||
if (data != NULL)
|
||||
{
|
||||
checksum.update((unsigned char *)data, stringTable->GetMemTable()->GetActualMemUsed());
|
||||
}
|
||||
|
||||
checksum.finalize();
|
||||
|
||||
checksum.hex_digest(md5String);
|
||||
checksum.raw_digest(md5);
|
||||
|
||||
stringTable->Reset();
|
||||
}
|
||||
|
||||
unsigned char * GetMD5()
|
||||
{
|
||||
return md5;
|
||||
}
|
||||
|
||||
unsigned char * GetMD5String()
|
||||
{
|
||||
return (unsigned char *)&md5String[0];
|
||||
}
|
||||
|
||||
private:
|
||||
MD5 checksum;
|
||||
unsigned char md5[16];
|
||||
char md5String[33];
|
||||
BaseStringTable *stringTable;
|
||||
};
|
||||
|
||||
struct FileData
|
||||
{
|
||||
SourceHook::String *filename;
|
||||
unsigned char checksum[33];
|
||||
};
|
||||
|
||||
|
||||
class FetcherThread : public IThread
|
||||
{
|
||||
public:
|
||||
FetcherThread()
|
||||
{
|
||||
//filenames = new BaseStringTable(200);
|
||||
memtable = new BaseMemTable(4096);
|
||||
}
|
||||
|
||||
~FetcherThread()
|
||||
{
|
||||
//delete filenames;
|
||||
SourceHook::CVector<FileData *>::iterator iter = filenames.begin();
|
||||
|
||||
FileData *curData;
|
||||
|
||||
while (iter != filenames.end())
|
||||
{
|
||||
curData = (*iter);
|
||||
delete curData->filename;
|
||||
delete curData;
|
||||
iter = filenames.erase(iter);
|
||||
}
|
||||
}
|
||||
|
||||
void RunThread(IThreadHandle *pHandle);
|
||||
void OnTerminate(IThreadHandle *pHandle, bool cancel);
|
||||
|
||||
private:
|
||||
int BuildGameDataQuery(char *buffer, int maxlen);
|
||||
void ProcessGameDataQuery(int SocketDescriptor);
|
||||
|
||||
/* These names are a lie. It's really NoMoreBlockingThan10Seconds */
|
||||
int NonBlockingRecv(int socketDescriptor, char *buffer, int len);
|
||||
int NonBlockingSend(int socketDescriptor, char *buffer, int len);
|
||||
|
||||
int ConnectSocket();
|
||||
|
||||
void HandleUpdateStatus(UpdateStatus status, short version[4]);
|
||||
|
||||
public:
|
||||
SourceHook::CVector<FileData *> filenames;
|
||||
private:
|
||||
BaseMemTable *memtable;
|
||||
};
|
||||
|
||||
extern BuildMD5ableBuffer g_MD5Builder;
|
||||
extern bool g_blockGameDataLoad;
|
||||
|
||||
#endif // _INCLUDE_SOURCEMOD_GAMEDATAFETCHER_H_
|
||||
@@ -0,0 +1,541 @@
|
||||
/**
|
||||
* vim: set ts=4 :
|
||||
* =============================================================================
|
||||
* SourceMod
|
||||
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
|
||||
* =============================================================================
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, version 3.0, as published by the
|
||||
* Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* As a special exception, AlliedModders LLC gives you permission to link the
|
||||
* code of this program (as well as its derivative works) to "Half-Life 2," the
|
||||
* "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
|
||||
* by the Valve Corporation. You must obey the GNU General Public License in
|
||||
* all respects for all other code used. Additionally, AlliedModders LLC grants
|
||||
* this exception to all derivative works. AlliedModders LLC defines further
|
||||
* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
|
||||
* or <http://www.sourcemod.net/license.php>.
|
||||
*
|
||||
* Version: $Id$
|
||||
*/
|
||||
|
||||
#include "HalfLife2.h"
|
||||
#include "sourcemod.h"
|
||||
#include "sourcemm_api.h"
|
||||
#include "UserMessages.h"
|
||||
#include "PlayerManager.h"
|
||||
#include "sm_stringutil.h"
|
||||
#include "GameConfigs.h"
|
||||
#include <compat_wrappers.h>
|
||||
|
||||
CHalfLife2 g_HL2;
|
||||
ConVar *sv_lan = NULL;
|
||||
|
||||
namespace SourceHook
|
||||
{
|
||||
template<>
|
||||
int HashFunction<datamap_t *>(datamap_t * const &k)
|
||||
{
|
||||
return reinterpret_cast<int>(k);
|
||||
}
|
||||
|
||||
template<>
|
||||
int Compare<datamap_t *>(datamap_t * const &k1, datamap_t * const &k2)
|
||||
{
|
||||
return (k1-k2);
|
||||
}
|
||||
}
|
||||
|
||||
CHalfLife2::CHalfLife2()
|
||||
{
|
||||
m_pClasses = sm_trie_create();
|
||||
}
|
||||
|
||||
CHalfLife2::~CHalfLife2()
|
||||
{
|
||||
sm_trie_destroy(m_pClasses);
|
||||
|
||||
List<DataTableInfo *>::iterator iter;
|
||||
DataTableInfo *pInfo;
|
||||
for (iter=m_Tables.begin(); iter!=m_Tables.end(); iter++)
|
||||
{
|
||||
pInfo = (*iter);
|
||||
delete pInfo;
|
||||
}
|
||||
|
||||
m_Tables.clear();
|
||||
|
||||
THash<datamap_t *, DataMapTrie>::iterator h_iter;
|
||||
for (h_iter=m_Maps.begin(); h_iter!=m_Maps.end(); h_iter++)
|
||||
{
|
||||
if (h_iter->val.trie)
|
||||
{
|
||||
sm_trie_destroy(h_iter->val.trie);
|
||||
h_iter->val.trie = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
m_Maps.clear();
|
||||
}
|
||||
|
||||
CSharedEdictChangeInfo *g_pSharedChangeInfo = NULL;
|
||||
bool is_original_engine = false;
|
||||
|
||||
void CHalfLife2::OnSourceModStartup(bool late)
|
||||
{
|
||||
/* The Ship currently is the only known game to use an older version of the engine */
|
||||
#if defined METAMOD_PLAPI_VERSION
|
||||
if (g_SMAPI->GetSourceEngineBuild() == SOURCE_ENGINE_ORIGINAL)
|
||||
#else
|
||||
if (strcasecmp(g_SourceMod.GetGameFolderName(), "ship") == 0)
|
||||
#endif
|
||||
{
|
||||
is_original_engine = true;
|
||||
}
|
||||
else if (g_pSharedChangeInfo == NULL)
|
||||
{
|
||||
g_pSharedChangeInfo = engine->GetSharedEdictChangeInfo();
|
||||
}
|
||||
}
|
||||
|
||||
void CHalfLife2::OnSourceModAllInitialized()
|
||||
{
|
||||
m_MsgTextMsg = g_UserMsgs.GetMessageIndex("TextMsg");
|
||||
m_HinTextMsg = g_UserMsgs.GetMessageIndex("HintText");
|
||||
m_VGUIMenu = g_UserMsgs.GetMessageIndex("VGUIMenu");
|
||||
g_ShareSys.AddInterface(NULL, this);
|
||||
}
|
||||
|
||||
#if !defined METAMOD_PLAPI_VERSION
|
||||
bool CHalfLife2::IsOriginalEngine()
|
||||
{
|
||||
return is_original_engine;
|
||||
}
|
||||
#endif
|
||||
|
||||
IChangeInfoAccessor *CBaseEdict::GetChangeAccessor()
|
||||
{
|
||||
return engine->GetChangeAccessor( (const edict_t *)this );
|
||||
}
|
||||
|
||||
bool UTIL_FindInSendTable(SendTable *pTable,
|
||||
const char *name,
|
||||
sm_sendprop_info_t *info,
|
||||
unsigned int offset)
|
||||
{
|
||||
const char *pname;
|
||||
int props = pTable->GetNumProps();
|
||||
SendProp *prop;
|
||||
|
||||
for (int i=0; i<props; i++)
|
||||
{
|
||||
prop = pTable->GetProp(i);
|
||||
pname = prop->GetName();
|
||||
if (pname && strcmp(name, pname) == 0)
|
||||
{
|
||||
info->prop = prop;
|
||||
info->actual_offset = offset + info->prop->GetOffset();
|
||||
return true;
|
||||
}
|
||||
if (prop->GetDataTable())
|
||||
{
|
||||
if (UTIL_FindInSendTable(prop->GetDataTable(),
|
||||
name,
|
||||
info,
|
||||
offset + prop->GetOffset())
|
||||
)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
typedescription_t *UTIL_FindInDataMap(datamap_t *pMap, const char *name)
|
||||
{
|
||||
while (pMap)
|
||||
{
|
||||
for (int i=0; i<pMap->dataNumFields; i++)
|
||||
{
|
||||
if (pMap->dataDesc[i].fieldName == NULL)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (strcmp(name, pMap->dataDesc[i].fieldName) == 0)
|
||||
{
|
||||
return &(pMap->dataDesc[i]);
|
||||
}
|
||||
if (pMap->dataDesc[i].td)
|
||||
{
|
||||
typedescription_t *_td;
|
||||
if ((_td=UTIL_FindInDataMap(pMap->dataDesc[i].td, name)) != NULL)
|
||||
{
|
||||
return _td;
|
||||
}
|
||||
}
|
||||
}
|
||||
pMap = pMap->baseMap;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ServerClass *CHalfLife2::FindServerClass(const char *classname)
|
||||
{
|
||||
DataTableInfo *pInfo = _FindServerClass(classname);
|
||||
|
||||
if (!pInfo)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return pInfo->sc;
|
||||
}
|
||||
|
||||
DataTableInfo *CHalfLife2::_FindServerClass(const char *classname)
|
||||
{
|
||||
DataTableInfo *pInfo = NULL;
|
||||
|
||||
if (!sm_trie_retrieve(m_pClasses, classname, (void **)&pInfo))
|
||||
{
|
||||
ServerClass *sc = gamedll->GetAllServerClasses();
|
||||
while (sc)
|
||||
{
|
||||
if (strcmp(classname, sc->GetName()) == 0)
|
||||
{
|
||||
pInfo = new DataTableInfo;
|
||||
pInfo->sc = sc;
|
||||
sm_trie_insert(m_pClasses, classname, pInfo);
|
||||
m_Tables.push_back(pInfo);
|
||||
break;
|
||||
}
|
||||
sc = sc->m_pNext;
|
||||
}
|
||||
if (!pInfo)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return pInfo;
|
||||
}
|
||||
|
||||
bool CHalfLife2::FindSendPropInfo(const char *classname, const char *offset, sm_sendprop_info_t *info)
|
||||
{
|
||||
DataTableInfo *pInfo;
|
||||
sm_sendprop_info_t *prop;
|
||||
|
||||
if ((pInfo = _FindServerClass(classname)) == NULL)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((prop = pInfo->lookup.retrieve(offset)) == NULL)
|
||||
{
|
||||
sm_sendprop_info_t temp_info;
|
||||
|
||||
if (!UTIL_FindInSendTable(pInfo->sc->m_pTable, offset, &temp_info, 0))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
pInfo->lookup.insert(offset, temp_info);
|
||||
*info = temp_info;
|
||||
}
|
||||
else
|
||||
{
|
||||
*info = *prop;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
SendProp *CHalfLife2::FindInSendTable(const char *classname, const char *offset)
|
||||
{
|
||||
sm_sendprop_info_t info;
|
||||
|
||||
if (!FindSendPropInfo(classname, offset, &info))
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return info.prop;
|
||||
}
|
||||
|
||||
typedescription_t *CHalfLife2::FindInDataMap(datamap_t *pMap, const char *offset)
|
||||
{
|
||||
typedescription_t *td = NULL;
|
||||
DataMapTrie &val = m_Maps[pMap];
|
||||
|
||||
if (!val.trie)
|
||||
{
|
||||
val.trie = sm_trie_create();
|
||||
}
|
||||
if (!sm_trie_retrieve(val.trie, offset, (void **)&td))
|
||||
{
|
||||
if ((td = UTIL_FindInDataMap(pMap, offset)) != NULL)
|
||||
{
|
||||
sm_trie_insert(val.trie, offset, td);
|
||||
}
|
||||
}
|
||||
|
||||
return td;
|
||||
}
|
||||
|
||||
void CHalfLife2::SetEdictStateChanged(edict_t *pEdict, unsigned short offset)
|
||||
{
|
||||
if (g_pSharedChangeInfo != NULL)
|
||||
{
|
||||
if (offset)
|
||||
{
|
||||
pEdict->StateChanged(offset);
|
||||
}
|
||||
else
|
||||
{
|
||||
pEdict->StateChanged();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
pEdict->m_fStateFlags |= FL_EDICT_CHANGED;
|
||||
}
|
||||
}
|
||||
|
||||
bool CHalfLife2::TextMsg(int client, int dest, const char *msg)
|
||||
{
|
||||
bf_write *pBitBuf = NULL;
|
||||
cell_t players[] = {client};
|
||||
|
||||
if ((pBitBuf = g_UserMsgs.StartMessage(m_MsgTextMsg, players, 1, USERMSG_RELIABLE)) == NULL)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
pBitBuf->WriteByte(dest);
|
||||
pBitBuf->WriteString(msg);
|
||||
g_UserMsgs.EndMessage();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CHalfLife2::HintTextMsg(int client, const char *msg)
|
||||
{
|
||||
bf_write *pBitBuf = NULL;
|
||||
cell_t players[] = {client};
|
||||
|
||||
if ((pBitBuf = g_UserMsgs.StartMessage(m_HinTextMsg, players, 1, USERMSG_RELIABLE)) == NULL)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const char *pre_byte = g_pGameConf->GetKeyValue("HintTextPreByte");
|
||||
if (pre_byte != NULL && strcmp(pre_byte, "yes") == 0)
|
||||
{
|
||||
pBitBuf->WriteByte(1);
|
||||
}
|
||||
pBitBuf->WriteString(msg);
|
||||
g_UserMsgs.EndMessage();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CHalfLife2::ShowVGUIMenu(int client, const char *name, KeyValues *data, bool show)
|
||||
{
|
||||
bf_write *pBitBuf = NULL;
|
||||
KeyValues *SubKey = NULL;
|
||||
int count = 0;
|
||||
cell_t players[] = {client};
|
||||
|
||||
if ((pBitBuf = g_UserMsgs.StartMessage(m_VGUIMenu, players, 1, USERMSG_RELIABLE)) == NULL)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (data)
|
||||
{
|
||||
SubKey = data->GetFirstSubKey();
|
||||
while (SubKey)
|
||||
{
|
||||
count++;
|
||||
SubKey = SubKey->GetNextKey();
|
||||
}
|
||||
SubKey = data->GetFirstSubKey();
|
||||
}
|
||||
|
||||
pBitBuf->WriteString(name);
|
||||
pBitBuf->WriteByte((show) ? 1 : 0);
|
||||
pBitBuf->WriteByte(count);
|
||||
while (SubKey)
|
||||
{
|
||||
pBitBuf->WriteString(SubKey->GetName());
|
||||
pBitBuf->WriteString(SubKey->GetString());
|
||||
SubKey = SubKey->GetNextKey();
|
||||
}
|
||||
g_UserMsgs.EndMessage();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CHalfLife2::AddToFakeCliCmdQueue(int client, int userid, const char *cmd)
|
||||
{
|
||||
DelayedFakeCliCmd *pFake;
|
||||
|
||||
if (m_FreeCmds.empty())
|
||||
{
|
||||
pFake = new DelayedFakeCliCmd;
|
||||
} else {
|
||||
pFake = m_FreeCmds.front();
|
||||
m_FreeCmds.pop();
|
||||
}
|
||||
|
||||
pFake->client = client;
|
||||
pFake->userid = userid;
|
||||
pFake->cmd.assign(cmd);
|
||||
|
||||
m_CmdQueue.push(pFake);
|
||||
}
|
||||
|
||||
void CHalfLife2::ProcessFakeCliCmdQueue()
|
||||
{
|
||||
while (!m_CmdQueue.empty())
|
||||
{
|
||||
DelayedFakeCliCmd *pFake = m_CmdQueue.first();
|
||||
|
||||
if (g_Players.GetClientOfUserId(pFake->userid) == pFake->client)
|
||||
{
|
||||
CPlayer *pPlayer = g_Players.GetPlayerByIndex(pFake->client);
|
||||
serverpluginhelpers->ClientCommand(pPlayer->GetEdict(), pFake->cmd.c_str());
|
||||
}
|
||||
|
||||
m_CmdQueue.pop();
|
||||
}
|
||||
}
|
||||
|
||||
bool CHalfLife2::IsLANServer()
|
||||
{
|
||||
sv_lan = icvar->FindVar("sv_lan");
|
||||
|
||||
if (!sv_lan)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return (sv_lan->GetInt() != 0);
|
||||
}
|
||||
|
||||
bool CHalfLife2::KVLoadFromFile(KeyValues *kv, IBaseFileSystem *filesystem, const char *resourceName, const char *pathID)
|
||||
{
|
||||
#if defined METAMOD_PLAPI_VERSION
|
||||
if (g_SMAPI->GetSourceEngineBuild() == SOURCE_ENGINE_ORIGINAL)
|
||||
#else
|
||||
if (strcasecmp(g_SourceMod.GetGameFolderName(), "ship") == 0)
|
||||
#endif
|
||||
{
|
||||
Assert(filesystem);
|
||||
#ifdef _MSC_VER
|
||||
Assert(_heapchk() == _HEAPOK);
|
||||
#endif
|
||||
|
||||
FileHandle_t f = filesystem->Open(resourceName, "rb", pathID);
|
||||
if (!f)
|
||||
return false;
|
||||
|
||||
// load file into a null-terminated buffer
|
||||
int fileSize = filesystem->Size(f);
|
||||
char *buffer = (char *)MemAllocScratch(fileSize + 1);
|
||||
|
||||
Assert(buffer);
|
||||
|
||||
filesystem->Read(buffer, fileSize, f); // read into local buffer
|
||||
|
||||
buffer[fileSize] = 0; // null terminate file as EOF
|
||||
|
||||
filesystem->Close( f ); // close file after reading
|
||||
|
||||
bool retOK = kv->LoadFromBuffer( resourceName, buffer, filesystem );
|
||||
|
||||
MemFreeScratch();
|
||||
|
||||
return retOK;
|
||||
}
|
||||
else
|
||||
{
|
||||
return kv->LoadFromFile(filesystem, resourceName, pathID);
|
||||
}
|
||||
}
|
||||
|
||||
void CHalfLife2::PushCommandStack(const CCommand *cmd)
|
||||
{
|
||||
CachedCommandInfo info;
|
||||
|
||||
info.args = cmd;
|
||||
#if !defined ORANGEBOX_BUILD
|
||||
strncopy(info.cmd, cmd->Arg(0), sizeof(info.cmd));
|
||||
#endif
|
||||
|
||||
m_CommandStack.push(info);
|
||||
}
|
||||
|
||||
const CCommand *CHalfLife2::PeekCommandStack()
|
||||
{
|
||||
if (m_CommandStack.empty())
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return m_CommandStack.front().args;
|
||||
}
|
||||
|
||||
void CHalfLife2::PopCommandStack()
|
||||
{
|
||||
m_CommandStack.pop();
|
||||
}
|
||||
|
||||
const char *CHalfLife2::CurrentCommandName()
|
||||
{
|
||||
#if defined ORANGEBOX_BUILD
|
||||
return m_CommandStack.front().args->Arg(0);
|
||||
#else
|
||||
return m_CommandStack.front().cmd;
|
||||
#endif
|
||||
}
|
||||
|
||||
void CHalfLife2::AddDelayedKick(int client, int userid, const char *msg)
|
||||
{
|
||||
DelayedKickInfo kick;
|
||||
|
||||
kick.client = client;
|
||||
kick.userid = userid;
|
||||
UTIL_Format(kick.buffer, sizeof(kick.buffer), "%s", msg);
|
||||
|
||||
m_DelayedKicks.push(kick);
|
||||
}
|
||||
|
||||
void CHalfLife2::ProcessDelayedKicks()
|
||||
{
|
||||
while (!m_DelayedKicks.empty())
|
||||
{
|
||||
DelayedKickInfo info = m_DelayedKicks.first();
|
||||
m_DelayedKicks.pop();
|
||||
|
||||
CPlayer *player = g_Players.GetPlayerByIndex(info.client);
|
||||
if (player == NULL || player->GetUserId() != info.userid)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
player->Kick(info.buffer);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,142 @@
|
||||
/**
|
||||
* vim: set ts=4 :
|
||||
* =============================================================================
|
||||
* SourceMod
|
||||
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
|
||||
* =============================================================================
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, version 3.0, as published by the
|
||||
* Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* As a special exception, AlliedModders LLC gives you permission to link the
|
||||
* code of this program (as well as its derivative works) to "Half-Life 2," the
|
||||
* "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
|
||||
* by the Valve Corporation. You must obey the GNU General Public License in
|
||||
* all respects for all other code used. Additionally, AlliedModders LLC grants
|
||||
* this exception to all derivative works. AlliedModders LLC defines further
|
||||
* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
|
||||
* or <http://www.sourcemod.net/license.php>.
|
||||
*
|
||||
* Version: $Id$
|
||||
*/
|
||||
|
||||
#ifndef _INCLUDE_SOURCEMOD_CHALFLIFE2_H_
|
||||
#define _INCLUDE_SOURCEMOD_CHALFLIFE2_H_
|
||||
|
||||
#include <sh_list.h>
|
||||
#include <sh_string.h>
|
||||
#include <sh_tinyhash.h>
|
||||
#include <sm_trie_tpl.h>
|
||||
#include "sm_trie.h"
|
||||
#include "sm_globals.h"
|
||||
#include "sm_queue.h"
|
||||
#include <IGameHelpers.h>
|
||||
#include <KeyValues.h>
|
||||
#include <server_class.h>
|
||||
#include <datamap.h>
|
||||
|
||||
class CCommand;
|
||||
|
||||
using namespace SourceHook;
|
||||
using namespace SourceMod;
|
||||
|
||||
#define HUD_PRINTTALK 3
|
||||
#define HUD_PRINTCENTER 4
|
||||
|
||||
struct DataTableInfo
|
||||
{
|
||||
ServerClass *sc;
|
||||
KTrie<sm_sendprop_info_t> lookup;
|
||||
};
|
||||
|
||||
struct DataMapTrie
|
||||
{
|
||||
DataMapTrie() : trie(NULL) {}
|
||||
Trie *trie;
|
||||
};
|
||||
|
||||
struct DelayedFakeCliCmd
|
||||
{
|
||||
String cmd;
|
||||
int client;
|
||||
int userid;
|
||||
};
|
||||
|
||||
struct CachedCommandInfo
|
||||
{
|
||||
const CCommand *args;
|
||||
#if !defined ORANGEBOX_BUILD
|
||||
char cmd[300];
|
||||
#endif
|
||||
};
|
||||
|
||||
struct DelayedKickInfo
|
||||
{
|
||||
int userid;
|
||||
int client;
|
||||
char buffer[384];
|
||||
};
|
||||
|
||||
class CHalfLife2 :
|
||||
public SMGlobalClass,
|
||||
public IGameHelpers
|
||||
{
|
||||
public:
|
||||
CHalfLife2();
|
||||
~CHalfLife2();
|
||||
public:
|
||||
void OnSourceModStartup(bool late);
|
||||
void OnSourceModAllInitialized();
|
||||
/*void OnSourceModAllShutdown();*/
|
||||
public: //IGameHelpers
|
||||
SendProp *FindInSendTable(const char *classname, const char *offset);
|
||||
bool FindSendPropInfo(const char *classname, const char *offset, sm_sendprop_info_t *info);
|
||||
datamap_t *GetDataMap(CBaseEntity *pEntity);
|
||||
ServerClass *FindServerClass(const char *classname);
|
||||
typedescription_t *FindInDataMap(datamap_t *pMap, const char *offset);
|
||||
void SetEdictStateChanged(edict_t *pEdict, unsigned short offset);
|
||||
bool TextMsg(int client, int dest, const char *msg);
|
||||
bool HintTextMsg(int client, const char *msg);
|
||||
bool ShowVGUIMenu(int client, const char *name, KeyValues *data, bool show);
|
||||
bool IsLANServer();
|
||||
bool KVLoadFromFile(KeyValues *kv, IBaseFileSystem *filesystem, const char *resourceName, const char *pathID = NULL);
|
||||
public:
|
||||
void AddToFakeCliCmdQueue(int client, int userid, const char *cmd);
|
||||
void ProcessFakeCliCmdQueue();
|
||||
public:
|
||||
void PushCommandStack(const CCommand *cmd);
|
||||
void PopCommandStack();
|
||||
const CCommand *PeekCommandStack();
|
||||
const char *CurrentCommandName();
|
||||
void AddDelayedKick(int client, int userid, const char *msg);
|
||||
void ProcessDelayedKicks();
|
||||
#if !defined METAMOD_PLAPI_VERSION
|
||||
bool IsOriginalEngine();
|
||||
#endif
|
||||
private:
|
||||
DataTableInfo *_FindServerClass(const char *classname);
|
||||
private:
|
||||
Trie *m_pClasses;
|
||||
List<DataTableInfo *> m_Tables;
|
||||
THash<datamap_t *, DataMapTrie> m_Maps;
|
||||
int m_MsgTextMsg;
|
||||
int m_HinTextMsg;
|
||||
int m_VGUIMenu;
|
||||
Queue<DelayedFakeCliCmd *> m_CmdQueue;
|
||||
CStack<DelayedFakeCliCmd *> m_FreeCmds;
|
||||
CStack<CachedCommandInfo> m_CommandStack;
|
||||
Queue<DelayedKickInfo> m_DelayedKicks;
|
||||
};
|
||||
|
||||
extern CHalfLife2 g_HL2;
|
||||
|
||||
#endif //_INCLUDE_SOURCEMOD_CHALFLIFE2_H_
|
||||
+520
@@ -0,0 +1,520 @@
|
||||
/**
|
||||
* vim: set ts=4 :
|
||||
* =============================================================================
|
||||
* SourceMod
|
||||
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
|
||||
* =============================================================================
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, version 3.0, as published by the
|
||||
* Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* As a special exception, AlliedModders LLC gives you permission to link the
|
||||
* code of this program (as well as its derivative works) to "Half-Life 2," the
|
||||
* "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
|
||||
* by the Valve Corporation. You must obey the GNU General Public License in
|
||||
* all respects for all other code used. Additionally, AlliedModders LLC grants
|
||||
* this exception to all derivative works. AlliedModders LLC defines further
|
||||
* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
|
||||
* or <http://www.sourcemod.net/license.php>.
|
||||
*
|
||||
* Version: $Id$
|
||||
*/
|
||||
|
||||
#include <time.h>
|
||||
#include "sourcemod.h"
|
||||
#include "sourcemm_api.h"
|
||||
#include "sm_stringutil.h"
|
||||
#include "Logger.h"
|
||||
#include "systems/LibrarySys.h"
|
||||
#include "TimerSys.h"
|
||||
#include "sm_version.h"
|
||||
|
||||
Logger g_Logger;
|
||||
|
||||
/**
|
||||
* :TODO: This should be creating the log folder if it doesn't exist
|
||||
*/
|
||||
|
||||
ConfigResult Logger::OnSourceModConfigChanged(const char *key,
|
||||
const char *value,
|
||||
ConfigSource source,
|
||||
char *error,
|
||||
size_t maxlength)
|
||||
{
|
||||
if (strcasecmp(key, "Logging") == 0)
|
||||
{
|
||||
bool state;
|
||||
|
||||
if (strcasecmp(value, "on") == 0)
|
||||
{
|
||||
state = true;
|
||||
} else if (strcasecmp(value, "off") == 0) {
|
||||
state = false;
|
||||
} else {
|
||||
UTIL_Format(error, maxlength, "Invalid value: must be \"on\" or \"off\"");
|
||||
return ConfigResult_Reject;
|
||||
}
|
||||
|
||||
if (source == ConfigSource_Console)
|
||||
{
|
||||
state ? EnableLogging() : DisableLogging();
|
||||
} else {
|
||||
m_InitialState = state;
|
||||
}
|
||||
|
||||
return ConfigResult_Accept;
|
||||
} else if (strcasecmp(key, "LogMode") == 0) {
|
||||
if (strcasecmp(value, "daily") == 0)
|
||||
{
|
||||
m_Mode = LoggingMode_Daily;
|
||||
} else if (strcasecmp(value, "map") == 0) {
|
||||
m_Mode = LoggingMode_PerMap;
|
||||
} else if (strcasecmp(value, "game") == 0) {
|
||||
m_Mode = LoggingMode_Game;
|
||||
} else {
|
||||
UTIL_Format(error, maxlength, "Invalid value: must be [daily|map|game]");
|
||||
return ConfigResult_Reject;
|
||||
}
|
||||
|
||||
return ConfigResult_Accept;
|
||||
}
|
||||
|
||||
return ConfigResult_Ignore;
|
||||
}
|
||||
|
||||
void Logger::OnSourceModStartup(bool late)
|
||||
{
|
||||
InitLogger(m_Mode);
|
||||
}
|
||||
|
||||
void Logger::OnSourceModAllShutdown()
|
||||
{
|
||||
CloseLogger();
|
||||
}
|
||||
|
||||
void Logger::OnSourceModLevelChange(const char *mapName)
|
||||
{
|
||||
MapChange(mapName);
|
||||
}
|
||||
|
||||
void Logger::_NewMapFile()
|
||||
{
|
||||
if (!m_Active)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* Append "Log file closed" to previous log file */
|
||||
_CloseFile();
|
||||
|
||||
char _filename[256];
|
||||
int i = 0;
|
||||
|
||||
time_t t;
|
||||
GetAdjustedTime(&t);
|
||||
tm *curtime = localtime(&t);
|
||||
|
||||
while (true)
|
||||
{
|
||||
g_SourceMod.BuildPath(Path_SM, _filename, sizeof(_filename), "logs/L%02d%02d%03d.log", curtime->tm_mon + 1, curtime->tm_mday, i);
|
||||
FILE *fp = fopen(_filename, "r");
|
||||
if (!fp)
|
||||
{
|
||||
break;
|
||||
}
|
||||
fclose(fp);
|
||||
i++;
|
||||
}
|
||||
m_NrmFileName.assign(_filename);
|
||||
|
||||
FILE *fp = fopen(m_NrmFileName.c_str(), "w");
|
||||
if (!fp)
|
||||
{
|
||||
char error[255];
|
||||
g_LibSys.GetPlatformError(error, sizeof(error));
|
||||
LogFatal("[SM] Unexpected fatal logging error (file \"%s\")", m_NrmFileName.c_str());
|
||||
LogFatal("[SM] Platform returned error: \"%s\"", error);
|
||||
LogFatal("[SM] Logging has been disabled.");
|
||||
m_Active = false;
|
||||
return;
|
||||
} else {
|
||||
char date[32];
|
||||
strftime(date, sizeof(date), "%m/%d/%Y - %H:%M:%S", curtime);
|
||||
fprintf(fp, "L %s: SourceMod log file started (file \"L%02d%02d%03d.log\") (Version \"%s\")\n", date, curtime->tm_mon + 1, curtime->tm_mday, i, SVN_FULL_VERSION);
|
||||
fclose(fp);
|
||||
}
|
||||
}
|
||||
|
||||
void Logger::_CloseFile()
|
||||
{
|
||||
if (!m_Active)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
FILE *fp = NULL;
|
||||
if (!m_NrmFileName.empty())
|
||||
{
|
||||
fp = fopen(m_NrmFileName.c_str(), "r+");
|
||||
if (fp)
|
||||
{
|
||||
fseek(fp, 0, SEEK_END);
|
||||
LogMessage("Log file closed.");
|
||||
fclose(fp);
|
||||
}
|
||||
m_NrmFileName.clear();
|
||||
}
|
||||
|
||||
if (!m_ErrMapStart)
|
||||
{
|
||||
return;
|
||||
}
|
||||
fp = fopen(m_ErrFileName.c_str(), "r+");
|
||||
if (fp)
|
||||
{
|
||||
fseek(fp, 0, SEEK_END);
|
||||
LogError("Error log file session closed.");
|
||||
fclose(fp);
|
||||
}
|
||||
m_ErrFileName.clear();
|
||||
}
|
||||
|
||||
void Logger::InitLogger(LoggingMode mode)
|
||||
{
|
||||
m_Mode = mode;
|
||||
m_Active = m_InitialState;
|
||||
|
||||
time_t t;
|
||||
GetAdjustedTime(&t);
|
||||
tm *curtime = localtime(&t);
|
||||
m_CurDay = curtime->tm_mday;
|
||||
|
||||
char _filename[256];
|
||||
g_SourceMod.BuildPath(Path_SM, _filename, sizeof(_filename), "logs/errors_%04d%02d%02d.log", curtime->tm_year + 1900, curtime->tm_mon + 1, curtime->tm_mday);
|
||||
m_ErrFileName.assign(_filename);
|
||||
|
||||
switch (m_Mode)
|
||||
{
|
||||
case LoggingMode_PerMap:
|
||||
{
|
||||
if (!m_Active)
|
||||
{
|
||||
m_DelayedStart = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case LoggingMode_Daily:
|
||||
{
|
||||
g_SourceMod.BuildPath(Path_SM, _filename, sizeof(_filename), "logs/L%04d%02d%02d.log", curtime->tm_year + 1900, curtime->tm_mon + 1, curtime->tm_mday);
|
||||
m_NrmFileName.assign(_filename);
|
||||
m_DailyPrintHdr = true;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
/* do nothing... */
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Logger::CloseLogger()
|
||||
{
|
||||
_CloseFile();
|
||||
}
|
||||
|
||||
void Logger::LogToOpenFile(FILE *fp, const char *msg, ...)
|
||||
{
|
||||
if (!m_Active)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
va_list ap;
|
||||
va_start(ap, msg);
|
||||
LogToOpenFileEx(fp, msg, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void Logger::LogToOpenFileEx(FILE *fp, const char *msg, va_list ap)
|
||||
{
|
||||
if (!m_Active)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
char buffer[3072];
|
||||
UTIL_FormatArgs(buffer, sizeof(buffer), msg, ap);
|
||||
|
||||
char date[32];
|
||||
time_t t;
|
||||
GetAdjustedTime(&t);
|
||||
tm *curtime = localtime(&t);
|
||||
strftime(date, sizeof(date), "%m/%d/%Y - %H:%M:%S", curtime);
|
||||
|
||||
fprintf(fp, "L %s: %s\n", date, buffer);
|
||||
g_SMAPI->ConPrintf("L %s: %s\n", date, buffer);
|
||||
}
|
||||
|
||||
void Logger::LogMessage(const char *vafmt, ...)
|
||||
{
|
||||
if (!m_Active)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_Mode == LoggingMode_Game)
|
||||
{
|
||||
va_list ap;
|
||||
va_start(ap, vafmt);
|
||||
_PrintToGameLog(vafmt, ap);
|
||||
va_end(ap);
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_DelayedStart)
|
||||
{
|
||||
m_DelayedStart = false;
|
||||
_NewMapFile();
|
||||
}
|
||||
|
||||
time_t t;
|
||||
GetAdjustedTime(&t);
|
||||
tm *curtime = localtime(&t);
|
||||
|
||||
FILE *fp = NULL;
|
||||
if (m_Mode == LoggingMode_PerMap)
|
||||
{
|
||||
fp = fopen(m_NrmFileName.c_str(), "a+");
|
||||
if (!fp)
|
||||
{
|
||||
_NewMapFile();
|
||||
fp = fopen(m_NrmFileName.c_str(), "a+");
|
||||
if (!fp)
|
||||
{
|
||||
goto print_error;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (m_CurDay != curtime->tm_mday)
|
||||
{
|
||||
char _filename[256];
|
||||
g_SourceMod.BuildPath(Path_SM, _filename, sizeof(_filename), "logs/L%04d%02d%02d.log", curtime->tm_year + 1900, curtime->tm_mon + 1, curtime->tm_mday);
|
||||
m_NrmFileName.assign(_filename);
|
||||
m_CurDay = curtime->tm_mday;
|
||||
m_DailyPrintHdr = true;
|
||||
}
|
||||
fp = fopen(m_NrmFileName.c_str(), "a+");
|
||||
}
|
||||
|
||||
if (fp)
|
||||
{
|
||||
if (m_DailyPrintHdr)
|
||||
{
|
||||
char date[32];
|
||||
m_DailyPrintHdr = false;
|
||||
strftime(date, sizeof(date), "%m/%d/%Y - %H:%M:%S", curtime);
|
||||
fprintf(fp, "L %s: SourceMod log file session started (file \"L%04d%02d%02d.log\") (Version \"%s\")\n", date, curtime->tm_year + 1900, curtime->tm_mon + 1, curtime->tm_mday, SVN_FULL_VERSION);
|
||||
}
|
||||
va_list ap;
|
||||
va_start(ap, vafmt);
|
||||
LogToOpenFileEx(fp, vafmt, ap);
|
||||
va_end(ap);
|
||||
fclose(fp);
|
||||
} else {
|
||||
goto print_error;
|
||||
}
|
||||
|
||||
return;
|
||||
print_error:
|
||||
char error[255];
|
||||
g_LibSys.GetPlatformError(error, sizeof(error));
|
||||
LogFatal("[SM] Unexpected fatal logging error (file \"%s\")", m_NrmFileName.c_str());
|
||||
LogFatal("[SM] Platform returned error: \"%s\"", error);
|
||||
LogFatal("[SM] Logging has been disabled.");
|
||||
m_Active = false;
|
||||
}
|
||||
|
||||
void Logger::LogError(const char *vafmt, ...)
|
||||
{
|
||||
if (!m_Active)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
time_t t;
|
||||
GetAdjustedTime(&t);
|
||||
tm *curtime = localtime(&t);
|
||||
|
||||
if (curtime->tm_mday != m_CurDay)
|
||||
{
|
||||
char _filename[256];
|
||||
g_SourceMod.BuildPath(Path_SM, _filename, sizeof(_filename), "logs/errors_%04d%02d%02d.log", curtime->tm_year + 1900, curtime->tm_mon + 1, curtime->tm_mday);
|
||||
m_ErrFileName.assign(_filename);
|
||||
m_CurDay = curtime->tm_mday;
|
||||
m_ErrMapStart = false;
|
||||
}
|
||||
|
||||
FILE *fp = fopen(m_ErrFileName.c_str(), "a+");
|
||||
if (fp)
|
||||
{
|
||||
if (!m_ErrMapStart)
|
||||
{
|
||||
char date[32];
|
||||
strftime(date, sizeof(date), "%m/%d/%Y - %H:%M:%S", curtime);
|
||||
fprintf(fp, "L %s: SourceMod error session started\n", date);
|
||||
fprintf(fp, "L %s: Info (map \"%s\") (file \"errors_%04d%02d%02d.log\")\n", date, m_CurMapName.c_str(), curtime->tm_year + 1900, curtime->tm_mon + 1, curtime->tm_mday);
|
||||
m_ErrMapStart = true;
|
||||
}
|
||||
va_list ap;
|
||||
va_start(ap, vafmt);
|
||||
LogToOpenFileEx(fp, vafmt, ap);
|
||||
va_end(ap);
|
||||
fclose(fp);
|
||||
}
|
||||
else
|
||||
{
|
||||
char error[255];
|
||||
g_LibSys.GetPlatformError(error, sizeof(error));
|
||||
LogFatal("[SM] Unexpected fatal logging error (file \"%s\")", m_NrmFileName.c_str());
|
||||
LogFatal("[SM] Platform returned error: \"%s\"", error);
|
||||
LogFatal("[SM] Logging has been disabled.");
|
||||
m_Active = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void Logger::MapChange(const char *mapname)
|
||||
{
|
||||
m_CurMapName.assign(mapname);
|
||||
|
||||
switch (m_Mode)
|
||||
{
|
||||
case LoggingMode_Daily:
|
||||
{
|
||||
LogMessage("-------- Mapchange to %s --------", mapname);
|
||||
break;
|
||||
}
|
||||
case LoggingMode_PerMap:
|
||||
{
|
||||
_NewMapFile();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
/* Do nothing... */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_ErrMapStart)
|
||||
{
|
||||
LogError("Error log file session closed.");
|
||||
}
|
||||
m_ErrMapStart = false;
|
||||
}
|
||||
|
||||
void Logger::_PrintToGameLog(const char *fmt, va_list ap)
|
||||
{
|
||||
char msg[3072];
|
||||
size_t len;
|
||||
|
||||
len = vsnprintf(msg, sizeof(msg)-2, fmt, ap);
|
||||
len = (len >= sizeof(msg)) ? (sizeof(msg) - 2) : len;
|
||||
|
||||
msg[len++] = '\n';
|
||||
msg[len] = '\0';
|
||||
|
||||
Engine_LogPrintWrapper(msg);
|
||||
}
|
||||
|
||||
const char *Logger::GetLogFileName(LogType type) const
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case LogType_Normal:
|
||||
{
|
||||
return m_NrmFileName.c_str();
|
||||
}
|
||||
case LogType_Error:
|
||||
{
|
||||
return m_ErrFileName.c_str();
|
||||
}
|
||||
default:
|
||||
{
|
||||
return "";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LoggingMode Logger::GetLoggingMode() const
|
||||
{
|
||||
return m_Mode;
|
||||
}
|
||||
|
||||
void Logger::EnableLogging()
|
||||
{
|
||||
if (m_Active)
|
||||
{
|
||||
return;
|
||||
}
|
||||
m_Active = true;
|
||||
LogMessage("[SM] Logging enabled manually by user.");
|
||||
}
|
||||
|
||||
void Logger::DisableLogging()
|
||||
{
|
||||
if (!m_Active)
|
||||
{
|
||||
return;
|
||||
}
|
||||
LogMessage("[SM] Logging disabled manually by user.");
|
||||
m_Active = false;
|
||||
}
|
||||
|
||||
void Logger::LogFatal(const char *msg, ...)
|
||||
{
|
||||
/* :TODO: make this print all pretty-like
|
||||
* In fact, the pretty log printing function should be abstracted.
|
||||
* It's already implemented twice which is bad.
|
||||
*/
|
||||
|
||||
va_list ap;
|
||||
char path[PLATFORM_MAX_PATH];
|
||||
|
||||
g_SourceMod.BuildPath(Path_Game, path, sizeof(path), "sourcemod_fatal.log");
|
||||
|
||||
FILE *fp = fopen(path, "at");
|
||||
if (fp)
|
||||
{
|
||||
m_Active = true;
|
||||
va_start(ap, msg);
|
||||
LogToOpenFileEx(fp, msg, ap);
|
||||
va_end(ap);
|
||||
m_Active = false;
|
||||
fclose(fp);
|
||||
}
|
||||
}
|
||||
|
||||
bool g_in_game_log_hook = false;
|
||||
|
||||
void Engine_LogPrintWrapper(const char *msg)
|
||||
{
|
||||
if (g_in_game_log_hook)
|
||||
{
|
||||
ENGINE_CALL(LogPrint)(msg);
|
||||
}
|
||||
else
|
||||
{
|
||||
engine->LogPrint(msg);
|
||||
}
|
||||
}
|
||||
+106
@@ -0,0 +1,106 @@
|
||||
/**
|
||||
* vim: set ts=4 :
|
||||
* =============================================================================
|
||||
* SourceMod
|
||||
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
|
||||
* =============================================================================
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, version 3.0, as published by the
|
||||
* Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* As a special exception, AlliedModders LLC gives you permission to link the
|
||||
* code of this program (as well as its derivative works) to "Half-Life 2," the
|
||||
* "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
|
||||
* by the Valve Corporation. You must obey the GNU General Public License in
|
||||
* all respects for all other code used. Additionally, AlliedModders LLC grants
|
||||
* this exception to all derivative works. AlliedModders LLC defines further
|
||||
* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
|
||||
* or <http://www.sourcemod.net/license.php>.
|
||||
*
|
||||
* Version: $Id$
|
||||
*/
|
||||
|
||||
#ifndef _INCLUDE_SOURCEMOD_CLOGGER_H_
|
||||
#define _INCLUDE_SOURCEMOD_CLOGGER_H_
|
||||
|
||||
#include <stdio.h>
|
||||
#include <sh_string.h>
|
||||
#include "sm_globals.h"
|
||||
|
||||
using namespace SourceHook;
|
||||
|
||||
enum LogType
|
||||
{
|
||||
LogType_Normal,
|
||||
LogType_Error
|
||||
};
|
||||
|
||||
enum LoggingMode
|
||||
{
|
||||
LoggingMode_Daily,
|
||||
LoggingMode_PerMap,
|
||||
LoggingMode_Game
|
||||
};
|
||||
|
||||
class Logger : public SMGlobalClass
|
||||
{
|
||||
public:
|
||||
Logger() : m_Mode(LoggingMode_Daily), m_ErrMapStart(false),
|
||||
m_Active(false), m_DelayedStart(false), m_DailyPrintHdr(false),
|
||||
m_InitialState(true)
|
||||
{
|
||||
}
|
||||
public: //SMGlobalClass
|
||||
ConfigResult OnSourceModConfigChanged(const char *key,
|
||||
const char *value,
|
||||
ConfigSource source,
|
||||
char *error,
|
||||
size_t maxlength);
|
||||
void OnSourceModStartup(bool late);
|
||||
void OnSourceModAllShutdown();
|
||||
void OnSourceModLevelChange(const char *mapName);
|
||||
public:
|
||||
void InitLogger(LoggingMode mode);
|
||||
void CloseLogger();
|
||||
void EnableLogging();
|
||||
void DisableLogging();
|
||||
void LogMessage(const char *msg, ...);
|
||||
void LogError(const char *msg, ...);
|
||||
void LogFatal(const char *msg, ...);
|
||||
void LogToOpenFile(FILE *fp, const char *msg, ...);
|
||||
void LogToOpenFileEx(FILE *fp, const char *msg, va_list ap);
|
||||
void MapChange(const char *mapname);
|
||||
const char *GetLogFileName(LogType type) const;
|
||||
LoggingMode GetLoggingMode() const;
|
||||
private:
|
||||
void _CloseFile();
|
||||
void _NewMapFile();
|
||||
void _PrintToGameLog(const char *fmt, va_list ap);
|
||||
private:
|
||||
String m_NrmFileName;
|
||||
String m_ErrFileName;
|
||||
String m_CurMapName;
|
||||
LoggingMode m_Mode;
|
||||
int m_CurDay;
|
||||
bool m_ErrMapStart;
|
||||
bool m_Active;
|
||||
bool m_DelayedStart;
|
||||
bool m_DailyPrintHdr;
|
||||
bool m_InitialState;
|
||||
};
|
||||
|
||||
void Engine_LogPrintWrapper(const char *msg);
|
||||
|
||||
extern bool g_in_game_log_hook;
|
||||
extern Logger g_Logger;
|
||||
|
||||
#endif // _INCLUDE_SOURCEMOD_CLOGGER_H_
|
||||
+148
@@ -0,0 +1,148 @@
|
||||
# (C)2004-2008 SourceMod Development Team
|
||||
# Makefile written by David "BAILOPAN" Anderson
|
||||
|
||||
SMSDK = ..
|
||||
SRCDS_BASE = ~/srcds
|
||||
HL2SDK_ORIG = ../../hl2sdk
|
||||
HL2SDK_OB = ../../hl2sdk-ob
|
||||
SOURCEMM14 = ../../sourcemm-1.4
|
||||
SOURCEMM16 = ../../sourcemm-1.6
|
||||
|
||||
#####################################
|
||||
### EDIT BELOW FOR OTHER PROJECTS ###
|
||||
#####################################
|
||||
|
||||
OBJECTS = AdminCache.cpp CDataPack.cpp ConCmdManager.cpp ConVarManager.cpp CoreConfig.cpp \
|
||||
Database.cpp DebugReporter.cpp EventManager.cpp GameConfigs.cpp HalfLife2.cpp Logger.cpp \
|
||||
MemoryUtils.cpp PlayerManager.cpp TextParsers.cpp TimerSys.cpp Translator.cpp UserMessages.cpp \
|
||||
sm_autonatives.cpp sm_memtable.cpp sm_srvcmds.cpp sm_stringutil.cpp sm_trie.cpp \
|
||||
sourcemm_api.cpp sourcemod.cpp MenuStyle_Base.cpp MenuStyle_Valve.cpp MenuManager.cpp \
|
||||
MenuStyle_Radio.cpp ChatTriggers.cpp ADTFactory.cpp MenuVoting.cpp sm_crc32.cpp \
|
||||
frame_hooks.cpp concmd_cleaner.cpp Profiler.cpp PhraseCollection.cpp NextMap.cpp \
|
||||
NativeOwner.cpp GameDataFetcher.cpp md5.cpp
|
||||
OBJECTS += smn_admin.cpp smn_bitbuffer.cpp smn_console.cpp smn_core.cpp \
|
||||
smn_datapacks.cpp smn_entities.cpp smn_events.cpp smn_fakenatives.cpp \
|
||||
smn_filesystem.cpp smn_float.cpp smn_functions.cpp smn_gameconfigs.cpp smn_halflife.cpp \
|
||||
smn_handles.cpp smn_keyvalues.cpp smn_banning.cpp smn_maplists.cpp \
|
||||
smn_lang.cpp smn_player.cpp smn_string.cpp smn_sorting.cpp smn_textparse.cpp smn_timers.cpp \
|
||||
smn_usermsgs.cpp smn_menus.cpp smn_database.cpp smn_vector.cpp smn_adt_array.cpp \
|
||||
smn_adt_trie.cpp smn_hudtext.cpp smn_adt_stack.cpp smn_nextmap.cpp
|
||||
OBJECTS += systems/ExtensionSys.cpp systems/ForwardSys.cpp systems/HandleSys.cpp \
|
||||
systems/LibrarySys.cpp systems/PluginInfoDatabase.cpp systems/PluginSys.cpp \
|
||||
systems/ShareSys.cpp vm/sp_vm_basecontext.cpp vm/sp_vm_engine.cpp \
|
||||
vm/sp_vm_function.cpp
|
||||
OBJECTS += thread/ThreadWorker.cpp thread/BaseWorker.cpp thread/PosixThreads.cpp ThreadSupport.cpp
|
||||
OBJECTS += zlib/adler32.c zlib/compress.c zlib/crc32.c zlib/deflate.c zlib/gzio.c \
|
||||
zlib/infback.c zlib/inffast.c zlib/inflate.c zlib/inftrees.c zlib/trees.c \
|
||||
zlib/uncompr.c zlib/zutil.c
|
||||
|
||||
##############################################
|
||||
### CONFIGURE ANY OTHER FLAGS/OPTIONS HERE ###
|
||||
##############################################
|
||||
|
||||
C_OPT_FLAGS = -DNDEBUG -O3 -funroll-loops -pipe -fno-strict-aliasing
|
||||
C_DEBUG_FLAGS = -D_DEBUG -DDEBUG -g -ggdb3
|
||||
C_GCC4_FLAGS = -fvisibility=hidden
|
||||
CPP_GCC4_FLAGS = -fvisibility-inlines-hidden
|
||||
CPP = gcc-4.1
|
||||
|
||||
override ENGSET = false
|
||||
ifeq "$(ENGINE)" "original"
|
||||
HL2SDK = $(HL2SDK_ORIG)
|
||||
HL2PUB = $(HL2SDK_ORIG)/public
|
||||
HL2LIB = $(HL2SDK_ORIG)/linux_sdk
|
||||
METAMOD = $(SOURCEMM14)
|
||||
INCLUDE += -I$(HL2SDK)/public/dlls
|
||||
SRCDS = $(SRCDS_BASE)
|
||||
BINARY = sourcemod.1.ep1.so
|
||||
override ENGSET = true
|
||||
endif
|
||||
ifeq "$(ENGINE)" "orangebox"
|
||||
HL2SDK = $(HL2SDK_OB)
|
||||
HL2PUB = $(HL2SDK_OB)/public
|
||||
HL2LIB = $(HL2SDK_OB)/linux_sdk
|
||||
CFLAGS += -DORANGEBOX_BUILD
|
||||
METAMOD = $(SOURCEMM16)
|
||||
INCLUDE += -I$(HL2SDK)/public/game/server
|
||||
SRCDS = $(SRCDS_BASE)/orangebox
|
||||
BINARY = sourcemod.2.ep2.so
|
||||
override ENGSET = true
|
||||
endif
|
||||
|
||||
LINK += $(HL2LIB)/tier1_i486.a $(HL2LIB)/mathlib_i486.a vstdlib_i486.so \
|
||||
tier0_i486.so -lpthread -static-libgcc
|
||||
|
||||
INCLUDE_SMSDK = -I$(SMSDK)/public -I$(SMSDK)/public/sourcepawn
|
||||
|
||||
INCLUDE_SM16 = -I. -I.. -I$(SOURCEMM16)/sourcehook $(INCLUDE_SMSDK)
|
||||
|
||||
INCLUDE += -I. -I.. -I$(HL2PUB) -I$(HL2PUB)/engine -I$(HL2PUB)/mathlib -I$(HL2PUB)/vstdlib \
|
||||
-I$(HL2PUB)/tier0 -I$(HL2PUB)/tier1 -I$(METAMOD) -I$(METAMOD)/sourcehook \
|
||||
-I$(METAMOD)/sourcemm -Isystems $(INCLUDE_SMSDK)
|
||||
|
||||
CFLAGS += -D_LINUX -Dstricmp=strcasecmp -D_stricmp=strcasecmp -D_strnicmp=strncasecmp -Dstrnicmp=strncasecmp \
|
||||
-D_snprintf=snprintf -D_vsnprintf=vsnprintf -D_alloca=alloca -Dstrcmpi=strcasecmp -Wall -Werror \
|
||||
-Wno-uninitialized -mfpmath=sse -msse -DSOURCEMOD_BUILD -DHAVE_STDINT_H -DSM_DEFAULT_THREADER -m32
|
||||
CPPFLAGS += -Wno-non-virtual-dtor -fno-exceptions -fno-rtti
|
||||
|
||||
################################################
|
||||
### DO NOT EDIT BELOW HERE FOR MOST PROJECTS ###
|
||||
################################################
|
||||
|
||||
ifeq "$(DEBUG)" "true"
|
||||
BIN_DIR = Debug.$(ENGINE)
|
||||
CFLAGS += $(C_DEBUG_FLAGS)
|
||||
else
|
||||
BIN_DIR = Release.$(ENGINE)
|
||||
CFLAGS += $(C_OPT_FLAGS)
|
||||
endif
|
||||
|
||||
GCC_VERSION := $(shell $(CPP) -dumpversion >&1 | cut -b1)
|
||||
ifeq "$(GCC_VERSION)" "4"
|
||||
CFLAGS += $(C_GCC4_FLAGS)
|
||||
CPPFLAGS += $(CPP_GCC4_FLAGS)
|
||||
endif
|
||||
|
||||
OBJ_LINUX := $(OBJECTS:%vm_engine.cpp=$(BIN_DIR)/%vm_engine.o)
|
||||
OBJ_LINUX := $(OBJ_LINUX:%.cpp=$(BIN_DIR)/%.o)
|
||||
OBJ_LINUX := $(OBJ_LINUX:%.c=$(BIN_DIR)/%.o)
|
||||
|
||||
$(BIN_DIR)/%vm_engine.o: %vm_engine.cpp
|
||||
$(CPP) $(INCLUDE_SM16) $(CFLAGS) $(CPPFLAGS) -o $@ -c $<
|
||||
|
||||
$(BIN_DIR)/%.o: %.cpp
|
||||
$(CPP) $(INCLUDE) $(CFLAGS) $(CPPFLAGS) -o $@ -c $<
|
||||
|
||||
$(BIN_DIR)/%.o: %.c
|
||||
$(CPP) $(INCLUDE) $(CFLAGS) -o $@ -c $<
|
||||
|
||||
all: check
|
||||
mkdir -p $(BIN_DIR)/systems
|
||||
mkdir -p $(BIN_DIR)/vm
|
||||
mkdir -p $(BIN_DIR)/zlib
|
||||
mkdir -p $(BIN_DIR)/thread
|
||||
ln -sf $(SRCDS)/bin/vstdlib_i486.so vstdlib_i486.so;
|
||||
ln -sf $(SRCDS)/bin/tier0_i486.so tier0_i486.so;
|
||||
$(MAKE) -f Makefile sourcemod
|
||||
|
||||
check:
|
||||
if [ "$(ENGSET)" == "false" ]; then \
|
||||
echo "You must supply ENGINE=orangebox or ENGINE=original"; \
|
||||
exit 1; \
|
||||
fi
|
||||
|
||||
sourcemod: check $(OBJ_LINUX)
|
||||
$(CPP) $(INCLUDE) $(OBJ_LINUX) $(LINK) -m32 -shared -ldl -lm -o$(BIN_DIR)/$(BINARY)
|
||||
|
||||
debug:
|
||||
$(MAKE) -f Makefile all DEBUG=true
|
||||
|
||||
default: all
|
||||
|
||||
clean: check
|
||||
rm -rf $(BIN_DIR)/*.o
|
||||
rm -rf $(BIN_DIR)/systems/*.o
|
||||
rm -rf $(BIN_DIR)/zlib/*.o
|
||||
rm -rf $(BIN_DIR)/vm/*.o
|
||||
rm -rf $(BIN_DIR)/thread/*.o
|
||||
rm -rf $(BIN_DIR)/$(BINARY)
|
||||
@@ -0,0 +1,220 @@
|
||||
/**
|
||||
* vim: set ts=4 :
|
||||
* =============================================================================
|
||||
* SourceMod
|
||||
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
|
||||
* =============================================================================
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, version 3.0, as published by the
|
||||
* Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* As a special exception, AlliedModders LLC gives you permission to link the
|
||||
* code of this program (as well as its derivative works) to "Half-Life 2," the
|
||||
* "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
|
||||
* by the Valve Corporation. You must obey the GNU General Public License in
|
||||
* all respects for all other code used. Additionally, AlliedModders LLC grants
|
||||
* this exception to all derivative works. AlliedModders LLC defines further
|
||||
* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
|
||||
* or <http://www.sourcemod.net/license.php>.
|
||||
*
|
||||
* Version: $Id$
|
||||
*/
|
||||
|
||||
#include "MemoryUtils.h"
|
||||
#include "ShareSys.h"
|
||||
#ifdef PLATFORM_LINUX
|
||||
#include <string.h>
|
||||
#include <elf.h>
|
||||
#endif
|
||||
|
||||
MemoryUtils g_MemUtils;
|
||||
|
||||
#if 0
|
||||
MemoryUtils::MemoryUtils()
|
||||
{
|
||||
#ifdef PLATFORM_WINDOWS
|
||||
|
||||
SYSTEM_INFO info;
|
||||
GetSystemInfo(&info);
|
||||
|
||||
m_PageSize = info.dwPageSize;
|
||||
|
||||
#elif defined PLATFORM_POSIX
|
||||
|
||||
m_PageSize = sysconf(_SC_PAGE_SIZE);
|
||||
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
void MemoryUtils::OnSourceModAllInitialized()
|
||||
{
|
||||
g_ShareSys.AddInterface(NULL, this);
|
||||
}
|
||||
|
||||
void *MemoryUtils::FindPattern(const void *libPtr, const char *pattern, size_t len)
|
||||
{
|
||||
DynLibInfo lib;
|
||||
bool found;
|
||||
char *ptr, *end;
|
||||
|
||||
memset(&lib, 0, sizeof(DynLibInfo));
|
||||
|
||||
if (!GetLibraryInfo(libPtr, lib))
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ptr = reinterpret_cast<char *>(lib.baseAddress);
|
||||
end = ptr + lib.memorySize;
|
||||
|
||||
while (ptr < end)
|
||||
{
|
||||
found = true;
|
||||
for (register size_t i = 0; i < len; i++)
|
||||
{
|
||||
if (pattern[i] != '\x2A' && pattern[i] != ptr[i])
|
||||
{
|
||||
found = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (found)
|
||||
return ptr;
|
||||
|
||||
ptr++;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool MemoryUtils::GetLibraryInfo(const void *libPtr, DynLibInfo &lib)
|
||||
{
|
||||
unsigned long baseAddr;
|
||||
|
||||
if (libPtr == NULL)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef PLATFORM_WINDOWS
|
||||
|
||||
MEMORY_BASIC_INFORMATION info;
|
||||
IMAGE_DOS_HEADER *dos;
|
||||
IMAGE_NT_HEADERS *pe;
|
||||
IMAGE_FILE_HEADER *file;
|
||||
IMAGE_OPTIONAL_HEADER *opt;
|
||||
|
||||
if (!VirtualQuery(libPtr, &info, sizeof(MEMORY_BASIC_INFORMATION)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
baseAddr = reinterpret_cast<unsigned long>(info.AllocationBase);
|
||||
|
||||
/* All this is for our insane sanity checks :o */
|
||||
dos = reinterpret_cast<IMAGE_DOS_HEADER *>(baseAddr);
|
||||
pe = reinterpret_cast<IMAGE_NT_HEADERS *>(baseAddr + dos->e_lfanew);
|
||||
file = &pe->FileHeader;
|
||||
opt = &pe->OptionalHeader;
|
||||
|
||||
/* Check PE magic and signature */
|
||||
if (dos->e_magic != IMAGE_DOS_SIGNATURE || pe->Signature != IMAGE_NT_SIGNATURE || opt->Magic != IMAGE_NT_OPTIONAL_HDR32_MAGIC)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Check architecture, which is 32-bit/x86 right now
|
||||
* Should change this for 64-bit if Valve gets their act together
|
||||
*/
|
||||
if (file->Machine != IMAGE_FILE_MACHINE_I386)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/* For our purposes, this must be a dynamic library */
|
||||
if ((file->Characteristics & IMAGE_FILE_DLL) == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Finally, we can do this */
|
||||
lib.memorySize = opt->SizeOfImage;
|
||||
|
||||
#elif defined PLATFORM_LINUX
|
||||
|
||||
Dl_info info;
|
||||
Elf32_Ehdr *file;
|
||||
Elf32_Phdr *phdr;
|
||||
uint16_t phdrCount;
|
||||
|
||||
if (!dladdr(libPtr, &info))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!info.dli_fbase || !info.dli_fname)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/* This is for our insane sanity checks :o */
|
||||
baseAddr = reinterpret_cast<unsigned long>(info.dli_fbase);
|
||||
file = reinterpret_cast<Elf32_Ehdr *>(baseAddr);
|
||||
|
||||
/* Check ELF magic */
|
||||
if (memcmp(ELFMAG, file->e_ident, SELFMAG) != 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Check ELF version */
|
||||
if (file->e_ident[EI_VERSION] != EV_CURRENT)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Check ELF architecture, which is 32-bit/x86 right now
|
||||
* Should change this for 64-bit if Valve gets their act together
|
||||
*/
|
||||
if (file->e_ident[EI_CLASS] != ELFCLASS32 || file->e_machine != EM_386 || file->e_ident[EI_DATA] != ELFDATA2LSB)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/* For our purposes, this must be a dynamic library/shared object */
|
||||
if (file->e_type != ET_DYN)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
phdrCount = file->e_phnum;
|
||||
phdr = reinterpret_cast<Elf32_Phdr *>(baseAddr + file->e_phoff);
|
||||
|
||||
/* Add up the memory sizes of segments marked as PT_LOAD as those are the only ones that should be in memory */
|
||||
for (uint16_t i = 0; i < phdrCount; i++)
|
||||
{
|
||||
Elf32_Phdr &hdr = phdr[i];
|
||||
|
||||
if (hdr.p_type == PT_LOAD)
|
||||
{
|
||||
lib.memorySize += hdr.p_memsz;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
lib.baseAddress = reinterpret_cast<void *>(baseAddr);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
/**
|
||||
* vim: set ts=4 :
|
||||
* =============================================================================
|
||||
* SourceMod
|
||||
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
|
||||
* =============================================================================
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, version 3.0, as published by the
|
||||
* Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* As a special exception, AlliedModders LLC gives you permission to link the
|
||||
* code of this program (as well as its derivative works) to "Half-Life 2," the
|
||||
* "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
|
||||
* by the Valve Corporation. You must obey the GNU General Public License in
|
||||
* all respects for all other code used. Additionally, AlliedModders LLC grants
|
||||
* this exception to all derivative works. AlliedModders LLC defines further
|
||||
* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
|
||||
* or <http://www.sourcemod.net/license.php>.
|
||||
*
|
||||
* Version: $Id$
|
||||
*/
|
||||
|
||||
#ifndef _INCLUDE_SOURCEMOD_MEMORYUTILS_H_
|
||||
#define _INCLUDE_SOURCEMOD_MEMORYUTILS_H_
|
||||
|
||||
#include <IMemoryUtils.h>
|
||||
#include "sm_globals.h"
|
||||
|
||||
using namespace SourceMod;
|
||||
|
||||
struct DynLibInfo
|
||||
{
|
||||
void *baseAddress;
|
||||
size_t memorySize;
|
||||
};
|
||||
|
||||
class MemoryUtils :
|
||||
public IMemoryUtils,
|
||||
public SMGlobalClass
|
||||
{
|
||||
public: // SMGlobalClass
|
||||
void OnSourceModAllInitialized();
|
||||
public: // IMemoryUtils
|
||||
void *FindPattern(const void *libPtr, const char *pattern, size_t len);
|
||||
private:
|
||||
bool GetLibraryInfo(const void *libPtr, DynLibInfo &lib);
|
||||
};
|
||||
|
||||
extern MemoryUtils g_MemUtils;
|
||||
|
||||
#endif // _INCLUDE_SOURCEMOD_MEMORYUTILS_H_
|
||||
@@ -0,0 +1,802 @@
|
||||
/**
|
||||
* vim: set ts=4 :
|
||||
* =============================================================================
|
||||
* SourceMod
|
||||
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
|
||||
* =============================================================================
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, version 3.0, as published by the
|
||||
* Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* As a special exception, AlliedModders LLC gives you permission to link the
|
||||
* code of this program (as well as its derivative works) to "Half-Life 2," the
|
||||
* "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
|
||||
* by the Valve Corporation. You must obey the GNU General Public License in
|
||||
* all respects for all other code used. Additionally, AlliedModders LLC grants
|
||||
* this exception to all derivative works. AlliedModders LLC defines further
|
||||
* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
|
||||
* or <http://www.sourcemod.net/license.php>.
|
||||
*
|
||||
* Version: $Id$
|
||||
*/
|
||||
|
||||
#include <time.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdlib.h>
|
||||
#include "MenuManager.h"
|
||||
#include "MenuVoting.h"
|
||||
#include "sm_stringutil.h"
|
||||
#include "sourcemm_api.h"
|
||||
#include "PlayerManager.h"
|
||||
#include "MenuStyle_Valve.h"
|
||||
#include "ShareSys.h"
|
||||
#include "HandleSys.h"
|
||||
#include "sourcemm_api.h"
|
||||
#include "Translator.h"
|
||||
|
||||
MenuManager g_Menus;
|
||||
VoteMenuHandler s_VoteHandler;
|
||||
|
||||
ConVar sm_menu_sounds("sm_menu_sounds", "1", 0, "Sets whether SourceMod menus play trigger sounds");
|
||||
|
||||
MenuManager::MenuManager()
|
||||
{
|
||||
m_Styles.push_back(&g_ValveMenuStyle);
|
||||
SetDefaultStyle(&g_ValveMenuStyle);
|
||||
}
|
||||
|
||||
void MenuManager::OnSourceModAllInitialized()
|
||||
{
|
||||
g_ShareSys.AddInterface(NULL, this);
|
||||
|
||||
HandleAccess access;
|
||||
g_HandleSys.InitAccessDefaults(NULL, &access);
|
||||
|
||||
/* Deny cloning to menus */
|
||||
access.access[HandleAccess_Clone] = HANDLE_RESTRICT_OWNER|HANDLE_RESTRICT_IDENTITY;
|
||||
m_MenuType = g_HandleSys.CreateType("IBaseMenu", this, 0, NULL, &access, g_pCoreIdent, NULL);
|
||||
|
||||
/* Also deny deletion to styles */
|
||||
access.access[HandleAccess_Delete] = HANDLE_RESTRICT_OWNER|HANDLE_RESTRICT_IDENTITY;
|
||||
m_StyleType = g_HandleSys.CreateType("IMenuStyle", this, 0, NULL, &access, g_pCoreIdent, NULL);
|
||||
}
|
||||
|
||||
void MenuManager::OnSourceModAllShutdown()
|
||||
{
|
||||
g_HandleSys.RemoveType(m_MenuType, g_pCoreIdent);
|
||||
g_HandleSys.RemoveType(m_StyleType, g_pCoreIdent);
|
||||
}
|
||||
|
||||
void MenuManager::OnHandleDestroy(HandleType_t type, void *object)
|
||||
{
|
||||
if (type == m_MenuType)
|
||||
{
|
||||
IBaseMenu *menu = (IBaseMenu *)object;
|
||||
menu->Destroy(false);
|
||||
}
|
||||
else if (type == m_StyleType)
|
||||
{
|
||||
/* Do nothing */
|
||||
}
|
||||
}
|
||||
|
||||
bool MenuManager::GetHandleApproxSize(HandleType_t type, void *object, unsigned int *pSize)
|
||||
{
|
||||
if (type == m_MenuType)
|
||||
{
|
||||
*pSize = ((IBaseMenu *)object)->GetApproxMemUsage();
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
*pSize = ((IMenuStyle *)object)->GetApproxMemUsage();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Handle_t MenuManager::CreateMenuHandle(IBaseMenu *menu, IdentityToken_t *pOwner)
|
||||
{
|
||||
if (m_MenuType == NO_HANDLE_TYPE)
|
||||
{
|
||||
return BAD_HANDLE;
|
||||
}
|
||||
|
||||
return g_HandleSys.CreateHandle(m_MenuType, menu, pOwner, g_pCoreIdent, NULL);
|
||||
}
|
||||
|
||||
Handle_t MenuManager::CreateStyleHandle(IMenuStyle *style)
|
||||
{
|
||||
if (m_StyleType == NO_HANDLE_TYPE)
|
||||
{
|
||||
return BAD_HANDLE;
|
||||
}
|
||||
|
||||
return g_HandleSys.CreateHandle(m_StyleType, style, g_pCoreIdent, g_pCoreIdent, NULL);
|
||||
}
|
||||
|
||||
HandleError MenuManager::ReadMenuHandle(Handle_t handle, IBaseMenu **menu)
|
||||
{
|
||||
HandleSecurity sec;
|
||||
|
||||
sec.pIdentity = g_pCoreIdent;
|
||||
sec.pOwner = NULL;
|
||||
|
||||
return g_HandleSys.ReadHandle(handle, m_MenuType, &sec, (void **)menu);
|
||||
}
|
||||
|
||||
HandleError MenuManager::ReadStyleHandle(Handle_t handle, IMenuStyle **style)
|
||||
{
|
||||
HandleSecurity sec;
|
||||
|
||||
sec.pIdentity = g_pCoreIdent;
|
||||
sec.pOwner = g_pCoreIdent;
|
||||
|
||||
return g_HandleSys.ReadHandle(handle, m_StyleType, &sec, (void **)style);
|
||||
}
|
||||
|
||||
bool MenuManager::SetDefaultStyle(IMenuStyle *style)
|
||||
{
|
||||
if (!style)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
m_pDefaultStyle = style;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
IMenuStyle *MenuManager::GetStyle(unsigned int index)
|
||||
{
|
||||
if (index >= GetStyleCount())
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return m_Styles[index];
|
||||
}
|
||||
|
||||
void MenuManager::AddStyle(IMenuStyle *style)
|
||||
{
|
||||
m_Styles.push_back(style);
|
||||
}
|
||||
|
||||
unsigned int MenuManager::GetStyleCount()
|
||||
{
|
||||
return (unsigned int)m_Styles.size();
|
||||
}
|
||||
|
||||
IMenuStyle *MenuManager::FindStyleByName(const char *name)
|
||||
{
|
||||
unsigned int count = GetStyleCount();
|
||||
for (unsigned int i=0; i<count; i++)
|
||||
{
|
||||
IMenuStyle *ptr = GetStyle(i);
|
||||
if (strcasecmp(ptr->GetStyleName(), name) == 0)
|
||||
{
|
||||
return ptr;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
inline bool IsSlotItem(IMenuPanel *display,
|
||||
unsigned int style)
|
||||
{
|
||||
if (!display->CanDrawItem(style))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if ((style & ITEMDRAW_IGNORE) == ITEMDRAW_IGNORE)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (style & ITEMDRAW_RAWLINE)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
IMenuPanel *MenuManager::RenderMenu(int client, menu_states_t &md, ItemOrder order)
|
||||
{
|
||||
IBaseMenu *menu = md.menu;
|
||||
|
||||
if (!menu)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct
|
||||
{
|
||||
unsigned int position;
|
||||
ItemDrawInfo draw;
|
||||
} drawItems[10];
|
||||
|
||||
/* Figure out how many items to draw */
|
||||
IMenuStyle *style = menu->GetDrawStyle();
|
||||
unsigned int pgn = menu->GetPagination();
|
||||
unsigned int maxItems = style->GetMaxPageItems();
|
||||
bool exitButton = (menu->GetMenuOptionFlags() & MENUFLAG_BUTTON_EXIT) == MENUFLAG_BUTTON_EXIT;
|
||||
|
||||
if (pgn != MENU_NO_PAGINATION)
|
||||
{
|
||||
maxItems = pgn;
|
||||
}
|
||||
else if (exitButton)
|
||||
{
|
||||
maxItems--;
|
||||
}
|
||||
|
||||
/* This is very not allowed! */
|
||||
if (maxItems < 2)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
unsigned int totalItems = menu->GetItemCount();
|
||||
unsigned int startItem = 0;
|
||||
|
||||
/* For pagination, find the starting point. */
|
||||
if (pgn != MENU_NO_PAGINATION)
|
||||
{
|
||||
if (order == ItemOrder_Ascending)
|
||||
{
|
||||
startItem = md.lastItem;
|
||||
/* This shouldn't happen with well-coded menus.
|
||||
* If the item is out of bounds, switch the order to
|
||||
* Items_Descending and make us start from the top.
|
||||
*/
|
||||
if (startItem >= totalItems)
|
||||
{
|
||||
startItem = totalItems - 1;
|
||||
order = ItemOrder_Descending;
|
||||
}
|
||||
}
|
||||
else if (order == ItemOrder_Descending)
|
||||
{
|
||||
startItem = md.firstItem;
|
||||
/* This shouldn't happen with well-coded menus.
|
||||
* If searching backwards doesn't give us enough room,
|
||||
* start from the beginning and change to ascending.
|
||||
*/
|
||||
if (startItem <= maxItems)
|
||||
{
|
||||
startItem = 0;
|
||||
order = ItemOrder_Ascending;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Get our Display pointer and initialize some crap */
|
||||
IMenuPanel *panel = menu->CreatePanel();
|
||||
IMenuHandler *mh = md.mh;
|
||||
bool foundExtra = false;
|
||||
unsigned int extraItem = 0;
|
||||
|
||||
if (panel == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* We keep searching until:
|
||||
* 1) There are no more items
|
||||
* 2) We reach one OVER the maximum number of slot items
|
||||
* 3) We have reached maxItems and pagination is MENU_NO_PAGINATION
|
||||
*/
|
||||
unsigned int i = startItem;
|
||||
unsigned int foundItems = 0;
|
||||
while (totalItems)
|
||||
{
|
||||
ItemDrawInfo &dr = drawItems[foundItems].draw;
|
||||
/* Is the item valid? */
|
||||
if (menu->GetItemInfo(i, &dr) != NULL)
|
||||
{
|
||||
/* Ask the user to change the style, if necessary */
|
||||
mh->OnMenuDrawItem(menu, client, i, dr.style);
|
||||
/* Check if it's renderable */
|
||||
if (IsSlotItem(panel, dr.style))
|
||||
{
|
||||
/* If we've already found the max number of items,
|
||||
* This means we should just cancel out and log our
|
||||
* "last item."
|
||||
*/
|
||||
if (foundItems >= maxItems)
|
||||
{
|
||||
foundExtra = true;
|
||||
extraItem = i;
|
||||
break;
|
||||
}
|
||||
drawItems[foundItems++].position = i;
|
||||
}
|
||||
}
|
||||
/* If there's no pagination, stop once the menu is full. */
|
||||
if (pgn == MENU_NO_PAGINATION)
|
||||
{
|
||||
/* If we've filled up, then stop */
|
||||
if (foundItems >= maxItems)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* If we're descending and this is the first item, stop */
|
||||
if (order == ItemOrder_Descending)
|
||||
{
|
||||
if (i == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
i--;
|
||||
}
|
||||
/* If we're ascending and this is the last item, stop */
|
||||
else if (order == ItemOrder_Ascending)
|
||||
{
|
||||
if (i >= totalItems - 1)
|
||||
{
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
/* There were no items to draw! */
|
||||
if (!foundItems)
|
||||
{
|
||||
panel->DeleteThis();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool displayPrev = false;
|
||||
bool displayNext = false;
|
||||
|
||||
/* This is an annoying process.
|
||||
* Skip it for non-paginated menus, which get special treatment.
|
||||
*/
|
||||
if (pgn != MENU_NO_PAGINATION)
|
||||
{
|
||||
if (foundExtra)
|
||||
{
|
||||
if (order == ItemOrder_Descending)
|
||||
{
|
||||
displayPrev = true;
|
||||
md.firstItem = extraItem;
|
||||
}
|
||||
else if (order == ItemOrder_Ascending)
|
||||
{
|
||||
displayNext = true;
|
||||
md.lastItem = extraItem;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int lastItem = 0;
|
||||
ItemDrawInfo dr;
|
||||
/* Find the last feasible item to search from. */
|
||||
if (order == ItemOrder_Descending)
|
||||
{
|
||||
lastItem = drawItems[0].position;
|
||||
if (lastItem >= totalItems - 1)
|
||||
{
|
||||
goto skip_search;
|
||||
}
|
||||
while (++lastItem < totalItems)
|
||||
{
|
||||
if (menu->GetItemInfo(lastItem, &dr) != NULL)
|
||||
{
|
||||
mh->OnMenuDrawItem(menu, client, lastItem, dr.style);
|
||||
if (IsSlotItem(panel, dr.style))
|
||||
{
|
||||
displayNext = true;
|
||||
md.lastItem = lastItem;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (order == ItemOrder_Ascending)
|
||||
{
|
||||
lastItem = drawItems[0].position;
|
||||
if (lastItem == 0)
|
||||
{
|
||||
goto skip_search;
|
||||
}
|
||||
lastItem--;
|
||||
while (lastItem != 0)
|
||||
{
|
||||
if (menu->GetItemInfo(lastItem, &dr) != NULL)
|
||||
{
|
||||
mh->OnMenuDrawItem(menu, client, lastItem, dr.style);
|
||||
if (IsSlotItem(panel, dr.style))
|
||||
{
|
||||
displayPrev = true;
|
||||
md.firstItem = lastItem;
|
||||
break;
|
||||
}
|
||||
}
|
||||
lastItem--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
skip_search:
|
||||
|
||||
/* Draw the item according to the order */
|
||||
menu_slots_t *slots = md.slots;
|
||||
unsigned int position = 0; /* Keep track of the last position */
|
||||
if (order == ItemOrder_Ascending)
|
||||
{
|
||||
md.item_on_page = drawItems[0].position;
|
||||
for (unsigned int i = 0; i < foundItems; i++)
|
||||
{
|
||||
ItemDrawInfo &dr = drawItems[i].draw;
|
||||
if ((position = mh->OnMenuDisplayItem(menu, client, panel, drawItems[i].position, dr)) == 0)
|
||||
{
|
||||
position = panel->DrawItem(dr);
|
||||
}
|
||||
if (position != 0)
|
||||
{
|
||||
slots[position].item = drawItems[i].position;
|
||||
if ((dr.style & ITEMDRAW_DISABLED) == ITEMDRAW_DISABLED)
|
||||
{
|
||||
slots[position].type = ItemSel_None;
|
||||
}
|
||||
else
|
||||
{
|
||||
slots[position].type = ItemSel_Item;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (order == ItemOrder_Descending)
|
||||
{
|
||||
unsigned int i = foundItems;
|
||||
/* NOTE: There will always be at least one item because
|
||||
* of the check earlier.
|
||||
*/
|
||||
md.item_on_page = drawItems[foundItems - 1].position;
|
||||
while (i--)
|
||||
{
|
||||
ItemDrawInfo &dr = drawItems[i].draw;
|
||||
if ((position = mh->OnMenuDisplayItem(menu, client, panel, drawItems[i].position, dr)) == 0)
|
||||
{
|
||||
position = panel->DrawItem(dr);
|
||||
}
|
||||
if (position != 0)
|
||||
{
|
||||
slots[position].item = drawItems[i].position;
|
||||
if ((dr.style & ITEMDRAW_DISABLED) == ITEMDRAW_DISABLED)
|
||||
{
|
||||
slots[position].type = ItemSel_None;
|
||||
}
|
||||
else
|
||||
{
|
||||
slots[position].type = ItemSel_Item;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Now, we need to check if we need to add anything extra */
|
||||
if (pgn != MENU_NO_PAGINATION || exitButton)
|
||||
{
|
||||
bool canDrawDisabled = panel->CanDrawItem(ITEMDRAW_DISABLED|ITEMDRAW_CONTROL);
|
||||
bool exitBackButton = false;
|
||||
char text[50];
|
||||
|
||||
if (pgn != MENU_NO_PAGINATION
|
||||
&& (menu->GetMenuOptionFlags() & MENUFLAG_BUTTON_EXITBACK) == MENUFLAG_BUTTON_EXITBACK)
|
||||
{
|
||||
exitBackButton = true;
|
||||
}
|
||||
|
||||
/* Calculate how many items we are allowed for control stuff */
|
||||
unsigned int padding = style->GetMaxPageItems() - maxItems;
|
||||
|
||||
/* Add the number of available slots */
|
||||
padding += (maxItems - foundItems);
|
||||
|
||||
/* Someday, if we are able to re-enable this, we will be very lucky men. */
|
||||
#if 0
|
||||
if (!style->FeatureExists(MenuStyleFeature_ImplicitExit))
|
||||
{
|
||||
#endif
|
||||
/* Even if we don't draw an exit button, we invalidate the slot. */
|
||||
padding--;
|
||||
#if 0
|
||||
} else {
|
||||
/* Otherwise, we don't draw anything and leave the slot available */
|
||||
exitButton = false;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (pgn != MENU_NO_PAGINATION)
|
||||
{
|
||||
/* Subtract two slots for the displayNext/displayPrev padding */
|
||||
padding -= 2;
|
||||
}
|
||||
|
||||
/* If we have an "Exit Back" button and the space to draw it, do so. */
|
||||
if (exitBackButton)
|
||||
{
|
||||
if (!displayPrev)
|
||||
{
|
||||
displayPrev = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
exitBackButton = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* We allow next/prev to be undrawn if neither exists.
|
||||
* Thus, we only need padding if one of them will be drawn,
|
||||
* or the exit button will be drawn.
|
||||
*/
|
||||
ItemDrawInfo padItem(NULL, ITEMDRAW_SPACER);
|
||||
if (exitButton || (displayNext || displayPrev))
|
||||
{
|
||||
/* If there are no control options,
|
||||
* Instead just pad with invisible slots.
|
||||
*/
|
||||
if (!displayPrev && !displayPrev)
|
||||
{
|
||||
padItem.style = ITEMDRAW_NOTEXT;
|
||||
}
|
||||
/* Add spacers so we can pad to the end */
|
||||
for (unsigned int i=0; i<padding; i++)
|
||||
{
|
||||
position = panel->DrawItem(padItem);
|
||||
slots[position].type = ItemSel_None;
|
||||
}
|
||||
}
|
||||
|
||||
/* Put a fake spacer before control stuff, if possible */
|
||||
if ((displayPrev || displayNext) || exitButton)
|
||||
{
|
||||
ItemDrawInfo draw("", ITEMDRAW_RAWLINE|ITEMDRAW_SPACER);
|
||||
panel->DrawItem(draw);
|
||||
}
|
||||
|
||||
ItemDrawInfo dr(text, 0);
|
||||
|
||||
/**
|
||||
* If we have one or the other, we need to have spacers for both.
|
||||
*/
|
||||
if (pgn != MENU_NO_PAGINATION)
|
||||
{
|
||||
if (displayPrev || displayNext)
|
||||
{
|
||||
/* PREVIOUS */
|
||||
ItemDrawInfo padCtrlItem(NULL, ITEMDRAW_SPACER|ITEMDRAW_CONTROL);
|
||||
if (displayPrev || canDrawDisabled)
|
||||
{
|
||||
if (exitBackButton)
|
||||
{
|
||||
if (!CoreTranslate(text, sizeof(text), "%T", 2, NULL, "Back", &client))
|
||||
{
|
||||
UTIL_Format(text, sizeof(text), "Back");
|
||||
}
|
||||
dr.style = ITEMDRAW_CONTROL;
|
||||
position = panel->DrawItem(dr);
|
||||
slots[position].type = ItemSel_ExitBack;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!CoreTranslate(text, sizeof(text), "%T", 2, NULL, "Previous", &client))
|
||||
{
|
||||
UTIL_Format(text, sizeof(text), "Previous");
|
||||
}
|
||||
dr.style = (displayPrev ? 0 : ITEMDRAW_DISABLED)|ITEMDRAW_CONTROL;
|
||||
position = panel->DrawItem(dr);
|
||||
slots[position].type = ItemSel_Back;
|
||||
}
|
||||
}
|
||||
else if (displayNext || exitButton)
|
||||
{
|
||||
/* If we can't display this, and there is an exit button,
|
||||
* we need to pad!
|
||||
*/
|
||||
position = panel->DrawItem(padCtrlItem);
|
||||
slots[position].type = ItemSel_None;
|
||||
}
|
||||
|
||||
/* NEXT */
|
||||
if (displayNext || canDrawDisabled)
|
||||
{
|
||||
if (!CoreTranslate(text, sizeof(text), "%T", 2, NULL, "Next", &client))
|
||||
{
|
||||
UTIL_Format(text, sizeof(text), "Next");
|
||||
}
|
||||
dr.style = (displayNext ? 0 : ITEMDRAW_DISABLED)|ITEMDRAW_CONTROL;
|
||||
position = panel->DrawItem(dr);
|
||||
slots[position].type = ItemSel_Next;
|
||||
}
|
||||
else if (exitButton)
|
||||
{
|
||||
/* If we can't display this,
|
||||
* but there is an "exit" button, we need to pad!
|
||||
*/
|
||||
position = panel->DrawItem(padCtrlItem);
|
||||
slots[position].type = ItemSel_None;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Otherwise, bump to two slots! */
|
||||
ItemDrawInfo numBump(NULL, ITEMDRAW_NOTEXT);
|
||||
position = panel->DrawItem(numBump);
|
||||
slots[position].type = ItemSel_None;
|
||||
position = panel->DrawItem(numBump);
|
||||
slots[position].type = ItemSel_None;
|
||||
}
|
||||
}
|
||||
|
||||
/* EXIT */
|
||||
if (exitButton)
|
||||
{
|
||||
if (!CoreTranslate(text, sizeof(text), "%T", 2, NULL, "Exit", &client))
|
||||
{
|
||||
UTIL_Format(text, sizeof(text), "Exit");
|
||||
}
|
||||
dr.style = ITEMDRAW_CONTROL;
|
||||
position = panel->DrawItem(dr);
|
||||
slots[position].type = ItemSel_Exit;
|
||||
}
|
||||
}
|
||||
|
||||
/* Lastly, fill in any slots we could have missed */
|
||||
for (unsigned int i = position + 1; i < 10; i++)
|
||||
{
|
||||
slots[i].type = ItemSel_None;
|
||||
}
|
||||
|
||||
/* Do title stuff */
|
||||
mh->OnMenuDisplay(menu, client, panel);
|
||||
panel->DrawTitle(menu->GetDefaultTitle(), true);
|
||||
|
||||
return panel;
|
||||
}
|
||||
|
||||
IMenuStyle *MenuManager::GetDefaultStyle()
|
||||
{
|
||||
return m_pDefaultStyle;
|
||||
}
|
||||
|
||||
bool MenuManager::MenuSoundsEnabled()
|
||||
{
|
||||
return (sm_menu_sounds.GetInt() != 0);
|
||||
}
|
||||
|
||||
ConfigResult MenuManager::OnSourceModConfigChanged(const char *key,
|
||||
const char *value,
|
||||
ConfigSource source,
|
||||
char *error,
|
||||
size_t maxlength)
|
||||
{
|
||||
if (strcmp(key, "MenuItemSound") == 0)
|
||||
{
|
||||
m_SelectSound.assign(value);
|
||||
return ConfigResult_Accept;
|
||||
} else if (strcmp(key, "MenuExitBackSound") == 0) {
|
||||
m_ExitBackSound.assign(value);
|
||||
return ConfigResult_Accept;
|
||||
} else if (strcmp(key, "MenuExitSound") == 0) {
|
||||
m_ExitSound.assign(value);
|
||||
return ConfigResult_Accept;
|
||||
}
|
||||
|
||||
return ConfigResult_Ignore;
|
||||
}
|
||||
|
||||
const char *MenuManager::GetMenuSound(ItemSelection sel)
|
||||
{
|
||||
const char *sound = NULL;
|
||||
|
||||
switch (sel)
|
||||
{
|
||||
case ItemSel_Back:
|
||||
case ItemSel_Next:
|
||||
case ItemSel_Item:
|
||||
{
|
||||
if (m_SelectSound.size() > 0)
|
||||
{
|
||||
sound = m_SelectSound.c_str();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ItemSel_ExitBack:
|
||||
{
|
||||
if (m_ExitBackSound.size() > 0)
|
||||
{
|
||||
sound = m_ExitBackSound.c_str();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ItemSel_Exit:
|
||||
{
|
||||
if (m_ExitSound.size() > 0)
|
||||
{
|
||||
sound = m_ExitSound.c_str();
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return sound;
|
||||
}
|
||||
|
||||
void MenuManager::OnSourceModLevelChange(const char *mapName)
|
||||
{
|
||||
if (m_SelectSound.size() > 0)
|
||||
{
|
||||
enginesound->PrecacheSound(m_SelectSound.c_str(), true);
|
||||
}
|
||||
if (m_ExitBackSound.size() > 0)
|
||||
{
|
||||
enginesound->PrecacheSound(m_ExitBackSound.c_str(), true);
|
||||
}
|
||||
if (m_ExitSound.size() > 0)
|
||||
{
|
||||
enginesound->PrecacheSound(m_ExitSound.c_str(), true);
|
||||
}
|
||||
}
|
||||
|
||||
void MenuManager::CancelMenu(IBaseMenu *menu)
|
||||
{
|
||||
if (s_VoteHandler.GetCurrentMenu() == menu
|
||||
&& !s_VoteHandler.IsCancelling())
|
||||
{
|
||||
s_VoteHandler.CancelVoting();
|
||||
return;
|
||||
}
|
||||
|
||||
menu->Cancel();
|
||||
}
|
||||
|
||||
bool MenuManager::StartVote(IBaseMenu *menu, unsigned int num_clients, int clients[], unsigned int max_time, unsigned int flags)
|
||||
{
|
||||
return s_VoteHandler.StartVote(menu, num_clients, clients, max_time, flags);
|
||||
}
|
||||
|
||||
bool MenuManager::IsVoteInProgress()
|
||||
{
|
||||
return s_VoteHandler.IsVoteInProgress();
|
||||
}
|
||||
|
||||
void MenuManager::CancelVoting()
|
||||
{
|
||||
s_VoteHandler.CancelVoting();
|
||||
}
|
||||
|
||||
unsigned int MenuManager::GetRemainingVoteDelay()
|
||||
{
|
||||
return s_VoteHandler.GetRemainingVoteDelay();
|
||||
}
|
||||
|
||||
bool MenuManager::IsClientInVotePool(int client)
|
||||
{
|
||||
return s_VoteHandler.IsClientInVotePool(client);
|
||||
}
|
||||
|
||||
bool MenuManager::RedrawClientVoteMenu(int client)
|
||||
{
|
||||
return s_VoteHandler.RedrawToClient(client);
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
/**
|
||||
* vim: set ts=4 :
|
||||
* =============================================================================
|
||||
* SourceMod
|
||||
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
|
||||
* =============================================================================
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, version 3.0, as published by the
|
||||
* Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* As a special exception, AlliedModders LLC gives you permission to link the
|
||||
* code of this program (as well as its derivative works) to "Half-Life 2," the
|
||||
* "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
|
||||
* by the Valve Corporation. You must obey the GNU General Public License in
|
||||
* all respects for all other code used. Additionally, AlliedModders LLC grants
|
||||
* this exception to all derivative works. AlliedModders LLC defines further
|
||||
* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
|
||||
* or <http://www.sourcemod.net/license.php>.
|
||||
*
|
||||
* Version: $Id$
|
||||
*/
|
||||
|
||||
#ifndef _INCLUDE_SOURCEMOD_MENUMANAGER_H_
|
||||
#define _INCLUDE_SOURCEMOD_MENUMANAGER_H_
|
||||
|
||||
#include <IMenuManager.h>
|
||||
#include <sh_vector.h>
|
||||
#include <sh_stack.h>
|
||||
#include <sh_list.h>
|
||||
#include <sh_string.h>
|
||||
#include "sm_memtable.h"
|
||||
#include "sm_globals.h"
|
||||
|
||||
using namespace SourceMod;
|
||||
using namespace SourceHook;
|
||||
|
||||
class MenuManager :
|
||||
public IMenuManager,
|
||||
public SMGlobalClass,
|
||||
public IHandleTypeDispatch
|
||||
{
|
||||
friend class BroadcastHandler;
|
||||
friend class VoteHandler;
|
||||
friend class CBaseMenu;
|
||||
friend class BaseMenuStyle;
|
||||
public:
|
||||
MenuManager();
|
||||
public: //SMGlobalClass
|
||||
void OnSourceModAllInitialized();
|
||||
void OnSourceModAllShutdown();
|
||||
ConfigResult OnSourceModConfigChanged(const char *key,
|
||||
const char *value,
|
||||
ConfigSource source,
|
||||
char *error,
|
||||
size_t maxlength);
|
||||
void OnSourceModLevelChange(const char *mapName);
|
||||
public: //IMenuManager
|
||||
virtual const char *GetInterfaceName()
|
||||
{
|
||||
return SMINTERFACE_MENUMANAGER_NAME;
|
||||
}
|
||||
virtual unsigned int GetInterfaceVersion()
|
||||
{
|
||||
return SMINTERFACE_MENUMANAGER_VERSION;
|
||||
}
|
||||
public:
|
||||
unsigned int GetStyleCount();
|
||||
IMenuStyle *GetStyle(unsigned int index);
|
||||
IMenuStyle *FindStyleByName(const char *name);
|
||||
IMenuStyle *GetDefaultStyle();
|
||||
void AddStyle(IMenuStyle *style);
|
||||
bool SetDefaultStyle(IMenuStyle *style);
|
||||
IMenuPanel *RenderMenu(int client, menu_states_t &states, ItemOrder order);
|
||||
void CancelMenu(IBaseMenu *menu);
|
||||
bool StartVote(IBaseMenu *menu,
|
||||
unsigned int num_clients,
|
||||
int clients[],
|
||||
unsigned int max_time,
|
||||
unsigned int flags=0);
|
||||
bool IsVoteInProgress();
|
||||
void CancelVoting();
|
||||
unsigned int GetRemainingVoteDelay();
|
||||
bool IsClientInVotePool(int client);
|
||||
bool RedrawClientVoteMenu(int client);
|
||||
public: //IHandleTypeDispatch
|
||||
void OnHandleDestroy(HandleType_t type, void *object);
|
||||
bool GetHandleApproxSize(HandleType_t type, void *object, unsigned int *pSize);
|
||||
public:
|
||||
HandleError ReadMenuHandle(Handle_t handle, IBaseMenu **menu);
|
||||
HandleError ReadStyleHandle(Handle_t handle, IMenuStyle **style);
|
||||
public:
|
||||
bool MenuSoundsEnabled();
|
||||
const char *GetMenuSound(ItemSelection sel);
|
||||
protected:
|
||||
Handle_t CreateMenuHandle(IBaseMenu *menu, IdentityToken_t *pOwner);
|
||||
Handle_t CreateStyleHandle(IMenuStyle *style);
|
||||
private:
|
||||
int m_ShowMenu;
|
||||
IMenuStyle *m_pDefaultStyle;
|
||||
CVector<IMenuStyle *> m_Styles;
|
||||
HandleType_t m_StyleType;
|
||||
HandleType_t m_MenuType;
|
||||
String m_SelectSound;
|
||||
String m_ExitBackSound;
|
||||
String m_ExitSound;
|
||||
};
|
||||
|
||||
extern MenuManager g_Menus;
|
||||
|
||||
#endif //_INCLUDE_SOURCEMOD_MENUMANAGER_H_
|
||||
@@ -0,0 +1,837 @@
|
||||
/**
|
||||
* vim: set ts=4 :
|
||||
* =============================================================================
|
||||
* SourceMod
|
||||
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
|
||||
* =============================================================================
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, version 3.0, as published by the
|
||||
* Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* As a special exception, AlliedModders LLC gives you permission to link the
|
||||
* code of this program (as well as its derivative works) to "Half-Life 2," the
|
||||
* "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
|
||||
* by the Valve Corporation. You must obey the GNU General Public License in
|
||||
* all respects for all other code used. Additionally, AlliedModders LLC grants
|
||||
* this exception to all derivative works. AlliedModders LLC defines further
|
||||
* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
|
||||
* or <http://www.sourcemod.net/license.php>.
|
||||
*
|
||||
* Version: $Id$
|
||||
*/
|
||||
|
||||
#include <stdarg.h>
|
||||
#include "sm_stringutil.h"
|
||||
#include "MenuStyle_Base.h"
|
||||
#include "PlayerManager.h"
|
||||
#include "MenuManager.h"
|
||||
#include "HandleSys.h"
|
||||
#include "CellRecipientFilter.h"
|
||||
#if defined MENU_DEBUG
|
||||
#include "Logger.h"
|
||||
#endif
|
||||
|
||||
BaseMenuStyle::BaseMenuStyle() : m_WatchList(256), m_hHandle(BAD_HANDLE)
|
||||
{
|
||||
}
|
||||
|
||||
Handle_t BaseMenuStyle::GetHandle()
|
||||
{
|
||||
/* Don't create the handle until we need it */
|
||||
if (m_hHandle == BAD_HANDLE)
|
||||
{
|
||||
m_hHandle = g_Menus.CreateStyleHandle(this);
|
||||
}
|
||||
|
||||
return m_hHandle;
|
||||
}
|
||||
|
||||
void BaseMenuStyle::AddClientToWatch(int client)
|
||||
{
|
||||
#if defined MENU_DEBUG
|
||||
g_Logger.LogMessage("[SM_MENU] AddClientToWatch(%d)", client);
|
||||
#endif
|
||||
m_WatchList.push_back(client);
|
||||
}
|
||||
|
||||
void BaseMenuStyle::RemoveClientFromWatch(int client)
|
||||
{
|
||||
#if defined MENU_DEBUG
|
||||
g_Logger.LogMessage("[SM_MENU] RemoveClientFromWatch(%d)", client);
|
||||
#endif
|
||||
m_WatchList.remove(client);
|
||||
}
|
||||
|
||||
void BaseMenuStyle::_CancelClientMenu(int client, MenuCancelReason reason, bool bAutoIgnore/* =false */)
|
||||
{
|
||||
#if defined MENU_DEBUG
|
||||
g_Logger.LogMessage("[SM_MENU] _CancelClientMenu() (client %d) (bAutoIgnore %d) (reason %d)", client, bAutoIgnore, reason);
|
||||
#endif
|
||||
CBaseMenuPlayer *player = GetMenuPlayer(client);
|
||||
menu_states_t &states = player->states;
|
||||
|
||||
bool bOldIgnore = player->bAutoIgnore;
|
||||
if (bAutoIgnore)
|
||||
{
|
||||
player->bAutoIgnore = true;
|
||||
}
|
||||
|
||||
/* Save states */
|
||||
IMenuHandler *mh = states.mh;
|
||||
IBaseMenu *menu = states.menu;
|
||||
|
||||
/* Clear menu */
|
||||
player->bInMenu = false;
|
||||
if (player->menuHoldTime)
|
||||
{
|
||||
RemoveClientFromWatch(client);
|
||||
}
|
||||
|
||||
/* Fire callbacks */
|
||||
mh->OnMenuCancel(menu, client, reason);
|
||||
|
||||
/* Only fire end if there's a valid menu */
|
||||
if (menu)
|
||||
{
|
||||
mh->OnMenuEnd(menu, MenuEnd_Cancelled);
|
||||
}
|
||||
|
||||
if (bAutoIgnore)
|
||||
{
|
||||
player->bAutoIgnore = bOldIgnore;
|
||||
}
|
||||
}
|
||||
|
||||
void BaseMenuStyle::CancelMenu(CBaseMenu *menu)
|
||||
{
|
||||
#if defined MENU_DEBUG
|
||||
g_Logger.LogMessage("[SM_MENU] CancelMenu() (menu %p)", menu);
|
||||
#endif
|
||||
int maxClients = g_Players.GetMaxClients();
|
||||
for (int i=1; i<=maxClients; i++)
|
||||
{
|
||||
CBaseMenuPlayer *player = GetMenuPlayer(i);
|
||||
if (player->bInMenu)
|
||||
{
|
||||
menu_states_t &states = player->states;
|
||||
if (states.menu == menu)
|
||||
{
|
||||
_CancelClientMenu(i, MenuCancel_Interrupted);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool BaseMenuStyle::CancelClientMenu(int client, bool autoIgnore)
|
||||
{
|
||||
#if defined MENU_DEBUG
|
||||
g_Logger.LogMessage("[SM_MENU] CancelClientMenu() (client %d) (bAutoIgnore %d)", client, autoIgnore);
|
||||
#endif
|
||||
if (client < 1 || client > g_Players.MaxClients())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
CBaseMenuPlayer *player = GetMenuPlayer(client);
|
||||
if (!player->bInMenu)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
_CancelClientMenu(client, MenuCancel_Interrupted, autoIgnore);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
MenuSource BaseMenuStyle::GetClientMenu(int client, void **object)
|
||||
{
|
||||
if (client < 1 || client > g_Players.GetMaxClients())
|
||||
{
|
||||
return MenuSource_None;
|
||||
}
|
||||
|
||||
CBaseMenuPlayer *player = GetMenuPlayer(client);
|
||||
|
||||
if (player->bInMenu)
|
||||
{
|
||||
IBaseMenu *menu;
|
||||
if ((menu=player->states.menu) != NULL)
|
||||
{
|
||||
if (object)
|
||||
{
|
||||
*object = menu;
|
||||
}
|
||||
return MenuSource_BaseMenu;
|
||||
}
|
||||
|
||||
return MenuSource_Display;
|
||||
} else if (player->bInExternMenu) {
|
||||
if (player->menuHoldTime != 0
|
||||
&& (gpGlobals->curtime > player->menuStartTime + player->menuHoldTime))
|
||||
{
|
||||
player->bInExternMenu = false;
|
||||
return MenuSource_None;
|
||||
}
|
||||
return MenuSource_External;
|
||||
}
|
||||
|
||||
return MenuSource_None;
|
||||
}
|
||||
|
||||
void BaseMenuStyle::OnClientDisconnected(int client)
|
||||
{
|
||||
#if defined MENU_DEBUG
|
||||
g_Logger.LogMessage("[SM_MENU] OnClientDisconnected(%d)", client);
|
||||
#endif
|
||||
CBaseMenuPlayer *player = GetMenuPlayer(client);
|
||||
if (!player->bInMenu)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_CancelClientMenu(client, MenuCancel_Disconnected, true);
|
||||
|
||||
player->bInMenu = false;
|
||||
player->bInExternMenu = false;
|
||||
}
|
||||
|
||||
static int do_lookup[256];
|
||||
void BaseMenuStyle::ProcessWatchList()
|
||||
{
|
||||
if (!m_WatchList.size())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
#if defined MENU_DEBUG
|
||||
g_Logger.LogMessage("BaseMenuStyle::ProcessWatchList(%d,%d,%d,%d,%d,%p)",
|
||||
m_WatchList.m_Size,
|
||||
m_WatchList.m_FirstLink,
|
||||
m_WatchList.m_FreeNodes,
|
||||
m_WatchList.m_LastLink,
|
||||
m_WatchList.m_MaxSize,
|
||||
m_WatchList.m_Nodes);
|
||||
#endif
|
||||
|
||||
unsigned int total = 0;
|
||||
for (FastLink<int>::iterator iter=m_WatchList.begin(); iter!=m_WatchList.end(); ++iter)
|
||||
{
|
||||
do_lookup[total++] = (*iter);
|
||||
}
|
||||
|
||||
#if defined MENU_DEBUG
|
||||
if (total)
|
||||
{
|
||||
g_Logger.LogMessage("[SM_MENU] ProcessWatchList() found %d clients", total);
|
||||
}
|
||||
#endif
|
||||
|
||||
int client;
|
||||
CBaseMenuPlayer *player;
|
||||
float curTime = gpGlobals->curtime;
|
||||
for (unsigned int i=0; i<total; i++)
|
||||
{
|
||||
client = do_lookup[i];
|
||||
player = GetMenuPlayer(client);
|
||||
#if defined MENU_DEBUG
|
||||
g_Logger.LogMessage("[SM_MENU] ProcessWatchList() (client %d) (bInMenu %d) (menuHoldTime %d) (curTime %f) (menuStartTime %f)",
|
||||
client,
|
||||
player->bInMenu,
|
||||
player->menuHoldTime,
|
||||
curTime,
|
||||
player->menuStartTime);
|
||||
#endif
|
||||
if (!player->bInMenu || !player->menuHoldTime)
|
||||
{
|
||||
#if defined MENU_DEBUG
|
||||
g_Logger.LogMessage("[SM_MENU] ProcessWatchList() removing client %d", client);
|
||||
#endif
|
||||
m_WatchList.remove(client);
|
||||
continue;
|
||||
}
|
||||
if (curTime > player->menuStartTime + player->menuHoldTime)
|
||||
{
|
||||
_CancelClientMenu(client, MenuCancel_Timeout, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BaseMenuStyle::ClientPressedKey(int client, unsigned int key_press)
|
||||
{
|
||||
#if defined MENU_DEBUG
|
||||
g_Logger.LogMessage("[SM_MENU] ClientPressedKey() (client %d) (key_press %d)", client, key_press);
|
||||
#endif
|
||||
CBaseMenuPlayer *player = GetMenuPlayer(client);
|
||||
|
||||
/* First question: Are we in a menu? */
|
||||
if (!player->bInMenu)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
bool cancel = false;
|
||||
unsigned int item = 0;
|
||||
MenuCancelReason reason = MenuCancel_Exit;
|
||||
MenuEndReason end_reason = MenuEnd_Selected;
|
||||
menu_states_t &states = player->states;
|
||||
|
||||
/* Save variables */
|
||||
IMenuHandler *mh = states.mh;
|
||||
IBaseMenu *menu = states.menu;
|
||||
|
||||
unsigned int item_on_page = states.item_on_page;
|
||||
|
||||
assert(mh != NULL);
|
||||
|
||||
if (menu == NULL)
|
||||
{
|
||||
item = key_press;
|
||||
} else if (key_press < 1 || key_press > GetMaxPageItems()) {
|
||||
cancel = true;
|
||||
} else {
|
||||
ItemSelection type = states.slots[key_press].type;
|
||||
|
||||
/* Check if we should play a sound about the type */
|
||||
if (g_Menus.MenuSoundsEnabled() &&
|
||||
(!menu || (menu->GetMenuOptionFlags() & MENUFLAG_NO_SOUND) != MENUFLAG_NO_SOUND))
|
||||
{
|
||||
CellRecipientFilter filter;
|
||||
cell_t clients[1];
|
||||
|
||||
clients[0] = client;
|
||||
filter.Initialize(clients, 1);
|
||||
|
||||
const char *sound = g_Menus.GetMenuSound(type);
|
||||
|
||||
if (sound != NULL)
|
||||
{
|
||||
edict_t *pEdict = engine->PEntityOfEntIndex(client);
|
||||
if (pEdict)
|
||||
{
|
||||
ICollideable *pCollideable = pEdict->GetCollideable();
|
||||
|
||||
if (pCollideable)
|
||||
{
|
||||
const Vector & pos = pCollideable->GetCollisionOrigin();
|
||||
|
||||
enginesound->EmitSound(filter,
|
||||
client,
|
||||
CHAN_AUTO,
|
||||
sound,
|
||||
VOL_NORM,
|
||||
ATTN_NORM,
|
||||
0,
|
||||
PITCH_NORM,
|
||||
&pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* For navigational items, we're going to redisplay */
|
||||
if (type == ItemSel_Back)
|
||||
{
|
||||
if (!RedoClientMenu(client, ItemOrder_Descending))
|
||||
{
|
||||
cancel = true;
|
||||
reason = MenuCancel_NoDisplay;
|
||||
end_reason = MenuEnd_Cancelled;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
} else if (type == ItemSel_Next) {
|
||||
if (!RedoClientMenu(client, ItemOrder_Ascending))
|
||||
{
|
||||
cancel = true; /* I like Saltines. */
|
||||
reason = MenuCancel_NoDisplay;
|
||||
end_reason = MenuEnd_Cancelled;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
} else if (type == ItemSel_Exit || type == ItemSel_None) {
|
||||
cancel = true;
|
||||
reason = MenuCancel_Exit;
|
||||
end_reason = MenuEnd_Exit;
|
||||
} else if (type == ItemSel_ExitBack) {
|
||||
cancel = true;
|
||||
reason = MenuCancel_ExitBack;
|
||||
end_reason = MenuEnd_ExitBack;
|
||||
} else {
|
||||
item = states.slots[key_press].item;
|
||||
}
|
||||
}
|
||||
|
||||
/* Clear states */
|
||||
player->bInMenu = false;
|
||||
if (player->menuHoldTime)
|
||||
{
|
||||
RemoveClientFromWatch(client);
|
||||
}
|
||||
|
||||
if (cancel)
|
||||
{
|
||||
mh->OnMenuCancel(menu, client, reason);
|
||||
} else {
|
||||
mh->OnMenuSelect(menu, client, item);
|
||||
if (mh->GetMenuAPIVersion2() >= 13)
|
||||
{
|
||||
mh->OnMenuSelect2(menu, client, item, item_on_page);
|
||||
}
|
||||
}
|
||||
|
||||
/* Only fire end for valid menus */
|
||||
if (menu)
|
||||
{
|
||||
mh->OnMenuEnd(menu, end_reason);
|
||||
}
|
||||
}
|
||||
|
||||
bool BaseMenuStyle::DoClientMenu(int client, IMenuPanel *menu, IMenuHandler *mh, unsigned int time)
|
||||
{
|
||||
#if defined MENU_DEBUG
|
||||
g_Logger.LogMessage("[SM_MENU] DoClientMenu() (client %d) (panel %p) (mh %p) (time %d)",
|
||||
client,
|
||||
menu,
|
||||
mh,
|
||||
time);
|
||||
#endif
|
||||
CPlayer *pPlayer = g_Players.GetPlayerByIndex(client);
|
||||
if (!pPlayer || pPlayer->IsFakeClient() || !pPlayer->IsInGame())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
CBaseMenuPlayer *player = GetMenuPlayer(client);
|
||||
if (player->bAutoIgnore)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/* For the duration of this, we are going to totally ignore whether
|
||||
* the player is already in a menu or not (except to cancel the old one).
|
||||
* Instead, we are simply going to ignore any further menu displays, so
|
||||
* this display can't be interrupted.
|
||||
*/
|
||||
player->bAutoIgnore = true;
|
||||
|
||||
/* Cancel any old menus */
|
||||
menu_states_t &states = player->states;
|
||||
if (player->bInMenu)
|
||||
{
|
||||
_CancelClientMenu(client, MenuCancel_Interrupted, true);
|
||||
}
|
||||
|
||||
states.firstItem = 0;
|
||||
states.lastItem = 0;
|
||||
states.menu = NULL;
|
||||
states.mh = mh;
|
||||
states.apiVers = SMINTERFACE_MENUMANAGER_VERSION;
|
||||
player->bInMenu = true;
|
||||
player->bInExternMenu = false;
|
||||
player->menuStartTime = gpGlobals->curtime;
|
||||
player->menuHoldTime = time;
|
||||
|
||||
if (time)
|
||||
{
|
||||
AddClientToWatch(client);
|
||||
}
|
||||
|
||||
/* Draw the display */
|
||||
SendDisplay(client, menu);
|
||||
|
||||
/* We can be interrupted again! */
|
||||
player->bAutoIgnore = false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BaseMenuStyle::DoClientMenu(int client,
|
||||
CBaseMenu *menu,
|
||||
unsigned int first_item,
|
||||
IMenuHandler *mh,
|
||||
unsigned int time)
|
||||
{
|
||||
#if defined MENU_DEBUG
|
||||
g_Logger.LogMessage("[SM_MENU] DoClientMenu() (client %d) (menu %p) (mh %p) (time %d)",
|
||||
client,
|
||||
menu,
|
||||
mh,
|
||||
time);
|
||||
#endif
|
||||
mh->OnMenuStart(menu);
|
||||
|
||||
CPlayer *pPlayer = g_Players.GetPlayerByIndex(client);
|
||||
if (!pPlayer || pPlayer->IsFakeClient() || !pPlayer->IsInGame())
|
||||
{
|
||||
#if defined MENU_DEBUG
|
||||
g_Logger.LogMessage("[SM_MENU] DoClientMenu(): Failed to display to client %d", client);
|
||||
#endif
|
||||
mh->OnMenuCancel(menu, client, MenuCancel_NoDisplay);
|
||||
mh->OnMenuEnd(menu, MenuEnd_Cancelled);
|
||||
return false;
|
||||
}
|
||||
|
||||
CBaseMenuPlayer *player = GetMenuPlayer(client);
|
||||
if (player->bAutoIgnore)
|
||||
{
|
||||
#if defined MENU_DEBUG
|
||||
g_Logger.LogMessage("[SM_MENU] DoClientMenu(): Client %d is autoIgnoring", client);
|
||||
#endif
|
||||
mh->OnMenuCancel(menu, client, MenuCancel_NoDisplay);
|
||||
mh->OnMenuEnd(menu, MenuEnd_Cancelled);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* For the duration of this, we are going to totally ignore whether
|
||||
* the player is already in a menu or not (except to cancel the old one).
|
||||
* Instead, we are simply going to ignore any further menu displays, so
|
||||
* this display can't be interrupted.
|
||||
*/
|
||||
player->bAutoIgnore = true;
|
||||
|
||||
/* Cancel any old menus */
|
||||
menu_states_t &states = player->states;
|
||||
if (player->bInMenu)
|
||||
{
|
||||
#if defined MENU_DEBUG
|
||||
g_Logger.LogMessage("[SM_MENU] DoClientMenu(): Cancelling old menu to client %d", client);
|
||||
#endif
|
||||
_CancelClientMenu(client, MenuCancel_Interrupted, true);
|
||||
}
|
||||
|
||||
states.firstItem = 0;
|
||||
states.lastItem = first_item;
|
||||
states.menu = menu;
|
||||
states.mh = mh;
|
||||
states.apiVers = SMINTERFACE_MENUMANAGER_VERSION;
|
||||
|
||||
IMenuPanel *display = g_Menus.RenderMenu(client, states, ItemOrder_Ascending);
|
||||
if (!display)
|
||||
{
|
||||
#if defined MENU_DEBUG
|
||||
g_Logger.LogMessage("[SM_MENU] DoClientMenu(): Failed to render to client %d", client);
|
||||
#endif
|
||||
player->bAutoIgnore = false;
|
||||
player->bInMenu = false;
|
||||
mh->OnMenuCancel(menu, client, MenuCancel_NoDisplay);
|
||||
mh->OnMenuEnd(menu, MenuEnd_Cancelled);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Finally, set our states */
|
||||
player->bInMenu = true;
|
||||
player->bInExternMenu = false;
|
||||
player->menuStartTime = gpGlobals->curtime;
|
||||
player->menuHoldTime = time;
|
||||
|
||||
if (time)
|
||||
{
|
||||
AddClientToWatch(client);
|
||||
}
|
||||
|
||||
/* Draw the display */
|
||||
SendDisplay(client, display);
|
||||
|
||||
/* Free the display pointer */
|
||||
display->DeleteThis();
|
||||
|
||||
/* We can be interrupted again! */
|
||||
player->bAutoIgnore = false;
|
||||
|
||||
#if defined MENU_DEBUG
|
||||
g_Logger.LogMessage("[SM_MENU] DoClientMenu() finished successfully (client %d)", client);
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BaseMenuStyle::RedoClientMenu(int client, ItemOrder order)
|
||||
{
|
||||
#if defined MENU_DEBUG
|
||||
g_Logger.LogMessage("[SM_MENU] RedoClientMenu() (client %d) (order %d)", client, order);
|
||||
#endif
|
||||
CBaseMenuPlayer *player = GetMenuPlayer(client);
|
||||
menu_states_t &states = player->states;
|
||||
|
||||
player->bAutoIgnore = true;
|
||||
IMenuPanel *display = g_Menus.RenderMenu(client, states, order);
|
||||
if (!display)
|
||||
{
|
||||
#if defined MENU_DEBUG
|
||||
g_Logger.LogMessage("[SM_MENU] RedoClientMenu(): Failed to render menu");
|
||||
#endif
|
||||
if (player->menuHoldTime)
|
||||
{
|
||||
RemoveClientFromWatch(client);
|
||||
}
|
||||
player->bAutoIgnore = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
SendDisplay(client, display);
|
||||
|
||||
display->DeleteThis();
|
||||
|
||||
player->bAutoIgnore = false;
|
||||
|
||||
#if defined MENU_DEBUG
|
||||
g_Logger.LogMessage("[SM_MENU] RedoClientMenu(): Succeeded to client %d", client);
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
CBaseMenu::CBaseMenu(IMenuHandler *pHandler, IMenuStyle *pStyle, IdentityToken_t *pOwner) :
|
||||
m_pStyle(pStyle), m_Strings(512), m_Pagination(7), m_bShouldDelete(false), m_bCancelling(false),
|
||||
m_pOwner(pOwner ? pOwner : g_pCoreIdent), m_bDeleting(false), m_bWillFreeHandle(false),
|
||||
m_hHandle(BAD_HANDLE), m_pHandler(pHandler), m_nFlags(MENUFLAG_BUTTON_EXIT)
|
||||
{
|
||||
}
|
||||
|
||||
CBaseMenu::~CBaseMenu()
|
||||
{
|
||||
}
|
||||
|
||||
Handle_t CBaseMenu::GetHandle()
|
||||
{
|
||||
if (!m_hHandle)
|
||||
{
|
||||
m_hHandle = g_Menus.CreateMenuHandle(this, m_pOwner);
|
||||
}
|
||||
|
||||
return m_hHandle;
|
||||
}
|
||||
|
||||
bool CBaseMenu::AppendItem(const char *info, const ItemDrawInfo &draw)
|
||||
{
|
||||
if (m_Pagination == (unsigned)MENU_NO_PAGINATION
|
||||
&& m_items.size() >= m_pStyle->GetMaxPageItems())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
CItem item;
|
||||
|
||||
item.infoString = m_Strings.AddString(info);
|
||||
if (draw.display)
|
||||
{
|
||||
item.displayString = m_Strings.AddString(draw.display);
|
||||
}
|
||||
item.style = draw.style;
|
||||
|
||||
m_items.push_back(item);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CBaseMenu::InsertItem(unsigned int position, const char *info, const ItemDrawInfo &draw)
|
||||
{
|
||||
if (m_Pagination == (unsigned)MENU_NO_PAGINATION
|
||||
&& m_items.size() >= m_pStyle->GetMaxPageItems())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (position >= m_items.size())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
CItem item;
|
||||
item.infoString = m_Strings.AddString(info);
|
||||
if (draw.display)
|
||||
{
|
||||
item.displayString = m_Strings.AddString(draw.display);
|
||||
}
|
||||
item.style = draw.style;
|
||||
|
||||
CVector<CItem>::iterator iter = m_items.iterAt(position);
|
||||
m_items.insert(iter, item);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CBaseMenu::RemoveItem(unsigned int position)
|
||||
{
|
||||
if (position >= m_items.size())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
m_items.erase(m_items.iterAt(position));
|
||||
|
||||
if (m_items.size() == 0)
|
||||
{
|
||||
m_Strings.Reset();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CBaseMenu::RemoveAllItems()
|
||||
{
|
||||
m_items.clear();
|
||||
m_Strings.Reset();
|
||||
}
|
||||
|
||||
const char *CBaseMenu::GetItemInfo(unsigned int position, ItemDrawInfo *draw/* =NULL */)
|
||||
{
|
||||
if (position >= m_items.size())
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (draw)
|
||||
{
|
||||
draw->display = m_Strings.GetString(m_items[position].displayString);
|
||||
draw->style = m_items[position].style;
|
||||
}
|
||||
|
||||
return m_Strings.GetString(m_items[position].infoString);
|
||||
}
|
||||
|
||||
unsigned int CBaseMenu::GetItemCount()
|
||||
{
|
||||
return m_items.size();
|
||||
}
|
||||
|
||||
bool CBaseMenu::SetPagination(unsigned int itemsPerPage)
|
||||
{
|
||||
if (itemsPerPage > 7 || itemsPerPage == 1)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (itemsPerPage == MENU_NO_PAGINATION
|
||||
&& m_Pagination != MENU_NO_PAGINATION)
|
||||
{
|
||||
m_nFlags &= ~MENUFLAG_BUTTON_EXIT;
|
||||
}
|
||||
|
||||
m_Pagination = itemsPerPage;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
unsigned int CBaseMenu::GetPagination()
|
||||
{
|
||||
return m_Pagination;
|
||||
}
|
||||
|
||||
IMenuStyle *CBaseMenu::GetDrawStyle()
|
||||
{
|
||||
return m_pStyle;
|
||||
}
|
||||
|
||||
void CBaseMenu::SetDefaultTitle(const char *message)
|
||||
{
|
||||
m_Title.assign(message);
|
||||
}
|
||||
|
||||
const char *CBaseMenu::GetDefaultTitle()
|
||||
{
|
||||
return m_Title.c_str();
|
||||
}
|
||||
|
||||
void CBaseMenu::Cancel()
|
||||
{
|
||||
if (m_bCancelling)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
#if defined MENU_DEBUG
|
||||
g_Logger.LogMessage("[SM_MENU] CBaseMenu::Cancel(%p) (m_bShouldDelete %d)",
|
||||
this,
|
||||
m_bShouldDelete);
|
||||
#endif
|
||||
|
||||
m_bCancelling = true;
|
||||
Cancel_Finally();
|
||||
m_bCancelling = false;
|
||||
|
||||
if (m_bShouldDelete)
|
||||
{
|
||||
InternalDelete();
|
||||
}
|
||||
}
|
||||
|
||||
void CBaseMenu::Destroy(bool releaseHandle)
|
||||
{
|
||||
/* Check if we shouldn't be here */
|
||||
if (m_bDeleting)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
#if defined MENU_DEBUG
|
||||
g_Logger.LogMessage("[SM_MENU] CBaseMenu::Destroy(%p) (release %d) (m_bCancelling %d) (m_bShouldDelete %d)",
|
||||
this,
|
||||
releaseHandle,
|
||||
m_bCancelling,
|
||||
m_bShouldDelete);
|
||||
#endif
|
||||
|
||||
/* Save the destruction hint about our handle */
|
||||
m_bWillFreeHandle = releaseHandle;
|
||||
|
||||
/* Now actually do stuff */
|
||||
if (!m_bCancelling || m_bShouldDelete)
|
||||
{
|
||||
Cancel();
|
||||
InternalDelete();
|
||||
} else {
|
||||
m_bShouldDelete = true;
|
||||
}
|
||||
}
|
||||
|
||||
void CBaseMenu::InternalDelete()
|
||||
{
|
||||
if (m_bWillFreeHandle && m_hHandle != BAD_HANDLE)
|
||||
{
|
||||
Handle_t hndl = m_hHandle;
|
||||
HandleSecurity sec;
|
||||
|
||||
sec.pOwner = m_pOwner;
|
||||
sec.pIdentity = g_pCoreIdent;
|
||||
|
||||
m_hHandle = BAD_HANDLE;
|
||||
m_bDeleting = true;
|
||||
g_HandleSys.FreeHandle(hndl, &sec);
|
||||
}
|
||||
|
||||
m_pHandler->OnMenuDestroy(this);
|
||||
|
||||
delete this;
|
||||
}
|
||||
|
||||
unsigned int CBaseMenu::GetMenuOptionFlags()
|
||||
{
|
||||
return m_nFlags;
|
||||
}
|
||||
|
||||
void CBaseMenu::SetMenuOptionFlags(unsigned int flags)
|
||||
{
|
||||
m_nFlags = flags;
|
||||
}
|
||||
|
||||
IMenuHandler *CBaseMenu::GetHandler()
|
||||
{
|
||||
return m_pHandler;
|
||||
}
|
||||
|
||||
unsigned int CBaseMenu::GetBaseMemUsage()
|
||||
{
|
||||
return m_Title.size()
|
||||
+ m_Strings.GetMemTable()->GetMemUsage()
|
||||
+ (m_items.size() * sizeof(CItem));
|
||||
}
|
||||
@@ -0,0 +1,157 @@
|
||||
/**
|
||||
* vim: set ts=4 :
|
||||
* =============================================================================
|
||||
* SourceMod
|
||||
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
|
||||
* =============================================================================
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, version 3.0, as published by the
|
||||
* Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* As a special exception, AlliedModders LLC gives you permission to link the
|
||||
* code of this program (as well as its derivative works) to "Half-Life 2," the
|
||||
* "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
|
||||
* by the Valve Corporation. You must obey the GNU General Public License in
|
||||
* all respects for all other code used. Additionally, AlliedModders LLC grants
|
||||
* this exception to all derivative works. AlliedModders LLC defines further
|
||||
* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
|
||||
* or <http://www.sourcemod.net/license.php>.
|
||||
*
|
||||
* Version: $Id$
|
||||
*/
|
||||
|
||||
#ifndef _INCLUDE_MENUSTYLE_BASE_H
|
||||
#define _INCLUDE_MENUSTYLE_BASE_H
|
||||
|
||||
#include <IMenuManager.h>
|
||||
#include <IPlayerHelpers.h>
|
||||
#include <sh_string.h>
|
||||
#include <sh_vector.h>
|
||||
#include "sm_memtable.h"
|
||||
#include "sm_fastlink.h"
|
||||
|
||||
using namespace SourceMod;
|
||||
using namespace SourceHook;
|
||||
|
||||
class CItem
|
||||
{
|
||||
public:
|
||||
CItem()
|
||||
{
|
||||
infoString = -1;
|
||||
displayString = -1;
|
||||
style = 0;
|
||||
access = 0;
|
||||
}
|
||||
public:
|
||||
int infoString;
|
||||
int displayString;
|
||||
unsigned int style;
|
||||
unsigned int access;
|
||||
};
|
||||
|
||||
class CBaseMenuPlayer
|
||||
{
|
||||
public:
|
||||
CBaseMenuPlayer() : bInMenu(false), bAutoIgnore(false), bInExternMenu(false)
|
||||
{
|
||||
}
|
||||
menu_states_t states;
|
||||
bool bInMenu;
|
||||
bool bAutoIgnore;
|
||||
float menuStartTime;
|
||||
unsigned int menuHoldTime;
|
||||
bool bInExternMenu;
|
||||
};
|
||||
|
||||
class CBaseMenu;
|
||||
|
||||
class BaseMenuStyle :
|
||||
public IMenuStyle,
|
||||
public IClientListener
|
||||
{
|
||||
public:
|
||||
BaseMenuStyle();
|
||||
public: //IMenuStyle
|
||||
bool CancelClientMenu(int client, bool autoIgnore/* =false */);
|
||||
MenuSource GetClientMenu(int client, void **object);
|
||||
Handle_t GetHandle();
|
||||
public: //IClientListener
|
||||
void OnClientDisconnected(int client);
|
||||
public: //what derived must implement
|
||||
virtual CBaseMenuPlayer *GetMenuPlayer(int client) =0;
|
||||
virtual void SendDisplay(int client, IMenuPanel *display) =0;
|
||||
public: //what derived may implement
|
||||
virtual bool DoClientMenu(int client,
|
||||
CBaseMenu *menu,
|
||||
unsigned int first_item,
|
||||
IMenuHandler *mh,
|
||||
unsigned int time);
|
||||
virtual bool DoClientMenu(int client, IMenuPanel *menu, IMenuHandler *mh, unsigned int time);
|
||||
virtual void AddClientToWatch(int client);
|
||||
virtual void RemoveClientFromWatch(int client);
|
||||
virtual void ProcessWatchList();
|
||||
public: //helpers
|
||||
void CancelMenu(CBaseMenu *menu);
|
||||
void ClientPressedKey(int client, unsigned int key_press);
|
||||
protected:
|
||||
void _CancelClientMenu(int client, MenuCancelReason reason, bool bAutoIgnore=false);
|
||||
bool RedoClientMenu(int client, ItemOrder order);
|
||||
protected:
|
||||
FastLink<int> m_WatchList;
|
||||
Handle_t m_hHandle;
|
||||
};
|
||||
|
||||
class CBaseMenu : public IBaseMenu
|
||||
{
|
||||
public:
|
||||
CBaseMenu(IMenuHandler *pHandler, IMenuStyle *pStyle, IdentityToken_t *pOwner);
|
||||
virtual ~CBaseMenu();
|
||||
public:
|
||||
virtual bool AppendItem(const char *info, const ItemDrawInfo &draw);
|
||||
virtual bool InsertItem(unsigned int position, const char *info, const ItemDrawInfo &draw);
|
||||
virtual bool RemoveItem(unsigned int position);
|
||||
virtual void RemoveAllItems();
|
||||
virtual const char *GetItemInfo(unsigned int position, ItemDrawInfo *draw=NULL);
|
||||
virtual unsigned int GetItemCount();
|
||||
virtual bool SetPagination(unsigned int itemsPerPage);
|
||||
virtual unsigned int GetPagination();
|
||||
virtual IMenuStyle *GetDrawStyle();
|
||||
virtual void SetDefaultTitle(const char *message);
|
||||
virtual const char *GetDefaultTitle();
|
||||
virtual void Cancel();
|
||||
virtual void Destroy(bool releaseHandle);
|
||||
virtual void Cancel_Finally() =0;
|
||||
virtual Handle_t GetHandle();
|
||||
virtual unsigned int GetMenuOptionFlags();
|
||||
virtual void SetMenuOptionFlags(unsigned int flags);
|
||||
virtual IMenuHandler *GetHandler();
|
||||
unsigned int GetBaseMemUsage();
|
||||
private:
|
||||
void InternalDelete();
|
||||
protected:
|
||||
String m_Title;
|
||||
IMenuStyle *m_pStyle;
|
||||
BaseStringTable m_Strings;
|
||||
unsigned int m_Pagination;
|
||||
CVector<CItem> m_items;
|
||||
bool m_bShouldDelete;
|
||||
bool m_bCancelling;
|
||||
IdentityToken_t *m_pOwner;
|
||||
bool m_bDeleting;
|
||||
bool m_bWillFreeHandle;
|
||||
Handle_t m_hHandle;
|
||||
IMenuHandler *m_pHandler;
|
||||
unsigned int m_nFlags;
|
||||
};
|
||||
|
||||
#endif //_INCLUDE_MENUSTYLE_BASE_H
|
||||
@@ -0,0 +1,566 @@
|
||||
/**
|
||||
* vim: set ts=4 :
|
||||
* =============================================================================
|
||||
* SourceMod
|
||||
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
|
||||
* =============================================================================
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, version 3.0, as published by the
|
||||
* Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* As a special exception, AlliedModders LLC gives you permission to link the
|
||||
* code of this program (as well as its derivative works) to "Half-Life 2," the
|
||||
* "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
|
||||
* by the Valve Corporation. You must obey the GNU General Public License in
|
||||
* all respects for all other code used. Additionally, AlliedModders LLC grants
|
||||
* this exception to all derivative works. AlliedModders LLC defines further
|
||||
* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
|
||||
* or <http://www.sourcemod.net/license.php>.
|
||||
*
|
||||
* Version: $Id$
|
||||
*/
|
||||
|
||||
#include "MenuStyle_Radio.h"
|
||||
#include "sm_stringutil.h"
|
||||
#include "UserMessages.h"
|
||||
#include "GameConfigs.h"
|
||||
#include "PlayerManager.h"
|
||||
#if defined MENU_DEBUG
|
||||
#include "Logger.h"
|
||||
#endif
|
||||
|
||||
extern const char *g_RadioNumTable[];
|
||||
CRadioStyle g_RadioMenuStyle;
|
||||
int g_ShowMenuId = -1;
|
||||
bool g_bRadioInit = false;
|
||||
unsigned int g_RadioMenuTimeout = 0;
|
||||
|
||||
CRadioStyle::CRadioStyle()
|
||||
{
|
||||
m_players = new CRadioMenuPlayer[256+1];
|
||||
for (size_t i = 0; i < 256+1; i++)
|
||||
{
|
||||
m_players[i].Radio_SetIndex(i);
|
||||
}
|
||||
}
|
||||
|
||||
void CRadioStyle::OnSourceModAllInitialized()
|
||||
{
|
||||
g_Players.AddClientListener(this);
|
||||
}
|
||||
|
||||
void CRadioStyle::OnSourceModLevelChange(const char *mapName)
|
||||
{
|
||||
if (g_bRadioInit)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
g_bRadioInit = true;
|
||||
const char *msg = g_pGameConf->GetKeyValue("HudRadioMenuMsg");
|
||||
if (!msg || msg[0] == '\0')
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
g_ShowMenuId = g_UserMsgs.GetMessageIndex(msg);
|
||||
|
||||
if (!IsSupported())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const char *val = g_pGameConf->GetKeyValue("RadioMenuTimeout");
|
||||
if (val != NULL)
|
||||
{
|
||||
g_RadioMenuTimeout = atoi(val);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_RadioMenuTimeout = 0;
|
||||
}
|
||||
|
||||
g_Menus.AddStyle(this);
|
||||
g_Menus.SetDefaultStyle(this);
|
||||
|
||||
g_UserMsgs.HookUserMessage(g_ShowMenuId, this, false);
|
||||
}
|
||||
|
||||
void CRadioStyle::OnSourceModShutdown()
|
||||
{
|
||||
g_Players.RemoveClientListener(this);
|
||||
g_UserMsgs.UnhookUserMessage(g_ShowMenuId, this, false);
|
||||
|
||||
while (!m_FreeDisplays.empty())
|
||||
{
|
||||
delete m_FreeDisplays.front();
|
||||
m_FreeDisplays.pop();
|
||||
}
|
||||
}
|
||||
|
||||
bool CRadioStyle::IsSupported()
|
||||
{
|
||||
return (g_ShowMenuId != -1);
|
||||
}
|
||||
|
||||
bool CRadioStyle::OnClientCommand(int client, const char *cmdname, const CCommand &cmd)
|
||||
{
|
||||
if (strcmp(cmdname, "menuselect") == 0)
|
||||
{
|
||||
if (!m_players[client].bInMenu)
|
||||
{
|
||||
m_players[client].bInExternMenu = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
int arg = atoi(cmd.Arg(1));
|
||||
ClientPressedKey(client, arg);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static unsigned int g_last_holdtime = 0;
|
||||
static unsigned int g_last_client_count = 0;
|
||||
static int g_last_clients[256];
|
||||
|
||||
void CRadioStyle::OnUserMessage(int msg_id, bf_write *bf, IRecipientFilter *pFilter)
|
||||
{
|
||||
int count = pFilter->GetRecipientCount();
|
||||
bf_read br(bf->GetBasePointer(), 3);
|
||||
|
||||
br.ReadWord();
|
||||
int c = br.ReadChar();
|
||||
|
||||
g_last_holdtime = (c == -1) ? 0 : (unsigned)c;
|
||||
|
||||
for (int i=0; i<count; i++)
|
||||
{
|
||||
g_last_clients[g_last_client_count++] = pFilter->GetRecipientIndex(i);
|
||||
}
|
||||
}
|
||||
|
||||
void CRadioStyle::OnUserMessageSent(int msg_id)
|
||||
{
|
||||
for (unsigned int i=0; i<g_last_client_count; i++)
|
||||
{
|
||||
int client = g_last_clients[i];
|
||||
#if defined MENU_DEBUG
|
||||
g_Logger.LogMessage("[SM_MENU] CRadioStyle got ShowMenu (client %d) (bInMenu %d)",
|
||||
client,
|
||||
m_players[client].bInExternMenu);
|
||||
#endif
|
||||
if (m_players[client].bInMenu)
|
||||
{
|
||||
_CancelClientMenu(client, MenuCancel_Interrupted, true);
|
||||
}
|
||||
m_players[client].bInExternMenu = true;
|
||||
m_players[client].menuHoldTime = g_last_holdtime;
|
||||
}
|
||||
g_last_client_count = 0;
|
||||
}
|
||||
|
||||
void CRadioStyle::SendDisplay(int client, IMenuPanel *display)
|
||||
{
|
||||
CRadioDisplay *rDisplay = (CRadioDisplay *)display;
|
||||
rDisplay->SendRawDisplay(client, m_players[client].menuHoldTime);
|
||||
}
|
||||
|
||||
IMenuPanel *CRadioStyle::CreatePanel()
|
||||
{
|
||||
return g_RadioMenuStyle.MakeRadioDisplay();
|
||||
}
|
||||
|
||||
IBaseMenu *CRadioStyle::CreateMenu(IMenuHandler *pHandler, IdentityToken_t *pOwner)
|
||||
{
|
||||
return new CRadioMenu(pHandler, pOwner);
|
||||
}
|
||||
|
||||
unsigned int CRadioStyle::GetMaxPageItems()
|
||||
{
|
||||
return 10;
|
||||
}
|
||||
|
||||
const char *CRadioStyle::GetStyleName()
|
||||
{
|
||||
return "radio";
|
||||
}
|
||||
|
||||
CBaseMenuPlayer *CRadioStyle::GetMenuPlayer(int client)
|
||||
{
|
||||
return &m_players[client];
|
||||
}
|
||||
|
||||
CRadioDisplay *CRadioStyle::MakeRadioDisplay(CRadioMenu *menu)
|
||||
{
|
||||
CRadioDisplay *display;
|
||||
if (m_FreeDisplays.empty())
|
||||
{
|
||||
display = new CRadioDisplay();
|
||||
}
|
||||
else
|
||||
{
|
||||
display = m_FreeDisplays.front();
|
||||
m_FreeDisplays.pop();
|
||||
display->Reset();
|
||||
}
|
||||
return display;
|
||||
}
|
||||
|
||||
IMenuPanel *CRadioStyle::MakeRadioDisplay(const char *str, int keys)
|
||||
{
|
||||
CRadioDisplay *pPanel = MakeRadioDisplay(NULL);
|
||||
|
||||
pPanel->DirectSet(str, keys);
|
||||
|
||||
return pPanel;
|
||||
}
|
||||
|
||||
void CRadioStyle::FreeRadioDisplay(CRadioDisplay *display)
|
||||
{
|
||||
m_FreeDisplays.push(display);
|
||||
}
|
||||
|
||||
CRadioMenuPlayer *CRadioStyle::GetRadioMenuPlayer(int client)
|
||||
{
|
||||
return &m_players[client];
|
||||
}
|
||||
|
||||
void CRadioStyle::ProcessWatchList()
|
||||
{
|
||||
if (!g_RadioMenuTimeout)
|
||||
{
|
||||
BaseMenuStyle::ProcessWatchList();
|
||||
return;
|
||||
}
|
||||
|
||||
BaseMenuStyle::ProcessWatchList();
|
||||
|
||||
CRadioMenuPlayer *pPlayer;
|
||||
unsigned int max_clients = g_Players.GetMaxClients();
|
||||
for (unsigned int i = 1; i <= max_clients; i++)
|
||||
{
|
||||
pPlayer = GetRadioMenuPlayer(i);
|
||||
if (!pPlayer->bInMenu || pPlayer->bInExternMenu)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (pPlayer->Radio_NeedsRefresh())
|
||||
{
|
||||
pPlayer->Radio_Refresh();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int CRadioStyle::GetApproxMemUsage()
|
||||
{
|
||||
return sizeof(CRadioStyle) + (sizeof(CRadioMenuPlayer) * 257);
|
||||
}
|
||||
|
||||
CRadioDisplay::CRadioDisplay()
|
||||
{
|
||||
Reset();
|
||||
}
|
||||
|
||||
CRadioDisplay::CRadioDisplay(CRadioMenu *menu)
|
||||
{
|
||||
Reset();
|
||||
}
|
||||
|
||||
bool CRadioDisplay::DrawRawLine(const char *rawline)
|
||||
{
|
||||
m_BufferText.append(rawline);
|
||||
m_BufferText.append("\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
void CRadioDisplay::Reset()
|
||||
{
|
||||
m_BufferText.assign("");
|
||||
m_Title.assign("");
|
||||
m_NextPos = 1;
|
||||
keys = 0;
|
||||
}
|
||||
|
||||
void CRadioDisplay::DirectSet(const char *str, int keymap)
|
||||
{
|
||||
m_Title.clear();
|
||||
m_BufferText.assign(str);
|
||||
keys = keymap;
|
||||
}
|
||||
|
||||
unsigned int CRadioDisplay::GetCurrentKey()
|
||||
{
|
||||
return m_NextPos;
|
||||
}
|
||||
|
||||
bool CRadioDisplay::SetCurrentKey(unsigned int key)
|
||||
{
|
||||
if (key < m_NextPos || m_NextPos > 10)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
m_NextPos = key;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CRadioDisplay::SendDisplay(int client, IMenuHandler *handler, unsigned int time)
|
||||
{
|
||||
return g_RadioMenuStyle.DoClientMenu(client, this, handler, time);
|
||||
}
|
||||
|
||||
bool CRadioDisplay::SetExtOption(MenuOption option, const void *valuePtr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
IMenuStyle *CRadioDisplay::GetParentStyle()
|
||||
{
|
||||
return &g_RadioMenuStyle;
|
||||
}
|
||||
|
||||
void CRadioDisplay::DrawTitle(const char *text, bool onlyIfEmpty/* =false */)
|
||||
{
|
||||
if (m_Title.size() != 0 && onlyIfEmpty)
|
||||
{
|
||||
return;
|
||||
}
|
||||
m_Title.assign(text);
|
||||
}
|
||||
|
||||
unsigned int CRadioDisplay::DrawItem(const ItemDrawInfo &item)
|
||||
{
|
||||
if (m_NextPos > 10 || !CanDrawItem(item.style))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (item.style & ITEMDRAW_RAWLINE)
|
||||
{
|
||||
if (item.style & ITEMDRAW_SPACER)
|
||||
{
|
||||
m_BufferText.append(" \n");
|
||||
} else {
|
||||
m_BufferText.append(item.display);
|
||||
m_BufferText.append("\n");
|
||||
}
|
||||
return 0;
|
||||
} else if (item.style & ITEMDRAW_SPACER) {
|
||||
m_BufferText.append(" \n");
|
||||
return m_NextPos++;
|
||||
} else if (item.style & ITEMDRAW_NOTEXT) {
|
||||
return m_NextPos++;
|
||||
}
|
||||
|
||||
if (item.style & ITEMDRAW_DISABLED)
|
||||
{
|
||||
m_BufferText.append(g_RadioNumTable[m_NextPos]);
|
||||
m_BufferText.append(item.display);
|
||||
m_BufferText.append("\n");
|
||||
} else {
|
||||
m_BufferText.append("->");
|
||||
m_BufferText.append(g_RadioNumTable[m_NextPos]);
|
||||
m_BufferText.append(item.display);
|
||||
m_BufferText.append("\n");
|
||||
keys |= (1<<(m_NextPos-1));
|
||||
}
|
||||
|
||||
return m_NextPos++;
|
||||
}
|
||||
|
||||
bool CRadioDisplay::CanDrawItem(unsigned int drawFlags)
|
||||
{
|
||||
if ((drawFlags & ITEMDRAW_IGNORE) == ITEMDRAW_IGNORE)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((drawFlags & ITEMDRAW_DISABLED) && (drawFlags & ITEMDRAW_CONTROL))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CRadioDisplay::SendRawDisplay(int client, unsigned int time)
|
||||
{
|
||||
int _sel_keys = (keys == 0) ? (1<<9) : keys;
|
||||
CRadioMenuPlayer *pPlayer = g_RadioMenuStyle.GetRadioMenuPlayer(client);
|
||||
pPlayer->Radio_Init(_sel_keys, m_Title.c_str(), m_BufferText.c_str());
|
||||
pPlayer->Radio_Refresh();
|
||||
}
|
||||
|
||||
void CRadioMenuPlayer::Radio_SetIndex(unsigned int index)
|
||||
{
|
||||
m_index = index;
|
||||
}
|
||||
|
||||
bool CRadioMenuPlayer::Radio_NeedsRefresh()
|
||||
{
|
||||
return (gpGlobals->curtime - display_last_refresh >= g_RadioMenuTimeout);
|
||||
}
|
||||
|
||||
void CRadioMenuPlayer::Radio_Init(int keys, const char *title, const char *text)
|
||||
{
|
||||
if (title[0] != '\0')
|
||||
{
|
||||
display_len = UTIL_Format(display_pkt,
|
||||
sizeof(display_pkt),
|
||||
"%s\n%s",
|
||||
title,
|
||||
text);
|
||||
}
|
||||
else
|
||||
{
|
||||
display_len = UTIL_Format(display_pkt,
|
||||
sizeof(display_pkt),
|
||||
"%s",
|
||||
text);
|
||||
}
|
||||
display_keys = keys;
|
||||
}
|
||||
|
||||
void CRadioMenuPlayer::Radio_Refresh()
|
||||
{
|
||||
cell_t players[1] = {m_index};
|
||||
char *ptr = display_pkt;
|
||||
char save = 0;
|
||||
size_t len = display_len;
|
||||
unsigned int time;
|
||||
|
||||
/* Compute the new time */
|
||||
if (menuHoldTime == 0)
|
||||
{
|
||||
time = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
time = menuHoldTime - (unsigned int)(gpGlobals->curtime - menuStartTime);
|
||||
}
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (len > 240)
|
||||
{
|
||||
save = ptr[240];
|
||||
ptr[240] = '\0';
|
||||
}
|
||||
bf_write *buffer = g_UserMsgs.StartMessage(g_ShowMenuId, players, 1, USERMSG_BLOCKHOOKS);
|
||||
buffer->WriteWord(display_keys);
|
||||
buffer->WriteChar(time ? time : -1);
|
||||
buffer->WriteByte( (len > 240) ? 1 : 0 );
|
||||
buffer->WriteString(ptr);
|
||||
g_UserMsgs.EndMessage();
|
||||
if (len > 240)
|
||||
{
|
||||
ptr[240] = save;
|
||||
ptr = &ptr[240];
|
||||
len -= 240;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
display_last_refresh = gpGlobals->curtime;
|
||||
}
|
||||
|
||||
int CRadioDisplay::GetAmountRemaining()
|
||||
{
|
||||
size_t amt = m_Title.size() + 1 + m_BufferText.size();
|
||||
if (amt >= 511)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
return (int)(511 - amt);
|
||||
}
|
||||
|
||||
void CRadioDisplay::DeleteThis()
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
|
||||
bool CRadioDisplay::SetSelectableKeys(unsigned int keymap)
|
||||
{
|
||||
keys = (signed)keymap;
|
||||
return true;
|
||||
}
|
||||
|
||||
unsigned int CRadioDisplay::GetApproxMemUsage()
|
||||
{
|
||||
return sizeof(CRadioDisplay)
|
||||
+ m_BufferText.size()
|
||||
+ m_Title.size();
|
||||
}
|
||||
|
||||
CRadioMenu::CRadioMenu(IMenuHandler *pHandler, IdentityToken_t *pOwner) :
|
||||
CBaseMenu(pHandler, &g_RadioMenuStyle, pOwner)
|
||||
{
|
||||
}
|
||||
|
||||
bool CRadioMenu::SetExtOption(MenuOption option, const void *valuePtr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
IMenuPanel *CRadioMenu::CreatePanel()
|
||||
{
|
||||
return g_RadioMenuStyle.MakeRadioDisplay(this);
|
||||
}
|
||||
|
||||
bool CRadioMenu::Display(int client, unsigned int time, IMenuHandler *alt_handler)
|
||||
{
|
||||
return DisplayAtItem(client, time, 0, alt_handler);
|
||||
}
|
||||
|
||||
bool CRadioMenu::DisplayAtItem(int client,
|
||||
unsigned int time,
|
||||
unsigned int start_item,
|
||||
IMenuHandler *alt_handler)
|
||||
{
|
||||
#if defined MENU_DEBUG
|
||||
g_Logger.LogMessage("[SM_MENU] CRadioMenu::Display(%p) (client %d) (time %d)",
|
||||
this,
|
||||
client,
|
||||
time);
|
||||
#endif
|
||||
if (m_bCancelling)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return g_RadioMenuStyle.DoClientMenu(client,
|
||||
this,
|
||||
start_item,
|
||||
alt_handler ? alt_handler : m_pHandler,
|
||||
time);
|
||||
}
|
||||
|
||||
void CRadioMenu::Cancel_Finally()
|
||||
{
|
||||
g_RadioMenuStyle.CancelMenu(this);
|
||||
}
|
||||
|
||||
unsigned int CRadioMenu::GetApproxMemUsage()
|
||||
{
|
||||
return sizeof(CRadioMenu) + GetBaseMemUsage();
|
||||
}
|
||||
|
||||
const char *g_RadioNumTable[11] =
|
||||
{
|
||||
"0. ", "1. ", "2. ", "3. ", "4. ", "5. ", "6. ", "7. ", "8. ", "9. ", "0. "
|
||||
};
|
||||
@@ -0,0 +1,151 @@
|
||||
/**
|
||||
* vim: set ts=4 :
|
||||
* =============================================================================
|
||||
* SourceMod
|
||||
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
|
||||
* =============================================================================
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, version 3.0, as published by the
|
||||
* Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* As a special exception, AlliedModders LLC gives you permission to link the
|
||||
* code of this program (as well as its derivative works) to "Half-Life 2," the
|
||||
* "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
|
||||
* by the Valve Corporation. You must obey the GNU General Public License in
|
||||
* all respects for all other code used. Additionally, AlliedModders LLC grants
|
||||
* this exception to all derivative works. AlliedModders LLC defines further
|
||||
* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
|
||||
* or <http://www.sourcemod.net/license.php>.
|
||||
*
|
||||
* Version: $Id$
|
||||
*/
|
||||
|
||||
#ifndef _INCLUDE_MENUSTYLE_RADIO_H
|
||||
#define _INCLUDE_MENUSTYLE_RADIO_H
|
||||
|
||||
#include "sm_globals.h"
|
||||
#include "MenuManager.h"
|
||||
#include "MenuStyle_Base.h"
|
||||
#include "sourcemm_api.h"
|
||||
#include <IPlayerHelpers.h>
|
||||
#include <IUserMessages.h>
|
||||
#include "sm_fastlink.h"
|
||||
#include <sh_stack.h>
|
||||
#include <compat_wrappers.h>
|
||||
|
||||
using namespace SourceMod;
|
||||
|
||||
class CRadioDisplay;
|
||||
class CRadioMenu;
|
||||
|
||||
class CRadioMenuPlayer : public CBaseMenuPlayer
|
||||
{
|
||||
public:
|
||||
void Radio_Init(int keys, const char *title, const char *buffer);
|
||||
bool Radio_NeedsRefresh();
|
||||
void Radio_Refresh();
|
||||
void Radio_SetIndex(unsigned int index);
|
||||
private:
|
||||
unsigned int m_index;
|
||||
size_t display_len;
|
||||
char display_pkt[512];
|
||||
int display_keys;
|
||||
float display_last_refresh;
|
||||
};
|
||||
|
||||
class CRadioStyle :
|
||||
public BaseMenuStyle,
|
||||
public SMGlobalClass,
|
||||
public IUserMessageListener
|
||||
{
|
||||
public:
|
||||
CRadioStyle();
|
||||
public: //SMGlobalClass
|
||||
void OnSourceModAllInitialized();
|
||||
void OnSourceModLevelChange(const char *mapName);
|
||||
void OnSourceModShutdown();
|
||||
public: //BaseMenuStyle
|
||||
CBaseMenuPlayer *GetMenuPlayer(int client);
|
||||
void SendDisplay(int client, IMenuPanel *display);
|
||||
void ProcessWatchList();
|
||||
public: //IMenuStyle
|
||||
const char *GetStyleName();
|
||||
IMenuPanel *CreatePanel();
|
||||
IBaseMenu *CreateMenu(IMenuHandler *pHandler, IdentityToken_t *pOwner);
|
||||
unsigned int GetMaxPageItems();
|
||||
unsigned int GetApproxMemUsage();
|
||||
public: //IUserMessageListener
|
||||
void OnUserMessage(int msg_id, bf_write *bf, IRecipientFilter *pFilter);
|
||||
void OnUserMessageSent(int msg_id);
|
||||
public:
|
||||
bool IsSupported();
|
||||
bool OnClientCommand(int client, const char *cmdname, const CCommand &cmd);
|
||||
public:
|
||||
CRadioDisplay *MakeRadioDisplay(CRadioMenu *menu=NULL);
|
||||
void FreeRadioDisplay(CRadioDisplay *display);
|
||||
CRadioMenuPlayer *GetRadioMenuPlayer(int client);
|
||||
IMenuPanel *MakeRadioDisplay(const char *str, int keys);
|
||||
private:
|
||||
CRadioMenuPlayer *m_players;
|
||||
CStack<CRadioDisplay *> m_FreeDisplays;
|
||||
};
|
||||
|
||||
class CRadioDisplay : public IMenuPanel
|
||||
{
|
||||
friend class CRadioStyle;
|
||||
public:
|
||||
CRadioDisplay();
|
||||
CRadioDisplay(CRadioMenu *menu);
|
||||
public: //IMenuPanel
|
||||
IMenuStyle *GetParentStyle();
|
||||
void Reset();
|
||||
void DrawTitle(const char *text, bool onlyIfEmpty=false);
|
||||
unsigned int DrawItem(const ItemDrawInfo &item);
|
||||
bool DrawRawLine(const char *rawline);
|
||||
bool SetExtOption(MenuOption option, const void *valuePtr);
|
||||
bool CanDrawItem(unsigned int drawFlags);
|
||||
bool SendDisplay(int client, IMenuHandler *handler, unsigned int time);
|
||||
void DeleteThis();
|
||||
void SendRawDisplay(int client, unsigned int time);
|
||||
bool SetSelectableKeys(unsigned int keymap);
|
||||
unsigned int GetCurrentKey();
|
||||
bool SetCurrentKey(unsigned int key);
|
||||
int GetAmountRemaining();
|
||||
unsigned int GetApproxMemUsage();
|
||||
public:
|
||||
void DirectSet(const char *str, int keymap);
|
||||
private:
|
||||
String m_BufferText;
|
||||
String m_Title;
|
||||
unsigned int m_NextPos;
|
||||
int keys;
|
||||
};
|
||||
|
||||
class CRadioMenu : public CBaseMenu
|
||||
{
|
||||
public:
|
||||
CRadioMenu(IMenuHandler *pHandler, IdentityToken_t *pOwner);
|
||||
public:
|
||||
bool SetExtOption(MenuOption option, const void *valuePtr);
|
||||
IMenuPanel *CreatePanel();
|
||||
bool Display(int client, unsigned int time, IMenuHandler *alt_handler=NULL);
|
||||
bool DisplayAtItem(int client,
|
||||
unsigned int time,
|
||||
unsigned int start_item,
|
||||
IMenuHandler *alt_handler/* =NULL */);
|
||||
void Cancel_Finally();
|
||||
unsigned int GetApproxMemUsage();
|
||||
};
|
||||
|
||||
extern CRadioStyle g_RadioMenuStyle;
|
||||
|
||||
#endif //_INCLUDE_MENUSTYLE_RADIO_H
|
||||
@@ -0,0 +1,445 @@
|
||||
/**
|
||||
* vim: set ts=4 :
|
||||
* =============================================================================
|
||||
* SourceMod
|
||||
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
|
||||
* =============================================================================
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, version 3.0, as published by the
|
||||
* Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* As a special exception, AlliedModders LLC gives you permission to link the
|
||||
* code of this program (as well as its derivative works) to "Half-Life 2," the
|
||||
* "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
|
||||
* by the Valve Corporation. You must obey the GNU General Public License in
|
||||
* all respects for all other code used. Additionally, AlliedModders LLC grants
|
||||
* this exception to all derivative works. AlliedModders LLC defines further
|
||||
* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
|
||||
* or <http://www.sourcemod.net/license.php>.
|
||||
*
|
||||
* Version: $Id$
|
||||
*/
|
||||
|
||||
#include "sm_stringutil.h"
|
||||
#include "PlayerManager.h"
|
||||
#include "MenuStyle_Valve.h"
|
||||
#include "Translator.h"
|
||||
#include "PlayerManager.h"
|
||||
#include "ConCmdManager.h"
|
||||
|
||||
SH_DECL_HOOK4_void(IServerPluginHelpers, CreateMessage, SH_NOATTRIB, false, edict_t *, DIALOG_TYPE, KeyValues *, IServerPluginCallbacks *);
|
||||
|
||||
ValveMenuStyle g_ValveMenuStyle;
|
||||
extern const char *g_OptionNumTable[];
|
||||
extern const char *g_OptionCmdTable[];
|
||||
CallClass<IServerPluginHelpers> *g_pSPHCC = NULL;
|
||||
|
||||
ValveMenuStyle::ValveMenuStyle() : m_players(new CValveMenuPlayer[256+1])
|
||||
{
|
||||
}
|
||||
|
||||
CBaseMenuPlayer *ValveMenuStyle::GetMenuPlayer(int client)
|
||||
{
|
||||
return &m_players[client];
|
||||
}
|
||||
|
||||
bool ValveMenuStyle::OnClientCommand(int client, const char *cmdname, const CCommand &cmd)
|
||||
{
|
||||
if (strcmp(cmdname, "sm_vmenuselect") == 0)
|
||||
{
|
||||
int key_press = atoi(cmd.Arg(1));
|
||||
g_ValveMenuStyle.ClientPressedKey(client, key_press);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void ValveMenuStyle::OnSourceModAllInitialized()
|
||||
{
|
||||
g_Players.AddClientListener(this);
|
||||
SH_ADD_HOOK_MEMFUNC(IServerPluginHelpers, CreateMessage, serverpluginhelpers, this, &ValveMenuStyle::HookCreateMessage, false);
|
||||
g_pSPHCC = SH_GET_CALLCLASS(serverpluginhelpers);
|
||||
}
|
||||
|
||||
void ValveMenuStyle::OnSourceModShutdown()
|
||||
{
|
||||
SH_RELEASE_CALLCLASS(g_pSPHCC);
|
||||
SH_REMOVE_HOOK_MEMFUNC(IServerPluginHelpers, CreateMessage, serverpluginhelpers, this, &ValveMenuStyle::HookCreateMessage, false);
|
||||
g_Players.RemoveClientListener(this);
|
||||
}
|
||||
|
||||
void ValveMenuStyle::HookCreateMessage(edict_t *pEdict,
|
||||
DIALOG_TYPE type,
|
||||
KeyValues *kv,
|
||||
IServerPluginCallbacks *plugin)
|
||||
{
|
||||
if (type != DIALOG_MENU)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int client = engine->IndexOfEdict(pEdict);
|
||||
if (client < 1 || client > 256)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
CValveMenuPlayer *player = &m_players[client];
|
||||
|
||||
/* We don't care if the player is in a menu because, for all intents and purposes,
|
||||
* the menu is completely private. Instead, we just figure out the level we'll need
|
||||
* in order to override it.
|
||||
*/
|
||||
player->curPrioLevel = kv->GetInt("level", player->curPrioLevel);
|
||||
|
||||
/* Oh no! What happens if we got a menu that overwrites ours?! */
|
||||
if (player->bInMenu)
|
||||
{
|
||||
/* Okay, let the external menu survive for now. It may live another
|
||||
* day to avenge its grandfather, killed in the great Menu Interruption
|
||||
* battle.
|
||||
*/
|
||||
_CancelClientMenu(client, MenuCancel_Interrupted, true);
|
||||
}
|
||||
}
|
||||
|
||||
IMenuPanel *ValveMenuStyle::CreatePanel()
|
||||
{
|
||||
return new CValveMenuDisplay();
|
||||
}
|
||||
|
||||
IBaseMenu *ValveMenuStyle::CreateMenu(IMenuHandler *pHandler, IdentityToken_t *pOwner)
|
||||
{
|
||||
return new CValveMenu(pHandler, pOwner);
|
||||
}
|
||||
|
||||
const char *ValveMenuStyle::GetStyleName()
|
||||
{
|
||||
return "valve";
|
||||
}
|
||||
|
||||
unsigned int ValveMenuStyle::GetMaxPageItems()
|
||||
{
|
||||
return 8;
|
||||
}
|
||||
|
||||
void ValveMenuStyle::SendDisplay(int client, IMenuPanel *display)
|
||||
{
|
||||
m_players[client].curPrioLevel--;
|
||||
CValveMenuDisplay *vDisplay = (CValveMenuDisplay *)display;
|
||||
vDisplay->SendRawDisplay(client, m_players[client].curPrioLevel, m_players[client].menuHoldTime);
|
||||
}
|
||||
|
||||
bool ValveMenuStyle::DoClientMenu(int client, IMenuPanel *menu, IMenuHandler *mh, unsigned int time)
|
||||
{
|
||||
if (vsp_interface == NULL)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return BaseMenuStyle::DoClientMenu(client, menu, mh, time);
|
||||
}
|
||||
|
||||
bool ValveMenuStyle::DoClientMenu(int client, CBaseMenu *menu, unsigned int first_item, IMenuHandler *mh, unsigned int time)
|
||||
{
|
||||
if (vsp_interface == NULL)
|
||||
{
|
||||
mh->OnMenuStart(menu);
|
||||
mh->OnMenuCancel(menu, client, MenuCancel_NoDisplay);
|
||||
mh->OnMenuEnd(menu, MenuEnd_Cancelled);
|
||||
return false;
|
||||
}
|
||||
|
||||
return BaseMenuStyle::DoClientMenu(client, menu, first_item, mh, time);
|
||||
}
|
||||
|
||||
unsigned int ValveMenuStyle::GetApproxMemUsage()
|
||||
{
|
||||
return sizeof(ValveMenuStyle) + (sizeof(CValveMenuPlayer) * 257);
|
||||
}
|
||||
|
||||
CValveMenuDisplay::CValveMenuDisplay()
|
||||
{
|
||||
m_pKv = NULL;
|
||||
Reset();
|
||||
}
|
||||
|
||||
CValveMenuDisplay::CValveMenuDisplay(CValveMenu *pMenu)
|
||||
{
|
||||
m_pKv = NULL;
|
||||
Reset();
|
||||
m_pKv->SetColor("color", pMenu->m_IntroColor);
|
||||
m_pKv->SetString("title", pMenu->m_IntroMsg);
|
||||
}
|
||||
|
||||
void CValveMenuDisplay::DeleteThis()
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
|
||||
CValveMenuDisplay::~CValveMenuDisplay()
|
||||
{
|
||||
m_pKv->deleteThis();
|
||||
}
|
||||
|
||||
IMenuStyle *CValveMenuDisplay::GetParentStyle()
|
||||
{
|
||||
return &g_ValveMenuStyle;
|
||||
}
|
||||
|
||||
void CValveMenuDisplay::Reset()
|
||||
{
|
||||
if (m_pKv)
|
||||
{
|
||||
m_pKv->deleteThis();
|
||||
}
|
||||
m_pKv = new KeyValues("menu");
|
||||
m_NextPos = 1;
|
||||
m_TitleDrawn = false;
|
||||
}
|
||||
|
||||
unsigned int CValveMenuDisplay::GetCurrentKey()
|
||||
{
|
||||
return m_NextPos;
|
||||
}
|
||||
|
||||
bool CValveMenuDisplay::SetCurrentKey(unsigned int key)
|
||||
{
|
||||
if (key < m_NextPos || key > 9)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
m_NextPos = key;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CValveMenuDisplay::SetExtOption(MenuOption option, const void *valuePtr)
|
||||
{
|
||||
if (option == MenuOption_IntroMessage)
|
||||
{
|
||||
m_pKv->SetString("title", (const char *)valuePtr);
|
||||
return true;
|
||||
} else if (option == MenuOption_IntroColor) {
|
||||
int *array = (int *)valuePtr;
|
||||
m_pKv->SetColor("color", Color(array[0], array[1], array[2], array[3]));
|
||||
return true;
|
||||
} else if (option == MenuOption_Priority) {
|
||||
m_pKv->SetInt("level", *(int *)valuePtr);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CValveMenuDisplay::CanDrawItem(unsigned int drawFlags)
|
||||
{
|
||||
/**
|
||||
* ITEMDRAW_RAWLINE - We can't draw random text, and this doesn't add a slot,
|
||||
* so it's completely safe to ignore it.
|
||||
* -----------------------------------------
|
||||
*/
|
||||
if (drawFlags & ITEMDRAW_RAWLINE)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Special cases, explained in DrawItem()
|
||||
*/
|
||||
if ((drawFlags & ITEMDRAW_NOTEXT)
|
||||
|| (drawFlags & ITEMDRAW_SPACER))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* We can't draw disabled text. We could bump the position, but we
|
||||
* want DirectDraw() to find some actual items to display.
|
||||
*/
|
||||
if (drawFlags & ITEMDRAW_DISABLED)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
unsigned int CValveMenuDisplay::DrawItem(const ItemDrawInfo &item)
|
||||
{
|
||||
if (m_NextPos > 9 || !CanDrawItem(item.style))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* For these cases we can't draw anything at all, but
|
||||
* we can at least bump the position since we were explicitly asked to.
|
||||
*/
|
||||
if ((item.style & ITEMDRAW_NOTEXT)
|
||||
|| (item.style & ITEMDRAW_SPACER))
|
||||
{
|
||||
return m_NextPos++;
|
||||
}
|
||||
|
||||
char buffer[255];
|
||||
UTIL_Format(buffer, sizeof(buffer), "%d. %s", m_NextPos, item.display);
|
||||
|
||||
KeyValues *ki = m_pKv->FindKey(g_OptionNumTable[m_NextPos], true);
|
||||
ki->SetString("command", g_OptionCmdTable[m_NextPos]);
|
||||
ki->SetString("msg", buffer);
|
||||
|
||||
return m_NextPos++;
|
||||
}
|
||||
|
||||
void CValveMenuDisplay::DrawTitle(const char *text, bool onlyIfEmpty/* =false */)
|
||||
{
|
||||
if (onlyIfEmpty && m_TitleDrawn)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
m_pKv->SetString("msg", text);
|
||||
m_TitleDrawn = true;
|
||||
}
|
||||
|
||||
bool CValveMenuDisplay::DrawRawLine(const char *rawline)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void CValveMenuDisplay::SendRawDisplay(int client, int priority, unsigned int time)
|
||||
{
|
||||
m_pKv->SetInt("level", priority);
|
||||
m_pKv->SetInt("time", time ? time : 200);
|
||||
|
||||
SH_CALL(g_pSPHCC, &IServerPluginHelpers::CreateMessage)(
|
||||
engine->PEntityOfEntIndex(client),
|
||||
DIALOG_MENU,
|
||||
m_pKv,
|
||||
vsp_interface);
|
||||
}
|
||||
|
||||
bool CValveMenuDisplay::SendDisplay(int client, IMenuHandler *handler, unsigned int time)
|
||||
{
|
||||
return g_ValveMenuStyle.DoClientMenu(client, this, handler, time);
|
||||
}
|
||||
|
||||
bool CValveMenuDisplay::SetSelectableKeys(unsigned int keymap)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
int CValveMenuDisplay::GetAmountRemaining()
|
||||
{
|
||||
/* :TODO: this is a lie, but nothing really seems meaningful... */
|
||||
return -1;
|
||||
}
|
||||
|
||||
unsigned int CValveMenuDisplay::GetApproxMemUsage()
|
||||
{
|
||||
return sizeof(CValveMenuDisplay) + (sizeof(KeyValues) * m_NextPos * 10);
|
||||
}
|
||||
|
||||
CValveMenu::CValveMenu(IMenuHandler *pHandler, IdentityToken_t *pOwner) :
|
||||
CBaseMenu(pHandler, &g_ValveMenuStyle, pOwner),
|
||||
m_IntroColor(255, 0, 0, 255)
|
||||
{
|
||||
strcpy(m_IntroMsg, "You have a menu, press ESC");
|
||||
m_Pagination = 5;
|
||||
}
|
||||
|
||||
void CValveMenu::Cancel_Finally()
|
||||
{
|
||||
g_ValveMenuStyle.CancelMenu(this);
|
||||
}
|
||||
|
||||
bool CValveMenu::SetPagination(unsigned int itemsPerPage)
|
||||
{
|
||||
if (itemsPerPage > 5)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return CBaseMenu::SetPagination(itemsPerPage);
|
||||
}
|
||||
|
||||
bool CValveMenu::SetExtOption(MenuOption option, const void *valuePtr)
|
||||
{
|
||||
if (option == MenuOption_IntroMessage)
|
||||
{
|
||||
strncopy(m_IntroMsg, (const char *)valuePtr, sizeof(m_IntroMsg));
|
||||
return true;
|
||||
} else if (option == MenuOption_IntroColor) {
|
||||
unsigned int *array = (unsigned int *)valuePtr;
|
||||
m_IntroColor = Color(array[0], array[1], array[2], array[3]);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CValveMenu::Display(int client, unsigned int time, IMenuHandler *alt_handler)
|
||||
{
|
||||
return DisplayAtItem(client, time, 0, alt_handler);
|
||||
}
|
||||
|
||||
bool CValveMenu::DisplayAtItem(int client,
|
||||
unsigned int time,
|
||||
unsigned int start_item,
|
||||
IMenuHandler *alt_handler/* =NULL */)
|
||||
{
|
||||
if (m_bCancelling)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return g_ValveMenuStyle.DoClientMenu(client, this, start_item, alt_handler ? alt_handler : m_pHandler, time);
|
||||
}
|
||||
|
||||
IMenuPanel *CValveMenu::CreatePanel()
|
||||
{
|
||||
return new CValveMenuDisplay(this);
|
||||
}
|
||||
|
||||
void CValveMenu::SetMenuOptionFlags(unsigned int flags)
|
||||
{
|
||||
flags |= MENUFLAG_BUTTON_EXIT;
|
||||
CBaseMenu::SetMenuOptionFlags(flags);
|
||||
}
|
||||
|
||||
unsigned int CValveMenu::GetApproxMemUsage()
|
||||
{
|
||||
return sizeof(CValveMenu) + GetBaseMemUsage();
|
||||
}
|
||||
|
||||
const char *g_OptionNumTable[] =
|
||||
{
|
||||
"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10"
|
||||
};
|
||||
|
||||
const char *g_OptionCmdTable[] =
|
||||
{
|
||||
"sm_vmenuselect 0", /* INVALID! */
|
||||
"sm_vmenuselect 1",
|
||||
"sm_vmenuselect 2",
|
||||
"sm_vmenuselect 3",
|
||||
"sm_vmenuselect 4",
|
||||
"sm_vmenuselect 5",
|
||||
"sm_vmenuselect 6",
|
||||
"sm_vmenuselect 7",
|
||||
"sm_vmenuselect 8",
|
||||
"sm_vmenuselect 9",
|
||||
"sm_vmenuselect 10"
|
||||
};
|
||||
@@ -0,0 +1,141 @@
|
||||
/**
|
||||
* vim: set ts=4 :
|
||||
* =============================================================================
|
||||
* SourceMod
|
||||
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
|
||||
* =============================================================================
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, version 3.0, as published by the
|
||||
* Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* As a special exception, AlliedModders LLC gives you permission to link the
|
||||
* code of this program (as well as its derivative works) to "Half-Life 2," the
|
||||
* "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
|
||||
* by the Valve Corporation. You must obey the GNU General Public License in
|
||||
* all respects for all other code used. Additionally, AlliedModders LLC grants
|
||||
* this exception to all derivative works. AlliedModders LLC defines further
|
||||
* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
|
||||
* or <http://www.sourcemod.net/license.php>.
|
||||
*
|
||||
* Version: $Id$
|
||||
*/
|
||||
|
||||
#ifndef _INCLUDE_MENUSTYLE_VALVE_H
|
||||
#define _INCLUDE_MENUSTYLE_VALVE_H
|
||||
|
||||
#include "sm_globals.h"
|
||||
#include "MenuManager.h"
|
||||
#include "MenuStyle_Base.h"
|
||||
#include "sourcemm_api.h"
|
||||
#include "KeyValues.h"
|
||||
#include "sm_fastlink.h"
|
||||
#include <compat_wrappers.h>
|
||||
|
||||
using namespace SourceMod;
|
||||
|
||||
class CValveMenuPlayer : public CBaseMenuPlayer
|
||||
{
|
||||
public:
|
||||
CValveMenuPlayer() : curPrioLevel(1)
|
||||
{
|
||||
}
|
||||
int curPrioLevel;
|
||||
};
|
||||
|
||||
class CValveMenu;
|
||||
class CValveMenuDisplay;
|
||||
|
||||
class ValveMenuStyle :
|
||||
public SMGlobalClass,
|
||||
public BaseMenuStyle
|
||||
{
|
||||
public:
|
||||
ValveMenuStyle();
|
||||
bool OnClientCommand(int client, const char *cmdname, const CCommand &cmd);
|
||||
public: //BaseMenuStyle
|
||||
CBaseMenuPlayer *GetMenuPlayer(int client);
|
||||
void SendDisplay(int client, IMenuPanel *display);
|
||||
bool DoClientMenu(int client, CBaseMenu *menu, unsigned int first_item, IMenuHandler *mh, unsigned int time);
|
||||
bool DoClientMenu(int client, IMenuPanel *menu, IMenuHandler *mh, unsigned int time);
|
||||
public: //SMGlobalClass
|
||||
void OnSourceModAllInitialized();
|
||||
void OnSourceModShutdown();
|
||||
public: //IMenuStyle
|
||||
const char *GetStyleName();
|
||||
IMenuPanel *CreatePanel();
|
||||
IBaseMenu *CreateMenu(IMenuHandler *pHandler, IdentityToken_t *pOwner);
|
||||
unsigned int GetMaxPageItems();
|
||||
unsigned int GetApproxMemUsage();
|
||||
private:
|
||||
void HookCreateMessage(edict_t *pEdict, DIALOG_TYPE type, KeyValues *kv, IServerPluginCallbacks *plugin);
|
||||
private:
|
||||
CValveMenuPlayer *m_players;
|
||||
};
|
||||
|
||||
class CValveMenu;
|
||||
|
||||
class CValveMenuDisplay : public IMenuPanel
|
||||
{
|
||||
public:
|
||||
CValveMenuDisplay();
|
||||
CValveMenuDisplay(CValveMenu *pMenu);
|
||||
~CValveMenuDisplay();
|
||||
public:
|
||||
IMenuStyle *GetParentStyle();
|
||||
void Reset();
|
||||
void DrawTitle(const char *text, bool onlyIfEmpty=false);
|
||||
unsigned int DrawItem(const ItemDrawInfo &item);
|
||||
bool DrawRawLine(const char *rawline);
|
||||
bool SendDisplay(int client, IMenuHandler *handler, unsigned int time);
|
||||
bool SetExtOption(MenuOption option, const void *valuePtr);
|
||||
bool CanDrawItem(unsigned int drawFlags);
|
||||
void SendRawDisplay(int client, int priority, unsigned int time);
|
||||
void DeleteThis();
|
||||
bool SetSelectableKeys(unsigned int keymap);
|
||||
unsigned int GetCurrentKey();
|
||||
bool SetCurrentKey(unsigned int key);
|
||||
int GetAmountRemaining();
|
||||
unsigned int GetApproxMemUsage();
|
||||
private:
|
||||
KeyValues *m_pKv;
|
||||
unsigned int m_NextPos;
|
||||
bool m_TitleDrawn;
|
||||
};
|
||||
|
||||
class CValveMenu : public CBaseMenu
|
||||
{
|
||||
friend class CValveMenuDisplay;
|
||||
public:
|
||||
CValveMenu(IMenuHandler *pHandler, IdentityToken_t *pOwner);
|
||||
public: //IBaseMenu
|
||||
bool SetExtOption(MenuOption option, const void *valuePtr);
|
||||
IMenuPanel *CreatePanel();
|
||||
bool GetExitButton();
|
||||
bool SetExitButton(bool set);
|
||||
bool SetPagination(unsigned int itemsPerPage);
|
||||
bool Display(int client, unsigned int time, IMenuHandler *alt_handler=NULL);
|
||||
bool DisplayAtItem(int client,
|
||||
unsigned int time,
|
||||
unsigned int start_item,
|
||||
IMenuHandler *alt_handler/* =NULL */);
|
||||
void SetMenuOptionFlags(unsigned int flags);
|
||||
unsigned int GetApproxMemUsage();
|
||||
public: //CBaseMenu
|
||||
void Cancel_Finally();
|
||||
private:
|
||||
Color m_IntroColor;
|
||||
char m_IntroMsg[128];
|
||||
};
|
||||
|
||||
extern ValveMenuStyle g_ValveMenuStyle;
|
||||
|
||||
#endif //_INCLUDE_MENUSTYLE_VALVE_H
|
||||
@@ -0,0 +1,491 @@
|
||||
/**
|
||||
* vim: set ts=4 :
|
||||
* =============================================================================
|
||||
* SourceMod
|
||||
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
|
||||
* =============================================================================
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, version 3.0, as published by the
|
||||
* Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* As a special exception, AlliedModders LLC gives you permission to link the
|
||||
* code of this program (as well as its derivative works) to "Half-Life 2," the
|
||||
* "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
|
||||
* by the Valve Corporation. You must obey the GNU General Public License in
|
||||
* all respects for all other code used. Additionally, AlliedModders LLC grants
|
||||
* this exception to all derivative works. AlliedModders LLC defines further
|
||||
* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
|
||||
* or <http://www.sourcemod.net/license.php>.
|
||||
*
|
||||
* Version: $Id$
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include "MenuVoting.h"
|
||||
#include "PlayerManager.h"
|
||||
#include "sourcemm_api.h"
|
||||
|
||||
float g_next_vote = 0.0f;
|
||||
|
||||
#if defined ORANGEBOX_BUILD
|
||||
void OnVoteDelayChange(IConVar *cvar, const char *value, float flOldValue);
|
||||
#else
|
||||
void OnVoteDelayChange(ConVar *cvar, const char *value);
|
||||
#endif
|
||||
ConVar sm_vote_delay("sm_vote_delay",
|
||||
"30",
|
||||
0,
|
||||
"Sets the recommended time in between public votes",
|
||||
false,
|
||||
0.0,
|
||||
false,
|
||||
0.0,
|
||||
OnVoteDelayChange);
|
||||
|
||||
#if defined ORANGEBOX_BUILD
|
||||
void OnVoteDelayChange(IConVar *cvar, const char *value, float flOldValue)
|
||||
#else
|
||||
void OnVoteDelayChange(ConVar *cvar, const char *value)
|
||||
#endif
|
||||
{
|
||||
/* See if the new vote delay isn't something we need to account for */
|
||||
if (sm_vote_delay.GetFloat() < 1.0f)
|
||||
{
|
||||
g_next_vote = 0.0f;
|
||||
return;
|
||||
}
|
||||
|
||||
/* If there was never a last vote, ignore this change */
|
||||
if (g_next_vote < 0.1f)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* Subtract the original value, then add the new one. */
|
||||
g_next_vote -= (float)atof(value);
|
||||
g_next_vote += sm_vote_delay.GetFloat();
|
||||
}
|
||||
|
||||
unsigned int VoteMenuHandler::GetRemainingVoteDelay()
|
||||
{
|
||||
if (g_next_vote <= gpGlobals->curtime)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return (unsigned int)(g_next_vote - gpGlobals->curtime);
|
||||
}
|
||||
|
||||
void VoteMenuHandler::OnSourceModAllInitialized()
|
||||
{
|
||||
g_Players.AddClientListener(this);
|
||||
}
|
||||
|
||||
void VoteMenuHandler::OnSourceModShutdown()
|
||||
{
|
||||
g_Players.RemoveClientListener(this);
|
||||
}
|
||||
|
||||
void VoteMenuHandler::OnSourceModLevelChange(const char *mapName)
|
||||
{
|
||||
g_next_vote = 0.0f;
|
||||
}
|
||||
|
||||
unsigned int VoteMenuHandler::GetMenuAPIVersion2()
|
||||
{
|
||||
return m_pHandler->GetMenuAPIVersion2();
|
||||
}
|
||||
|
||||
void VoteMenuHandler::OnClientDisconnected(int client)
|
||||
{
|
||||
if (!IsVoteInProgress())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* Wipe out their vote if they had one. We have to make sure the the the
|
||||
* newly connected client is not allowed to vote.
|
||||
*/
|
||||
int item;
|
||||
if ((item = m_ClientVotes[client]) >= -1)
|
||||
{
|
||||
if (item >= 0)
|
||||
{
|
||||
assert((unsigned)item < m_Items);
|
||||
assert(m_Votes[item] > 0);
|
||||
m_Votes[item]--;
|
||||
}
|
||||
m_ClientVotes[client] = -2;
|
||||
}
|
||||
}
|
||||
|
||||
bool VoteMenuHandler::IsVoteInProgress()
|
||||
{
|
||||
return (m_pCurMenu != NULL);
|
||||
}
|
||||
|
||||
bool VoteMenuHandler::StartVote(IBaseMenu *menu,
|
||||
unsigned int num_clients,
|
||||
int clients[],
|
||||
unsigned int max_time,
|
||||
unsigned int flags/* =0 */)
|
||||
{
|
||||
if (!InitializeVoting(menu, menu->GetHandler(), max_time, flags))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Note: we can use game time and not universal time because
|
||||
* if we're voting then players are in-game.
|
||||
*/
|
||||
|
||||
float fVoteDelay = sm_vote_delay.GetFloat();
|
||||
if (fVoteDelay < 1.0)
|
||||
{
|
||||
g_next_vote = 0.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* This little trick breaks for infinite votes!
|
||||
* However, we just ignore that since those 1) shouldn't exist and
|
||||
* 2) people must be checking IsVoteInProgress() beforehand anyway.
|
||||
*/
|
||||
g_next_vote = gpGlobals->curtime + fVoteDelay + (float)max_time;
|
||||
}
|
||||
|
||||
m_fStartTime = gpGlobals->curtime;
|
||||
m_nMenuTime = max_time;
|
||||
|
||||
for (unsigned int i=0; i<num_clients; i++)
|
||||
{
|
||||
if (clients[i] < 1 || clients[i] > 256)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
menu->Display(clients[i], max_time, this);
|
||||
}
|
||||
|
||||
StartVoting();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool VoteMenuHandler::IsClientInVotePool(int client)
|
||||
{
|
||||
if (client < 1
|
||||
|| client > g_Players.MaxClients()
|
||||
|| m_pCurMenu == NULL)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return (m_ClientVotes[client] > -2);
|
||||
}
|
||||
|
||||
bool VoteMenuHandler::GetClientVoteChoice(int client, unsigned int *pItem)
|
||||
{
|
||||
if (!IsClientInVotePool(client)
|
||||
|| m_ClientVotes[client] == -1)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
*pItem = m_ClientVotes[client];
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool VoteMenuHandler::RedrawToClient(int client)
|
||||
{
|
||||
unsigned int time_limit;
|
||||
|
||||
if (!IsClientInVotePool(client))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_nMenuTime == MENU_TIME_FOREVER)
|
||||
{
|
||||
time_limit = m_nMenuTime;
|
||||
}
|
||||
else
|
||||
{
|
||||
time_limit = (int)((float)m_nMenuTime - (gpGlobals->curtime - m_fStartTime));
|
||||
|
||||
/* Make sure this doesn't round to zero */
|
||||
if (time_limit == MENU_TIME_FOREVER)
|
||||
{
|
||||
time_limit = 1;
|
||||
}
|
||||
}
|
||||
|
||||
return m_pCurMenu->Display(client, time_limit, this);
|
||||
}
|
||||
|
||||
bool VoteMenuHandler::InitializeVoting(IBaseMenu *menu,
|
||||
IMenuHandler *handler,
|
||||
unsigned int time,
|
||||
unsigned int flags)
|
||||
{
|
||||
if (IsVoteInProgress())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
InternalReset();
|
||||
|
||||
/* Mark all clients as not voting */
|
||||
for (int i=1; i<=gpGlobals->maxClients; i++)
|
||||
{
|
||||
m_ClientVotes[i] = -2;
|
||||
}
|
||||
|
||||
m_Items = menu->GetItemCount();
|
||||
|
||||
if (m_Votes.size() < (size_t)m_Items)
|
||||
{
|
||||
/* Only clear the items we need to... */
|
||||
size_t size = m_Votes.size();
|
||||
for (size_t i=0; i<size; i++)
|
||||
{
|
||||
m_Votes[i] = 0;
|
||||
}
|
||||
m_Votes.resize(m_Items, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (unsigned int i=0; i<m_Items; i++)
|
||||
{
|
||||
m_Votes[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
m_pCurMenu = menu;
|
||||
m_VoteTime = time;
|
||||
m_VoteFlags = flags;
|
||||
m_pHandler = handler;
|
||||
|
||||
m_pHandler->OnMenuStart(m_pCurMenu);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void VoteMenuHandler::StartVoting()
|
||||
{
|
||||
if (!m_pCurMenu)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
m_bStarted = true;
|
||||
|
||||
m_pHandler->OnMenuVoteStart(m_pCurMenu);
|
||||
|
||||
/* By now we know how many clients were set.
|
||||
* If there are none, we should end IMMEDIATELY.
|
||||
*/
|
||||
if (m_Clients == 0)
|
||||
{
|
||||
EndVoting();
|
||||
}
|
||||
}
|
||||
|
||||
void VoteMenuHandler::DecrementPlayerCount()
|
||||
{
|
||||
assert(m_Clients > 0);
|
||||
|
||||
m_Clients--;
|
||||
|
||||
if (m_bStarted && m_Clients == 0)
|
||||
{
|
||||
EndVoting();
|
||||
}
|
||||
}
|
||||
|
||||
int SortVoteItems(const void *item1, const void *item2)
|
||||
{
|
||||
return ((menu_vote_result_t::menu_item_vote_t *)item2)->count
|
||||
- ((menu_vote_result_t::menu_item_vote_t *)item1)->count;
|
||||
}
|
||||
|
||||
void VoteMenuHandler::EndVoting()
|
||||
{
|
||||
/* Set when the next delay ends. We ignore cancellation because a menu
|
||||
* was, at one point, displayed, which is all that counts. However, we
|
||||
* do re-calculate the time just in case the menu had no time limit.
|
||||
*/
|
||||
float fVoteDelay = sm_vote_delay.GetFloat();
|
||||
if (fVoteDelay < 1.0)
|
||||
{
|
||||
g_next_vote = 0.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
g_next_vote = gpGlobals->curtime + fVoteDelay;
|
||||
}
|
||||
|
||||
if (m_bCancelled)
|
||||
{
|
||||
/* If we were cancelled, don't bother tabulating anything.
|
||||
* Reset just in case someone tries to redraw, which means
|
||||
* we need to save our states.
|
||||
*/
|
||||
IBaseMenu *menu = m_pCurMenu;
|
||||
IMenuHandler *handler = m_pHandler;
|
||||
InternalReset();
|
||||
handler->OnMenuVoteCancel(menu, VoteCancel_Generic);
|
||||
handler->OnMenuEnd(menu, MenuEnd_VotingCancelled);
|
||||
return;
|
||||
}
|
||||
|
||||
menu_vote_result_t vote;
|
||||
menu_vote_result_t::menu_client_vote_t client_vote[256];
|
||||
menu_vote_result_t::menu_item_vote_t item_vote[256];
|
||||
|
||||
memset(&vote, 0, sizeof(vote));
|
||||
|
||||
/* Build the item list */
|
||||
for (unsigned int i=0; i<m_Items; i++)
|
||||
{
|
||||
if (m_Votes[i] > 0)
|
||||
{
|
||||
item_vote[vote.num_items].count = m_Votes[i];
|
||||
item_vote[vote.num_items].item = i;
|
||||
vote.num_votes += m_Votes[i];
|
||||
vote.num_items++;
|
||||
}
|
||||
}
|
||||
vote.item_list = item_vote;
|
||||
|
||||
if (!vote.num_votes)
|
||||
{
|
||||
IBaseMenu *menu = m_pCurMenu;
|
||||
IMenuHandler *handler = m_pHandler;
|
||||
InternalReset();
|
||||
handler->OnMenuVoteCancel(menu, VoteCancel_NoVotes);
|
||||
handler->OnMenuEnd(menu, MenuEnd_VotingCancelled);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Build the client list */
|
||||
for (int i=1; i<=gpGlobals->maxClients; i++)
|
||||
{
|
||||
if (m_ClientVotes[i] >= -1)
|
||||
{
|
||||
client_vote[vote.num_clients].client = i;
|
||||
client_vote[vote.num_clients].item = m_ClientVotes[i];
|
||||
vote.num_clients++;
|
||||
}
|
||||
}
|
||||
vote.client_list = client_vote;
|
||||
|
||||
/* Sort the item list descending like we promised */
|
||||
qsort(item_vote,
|
||||
vote.num_items,
|
||||
sizeof(menu_vote_result_t::menu_item_vote_t),
|
||||
SortVoteItems);
|
||||
|
||||
/* Save states, then clear what we've saved.
|
||||
* This makes us re-entrant, which is always the safe way to go.
|
||||
*/
|
||||
IBaseMenu *menu = m_pCurMenu;
|
||||
IMenuHandler *handler = m_pHandler;
|
||||
InternalReset();
|
||||
|
||||
/* Send vote info */
|
||||
handler->OnMenuVoteResults(menu, &vote);
|
||||
handler->OnMenuEnd(menu, MenuEnd_VotingDone);
|
||||
}
|
||||
|
||||
void VoteMenuHandler::OnMenuStart(IBaseMenu *menu)
|
||||
{
|
||||
m_Clients++;
|
||||
}
|
||||
|
||||
void VoteMenuHandler::OnMenuEnd(IBaseMenu *menu, MenuEndReason reason)
|
||||
{
|
||||
DecrementPlayerCount();
|
||||
}
|
||||
|
||||
void VoteMenuHandler::OnMenuCancel(IBaseMenu *menu, int client, MenuCancelReason reason)
|
||||
{
|
||||
m_pHandler->OnMenuCancel(menu, client, reason);
|
||||
}
|
||||
|
||||
void VoteMenuHandler::OnMenuDisplay(IBaseMenu *menu, int client, IMenuPanel *display)
|
||||
{
|
||||
m_ClientVotes[client] = -1;
|
||||
m_pHandler->OnMenuDisplay(menu, client, display);
|
||||
}
|
||||
|
||||
unsigned int VoteMenuHandler::OnMenuDisplayItem(IBaseMenu *menu, int client, IMenuPanel *panel, unsigned int item, const ItemDrawInfo &dr)
|
||||
{
|
||||
return m_pHandler->OnMenuDisplayItem(menu, client, panel, item, dr);
|
||||
}
|
||||
|
||||
void VoteMenuHandler::OnMenuDrawItem(IBaseMenu *menu, int client, unsigned int item, unsigned int &style)
|
||||
{
|
||||
m_pHandler->OnMenuDrawItem(menu, client, item, style);
|
||||
}
|
||||
|
||||
void VoteMenuHandler::OnMenuSelect(IBaseMenu *menu, int client, unsigned int item)
|
||||
{
|
||||
/* Check by our item count, NOT the vote array size */
|
||||
if (item < m_Items)
|
||||
{
|
||||
m_ClientVotes[client] = item;
|
||||
m_Votes[item]++;
|
||||
m_NumVotes++;
|
||||
}
|
||||
|
||||
m_pHandler->OnMenuSelect(menu, client, item);
|
||||
}
|
||||
|
||||
void VoteMenuHandler::OnMenuSelect2(IBaseMenu *menu, int client, unsigned int item, unsigned int item_on_page)
|
||||
{
|
||||
if (m_pHandler->GetMenuAPIVersion2() >= 13)
|
||||
{
|
||||
m_pHandler->OnMenuSelect2(menu, client, item, item_on_page);
|
||||
}
|
||||
}
|
||||
|
||||
void VoteMenuHandler::InternalReset()
|
||||
{
|
||||
m_Clients = 0;
|
||||
m_Items = 0;
|
||||
m_bStarted = false;
|
||||
m_pCurMenu = NULL;
|
||||
m_NumVotes = 0;
|
||||
m_bCancelled = false;
|
||||
m_pHandler = NULL;
|
||||
}
|
||||
|
||||
void VoteMenuHandler::CancelVoting()
|
||||
{
|
||||
if (m_bCancelled || !m_pCurMenu)
|
||||
{
|
||||
return;
|
||||
}
|
||||
m_bCancelled = true;
|
||||
m_pCurMenu->Cancel();
|
||||
}
|
||||
|
||||
IBaseMenu *VoteMenuHandler::GetCurrentMenu()
|
||||
{
|
||||
return m_pCurMenu;
|
||||
}
|
||||
|
||||
bool VoteMenuHandler::IsCancelling()
|
||||
{
|
||||
return m_bCancelled;
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
/**
|
||||
* vim: set ts=4 :
|
||||
* =============================================================================
|
||||
* SourceMod
|
||||
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
|
||||
* =============================================================================
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, version 3.0, as published by the
|
||||
* Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* As a special exception, AlliedModders LLC gives you permission to link the
|
||||
* code of this program (as well as its derivative works) to "Half-Life 2," the
|
||||
* "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
|
||||
* by the Valve Corporation. You must obey the GNU General Public License in
|
||||
* all respects for all other code used. Additionally, AlliedModders LLC grants
|
||||
* this exception to all derivative works. AlliedModders LLC defines further
|
||||
* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
|
||||
* or <http://www.sourcemod.net/license.php>.
|
||||
*
|
||||
* Version: $Id$
|
||||
*/
|
||||
|
||||
#ifndef _INCLUDE_SOURCEMOD_MENUVOTING_H_
|
||||
#define _INCLUDE_SOURCEMOD_MENUVOTING_H_
|
||||
|
||||
#include <IMenuManager.h>
|
||||
#include <IPlayerHelpers.h>
|
||||
#include <sh_vector.h>
|
||||
#include "sm_globals.h"
|
||||
|
||||
using namespace SourceHook;
|
||||
using namespace SourceMod;
|
||||
|
||||
class VoteMenuHandler :
|
||||
public IMenuHandler,
|
||||
public SMGlobalClass,
|
||||
public IClientListener
|
||||
{
|
||||
public: //SMGlobalClass
|
||||
void OnSourceModAllInitialized();
|
||||
void OnSourceModShutdown();
|
||||
void OnSourceModLevelChange(const char *mapName);
|
||||
public: //IClientListener
|
||||
void OnClientDisconnected(int client);
|
||||
public: //IMenuHandler
|
||||
unsigned int GetMenuAPIVersion2();
|
||||
void OnMenuStart(IBaseMenu *menu);
|
||||
void OnMenuDisplay(IBaseMenu *menu, int client, IMenuPanel *display);
|
||||
void OnMenuSelect(IBaseMenu *menu, int client, unsigned int item);
|
||||
void OnMenuSelect2(IBaseMenu *menu, int client, unsigned int item, unsigned int item_on_page);
|
||||
void OnMenuCancel(IBaseMenu *menu, int client, MenuCancelReason reason);
|
||||
void OnMenuEnd(IBaseMenu *menu, MenuEndReason reason);
|
||||
void OnMenuDrawItem(IBaseMenu *menu, int client, unsigned int item, unsigned int &style);
|
||||
unsigned int OnMenuDisplayItem(IBaseMenu *menu, int client, IMenuPanel *panel, unsigned int item, const ItemDrawInfo &dr);
|
||||
public:
|
||||
bool StartVote(IBaseMenu *menu,
|
||||
unsigned int num_clients,
|
||||
int clients[],
|
||||
unsigned int max_time,
|
||||
unsigned int flags=0);
|
||||
bool IsVoteInProgress();
|
||||
void CancelVoting();
|
||||
IBaseMenu *GetCurrentMenu();
|
||||
bool IsCancelling();
|
||||
unsigned int GetRemainingVoteDelay();
|
||||
bool IsClientInVotePool(int client);
|
||||
bool GetClientVoteChoice(int client, unsigned int *pItem);
|
||||
bool RedrawToClient(int client);
|
||||
private:
|
||||
void Reset(IMenuHandler *mh);
|
||||
void DecrementPlayerCount();
|
||||
void EndVoting();
|
||||
void InternalReset();
|
||||
bool InitializeVoting(IBaseMenu *menu,
|
||||
IMenuHandler *handler,
|
||||
unsigned int time,
|
||||
unsigned int flags);
|
||||
void StartVoting();
|
||||
private:
|
||||
IMenuHandler *m_pHandler;
|
||||
unsigned int m_Clients;
|
||||
unsigned int m_Items;
|
||||
CVector<unsigned int> m_Votes;
|
||||
IBaseMenu *m_pCurMenu;
|
||||
bool m_bStarted;
|
||||
bool m_bCancelled;
|
||||
unsigned int m_NumVotes;
|
||||
unsigned int m_VoteTime;
|
||||
unsigned int m_VoteFlags;
|
||||
float m_fStartTime;
|
||||
unsigned int m_nMenuTime;
|
||||
int m_ClientVotes[256+1];
|
||||
};
|
||||
|
||||
#endif //_INCLUDE_SOURCEMOD_MENUVOTING_H_
|
||||
@@ -0,0 +1,140 @@
|
||||
#include "NativeOwner.h"
|
||||
#include "ShareSys.h"
|
||||
#include "PluginSys.h"
|
||||
|
||||
void CNativeOwner::SetMarkSerial(unsigned int serial)
|
||||
{
|
||||
m_nMarkSerial = serial;
|
||||
}
|
||||
|
||||
unsigned int CNativeOwner::GetMarkSerial()
|
||||
{
|
||||
return m_nMarkSerial;
|
||||
}
|
||||
|
||||
void CNativeOwner::AddDependent(CPlugin *pPlugin)
|
||||
{
|
||||
m_Dependents.push_back(pPlugin);
|
||||
}
|
||||
|
||||
void CNativeOwner::AddWeakRef(const WeakNative & ref)
|
||||
{
|
||||
m_WeakRefs.push_back(ref);
|
||||
}
|
||||
|
||||
void CNativeOwner::AddNatives(const sp_nativeinfo_t *natives)
|
||||
{
|
||||
NativeEntry *pEntry;
|
||||
|
||||
for (unsigned int i = 0; natives[i].func != NULL && natives[i].name != NULL; i++)
|
||||
{
|
||||
if ((pEntry = g_ShareSys.AddNativeToCache(this, &natives[i])) == NULL)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
m_Natives.push_back(pEntry);
|
||||
}
|
||||
}
|
||||
|
||||
void CNativeOwner::PropogateMarkSerial(unsigned int serial)
|
||||
{
|
||||
CNativeOwner *pOwner;
|
||||
List<CPlugin *>::iterator iter;
|
||||
|
||||
for (iter = m_Dependents.begin();
|
||||
iter != m_Dependents.end();
|
||||
iter++)
|
||||
{
|
||||
pOwner = (*iter);
|
||||
pOwner->SetMarkSerial(serial);
|
||||
}
|
||||
}
|
||||
|
||||
void CNativeOwner::UnbindWeakRef(const WeakNative & ref)
|
||||
{
|
||||
sp_native_t *native;
|
||||
IPluginContext *pContext;
|
||||
|
||||
pContext = ref.pl->GetBaseContext();
|
||||
if ((pContext->GetNativeByIndex(ref.idx, &native)) == SP_ERROR_NONE)
|
||||
{
|
||||
/* If there is no reference, the native must be unbound */
|
||||
if (ref.entry == NULL)
|
||||
{
|
||||
native->status = SP_NATIVE_UNBOUND;
|
||||
native->pfn = NULL;
|
||||
}
|
||||
/* If we've cached a reference, it's a core native we can
|
||||
* rebind back to its original (this was a replacement).
|
||||
*/
|
||||
else
|
||||
{
|
||||
native->pfn = ref.entry->func;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CNativeOwner::DropEverything()
|
||||
{
|
||||
NativeEntry *pEntry;
|
||||
List<WeakNative>::iterator iter;
|
||||
List<NativeEntry *>::iterator ntv_iter;
|
||||
|
||||
/* Unbind and remove all weak references to us */
|
||||
iter = m_WeakRefs.begin();
|
||||
while (iter != m_WeakRefs.end())
|
||||
{
|
||||
UnbindWeakRef((*iter));
|
||||
iter = m_WeakRefs.erase(iter);
|
||||
}
|
||||
|
||||
/* Unmark our replacement natives */
|
||||
ntv_iter = m_ReplacedNatives.begin();
|
||||
while (ntv_iter != m_ReplacedNatives.end())
|
||||
{
|
||||
pEntry = (*ntv_iter);
|
||||
pEntry->replacement.func = NULL;
|
||||
pEntry->replacement.owner = NULL;
|
||||
ntv_iter = m_ReplacedNatives.erase(ntv_iter);
|
||||
}
|
||||
|
||||
/* Strip all of our natives from the cache */
|
||||
ntv_iter = m_Natives.begin();
|
||||
while (ntv_iter != m_Natives.end())
|
||||
{
|
||||
g_ShareSys.ClearNativeFromCache(this, (*ntv_iter)->name);
|
||||
ntv_iter = m_Natives.erase(ntv_iter);
|
||||
}
|
||||
}
|
||||
|
||||
void CNativeOwner::DropWeakRefsTo(CPlugin *pPlugin)
|
||||
{
|
||||
List<WeakNative>::iterator iter;
|
||||
|
||||
iter = m_WeakRefs.begin();
|
||||
while (iter != m_WeakRefs.end())
|
||||
{
|
||||
WeakNative & ref = (*iter);
|
||||
|
||||
if (ref.pl == pPlugin)
|
||||
{
|
||||
iter = m_WeakRefs.erase(iter);
|
||||
}
|
||||
else
|
||||
{
|
||||
iter++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CNativeOwner::DropRefsTo(CPlugin *pPlugin)
|
||||
{
|
||||
m_Dependents.remove(pPlugin);
|
||||
DropWeakRefsTo(pPlugin);
|
||||
}
|
||||
|
||||
void CNativeOwner::AddReplacedNative(NativeEntry *pEntry)
|
||||
{
|
||||
m_ReplacedNatives.push_back(pEntry);
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
#ifndef _INCLUDE_SOURCEMOD_NATIVE_OWNER_H_
|
||||
#define _INCLUDE_SOURCEMOD_NATIVE_OWNER_H_
|
||||
|
||||
#include <sp_vm_types.h>
|
||||
#include <sh_list.h>
|
||||
|
||||
class CPlugin;
|
||||
struct NativeEntry;
|
||||
|
||||
struct WeakNative
|
||||
{
|
||||
WeakNative(CPlugin *plugin, uint32_t index) :
|
||||
pl(plugin), idx(index), entry(NULL)
|
||||
{
|
||||
pl = plugin;
|
||||
idx = index;
|
||||
}
|
||||
WeakNative(CPlugin *plugin, uint32_t index, NativeEntry *pEntry) :
|
||||
pl(plugin), idx(index), entry(pEntry)
|
||||
{
|
||||
pl = plugin;
|
||||
idx = index;
|
||||
}
|
||||
CPlugin *pl;
|
||||
uint32_t idx;
|
||||
NativeEntry *entry;
|
||||
};
|
||||
|
||||
using namespace SourceHook;
|
||||
|
||||
class CNativeOwner
|
||||
{
|
||||
public:
|
||||
virtual void DropEverything();
|
||||
public:
|
||||
void AddNatives(const sp_nativeinfo_t *info);
|
||||
public:
|
||||
void SetMarkSerial(unsigned int serial);
|
||||
unsigned int GetMarkSerial();
|
||||
void PropogateMarkSerial(unsigned int serial);
|
||||
public:
|
||||
void AddDependent(CPlugin *pPlugin);
|
||||
void AddWeakRef(const WeakNative & ref);
|
||||
void DropRefsTo(CPlugin *pPlugin);
|
||||
void AddReplacedNative(NativeEntry *pEntry);
|
||||
private:
|
||||
void DropWeakRefsTo(CPlugin *pPlugin);
|
||||
void UnbindWeakRef(const WeakNative & ref);
|
||||
protected:
|
||||
List<CPlugin *> m_Dependents;
|
||||
unsigned int m_nMarkSerial;
|
||||
List<WeakNative> m_WeakRefs;
|
||||
List<NativeEntry *> m_Natives;
|
||||
List<NativeEntry *> m_ReplacedNatives;
|
||||
};
|
||||
|
||||
#endif //_INCLUDE_SOURCEMOD_NATIVE_OWNER_H_
|
||||
@@ -0,0 +1,90 @@
|
||||
/**
|
||||
* vim: set ts=4 :
|
||||
* =============================================================================
|
||||
* SourceMod
|
||||
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
|
||||
* =============================================================================
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, version 3.0, as published by the
|
||||
* Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* As a special exception, AlliedModders LLC gives you permission to link the
|
||||
* code of this program (as well as its derivative works) to "Half-Life 2," the
|
||||
* "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
|
||||
* by the Valve Corporation. You must obey the GNU General Public License in
|
||||
* all respects for all other code used. Additionally, AlliedModders LLC grants
|
||||
* this exception to all derivative works. AlliedModders LLC defines further
|
||||
* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
|
||||
* or <http://www.sourcemod.net/license.php>.
|
||||
*
|
||||
* Version: $Id$
|
||||
*/
|
||||
|
||||
#include "NextMap.h"
|
||||
#include "Logger.h"
|
||||
#include "sourcemm_api.h"
|
||||
#include "sm_stringutil.h"
|
||||
|
||||
NextMapManager g_NextMap;
|
||||
|
||||
SH_DECL_HOOK2_void(IVEngineServer, ChangeLevel, SH_NOATTRIB, 0, const char *, const char *);
|
||||
|
||||
ConVar sm_nextmap("sm_nextmap", "", FCVAR_NOTIFY);
|
||||
|
||||
void NextMapManager::OnSourceModAllInitialized_Post()
|
||||
{
|
||||
#if defined ORANGEBOX_BUILD
|
||||
SH_ADD_HOOK(IVEngineServer, ChangeLevel, engine, SH_MEMBER(this, &NextMapManager::HookChangeLevel), false);
|
||||
#else
|
||||
SH_ADD_HOOK_MEMFUNC(IVEngineServer, ChangeLevel, engine, this, &NextMapManager::HookChangeLevel, false);
|
||||
#endif
|
||||
}
|
||||
|
||||
void NextMapManager::OnSourceModShutdown()
|
||||
{
|
||||
#if defined ORANGEBOX_BUILD
|
||||
SH_REMOVE_HOOK(IVEngineServer, ChangeLevel, engine, SH_MEMBER(this, &NextMapManager::HookChangeLevel), false);
|
||||
#else
|
||||
SH_REMOVE_HOOK_MEMFUNC(IVEngineServer, ChangeLevel, engine, this, &NextMapManager::HookChangeLevel, false);
|
||||
#endif
|
||||
}
|
||||
|
||||
const char *NextMapManager::GetNextMap()
|
||||
{
|
||||
return sm_nextmap.GetString();
|
||||
}
|
||||
|
||||
bool NextMapManager::SetNextMap(const char *map)
|
||||
{
|
||||
if (!engine->IsMapValid(map))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
sm_nextmap.SetValue(map);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void NextMapManager::HookChangeLevel(const char *map, const char *unknown)
|
||||
{
|
||||
const char *newmap = sm_nextmap.GetString();
|
||||
|
||||
if (newmap[0] == 0 || !engine->IsMapValid(newmap))
|
||||
{
|
||||
RETURN_META(MRES_IGNORED);
|
||||
}
|
||||
|
||||
g_Logger.LogMessage("[SM] Changed map to \"%s\"", newmap);
|
||||
|
||||
RETURN_META_NEWPARAMS(MRES_IGNORED, &IVEngineServer::ChangeLevel, (newmap, unknown));
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/**
|
||||
* vim: set ts=4 :
|
||||
* =============================================================================
|
||||
* SourceMod
|
||||
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
|
||||
* =============================================================================
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, version 3.0, as published by the
|
||||
* Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* As a special exception, AlliedModders LLC gives you permission to link the
|
||||
* code of this program (as well as its derivative works) to "Half-Life 2," the
|
||||
* "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
|
||||
* by the Valve Corporation. You must obey the GNU General Public License in
|
||||
* all respects for all other code used. Additionally, AlliedModders LLC grants
|
||||
* this exception to all derivative works. AlliedModders LLC defines further
|
||||
* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
|
||||
* or <http://www.sourcemod.net/license.php>.
|
||||
*
|
||||
* Version: $Id$
|
||||
*/
|
||||
|
||||
#ifndef _INCLUDE_SOURCEMOD_NEXTMAP_H_
|
||||
#define _INCLUDE_SOURCEMOD_NEXTMAP_H_
|
||||
|
||||
#include "sm_globals.h"
|
||||
#include "eiface.h"
|
||||
|
||||
class NextMapManager : public SMGlobalClass
|
||||
{
|
||||
public:
|
||||
void OnSourceModAllInitialized_Post();
|
||||
void OnSourceModShutdown();
|
||||
|
||||
const char *GetNextMap();
|
||||
bool SetNextMap(const char *map);
|
||||
|
||||
void HookChangeLevel(const char *map, const char *unknown);
|
||||
};
|
||||
|
||||
extern NextMapManager g_NextMap;
|
||||
|
||||
#endif //_INCLUDE_SOURCEMOD_NEXTMAP_H_
|
||||
@@ -0,0 +1,131 @@
|
||||
/**
|
||||
* vim: set ts=4 :
|
||||
* =============================================================================
|
||||
* SourceMod
|
||||
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
|
||||
* =============================================================================
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, version 3.0, as published by the
|
||||
* Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* As a special exception, AlliedModders LLC gives you permission to link the
|
||||
* code of this program (as well as its derivative works) to "Half-Life 2," the
|
||||
* "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
|
||||
* by the Valve Corporation. You must obey the GNU General Public License in
|
||||
* all respects for all other code used. Additionally, AlliedModders LLC grants
|
||||
* this exception to all derivative works. AlliedModders LLC defines further
|
||||
* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
|
||||
* or <http://www.sourcemod.net/license.php>.
|
||||
*
|
||||
* Version: $Id$
|
||||
*/
|
||||
|
||||
#include "PhraseCollection.h"
|
||||
#include "Translator.h"
|
||||
#include "sm_stringutil.h"
|
||||
|
||||
CPhraseCollection::CPhraseCollection()
|
||||
{
|
||||
}
|
||||
|
||||
CPhraseCollection::~CPhraseCollection()
|
||||
{
|
||||
}
|
||||
|
||||
void CPhraseCollection::Destroy()
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
|
||||
IPhraseFile *CPhraseCollection::AddPhraseFile(const char *filename)
|
||||
{
|
||||
size_t i;
|
||||
unsigned int fid;
|
||||
IPhraseFile *pFile;
|
||||
char full_name[PLATFORM_MAX_PATH];
|
||||
|
||||
/* No compat shim here. The user should have read the doc. */
|
||||
UTIL_Format(full_name, sizeof(full_name), "%s.txt", filename);
|
||||
|
||||
fid = g_Translator.FindOrAddPhraseFile(full_name);
|
||||
pFile = g_Translator.GetFileByIndex(fid);
|
||||
|
||||
for (i = 0; i < m_Files.size(); i++)
|
||||
{
|
||||
if (m_Files[i] == pFile)
|
||||
{
|
||||
return pFile;
|
||||
}
|
||||
}
|
||||
|
||||
m_Files.push_back(pFile);
|
||||
|
||||
return pFile;
|
||||
}
|
||||
|
||||
unsigned int CPhraseCollection::GetFileCount()
|
||||
{
|
||||
return (unsigned int)m_Files.size();
|
||||
}
|
||||
|
||||
IPhraseFile *CPhraseCollection::GetFile(unsigned int file)
|
||||
{
|
||||
if (file >= m_Files.size())
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return m_Files[file];
|
||||
}
|
||||
|
||||
TransError CPhraseCollection::FindTranslation(const char *key, unsigned int langid, Translation *pTrans)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < m_Files.size(); i++)
|
||||
{
|
||||
if (m_Files[i]->GetTranslation(key, langid, pTrans) == Trans_Okay)
|
||||
{
|
||||
return Trans_Okay;
|
||||
}
|
||||
}
|
||||
|
||||
return Trans_BadPhrase;
|
||||
}
|
||||
|
||||
bool CPhraseCollection::FormatString(char *buffer,
|
||||
size_t maxlength,
|
||||
const char *format,
|
||||
void **params,
|
||||
unsigned int numparams,
|
||||
size_t *pOutLength,
|
||||
const char **pFailPhrase)
|
||||
{
|
||||
unsigned int arg;
|
||||
|
||||
arg = 0;
|
||||
if (!gnprintf(buffer, maxlength, format, this, params, numparams, arg, pOutLength, pFailPhrase))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (arg != numparams)
|
||||
{
|
||||
if (pFailPhrase != NULL)
|
||||
{
|
||||
*pFailPhrase = NULL;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
/**
|
||||
* vim: set ts=4 :
|
||||
* =============================================================================
|
||||
* SourceMod
|
||||
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
|
||||
* =============================================================================
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, version 3.0, as published by the
|
||||
* Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* As a special exception, AlliedModders LLC gives you permission to link the
|
||||
* code of this program (as well as its derivative works) to "Half-Life 2," the
|
||||
* "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
|
||||
* by the Valve Corporation. You must obey the GNU General Public License in
|
||||
* all respects for all other code used. Additionally, AlliedModders LLC grants
|
||||
* this exception to all derivative works. AlliedModders LLC defines further
|
||||
* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
|
||||
* or <http://www.sourcemod.net/license.php>.
|
||||
*
|
||||
* Version: $Id$
|
||||
*/
|
||||
|
||||
#ifndef _INCLUDE_SOURCEMOD_PHRASECOLLECTION_H_
|
||||
#define _INCLUDE_SOURCEMOD_PHRASECOLLECTION_H_
|
||||
|
||||
#include <string.h>
|
||||
#include <sh_vector.h>
|
||||
#include <ITranslator.h>
|
||||
|
||||
using namespace SourceHook;
|
||||
using namespace SourceMod;
|
||||
|
||||
class CPhraseCollection : public IPhraseCollection
|
||||
{
|
||||
public:
|
||||
CPhraseCollection();
|
||||
~CPhraseCollection();
|
||||
public:
|
||||
IPhraseFile *AddPhraseFile(const char *filename);
|
||||
unsigned int GetFileCount();
|
||||
IPhraseFile *GetFile(unsigned int file);
|
||||
void Destroy();
|
||||
TransError FindTranslation(const char *key, unsigned int langid, Translation *pTrans);
|
||||
bool FormatString(
|
||||
char *buffer,
|
||||
size_t maxlength,
|
||||
const char *format,
|
||||
void **params,
|
||||
unsigned int numparams,
|
||||
size_t *pOutLength,
|
||||
const char **pFailPhrase);
|
||||
private:
|
||||
CVector<IPhraseFile *> m_Files;
|
||||
};
|
||||
|
||||
#endif //_INCLUDE_SOURCEMOD_PHRASECOLLECTION_H_
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,203 @@
|
||||
/**
|
||||
* vim: set ts=4 :
|
||||
* =============================================================================
|
||||
* SourceMod
|
||||
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
|
||||
* =============================================================================
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, version 3.0, as published by the
|
||||
* Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* As a special exception, AlliedModders LLC gives you permission to link the
|
||||
* code of this program (as well as its derivative works) to "Half-Life 2," the
|
||||
* "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
|
||||
* by the Valve Corporation. You must obey the GNU General Public License in
|
||||
* all respects for all other code used. Additionally, AlliedModders LLC grants
|
||||
* this exception to all derivative works. AlliedModders LLC defines further
|
||||
* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
|
||||
* or <http://www.sourcemod.net/license.php>.
|
||||
*
|
||||
* Version: $Id$
|
||||
*/
|
||||
|
||||
#ifndef _INCLUDE_SOURCEMOD_CPLAYERMANAGER_H_
|
||||
#define _INCLUDE_SOURCEMOD_CPLAYERMANAGER_H_
|
||||
|
||||
#include "sm_globals.h"
|
||||
#include <eiface.h>
|
||||
#include "sourcemm_api.h"
|
||||
#include <IForwardSys.h>
|
||||
#include <IPlayerHelpers.h>
|
||||
#include <IAdminSystem.h>
|
||||
#include <sh_string.h>
|
||||
#include <sh_list.h>
|
||||
#include <sh_vector.h>
|
||||
|
||||
using namespace SourceHook;
|
||||
|
||||
#define PLAYER_LIFE_UNKNOWN 0
|
||||
#define PLAYER_LIFE_ALIVE 1
|
||||
#define PLAYER_LIFE_DEAD 2
|
||||
|
||||
#define MIN_API_FOR_ADMINCALLS 7
|
||||
|
||||
class CPlayer : public IGamePlayer
|
||||
{
|
||||
friend class PlayerManager;
|
||||
public:
|
||||
CPlayer();
|
||||
public:
|
||||
const char *GetName();
|
||||
const char *GetIPAddress();
|
||||
const char *GetAuthString();
|
||||
edict_t *GetEdict();
|
||||
bool IsInGame();
|
||||
bool WasCountedAsInGame();
|
||||
bool IsConnected();
|
||||
bool IsAuthorized();
|
||||
bool IsFakeClient();
|
||||
void SetAdminId(AdminId id, bool temporary);
|
||||
AdminId GetAdminId();
|
||||
void Kick(const char *str);
|
||||
IPlayerInfo *GetPlayerInfo();
|
||||
unsigned int GetLanguageId();
|
||||
int GetUserId();
|
||||
bool RunAdminCacheChecks();
|
||||
void NotifyPostAdminChecks();
|
||||
public:
|
||||
void DoBasicAdminChecks();
|
||||
bool IsInKickQueue();
|
||||
void MarkAsBeingKicked();
|
||||
int GetLifeState();
|
||||
private:
|
||||
void Initialize(const char *name, const char *ip, edict_t *pEntity);
|
||||
void Connect();
|
||||
void Disconnect();
|
||||
void SetName(const char *name);
|
||||
void DumpAdmin(bool deleting);
|
||||
void Authorize(const char *auth);
|
||||
void Authorize_Post();
|
||||
void DoPostConnectAuthorization();
|
||||
private:
|
||||
bool m_IsConnected;
|
||||
bool m_IsInGame;
|
||||
bool m_IsAuthorized;
|
||||
bool m_bIsInKickQueue;
|
||||
String m_Name;
|
||||
String m_Ip;
|
||||
String m_IpNoPort;
|
||||
String m_AuthID;
|
||||
AdminId m_Admin;
|
||||
bool m_TempAdmin;
|
||||
edict_t *m_pEdict;
|
||||
IPlayerInfo *m_Info;
|
||||
String m_LastPassword;
|
||||
bool m_bAdminCheckSignalled;
|
||||
int m_iIndex;
|
||||
unsigned int m_LangId;
|
||||
int m_UserId;
|
||||
};
|
||||
|
||||
class PlayerManager :
|
||||
public SMGlobalClass,
|
||||
public IPlayerManager
|
||||
{
|
||||
friend class CPlayer;
|
||||
public:
|
||||
PlayerManager();
|
||||
~PlayerManager();
|
||||
public: //SMGlobalClass
|
||||
void OnSourceModAllInitialized();
|
||||
void OnSourceModShutdown();
|
||||
void OnSourceModLevelEnd();
|
||||
ConfigResult OnSourceModConfigChanged(const char *key, const char *value, ConfigSource source, char *error, size_t maxlength);
|
||||
public:
|
||||
CPlayer *GetPlayerByIndex(int client) const;
|
||||
void RunAuthChecks();
|
||||
void ClearAdminId(AdminId id);
|
||||
void ClearAllAdmins();
|
||||
public:
|
||||
bool OnClientConnect(edict_t *pEntity, const char *pszName, const char *pszAddress, char *reject, int maxrejectlen);
|
||||
bool OnClientConnect_Post(edict_t *pEntity, const char *pszName, const char *pszAddress, char *reject, int maxrejectlen);
|
||||
void OnClientPutInServer(edict_t *pEntity, char const *playername);
|
||||
void OnClientDisconnect(edict_t *pEntity);
|
||||
void OnClientDisconnect_Post(edict_t *pEntity);
|
||||
#if defined ORANGEBOX_BUILD
|
||||
void OnClientCommand(edict_t *pEntity, const CCommand &args);
|
||||
#else
|
||||
void OnClientCommand(edict_t *pEntity);
|
||||
#endif
|
||||
void OnClientSettingsChanged(edict_t *pEntity);
|
||||
//void OnClientSettingsChanged_Pre(edict_t *pEntity);
|
||||
public: //IPlayerManager
|
||||
void AddClientListener(IClientListener *listener);
|
||||
void RemoveClientListener(IClientListener *listener);
|
||||
IGamePlayer *GetGamePlayer(int client);
|
||||
IGamePlayer *GetGamePlayer(edict_t *pEdict);
|
||||
int GetMaxClients();
|
||||
int GetNumPlayers();
|
||||
int GetClientOfUserId(int userid);
|
||||
bool IsServerActivated();
|
||||
int FilterCommandTarget(IGamePlayer *pAdmin, IGamePlayer *pTarget, int flags);
|
||||
int InternalFilterCommandTarget(CPlayer *pAdmin, CPlayer *pTarget, int flags);
|
||||
void RegisterCommandTargetProcessor(ICommandTargetProcessor *pHandler);
|
||||
void UnregisterCommandTargetProcessor(ICommandTargetProcessor *pHandler);
|
||||
void ProcessCommandTarget(cmd_target_info_t *info);
|
||||
public:
|
||||
inline int MaxClients()
|
||||
{
|
||||
return m_maxClients;
|
||||
}
|
||||
inline int NumPlayers()
|
||||
{
|
||||
return m_PlayerCount;
|
||||
}
|
||||
inline int ListenClient()
|
||||
{
|
||||
return m_ListenClient;
|
||||
}
|
||||
bool CheckSetAdmin(int index, CPlayer *pPlayer, AdminId id);
|
||||
bool CheckSetAdminName(int index, CPlayer *pPlayer, AdminId id);
|
||||
const char *GetPassInfoVar();
|
||||
void RecheckAnyAdmins();
|
||||
unsigned int GetReplyTo();
|
||||
unsigned int SetReplyTo(unsigned int reply);
|
||||
private:
|
||||
void OnServerActivate(edict_t *pEdictList, int edictCount, int clientMax);
|
||||
private:
|
||||
List<IClientListener *> m_hooks;
|
||||
IForward *m_clconnect;
|
||||
IForward *m_cldisconnect;
|
||||
IForward *m_cldisconnect_post;
|
||||
IForward *m_clputinserver;
|
||||
IForward *m_clcommand;
|
||||
IForward *m_clinfochanged;
|
||||
IForward *m_clauth;
|
||||
IForward *m_onActivate;
|
||||
IForward *m_onActivate2;
|
||||
CPlayer *m_Players;
|
||||
int *m_UserIdLookUp;
|
||||
int m_maxClients;
|
||||
int m_PlayerCount;
|
||||
bool m_FirstPass;
|
||||
unsigned int *m_AuthQueue;
|
||||
String m_PassInfoVar;
|
||||
bool m_QueryLang;
|
||||
bool m_bIsListenServer;
|
||||
int m_ListenClient;
|
||||
};
|
||||
|
||||
extern PlayerManager g_Players;
|
||||
extern bool g_OnMapStarted;
|
||||
extern const unsigned int *g_NumPlayersToAuth;
|
||||
|
||||
#endif //_INCLUDE_SOURCEMOD_CPLAYERMANAGER_H_
|
||||
@@ -0,0 +1,487 @@
|
||||
/**
|
||||
* vim: set ts=4 :
|
||||
* =============================================================================
|
||||
* SourceMod
|
||||
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
|
||||
* =============================================================================
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, version 3.0, as published by the
|
||||
* Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* As a special exception, AlliedModders LLC gives you permission to link the
|
||||
* code of this program (as well as its derivative works) to "Half-Life 2," the
|
||||
* "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
|
||||
* by the Valve Corporation. You must obey the GNU General Public License in
|
||||
* all respects for all other code used. Additionally, AlliedModders LLC grants
|
||||
* this exception to all derivative works. AlliedModders LLC defines further
|
||||
* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
|
||||
* or <http://www.sourcemod.net/license.php>.
|
||||
*
|
||||
* Version: $Id$
|
||||
*/
|
||||
|
||||
#include "Profiler.h"
|
||||
#include "PluginSys.h"
|
||||
#include "sm_stringutil.h"
|
||||
#include "Logger.h"
|
||||
#if defined PLATFORM_POSIX
|
||||
#include <sys/time.h>
|
||||
#include <time.h>
|
||||
#endif
|
||||
|
||||
ProfileEngine g_Profiler;
|
||||
IProfiler *sm_profiler = &g_Profiler;
|
||||
|
||||
#if defined PLATFORM_WINDOWS
|
||||
double WINDOWS_PERFORMANCE_FREQUENCY;
|
||||
#endif
|
||||
|
||||
class EmptyProfiler : public IProfiler
|
||||
{
|
||||
public:
|
||||
void OnNativeBegin(IPluginContext *pContext, sp_native_t *native)
|
||||
{
|
||||
}
|
||||
void OnNativeEnd()
|
||||
{
|
||||
}
|
||||
void OnFunctionBegin(IPluginContext *pContext, const char *name)
|
||||
{
|
||||
}
|
||||
void OnFunctionEnd()
|
||||
{
|
||||
}
|
||||
int OnCallbackBegin(IPluginContext *pContext, sp_public_t *pubfunc)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
void OnCallbackEnd(int serial)
|
||||
{
|
||||
}
|
||||
} s_EmptyProfiler;
|
||||
|
||||
inline void InitProfPoint(prof_point_t &pt)
|
||||
{
|
||||
#if defined PLATFORM_WINDOWS
|
||||
QueryPerformanceCounter(&pt.value);
|
||||
#elif defined PLATFORM_POSIX
|
||||
gettimeofday(&pt.value, NULL);
|
||||
#endif
|
||||
pt.is_set = true;
|
||||
}
|
||||
|
||||
ProfileEngine::ProfileEngine()
|
||||
{
|
||||
m_serial = 0;
|
||||
|
||||
#if defined PLATFORM_WINDOWS
|
||||
LARGE_INTEGER pf;
|
||||
|
||||
if (QueryPerformanceFrequency(&pf))
|
||||
{
|
||||
WINDOWS_PERFORMANCE_FREQUENCY = 1.0 / (double)(pf.QuadPart);
|
||||
}
|
||||
else
|
||||
{
|
||||
WINDOWS_PERFORMANCE_FREQUENCY = -1.0;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (IsEnabled())
|
||||
{
|
||||
InitProfPoint(m_ProfStart);
|
||||
}
|
||||
else
|
||||
{
|
||||
sm_profiler = &s_EmptyProfiler;
|
||||
}
|
||||
}
|
||||
|
||||
bool ProfileEngine::IsEnabled()
|
||||
{
|
||||
#if defined PLATFORM_WINDOWS
|
||||
return (WINDOWS_PERFORMANCE_FREQUENCY > 0.0);
|
||||
#elif defined PLATFORM_POSIX
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
inline double DiffProfPoints(const prof_point_t &start, const prof_point_t &end)
|
||||
{
|
||||
double seconds;
|
||||
|
||||
#if defined PLATFORM_WINDOWS
|
||||
LONGLONG diff;
|
||||
|
||||
diff = end.value.QuadPart - start.value.QuadPart;
|
||||
seconds = diff * WINDOWS_PERFORMANCE_FREQUENCY;
|
||||
#elif defined PLATFORM_POSIX
|
||||
seconds = (double)(end.value.tv_sec - start.value.tv_sec);
|
||||
|
||||
if (start.value.tv_usec > end.value.tv_usec)
|
||||
{
|
||||
seconds -= 1.0;
|
||||
seconds += (double)(1000000 - (start.value.tv_usec - end.value.tv_usec)) / 1000000.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
seconds += (double)(end.value.tv_usec - start.value.tv_usec) / 1000000.0;
|
||||
}
|
||||
#endif
|
||||
|
||||
return seconds;
|
||||
}
|
||||
|
||||
inline double CalcAtomTime(const prof_atom_t &atom)
|
||||
{
|
||||
if (!atom.end.is_set)
|
||||
{
|
||||
return atom.base_time;
|
||||
}
|
||||
|
||||
return atom.base_time + DiffProfPoints(atom.start, atom.end);
|
||||
}
|
||||
|
||||
void ProfileEngine::OnNativeBegin(IPluginContext *pContext, sp_native_t *native)
|
||||
{
|
||||
PushProfileStack(pContext, SP_PROF_NATIVES, native->name);
|
||||
}
|
||||
|
||||
void ProfileEngine::OnNativeEnd()
|
||||
{
|
||||
assert(!m_AtomStack.empty());
|
||||
assert(m_AtomStack.front().atom_type == SP_PROF_NATIVES);
|
||||
|
||||
PopProfileStack(&m_Natives);
|
||||
}
|
||||
|
||||
void ProfileEngine::OnFunctionBegin(IPluginContext *pContext, const char *name)
|
||||
{
|
||||
PushProfileStack(pContext, SP_PROF_FUNCTIONS, name);
|
||||
}
|
||||
|
||||
void ProfileEngine::OnFunctionEnd()
|
||||
{
|
||||
assert(!m_AtomStack.empty());
|
||||
assert(m_AtomStack.front().atom_type == SP_PROF_FUNCTIONS);
|
||||
|
||||
PopProfileStack(&m_Functions);
|
||||
}
|
||||
|
||||
int ProfileEngine::OnCallbackBegin(IPluginContext *pContext, sp_public_t *pubfunc)
|
||||
{
|
||||
PushProfileStack(pContext, SP_PROF_CALLBACKS, pubfunc->name);
|
||||
|
||||
return m_serial;
|
||||
}
|
||||
|
||||
void ProfileEngine::OnCallbackEnd(int serial)
|
||||
{
|
||||
assert(!m_AtomStack.empty());
|
||||
|
||||
/**
|
||||
* Account for the situation where the JIT discards the
|
||||
* stack because there was an RTE of sorts.
|
||||
*/
|
||||
if (m_AtomStack.front().atom_type != SP_PROF_CALLBACKS
|
||||
&& m_AtomStack.front().atom_serial != serial)
|
||||
{
|
||||
prof_atom_t atom;
|
||||
double total_time;
|
||||
|
||||
/* There was an error, and we need to discard things. */
|
||||
total_time = 0.0;
|
||||
while (!m_AtomStack.empty()
|
||||
&& m_AtomStack.front().atom_type != SP_PROF_CALLBACKS
|
||||
&& m_AtomStack.front().atom_serial != serial)
|
||||
{
|
||||
total_time += CalcAtomTime(m_AtomStack.front());
|
||||
m_AtomStack.pop();
|
||||
}
|
||||
|
||||
/**
|
||||
* Now we can end and discard ourselves, without saving the data.
|
||||
* Since this data is all erroneous anyway, we don't care if it's
|
||||
* not totally accurate.
|
||||
*/
|
||||
|
||||
assert(!m_AtomStack.empty());
|
||||
atom = m_AtomStack.front();
|
||||
m_AtomStack.pop();
|
||||
|
||||
/* Note: We don't need to resume ourselves because end is set by Pause(). */
|
||||
total_time += CalcAtomTime(atom);
|
||||
|
||||
ResumeParent(total_time);
|
||||
return;
|
||||
}
|
||||
|
||||
PopProfileStack(&m_Callbacks);
|
||||
}
|
||||
|
||||
void ProfileEngine::PushProfileStack(IPluginContext *ctx, int type, const char *name)
|
||||
{
|
||||
prof_atom_t atom;
|
||||
|
||||
PauseParent();
|
||||
|
||||
atom.atom_type = type;
|
||||
atom.base_time = 0.0;
|
||||
atom.ctx = ctx->GetContext();
|
||||
atom.name = name;
|
||||
atom.end.is_set = false;
|
||||
|
||||
if (type == SP_PROF_CALLBACKS)
|
||||
{
|
||||
atom.atom_serial = ++m_serial;
|
||||
}
|
||||
else
|
||||
{
|
||||
atom.atom_serial = 0;
|
||||
}
|
||||
|
||||
m_AtomStack.push(atom);
|
||||
|
||||
/* Note: We do this after because the stack could grow and skew results */
|
||||
InitProfPoint(m_AtomStack.front().start);
|
||||
}
|
||||
|
||||
void ProfileEngine::PopProfileStack(ProfileReport *reporter)
|
||||
{
|
||||
double total_time;
|
||||
|
||||
prof_atom_t &atom = m_AtomStack.front();
|
||||
|
||||
/* We're okay to cache our used time. */
|
||||
InitProfPoint(atom.end);
|
||||
total_time = CalcAtomTime(atom);
|
||||
|
||||
/* Now it's time to save this! This may do a lot of computations which
|
||||
* is why we've cached the time beforehand.
|
||||
*/
|
||||
reporter->SaveAtom(atom);
|
||||
m_AtomStack.pop();
|
||||
|
||||
/* Finally, tell our parent how much time we used. */
|
||||
ResumeParent(total_time);
|
||||
}
|
||||
|
||||
void ProfileEngine::PauseParent()
|
||||
{
|
||||
if (m_AtomStack.empty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
InitProfPoint(m_AtomStack.front().end);
|
||||
}
|
||||
|
||||
void ProfileEngine::ResumeParent(double addTime)
|
||||
{
|
||||
if (m_AtomStack.empty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
prof_atom_t &atom = m_AtomStack.front();
|
||||
|
||||
/* Move its "paused time" to its base (known) time,
|
||||
* then reset the start/end. Note that since CalcAtomTime()
|
||||
* reads the base time, we SHOULD NOT use += to add.
|
||||
*/
|
||||
atom.base_time = CalcAtomTime(atom);
|
||||
atom.base_time += addTime;
|
||||
InitProfPoint(atom.start);
|
||||
atom.end.is_set = false;
|
||||
}
|
||||
|
||||
void ProfileEngine::Clear()
|
||||
{
|
||||
m_Natives.Clear();
|
||||
m_Callbacks.Clear();
|
||||
m_Functions.Clear();
|
||||
InitProfPoint(m_ProfStart);
|
||||
}
|
||||
|
||||
void ProfileEngine::OnSourceModAllInitialized()
|
||||
{
|
||||
g_RootMenu.AddRootConsoleCommand("profiler", "Profiler commands", this);
|
||||
}
|
||||
|
||||
void ProfileEngine::OnSourceModShutdown()
|
||||
{
|
||||
g_RootMenu.RemoveRootConsoleCommand("profiler", this);
|
||||
}
|
||||
|
||||
void ProfileEngine::OnRootConsoleCommand(const char *cmdname, const CCommand &command)
|
||||
{
|
||||
if (command.ArgC() >= 3)
|
||||
{
|
||||
if (strcmp(command.Arg(2), "flush") == 0)
|
||||
{
|
||||
FILE *fp;
|
||||
char path[256];
|
||||
|
||||
g_SourceMod.BuildPath(Path_SM, path, sizeof(path), "logs/profile_%d.xml", (int)time(NULL));
|
||||
|
||||
if ((fp = fopen(path, "wt")) == NULL)
|
||||
{
|
||||
g_RootMenu.ConsolePrint("Failed, could not open file for writing: %s", path);
|
||||
return;
|
||||
}
|
||||
|
||||
GenerateReport(fp);
|
||||
|
||||
fclose(fp);
|
||||
|
||||
g_RootMenu.ConsolePrint("Profiler report generated as: %s\n", path);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
g_RootMenu.ConsolePrint("Profiler commands:");
|
||||
g_RootMenu.DrawGenericOption("flush", "Flushes statistics to disk and starts over");
|
||||
}
|
||||
|
||||
bool ProfileEngine::GenerateReport(FILE *fp)
|
||||
{
|
||||
time_t t;
|
||||
double total_time;
|
||||
prof_point_t end_time;
|
||||
|
||||
InitProfPoint(end_time);
|
||||
total_time = DiffProfPoints(m_ProfStart, end_time);
|
||||
|
||||
t = time(NULL);
|
||||
|
||||
fprintf(fp, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n");
|
||||
fprintf(fp, "<profile time=\"%d\" uptime=\"%f\">\n", (int)t, total_time);
|
||||
WriteReport(fp, &m_Natives, "natives");
|
||||
WriteReport(fp, &m_Callbacks, "callbacks");
|
||||
WriteReport(fp, &m_Functions, "functions");
|
||||
fprintf(fp, "</profile>\n");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ProfileEngine::WriteReport(FILE *fp, ProfileReport *report, const char *name)
|
||||
{
|
||||
size_t i, num;
|
||||
prof_atom_report_t *ar;
|
||||
char new_name[512];
|
||||
|
||||
fprintf(fp, " <report name=\"%s\">\n", name);
|
||||
|
||||
num = report->GetNumReports();
|
||||
for (i = 0; i < num; i++)
|
||||
{
|
||||
ar = report->GetReport(i);
|
||||
|
||||
strncopy(new_name, ar->atom_name, sizeof(new_name));
|
||||
UTIL_ReplaceAll(new_name, sizeof(new_name), "<", "<");
|
||||
UTIL_ReplaceAll(new_name, sizeof(new_name), ">", ">");
|
||||
|
||||
fprintf(fp, " <item name=\"%s\" numcalls=\"%d\" mintime=\"%f\" maxtime=\"%f\" totaltime=\"%f\"/>\n",
|
||||
new_name,
|
||||
ar->num_calls,
|
||||
ar->min_time,
|
||||
ar->max_time,
|
||||
ar->total_time);
|
||||
}
|
||||
|
||||
fprintf(fp, " </report>\n");
|
||||
}
|
||||
|
||||
ProfileReport::~ProfileReport()
|
||||
{
|
||||
for (size_t i = 0; i < m_Reports.size(); i++)
|
||||
{
|
||||
delete m_Reports[i];
|
||||
}
|
||||
}
|
||||
|
||||
void ProfileReport::Clear()
|
||||
{
|
||||
m_ReportLookup.clear();
|
||||
for (size_t i = 0; i < m_Reports.size(); i++)
|
||||
{
|
||||
delete m_Reports[i];
|
||||
}
|
||||
m_Reports.clear();
|
||||
}
|
||||
|
||||
size_t ProfileReport::GetNumReports()
|
||||
{
|
||||
return m_Reports.size();
|
||||
}
|
||||
|
||||
prof_atom_report_t *ProfileReport::GetReport(size_t i)
|
||||
{
|
||||
return m_Reports[i];
|
||||
}
|
||||
|
||||
void ProfileReport::SaveAtom(const prof_atom_t &atom)
|
||||
{
|
||||
double atom_time;
|
||||
char full_name[256];
|
||||
prof_atom_report_t **pReport, *report;
|
||||
|
||||
if (atom.atom_type == SP_PROF_NATIVES)
|
||||
{
|
||||
strncopy(full_name, atom.name, sizeof(full_name));
|
||||
}
|
||||
else
|
||||
{
|
||||
CPlugin *pl;
|
||||
const char *file;
|
||||
|
||||
file = "unknown";
|
||||
if ((pl = g_PluginSys.GetPluginByCtx(atom.ctx)) != NULL)
|
||||
{
|
||||
file = pl->GetFilename();
|
||||
}
|
||||
|
||||
UTIL_Format(full_name, sizeof(full_name), "%s!%s", file, atom.name);
|
||||
}
|
||||
|
||||
atom_time = CalcAtomTime(atom);
|
||||
|
||||
if ((pReport = m_ReportLookup.retrieve(full_name)) == NULL)
|
||||
{
|
||||
report = new prof_atom_report_t;
|
||||
|
||||
strncopy(report->atom_name, full_name, sizeof(report->atom_name));
|
||||
report->max_time = atom_time;
|
||||
report->min_time = atom_time;
|
||||
report->num_calls = 1;
|
||||
report->total_time = atom_time;
|
||||
|
||||
m_ReportLookup.insert(full_name, report);
|
||||
m_Reports.push_back(report);
|
||||
}
|
||||
else
|
||||
{
|
||||
report = *pReport;
|
||||
|
||||
if (atom_time > report->max_time)
|
||||
{
|
||||
report->max_time = atom_time;
|
||||
}
|
||||
if (atom_time < report->min_time)
|
||||
{
|
||||
report->min_time = atom_time;
|
||||
}
|
||||
report->num_calls++;
|
||||
report->total_time += atom_time;
|
||||
}
|
||||
}
|
||||
+131
@@ -0,0 +1,131 @@
|
||||
/**
|
||||
* vim: set ts=4 :
|
||||
* =============================================================================
|
||||
* SourceMod
|
||||
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
|
||||
* =============================================================================
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, version 3.0, as published by the
|
||||
* Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* As a special exception, AlliedModders LLC gives you permission to link the
|
||||
* code of this program (as well as its derivative works) to "Half-Life 2," the
|
||||
* "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
|
||||
* by the Valve Corporation. You must obey the GNU General Public License in
|
||||
* all respects for all other code used. Additionally, AlliedModders LLC grants
|
||||
* this exception to all derivative works. AlliedModders LLC defines further
|
||||
* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
|
||||
* or <http://www.sourcemod.net/license.php>.
|
||||
*
|
||||
* Version: $Id$
|
||||
*/
|
||||
|
||||
#ifndef _INCLUDE_SOURCEMOD_PLUGIN_PROFILER_H_
|
||||
#define _INCLUDE_SOURCEMOD_PLUGIN_PROFILER_H_
|
||||
|
||||
#include <sp_vm_api.h>
|
||||
#include <sm_platform.h>
|
||||
#include <sm_trie_tpl.h>
|
||||
#include <sh_vector.h>
|
||||
#include <sh_stack.h>
|
||||
#include <stdio.h>
|
||||
#include "sm_globals.h"
|
||||
#include "sm_srvcmds.h"
|
||||
|
||||
using namespace SourcePawn;
|
||||
using namespace SourceHook;
|
||||
|
||||
struct prof_point_t
|
||||
{
|
||||
#if defined PLATFORM_WINDOWS
|
||||
LARGE_INTEGER value;
|
||||
#elif defined PLATFORM_POSIX
|
||||
struct timeval value;
|
||||
#endif
|
||||
bool is_set;
|
||||
};
|
||||
|
||||
struct prof_atom_t
|
||||
{
|
||||
int atom_type; /* Type of object we're profiling */
|
||||
int atom_serial; /* Serial number, if appropriate */
|
||||
sp_context_t *ctx; /* Plugin context. */
|
||||
const char *name; /* Name of the function */
|
||||
prof_point_t start; /* Start time */
|
||||
prof_point_t end; /* End time */
|
||||
double base_time; /* Known time from children or pausing. */
|
||||
};
|
||||
|
||||
struct prof_atom_report_t
|
||||
{
|
||||
char atom_name[256]; /* Full name to shove to logs */
|
||||
double total_time; /* Total time spent executing, in s */
|
||||
unsigned int num_calls; /* Number of invocations */
|
||||
double min_time; /* Min time spent in one call, in s */
|
||||
double max_time; /* Max time spent in one call, in s */
|
||||
};
|
||||
|
||||
class ProfileReport
|
||||
{
|
||||
public:
|
||||
~ProfileReport();
|
||||
public:
|
||||
void SaveAtom(const prof_atom_t &atom);
|
||||
size_t GetNumReports();
|
||||
prof_atom_report_t *GetReport(size_t i);
|
||||
void Clear();
|
||||
private:
|
||||
KTrie<prof_atom_report_t *> m_ReportLookup;
|
||||
CVector<prof_atom_report_t *> m_Reports;
|
||||
};
|
||||
|
||||
class ProfileEngine :
|
||||
public SMGlobalClass,
|
||||
public IRootConsoleCommand,
|
||||
public IProfiler
|
||||
{
|
||||
public:
|
||||
ProfileEngine();
|
||||
public:
|
||||
bool IsEnabled();
|
||||
bool GenerateReport(FILE *fp);
|
||||
void Clear();
|
||||
public: //SMGlobalClass
|
||||
void OnSourceModAllInitialized();
|
||||
void OnSourceModShutdown();
|
||||
public: //IRootConsoleCommand
|
||||
void OnRootConsoleCommand(const char *cmdname, const CCommand &command);
|
||||
public: //IProfiler
|
||||
void OnNativeBegin(IPluginContext *pContext, sp_native_t *native);
|
||||
void OnNativeEnd() ;
|
||||
void OnFunctionBegin(IPluginContext *pContext, const char *name);
|
||||
void OnFunctionEnd();
|
||||
int OnCallbackBegin(IPluginContext *pContext, sp_public_t *pubfunc);
|
||||
void OnCallbackEnd(int serial);
|
||||
private:
|
||||
void PushProfileStack(IPluginContext *ctx, int type, const char *name);
|
||||
void PopProfileStack(ProfileReport *reporter);
|
||||
void PauseParent();
|
||||
void ResumeParent(double addTime);
|
||||
void WriteReport(FILE *fp, ProfileReport *report, const char *name);
|
||||
private:
|
||||
CStack<prof_atom_t> m_AtomStack;
|
||||
ProfileReport m_Callbacks;
|
||||
ProfileReport m_Functions;
|
||||
ProfileReport m_Natives;
|
||||
int m_serial;
|
||||
prof_point_t m_ProfStart;
|
||||
};
|
||||
|
||||
extern ProfileEngine g_Profiler;
|
||||
|
||||
#endif //_INCLUDE_SOURCEMOD_PLUGIN_PROFILER_H_
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,91 @@
|
||||
/**
|
||||
* vim: set ts=4 :
|
||||
* =============================================================================
|
||||
* SourceMod
|
||||
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
|
||||
* =============================================================================
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, version 3.0, as published by the
|
||||
* Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* As a special exception, AlliedModders LLC gives you permission to link the
|
||||
* code of this program (as well as its derivative works) to "Half-Life 2," the
|
||||
* "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
|
||||
* by the Valve Corporation. You must obey the GNU General Public License in
|
||||
* all respects for all other code used. Additionally, AlliedModders LLC grants
|
||||
* this exception to all derivative works. AlliedModders LLC defines further
|
||||
* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
|
||||
* or <http://www.sourcemod.net/license.php>.
|
||||
*
|
||||
* Version: $Id$
|
||||
*/
|
||||
|
||||
#ifndef _INCLUDE_SOURCEMOD_TEXTPARSERS_H_
|
||||
#define _INCLUDE_SOURCEMOD_TEXTPARSERS_H_
|
||||
|
||||
#include <ITextParsers.h>
|
||||
#include "sm_globals.h"
|
||||
|
||||
using namespace SourceMod;
|
||||
|
||||
/**
|
||||
* @param void * IN: Stream pointer
|
||||
* @param char * IN/OUT: Stream buffer
|
||||
* @param size_t IN: Maximum size of buffer
|
||||
* @param unsigned int * OUT: Number of bytes read (0 = end of stream)
|
||||
* @return True on success, false on failure
|
||||
*/
|
||||
typedef bool (*STREAMREADER)(void *, char *, size_t, unsigned int *);
|
||||
|
||||
class TextParsers :
|
||||
public ITextParsers,
|
||||
public SMGlobalClass
|
||||
{
|
||||
public:
|
||||
TextParsers();
|
||||
public: //SMGlobalClass
|
||||
void OnSourceModAllInitialized();
|
||||
public:
|
||||
bool ParseFile_INI(const char *file,
|
||||
ITextListener_INI *ini_listener,
|
||||
unsigned int *line,
|
||||
unsigned int *col);
|
||||
|
||||
SMCError ParseFile_SMC(const char *file,
|
||||
ITextListener_SMC *smc_listener,
|
||||
SMCStates *states);
|
||||
|
||||
SMCError ParseSMCFile(const char *file,
|
||||
ITextListener_SMC *smc_listener,
|
||||
SMCStates *states,
|
||||
char *buffer,
|
||||
size_t maxsize);
|
||||
|
||||
unsigned int GetUTF8CharBytes(const char *stream);
|
||||
|
||||
const char *GetSMCErrorString(SMCError err);
|
||||
bool IsWhitespace(const char *stream);
|
||||
private:
|
||||
SMCError ParseString_SMC(const char *stream,
|
||||
ITextListener_SMC *smc,
|
||||
SMCStates *states);
|
||||
SMCError ParseStream_SMC(void *stream,
|
||||
STREAMREADER srdr,
|
||||
ITextListener_SMC *smc,
|
||||
SMCStates *states);
|
||||
|
||||
};
|
||||
|
||||
extern TextParsers g_TextParser;
|
||||
|
||||
#endif //_INCLUDE_SOURCEMOD_TEXTPARSERS_H_
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
/**
|
||||
* vim: set ts=4 :
|
||||
* =============================================================================
|
||||
* SourceMod
|
||||
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
|
||||
* =============================================================================
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, version 3.0, as published by the
|
||||
* Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* As a special exception, AlliedModders LLC gives you permission to link the
|
||||
* code of this program (as well as its derivative works) to "Half-Life 2," the
|
||||
* "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
|
||||
* by the Valve Corporation. You must obey the GNU General Public License in
|
||||
* all respects for all other code used. Additionally, AlliedModders LLC grants
|
||||
* this exception to all derivative works. AlliedModders LLC defines further
|
||||
* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
|
||||
* or <http://www.sourcemod.net/license.php>.
|
||||
*
|
||||
* Version: $Id$
|
||||
*/
|
||||
|
||||
#include "ThreadSupport.h"
|
||||
#include "sm_globals.h"
|
||||
#include "ShareSys.h"
|
||||
|
||||
#if defined PLATFORM_POSIX
|
||||
#include "thread/PosixThreads.h"
|
||||
#elif defined PLATFORM_WINDOWS
|
||||
#include "thread/WinThreads.h"
|
||||
#endif
|
||||
|
||||
MainThreader g_MainThreader;
|
||||
IThreader *g_pThreader = &g_MainThreader;
|
||||
|
||||
class RegThreadStuff : public SMGlobalClass
|
||||
{
|
||||
public:
|
||||
void OnSourceModAllInitialized()
|
||||
{
|
||||
g_ShareSys.AddInterface(NULL, g_pThreader);
|
||||
}
|
||||
} s_RegThreadStuff;
|
||||
@@ -0,0 +1,41 @@
|
||||
/**
|
||||
* vim: set ts=4 :
|
||||
* =============================================================================
|
||||
* SourceMod
|
||||
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
|
||||
* =============================================================================
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, version 3.0, as published by the
|
||||
* Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* As a special exception, AlliedModders LLC gives you permission to link the
|
||||
* code of this program (as well as its derivative works) to "Half-Life 2," the
|
||||
* "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
|
||||
* by the Valve Corporation. You must obey the GNU General Public License in
|
||||
* all respects for all other code used. Additionally, AlliedModders LLC grants
|
||||
* this exception to all derivative works. AlliedModders LLC defines further
|
||||
* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
|
||||
* or <http://www.sourcemod.net/license.php>.
|
||||
*
|
||||
* Version: $Id$
|
||||
*/
|
||||
|
||||
#ifndef _INCLUDE_SOURCEMOD_THREAD_SUPPORT_H
|
||||
#define _INCLUDE_SOURCEMOD_THREAD_SUPPORT_H
|
||||
|
||||
#include <IThreader.h>
|
||||
|
||||
using namespace SourceMod;
|
||||
|
||||
extern IThreader *g_pThreader;
|
||||
|
||||
#endif //_INCLUDE_SOURCEMOD_THREAD_SUPPORT_H
|
||||
@@ -0,0 +1,487 @@
|
||||
/**
|
||||
* vim: set ts=4 :
|
||||
* =============================================================================
|
||||
* SourceMod
|
||||
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
|
||||
* =============================================================================
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, version 3.0, as published by the
|
||||
* Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* As a special exception, AlliedModders LLC gives you permission to link the
|
||||
* code of this program (as well as its derivative works) to "Half-Life 2," the
|
||||
* "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
|
||||
* by the Valve Corporation. You must obey the GNU General Public License in
|
||||
* all respects for all other code used. Additionally, AlliedModders LLC grants
|
||||
* this exception to all derivative works. AlliedModders LLC defines further
|
||||
* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
|
||||
* or <http://www.sourcemod.net/license.php>.
|
||||
*
|
||||
* Version: $Id$
|
||||
*/
|
||||
|
||||
#include <time.h>
|
||||
#include "TimerSys.h"
|
||||
#include "ForwardSys.h"
|
||||
#include "sourcemm_api.h"
|
||||
#include "frame_hooks.h"
|
||||
#include "ConVarManager.h"
|
||||
|
||||
#define TIMER_MIN_ACCURACY 0.1
|
||||
|
||||
TimerSystem g_Timers;
|
||||
double g_fUniversalTime = 0.0f;
|
||||
float g_fGameStartTime = 0.0f; /* Game game start time, non-universal */
|
||||
double g_fTimerThink = 0.0f; /* Timer's next think time */
|
||||
const double *g_pUniversalTime = &g_fUniversalTime;
|
||||
ConVar *mp_timelimit = NULL;
|
||||
int g_TimeLeftMode = 0;
|
||||
|
||||
ConVar sm_time_adjustment("sm_time_adjustment", "0", 0, "Adjusts the server time in seconds");
|
||||
|
||||
inline double GetSimulatedTime()
|
||||
{
|
||||
return g_fUniversalTime;
|
||||
}
|
||||
|
||||
time_t GetAdjustedTime(time_t *buf)
|
||||
{
|
||||
time_t val = time(NULL) + sm_time_adjustment.GetInt();
|
||||
if (buf)
|
||||
{
|
||||
*buf = val;
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
class DefaultMapTimer :
|
||||
public IMapTimer,
|
||||
public SMGlobalClass,
|
||||
public IConVarChangeListener
|
||||
{
|
||||
public:
|
||||
DefaultMapTimer()
|
||||
{
|
||||
m_bInUse = false;
|
||||
}
|
||||
|
||||
void OnSourceModLevelChange(const char *mapName)
|
||||
{
|
||||
g_fGameStartTime = 0.0f;
|
||||
}
|
||||
|
||||
int GetMapTimeLimit()
|
||||
{
|
||||
return mp_timelimit->GetInt();
|
||||
}
|
||||
|
||||
void SetMapTimerStatus(bool enabled)
|
||||
{
|
||||
if (enabled && !m_bInUse)
|
||||
{
|
||||
Enable();
|
||||
}
|
||||
else if (!enabled && m_bInUse)
|
||||
{
|
||||
Disable();
|
||||
}
|
||||
m_bInUse = enabled;
|
||||
}
|
||||
|
||||
void ExtendMapTimeLimit(int extra_time)
|
||||
{
|
||||
if (extra_time == 0)
|
||||
{
|
||||
mp_timelimit->SetValue(0);
|
||||
return;
|
||||
}
|
||||
|
||||
extra_time /= 60;
|
||||
|
||||
mp_timelimit->SetValue(mp_timelimit->GetInt() + extra_time);
|
||||
}
|
||||
|
||||
void OnConVarChanged(ConVar *pConVar, const char *oldValue, float flOldValue)
|
||||
{
|
||||
g_Timers.MapTimeLeftChanged();
|
||||
}
|
||||
|
||||
private:
|
||||
void Enable()
|
||||
{
|
||||
g_ConVarManager.AddConVarChangeListener("mp_timelimit", this);
|
||||
}
|
||||
|
||||
void Disable()
|
||||
{
|
||||
g_ConVarManager.RemoveConVarChangeListener("mp_timelimit", this);
|
||||
}
|
||||
|
||||
private:
|
||||
bool m_bInUse;
|
||||
} s_DefaultMapTimer;
|
||||
|
||||
/**
|
||||
* If the ticking process has run amok (should be impossible), we
|
||||
* take care of this by "skipping" the in-between time, to prevent
|
||||
* a bazillion times from firing on accident. This has the result
|
||||
* that a drastic jump in time will continue acting normally. Users
|
||||
* may not expect this, but... I think it is the best solution.
|
||||
*/
|
||||
inline double CalcNextThink(double last, float interval)
|
||||
{
|
||||
if (g_fUniversalTime - last - interval <= TIMER_MIN_ACCURACY)
|
||||
{
|
||||
return last + interval;
|
||||
}
|
||||
else
|
||||
{
|
||||
return g_fUniversalTime + interval;
|
||||
}
|
||||
}
|
||||
|
||||
void ITimer::Initialize(ITimedEvent *pCallbacks, float fInterval, float fToExec, void *pData, int flags)
|
||||
{
|
||||
m_Listener = pCallbacks;
|
||||
m_Interval = fInterval;
|
||||
m_ToExec = fToExec;
|
||||
m_pData = pData;
|
||||
m_Flags = flags;
|
||||
m_InExec = false;
|
||||
m_KillMe = false;
|
||||
}
|
||||
|
||||
TimerSystem::TimerSystem()
|
||||
{
|
||||
m_pMapTimer = NULL;
|
||||
m_bHasMapTickedYet = false;
|
||||
m_bHasMapSimulatedYet = false;
|
||||
m_fLastTickedTime = 0.0f;
|
||||
}
|
||||
|
||||
TimerSystem::~TimerSystem()
|
||||
{
|
||||
CStack<ITimer *>::iterator iter;
|
||||
for (iter=m_FreeTimers.begin(); iter!=m_FreeTimers.end(); iter++)
|
||||
{
|
||||
delete (*iter);
|
||||
}
|
||||
m_FreeTimers.popall();
|
||||
}
|
||||
|
||||
void TimerSystem::OnSourceModAllInitialized()
|
||||
{
|
||||
g_ShareSys.AddInterface(NULL, this);
|
||||
m_pOnGameFrame = g_Forwards.CreateForward("OnGameFrame", ET_Ignore, 0, NULL);
|
||||
m_pOnMapTimeLeftChanged = g_Forwards.CreateForward("OnMapTimeLeftChanged", ET_Ignore, 0, NULL);
|
||||
}
|
||||
|
||||
void TimerSystem::OnSourceModGameInitialized()
|
||||
{
|
||||
mp_timelimit = icvar->FindVar("mp_timelimit");
|
||||
|
||||
if (m_pMapTimer == NULL && mp_timelimit != NULL)
|
||||
{
|
||||
SetMapTimer(&s_DefaultMapTimer);
|
||||
}
|
||||
}
|
||||
|
||||
void TimerSystem::OnSourceModShutdown()
|
||||
{
|
||||
SetMapTimer(NULL);
|
||||
g_Forwards.ReleaseForward(m_pOnGameFrame);
|
||||
g_Forwards.ReleaseForward(m_pOnMapTimeLeftChanged);
|
||||
}
|
||||
|
||||
void TimerSystem::OnSourceModLevelEnd()
|
||||
{
|
||||
m_bHasMapTickedYet = false;
|
||||
m_bHasMapSimulatedYet = false;
|
||||
}
|
||||
|
||||
void TimerSystem::GameFrame(bool simulating)
|
||||
{
|
||||
if (simulating && m_bHasMapTickedYet)
|
||||
{
|
||||
g_fUniversalTime += gpGlobals->curtime - m_fLastTickedTime;
|
||||
if (!m_bHasMapSimulatedYet)
|
||||
{
|
||||
m_bHasMapSimulatedYet = true;
|
||||
MapTimeLeftChanged();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
g_fUniversalTime += gpGlobals->interval_per_tick;
|
||||
}
|
||||
|
||||
m_fLastTickedTime = gpGlobals->curtime;
|
||||
m_bHasMapTickedYet = true;
|
||||
|
||||
if (g_fUniversalTime >= g_fTimerThink)
|
||||
{
|
||||
RunFrame();
|
||||
|
||||
g_fTimerThink = CalcNextThink(g_fTimerThink, TIMER_MIN_ACCURACY);
|
||||
}
|
||||
|
||||
RunFrameHooks(simulating);
|
||||
|
||||
if (m_pOnGameFrame->GetFunctionCount())
|
||||
{
|
||||
m_pOnGameFrame->Execute(NULL);
|
||||
}
|
||||
}
|
||||
|
||||
void TimerSystem::RunFrame()
|
||||
{
|
||||
ITimer *pTimer;
|
||||
TimerIter iter;
|
||||
|
||||
double curtime = GetSimulatedTime();
|
||||
for (iter=m_SingleTimers.begin(); iter!=m_SingleTimers.end(); )
|
||||
{
|
||||
pTimer = (*iter);
|
||||
if (curtime >= pTimer->m_ToExec)
|
||||
{
|
||||
pTimer->m_InExec = true;
|
||||
pTimer->m_Listener->OnTimer(pTimer, pTimer->m_pData);
|
||||
pTimer->m_Listener->OnTimerEnd(pTimer, pTimer->m_pData);
|
||||
iter = m_SingleTimers.erase(iter);
|
||||
m_FreeTimers.push(pTimer);
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ResultType res;
|
||||
for (iter=m_LoopTimers.begin(); iter!=m_LoopTimers.end(); )
|
||||
{
|
||||
pTimer = (*iter);
|
||||
if (curtime >= pTimer->m_ToExec)
|
||||
{
|
||||
pTimer->m_InExec = true;
|
||||
res = pTimer->m_Listener->OnTimer(pTimer, pTimer->m_pData);
|
||||
if (pTimer->m_KillMe || (res == Pl_Stop))
|
||||
{
|
||||
pTimer->m_Listener->OnTimerEnd(pTimer, pTimer->m_pData);
|
||||
iter = m_LoopTimers.erase(iter);
|
||||
m_FreeTimers.push(pTimer);
|
||||
continue;
|
||||
}
|
||||
pTimer->m_InExec = false;
|
||||
pTimer->m_ToExec = CalcNextThink(pTimer->m_ToExec, pTimer->m_Interval);
|
||||
}
|
||||
iter++;
|
||||
}
|
||||
}
|
||||
|
||||
ITimer *TimerSystem::CreateTimer(ITimedEvent *pCallbacks, float fInterval, void *pData, int flags)
|
||||
{
|
||||
ITimer *pTimer;
|
||||
TimerIter iter;
|
||||
float to_exec = GetSimulatedTime() + fInterval;
|
||||
|
||||
if (m_FreeTimers.empty())
|
||||
{
|
||||
pTimer = new ITimer;
|
||||
} else {
|
||||
pTimer = m_FreeTimers.front();
|
||||
m_FreeTimers.pop();
|
||||
}
|
||||
|
||||
pTimer->Initialize(pCallbacks, fInterval, to_exec, pData, flags);
|
||||
|
||||
if (flags & TIMER_FLAG_REPEAT)
|
||||
{
|
||||
m_LoopTimers.push_back(pTimer);
|
||||
goto return_timer;
|
||||
}
|
||||
|
||||
if (m_SingleTimers.size() >= 1)
|
||||
{
|
||||
iter = --m_SingleTimers.end();
|
||||
if ((*iter)->m_ToExec <= to_exec)
|
||||
{
|
||||
goto normal_insert_end;
|
||||
}
|
||||
}
|
||||
|
||||
for (iter=m_SingleTimers.begin(); iter!=m_SingleTimers.end(); iter++)
|
||||
{
|
||||
if ((*iter)->m_ToExec >= to_exec)
|
||||
{
|
||||
m_SingleTimers.insert(iter, pTimer);
|
||||
goto return_timer;
|
||||
}
|
||||
}
|
||||
|
||||
normal_insert_end:
|
||||
m_SingleTimers.push_back(pTimer);
|
||||
|
||||
return_timer:
|
||||
return pTimer;
|
||||
}
|
||||
|
||||
void TimerSystem::FireTimerOnce(ITimer *pTimer, bool delayExec)
|
||||
{
|
||||
ResultType res;
|
||||
|
||||
if (pTimer->m_InExec)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
pTimer->m_InExec = true;
|
||||
res = pTimer->m_Listener->OnTimer(pTimer, pTimer->m_pData);
|
||||
|
||||
if (!(pTimer->m_Flags & TIMER_FLAG_REPEAT))
|
||||
{
|
||||
pTimer->m_Listener->OnTimerEnd(pTimer, pTimer->m_pData);
|
||||
m_SingleTimers.remove(pTimer);
|
||||
m_FreeTimers.push(pTimer);
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((res != Pl_Stop) && !pTimer->m_KillMe)
|
||||
{
|
||||
if (delayExec)
|
||||
{
|
||||
pTimer->m_ToExec = GetSimulatedTime() + pTimer->m_Interval;
|
||||
}
|
||||
pTimer->m_InExec = false;
|
||||
return;
|
||||
}
|
||||
pTimer->m_Listener->OnTimerEnd(pTimer, pTimer->m_pData);
|
||||
m_LoopTimers.remove(pTimer);
|
||||
m_FreeTimers.push(pTimer);
|
||||
}
|
||||
}
|
||||
|
||||
void TimerSystem::KillTimer(ITimer *pTimer)
|
||||
{
|
||||
if (pTimer->m_KillMe)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (pTimer->m_InExec)
|
||||
{
|
||||
pTimer->m_KillMe = true;
|
||||
return;
|
||||
}
|
||||
|
||||
pTimer->m_InExec = true; /* The timer it's not really executed but this check needs to be done */
|
||||
pTimer->m_Listener->OnTimerEnd(pTimer, pTimer->m_pData);
|
||||
|
||||
if (pTimer->m_Flags & TIMER_FLAG_REPEAT)
|
||||
{
|
||||
m_LoopTimers.remove(pTimer);
|
||||
} else {
|
||||
m_SingleTimers.remove(pTimer);
|
||||
}
|
||||
|
||||
m_FreeTimers.push(pTimer);
|
||||
}
|
||||
|
||||
CStack<ITimer *> s_tokill;
|
||||
void TimerSystem::RemoveMapChangeTimers()
|
||||
{
|
||||
ITimer *pTimer;
|
||||
TimerIter iter;
|
||||
|
||||
for (iter=m_SingleTimers.begin(); iter!=m_SingleTimers.end(); iter++)
|
||||
{
|
||||
pTimer = (*iter);
|
||||
if (pTimer->m_Flags & TIMER_FLAG_NO_MAPCHANGE)
|
||||
{
|
||||
s_tokill.push(pTimer);
|
||||
}
|
||||
}
|
||||
|
||||
for (iter=m_LoopTimers.begin(); iter!=m_LoopTimers.end(); iter++)
|
||||
{
|
||||
pTimer = (*iter);
|
||||
if (pTimer->m_Flags & TIMER_FLAG_NO_MAPCHANGE)
|
||||
{
|
||||
s_tokill.push(pTimer);
|
||||
}
|
||||
}
|
||||
|
||||
while (!s_tokill.empty())
|
||||
{
|
||||
KillTimer(s_tokill.front());
|
||||
s_tokill.pop();
|
||||
}
|
||||
}
|
||||
|
||||
IMapTimer *TimerSystem::SetMapTimer(IMapTimer *pTimer)
|
||||
{
|
||||
IMapTimer *old = m_pMapTimer;
|
||||
|
||||
m_pMapTimer = pTimer;
|
||||
|
||||
if (m_pMapTimer)
|
||||
{
|
||||
m_pMapTimer->SetMapTimerStatus(true);
|
||||
}
|
||||
|
||||
if (old)
|
||||
{
|
||||
old->SetMapTimerStatus(false);
|
||||
}
|
||||
|
||||
return old;
|
||||
}
|
||||
|
||||
IMapTimer *TimerSystem::GetMapTimer()
|
||||
{
|
||||
return m_pMapTimer;
|
||||
}
|
||||
|
||||
void TimerSystem::MapTimeLeftChanged()
|
||||
{
|
||||
m_pOnMapTimeLeftChanged->Execute(NULL);
|
||||
}
|
||||
|
||||
void TimerSystem::NotifyOfGameStart(float offset)
|
||||
{
|
||||
g_fGameStartTime = gpGlobals->curtime + offset;
|
||||
}
|
||||
|
||||
float TimerSystem::GetTickedTime()
|
||||
{
|
||||
return g_fUniversalTime;
|
||||
}
|
||||
|
||||
bool TimerSystem::GetMapTimeLeft(float *time_left)
|
||||
{
|
||||
if (!m_pMapTimer)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
int time_limit;
|
||||
if (!m_bHasMapSimulatedYet || (time_limit = m_pMapTimer->GetMapTimeLimit()) < 1)
|
||||
{
|
||||
*time_left = -1.0f;
|
||||
}
|
||||
else
|
||||
{
|
||||
*time_left = (g_fGameStartTime + time_limit * 60.0f) - gpGlobals->curtime;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
+110
@@ -0,0 +1,110 @@
|
||||
/**
|
||||
* vim: set ts=4 :
|
||||
* =============================================================================
|
||||
* SourceMod
|
||||
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
|
||||
* =============================================================================
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, version 3.0, as published by the
|
||||
* Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* As a special exception, AlliedModders LLC gives you permission to link the
|
||||
* code of this program (as well as its derivative works) to "Half-Life 2," the
|
||||
* "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
|
||||
* by the Valve Corporation. You must obey the GNU General Public License in
|
||||
* all respects for all other code used. Additionally, AlliedModders LLC grants
|
||||
* this exception to all derivative works. AlliedModders LLC defines further
|
||||
* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
|
||||
* or <http://www.sourcemod.net/license.php>.
|
||||
*
|
||||
* Version: $Id$
|
||||
*/
|
||||
|
||||
#ifndef _INCLUDE_SOURCEMOD_CTIMERSYS_H_
|
||||
#define _INCLUDE_SOURCEMOD_CTIMERSYS_H_
|
||||
|
||||
#include "ShareSys.h"
|
||||
#include <ITimerSystem.h>
|
||||
#include <sh_stack.h>
|
||||
#include <sh_list.h>
|
||||
#include "sourcemm_api.h"
|
||||
|
||||
using namespace SourceHook;
|
||||
using namespace SourceMod;
|
||||
|
||||
typedef List<ITimer *> TimerList;
|
||||
typedef List<ITimer *>::iterator TimerIter;
|
||||
|
||||
class SourceMod::ITimer
|
||||
{
|
||||
public:
|
||||
void Initialize(ITimedEvent *pCallbacks, float fInterval, float fToExec, void *pData, int flags);
|
||||
ITimedEvent *m_Listener;
|
||||
void *m_pData;
|
||||
float m_Interval;
|
||||
double m_ToExec;
|
||||
int m_Flags;
|
||||
bool m_InExec;
|
||||
bool m_KillMe;
|
||||
};
|
||||
|
||||
class TimerSystem :
|
||||
public ITimerSystem,
|
||||
public SMGlobalClass
|
||||
{
|
||||
public:
|
||||
TimerSystem();
|
||||
~TimerSystem();
|
||||
public: //SMGlobalClass
|
||||
void OnSourceModAllInitialized();
|
||||
void OnSourceModLevelEnd();
|
||||
void OnSourceModGameInitialized();
|
||||
void OnSourceModShutdown();
|
||||
public: //ITimerSystem
|
||||
ITimer *CreateTimer(ITimedEvent *pCallbacks, float fInterval, void *pData, int flags);
|
||||
void KillTimer(ITimer *pTimer);
|
||||
void FireTimerOnce(ITimer *pTimer, bool delayExec=false);
|
||||
void MapTimeLeftChanged();
|
||||
IMapTimer *SetMapTimer(IMapTimer *pTimer);
|
||||
float GetTickedTime();
|
||||
void NotifyOfGameStart(float offset /* = 0.0f */);
|
||||
bool GetMapTimeLeft(float *pTime);
|
||||
public:
|
||||
void RunFrame();
|
||||
void RemoveMapChangeTimers();
|
||||
void GameFrame(bool simulating);
|
||||
IMapTimer *GetMapTimer();
|
||||
private:
|
||||
List<ITimer *> m_SingleTimers;
|
||||
List<ITimer *> m_LoopTimers;
|
||||
CStack<ITimer *> m_FreeTimers;
|
||||
IMapTimer *m_pMapTimer;
|
||||
|
||||
/* This is stuff for our manual ticking escapades. */
|
||||
bool m_bHasMapTickedYet; /** Has the map ticked yet? */
|
||||
bool m_bHasMapSimulatedYet; /** Has the map simulated yet? */
|
||||
float m_fLastTickedTime; /** Last time that the game currently gave
|
||||
us while ticking.
|
||||
*/
|
||||
|
||||
IForward *m_pOnGameFrame;
|
||||
IForward *m_pOnMapTimeLeftChanged;
|
||||
};
|
||||
|
||||
time_t GetAdjustedTime(time_t *buf = NULL);
|
||||
|
||||
extern const double *g_pUniversalTime;
|
||||
extern TimerSystem g_Timers;
|
||||
extern int g_TimeLeftMode;
|
||||
|
||||
#endif //_INCLUDE_SOURCEMOD_CTIMERSYS_H_
|
||||
|
||||
+1076
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,177 @@
|
||||
/**
|
||||
* vim: set ts=4 :
|
||||
* =============================================================================
|
||||
* SourceMod
|
||||
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
|
||||
* =============================================================================
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, version 3.0, as published by the
|
||||
* Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* As a special exception, AlliedModders LLC gives you permission to link the
|
||||
* code of this program (as well as its derivative works) to "Half-Life 2," the
|
||||
* "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
|
||||
* by the Valve Corporation. You must obey the GNU General Public License in
|
||||
* all respects for all other code used. Additionally, AlliedModders LLC grants
|
||||
* this exception to all derivative works. AlliedModders LLC defines further
|
||||
* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
|
||||
* or <http://www.sourcemod.net/license.php>.
|
||||
*
|
||||
* Version: $Id$
|
||||
*/
|
||||
|
||||
#ifndef _INCLUDE_SOURCEMOD_TRANSLATOR_H_
|
||||
#define _INCLUDE_SOURCEMOD_TRANSLATOR_H_
|
||||
|
||||
#include "sm_trie.h"
|
||||
#include <sh_string.h>
|
||||
#include <sh_vector.h>
|
||||
#include "sm_globals.h"
|
||||
#include "sm_memtable.h"
|
||||
#include "ITextParsers.h"
|
||||
#include <ITranslator.h>
|
||||
|
||||
#define MAX_TRANSLATE_PARAMS 32
|
||||
#define CORELANG_ENGLISH 0
|
||||
|
||||
/* :TODO: write a templatized version of tries? */
|
||||
|
||||
using namespace SourceHook;
|
||||
class Translator;
|
||||
|
||||
enum PhraseParseState
|
||||
{
|
||||
PPS_None = 0,
|
||||
PPS_Phrases,
|
||||
PPS_InPhrase,
|
||||
};
|
||||
|
||||
struct Language
|
||||
{
|
||||
char m_code2[3];
|
||||
int m_FullName;
|
||||
};
|
||||
|
||||
class CPhraseFile :
|
||||
public ITextListener_SMC,
|
||||
public IPhraseFile
|
||||
{
|
||||
public:
|
||||
CPhraseFile(Translator *pTranslator, const char *file);
|
||||
~CPhraseFile();
|
||||
public:
|
||||
void ReparseFile();
|
||||
const char *GetFilename();
|
||||
TransError GetTranslation(const char *szPhrase, unsigned int lang_id, Translation *pTrans);
|
||||
public: //ITextListener_SMC
|
||||
void ReadSMC_ParseStart();
|
||||
SMCResult ReadSMC_NewSection(const SMCStates *states, const char *name);
|
||||
SMCResult ReadSMC_KeyValue(const SMCStates *states, const char *key, const char *value);
|
||||
SMCResult ReadSMC_LeavingSection(const SMCStates *states);
|
||||
void ReadSMC_ParseEnd(bool halted, bool failed);
|
||||
private:
|
||||
void ParseError(const char *message, ...);
|
||||
void ParseWarning(const char *message, ...);
|
||||
private:
|
||||
Trie *m_pPhraseLookup;
|
||||
String m_File;
|
||||
Translator *m_pTranslator;
|
||||
PhraseParseState m_ParseState;
|
||||
int m_CurPhrase;
|
||||
BaseMemTable *m_pMemory;
|
||||
BaseStringTable *m_pStringTab;
|
||||
unsigned int m_LangCount;
|
||||
String m_ParseError;
|
||||
String m_LastPhraseString;
|
||||
bool m_FileLogged;
|
||||
};
|
||||
|
||||
class Translator :
|
||||
public ITextListener_SMC,
|
||||
public SMGlobalClass,
|
||||
public ITranslator
|
||||
{
|
||||
public:
|
||||
Translator();
|
||||
~Translator();
|
||||
public: // SMGlobalClass
|
||||
ConfigResult OnSourceModConfigChanged(const char *key,
|
||||
const char *value,
|
||||
ConfigSource source,
|
||||
char *error,
|
||||
size_t maxlength);
|
||||
void OnSourceModAllInitialized();
|
||||
void OnSourceModLevelChange(const char *mapName);
|
||||
void OnSourceModShutdown();
|
||||
public: // ITextListener_SMC
|
||||
void ReadSMC_ParseStart();
|
||||
SMCResult ReadSMC_NewSection(const SMCStates *states, const char *name);
|
||||
SMCResult ReadSMC_KeyValue(const SMCStates *states, const char *key, const char *value);
|
||||
SMCResult ReadSMC_LeavingSection(const SMCStates *states);
|
||||
public:
|
||||
void RebuildLanguageDatabase(const char *lang_header_file);
|
||||
unsigned int FindOrAddPhraseFile(const char *phrase_file);
|
||||
BaseStringTable *GetStringTable();
|
||||
unsigned int GetLanguageCount();
|
||||
bool GetLanguageInfo(unsigned int number, const char **code, const char **name);
|
||||
bool GetLanguageByCode(const char *code, unsigned int *index);
|
||||
bool GetLanguageByName(const char *name, unsigned int *index);
|
||||
CPhraseFile *GetFileByIndex(unsigned int index);
|
||||
public: //ITranslator
|
||||
unsigned int GetServerLanguage();
|
||||
unsigned int GetClientLanguage(int client);
|
||||
const char *GetInterfaceName();
|
||||
unsigned int GetInterfaceVersion();
|
||||
IPhraseCollection *CreatePhraseCollection();
|
||||
int SetGlobalTarget(int index);
|
||||
int GetGlobalTarget() const;
|
||||
size_t FormatString(
|
||||
char *buffer,
|
||||
size_t maxlength,
|
||||
SourcePawn::IPluginContext *pContext,
|
||||
const cell_t *params,
|
||||
unsigned int param);
|
||||
bool FormatString(
|
||||
char *buffer,
|
||||
size_t maxlength,
|
||||
const char *format,
|
||||
IPhraseCollection *pPhrases,
|
||||
void **params,
|
||||
unsigned int numparams,
|
||||
size_t *pOutLength,
|
||||
const char **pFailPhrase);
|
||||
private:
|
||||
bool AddLanguage(const char *langcode, const char *description);
|
||||
private:
|
||||
CVector<Language *> m_Languages;
|
||||
CVector<CPhraseFile *> m_Files;
|
||||
BaseStringTable *m_pStringTab;
|
||||
Trie *m_pLCodeLookup;
|
||||
bool m_InLanguageSection;
|
||||
String m_CustomError;
|
||||
unsigned int m_ServerLang;
|
||||
char m_InitialLang[3];
|
||||
};
|
||||
|
||||
/* Nice little wrapper to handle error logging and whatnot */
|
||||
bool CoreTranslate(char *buffer,
|
||||
size_t maxlength,
|
||||
const char *format,
|
||||
unsigned int numparams,
|
||||
size_t *pOutLength,
|
||||
...);
|
||||
|
||||
extern IPhraseCollection *g_pCorePhrases;
|
||||
extern unsigned int g_pCorePhraseID;
|
||||
extern Translator g_Translator;
|
||||
|
||||
#endif //_INCLUDE_SOURCEMOD_TRANSLATOR_H_
|
||||
@@ -0,0 +1,477 @@
|
||||
/**
|
||||
* vim: set ts=4 :
|
||||
* =============================================================================
|
||||
* SourceMod
|
||||
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
|
||||
* =============================================================================
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, version 3.0, as published by the
|
||||
* Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* As a special exception, AlliedModders LLC gives you permission to link the
|
||||
* code of this program (as well as its derivative works) to "Half-Life 2," the
|
||||
* "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
|
||||
* by the Valve Corporation. You must obey the GNU General Public License in
|
||||
* all respects for all other code used. Additionally, AlliedModders LLC grants
|
||||
* this exception to all derivative works. AlliedModders LLC defines further
|
||||
* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
|
||||
* or <http://www.sourcemod.net/license.php>.
|
||||
*
|
||||
* Version: $Id$
|
||||
*/
|
||||
|
||||
#include "UserMessages.h"
|
||||
#include "sm_stringutil.h"
|
||||
|
||||
UserMessages g_UserMsgs;
|
||||
|
||||
SH_DECL_HOOK2(IVEngineServer, UserMessageBegin, SH_NOATTRIB, 0, bf_write *, IRecipientFilter *, int);
|
||||
SH_DECL_HOOK0_void(IVEngineServer, MessageEnd, SH_NOATTRIB, 0);
|
||||
|
||||
UserMessages::UserMessages() : m_InterceptBuffer(m_pBase, 2500)
|
||||
{
|
||||
m_Names = sm_trie_create();
|
||||
m_HookCount = 0;
|
||||
m_InExec = false;
|
||||
m_InHook = false;
|
||||
m_CurFlags = 0;
|
||||
m_CurId = INVALID_MESSAGE_ID;
|
||||
}
|
||||
|
||||
UserMessages::~UserMessages()
|
||||
{
|
||||
sm_trie_destroy(m_Names);
|
||||
|
||||
CStack<ListenerInfo *>::iterator iter;
|
||||
for (iter=m_FreeListeners.begin(); iter!=m_FreeListeners.end(); iter++)
|
||||
{
|
||||
delete (*iter);
|
||||
}
|
||||
m_FreeListeners.popall();
|
||||
}
|
||||
|
||||
void UserMessages::OnSourceModStartup(bool late)
|
||||
{
|
||||
/* -1 means SourceMM was unable to get the user message list */
|
||||
m_FallbackSearch = (g_SMAPI->GetUserMessageCount() == -1);
|
||||
}
|
||||
|
||||
void UserMessages::OnSourceModAllInitialized()
|
||||
{
|
||||
g_ShareSys.AddInterface(NULL, this);
|
||||
}
|
||||
|
||||
void UserMessages::OnSourceModAllShutdown()
|
||||
{
|
||||
if (m_HookCount)
|
||||
{
|
||||
SH_REMOVE_HOOK_MEMFUNC(IVEngineServer, UserMessageBegin, engine, this, &UserMessages::OnStartMessage_Pre, false);
|
||||
SH_REMOVE_HOOK_MEMFUNC(IVEngineServer, UserMessageBegin, engine, this, &UserMessages::OnStartMessage_Post, true);
|
||||
SH_REMOVE_HOOK_MEMFUNC(IVEngineServer, MessageEnd, engine, this, &UserMessages::OnMessageEnd_Pre, false);
|
||||
SH_REMOVE_HOOK_MEMFUNC(IVEngineServer, MessageEnd, engine, this, &UserMessages::OnMessageEnd_Post, true);
|
||||
}
|
||||
m_HookCount = 0;
|
||||
}
|
||||
|
||||
int UserMessages::GetMessageIndex(const char *msg)
|
||||
{
|
||||
int msgid;
|
||||
|
||||
if (!sm_trie_retrieve(m_Names, msg, reinterpret_cast<void **>(&msgid)))
|
||||
{
|
||||
if (m_FallbackSearch)
|
||||
{
|
||||
char msgbuf[64];
|
||||
int size;
|
||||
msgid = 0;
|
||||
|
||||
while (gamedll->GetUserMessageInfo(msgid, msgbuf, sizeof(msgbuf), size))
|
||||
{
|
||||
if (strcmp(msgbuf, msg) == 0)
|
||||
{
|
||||
sm_trie_insert(m_Names, msg, reinterpret_cast<void *>(msgid));
|
||||
return msgid;
|
||||
}
|
||||
msgid++;
|
||||
}
|
||||
}
|
||||
|
||||
msgid = g_SMAPI->FindUserMessage(msg);
|
||||
|
||||
if (msgid != INVALID_MESSAGE_ID)
|
||||
{
|
||||
sm_trie_insert(m_Names, msg, reinterpret_cast<void *>(msgid));
|
||||
}
|
||||
}
|
||||
|
||||
return msgid;
|
||||
}
|
||||
|
||||
bool UserMessages::GetMessageName(int msgid, char *buffer, size_t maxlength) const
|
||||
{
|
||||
if (m_FallbackSearch)
|
||||
{
|
||||
int size;
|
||||
return gamedll->GetUserMessageInfo(msgid, buffer, maxlength, size);
|
||||
}
|
||||
|
||||
const char *msg = g_SMAPI->GetUserMessage(msgid);
|
||||
|
||||
if (msg)
|
||||
{
|
||||
strncopy(buffer, msg, maxlength);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bf_write *UserMessages::StartMessage(int msg_id, const cell_t players[], unsigned int playersNum, int flags)
|
||||
{
|
||||
bf_write *buffer;
|
||||
|
||||
if (m_InExec || m_InHook)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
if (msg_id < 0 || msg_id >= 255)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
m_CellRecFilter.Initialize(players, playersNum);
|
||||
|
||||
m_CurFlags = flags;
|
||||
if (m_CurFlags & USERMSG_INITMSG)
|
||||
{
|
||||
m_CellRecFilter.SetToInit(true);
|
||||
}
|
||||
if (m_CurFlags & USERMSG_RELIABLE)
|
||||
{
|
||||
m_CellRecFilter.SetToReliable(true);
|
||||
}
|
||||
|
||||
m_InExec = true;
|
||||
|
||||
if (m_CurFlags & USERMSG_BLOCKHOOKS)
|
||||
{
|
||||
buffer = ENGINE_CALL(UserMessageBegin)(static_cast<IRecipientFilter *>(&m_CellRecFilter), msg_id);
|
||||
} else {
|
||||
buffer = engine->UserMessageBegin(static_cast<IRecipientFilter *>(&m_CellRecFilter), msg_id);
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
bool UserMessages::EndMessage()
|
||||
{
|
||||
if (!m_InExec)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_CurFlags & USERMSG_BLOCKHOOKS)
|
||||
{
|
||||
ENGINE_CALL(MessageEnd)();
|
||||
} else {
|
||||
engine->MessageEnd();
|
||||
}
|
||||
|
||||
m_InExec = false;
|
||||
m_CurFlags = 0;
|
||||
m_CellRecFilter.Reset();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UserMessages::HookUserMessage(int msg_id, IUserMessageListener *pListener, bool intercept)
|
||||
{
|
||||
if (msg_id < 0 || msg_id >= 255)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
ListenerInfo *pInfo;
|
||||
if (m_FreeListeners.empty())
|
||||
{
|
||||
pInfo = new ListenerInfo;
|
||||
} else {
|
||||
pInfo = m_FreeListeners.front();
|
||||
m_FreeListeners.pop();
|
||||
}
|
||||
|
||||
pInfo->Callback = pListener;
|
||||
pInfo->IsHooked = false;
|
||||
pInfo->KillMe = false;
|
||||
|
||||
if (!m_HookCount++)
|
||||
{
|
||||
SH_ADD_HOOK_MEMFUNC(IVEngineServer, UserMessageBegin, engine, this, &UserMessages::OnStartMessage_Pre, false);
|
||||
SH_ADD_HOOK_MEMFUNC(IVEngineServer, UserMessageBegin, engine, this, &UserMessages::OnStartMessage_Post, true);
|
||||
SH_ADD_HOOK_MEMFUNC(IVEngineServer, MessageEnd, engine, this, &UserMessages::OnMessageEnd_Pre, false);
|
||||
SH_ADD_HOOK_MEMFUNC(IVEngineServer, MessageEnd, engine, this, &UserMessages::OnMessageEnd_Post, true);
|
||||
}
|
||||
|
||||
if (intercept)
|
||||
{
|
||||
m_msgIntercepts[msg_id].push_back(pInfo);
|
||||
} else {
|
||||
m_msgHooks[msg_id].push_back(pInfo);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UserMessages::UnhookUserMessage(int msg_id, IUserMessageListener *pListener, bool intercept)
|
||||
{
|
||||
MsgList *pList;
|
||||
MsgIter iter;
|
||||
ListenerInfo *pInfo;
|
||||
bool deleted = false;
|
||||
|
||||
if (msg_id < 0 || msg_id >= 255)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
pList = (intercept) ? &m_msgIntercepts[msg_id] : &m_msgHooks[msg_id];
|
||||
for (iter=pList->begin(); iter!=pList->end(); iter++)
|
||||
{
|
||||
pInfo = (*iter);
|
||||
if (pInfo->Callback == pListener)
|
||||
{
|
||||
if (pInfo->IsHooked)
|
||||
{
|
||||
pInfo->KillMe = true;
|
||||
return true;
|
||||
}
|
||||
pList->erase(iter);
|
||||
deleted = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (deleted)
|
||||
{
|
||||
_DecRefCounter();
|
||||
}
|
||||
|
||||
return deleted;
|
||||
}
|
||||
|
||||
void UserMessages::_DecRefCounter()
|
||||
{
|
||||
if (--m_HookCount == 0)
|
||||
{
|
||||
SH_REMOVE_HOOK_MEMFUNC(IVEngineServer, UserMessageBegin, engine, this, &UserMessages::OnStartMessage_Pre, false);
|
||||
SH_REMOVE_HOOK_MEMFUNC(IVEngineServer, UserMessageBegin, engine, this, &UserMessages::OnStartMessage_Post, true);
|
||||
SH_REMOVE_HOOK_MEMFUNC(IVEngineServer, MessageEnd, engine, this, &UserMessages::OnMessageEnd_Pre, false);
|
||||
SH_REMOVE_HOOK_MEMFUNC(IVEngineServer, MessageEnd, engine, this, &UserMessages::OnMessageEnd_Post, true);
|
||||
}
|
||||
}
|
||||
|
||||
bf_write *UserMessages::OnStartMessage_Pre(IRecipientFilter *filter, int msg_type)
|
||||
{
|
||||
bool is_intercept_empty = m_msgIntercepts[msg_type].empty();
|
||||
bool is_hook_empty = m_msgHooks[msg_type].empty();
|
||||
|
||||
if ((is_intercept_empty && is_hook_empty)
|
||||
|| (m_InExec && (m_CurFlags & USERMSG_BLOCKHOOKS)))
|
||||
{
|
||||
m_InHook = false;
|
||||
RETURN_META_VALUE(MRES_IGNORED, NULL);
|
||||
}
|
||||
|
||||
m_CurId = msg_type;
|
||||
m_CurRecFilter = filter;
|
||||
m_InHook = true;
|
||||
m_BlockEndPost = false;
|
||||
|
||||
if (!is_intercept_empty)
|
||||
{
|
||||
m_InterceptBuffer.Reset();
|
||||
RETURN_META_VALUE(MRES_SUPERCEDE, &m_InterceptBuffer);
|
||||
}
|
||||
|
||||
RETURN_META_VALUE(MRES_IGNORED, NULL);
|
||||
}
|
||||
|
||||
bf_write *UserMessages::OnStartMessage_Post(IRecipientFilter *filter, int msg_type)
|
||||
{
|
||||
if (!m_InHook)
|
||||
{
|
||||
RETURN_META_VALUE(MRES_IGNORED, NULL);
|
||||
}
|
||||
|
||||
m_OrigBuffer = META_RESULT_ORIG_RET(bf_write *);
|
||||
|
||||
RETURN_META_VALUE(MRES_IGNORED, NULL);
|
||||
}
|
||||
|
||||
void UserMessages::OnMessageEnd_Post()
|
||||
{
|
||||
if (!m_InHook || m_BlockEndPost)
|
||||
{
|
||||
RETURN_META(MRES_IGNORED);
|
||||
}
|
||||
|
||||
MsgList *pList;
|
||||
MsgIter iter;
|
||||
ListenerInfo *pInfo;
|
||||
|
||||
pList = &m_msgIntercepts[m_CurId];
|
||||
for (iter=pList->begin(); iter!=pList->end(); )
|
||||
{
|
||||
pInfo = (*iter);
|
||||
pInfo->IsHooked = true;
|
||||
pInfo->Callback->OnUserMessageSent(m_CurId);
|
||||
|
||||
if (pInfo->KillMe)
|
||||
{
|
||||
iter = pList->erase(iter);
|
||||
m_FreeListeners.push(pInfo);
|
||||
_DecRefCounter();
|
||||
continue;
|
||||
}
|
||||
|
||||
pInfo->IsHooked = false;
|
||||
iter++;
|
||||
}
|
||||
|
||||
m_InHook = false;
|
||||
|
||||
pList = &m_msgHooks[m_CurId];
|
||||
for (iter=pList->begin(); iter!=pList->end(); )
|
||||
{
|
||||
pInfo = (*iter);
|
||||
pInfo->IsHooked = true;
|
||||
pInfo->Callback->OnUserMessageSent(m_CurId);
|
||||
|
||||
if (pInfo->KillMe)
|
||||
{
|
||||
iter = pList->erase(iter);
|
||||
m_FreeListeners.push(pInfo);
|
||||
_DecRefCounter();
|
||||
continue;
|
||||
}
|
||||
|
||||
pInfo->IsHooked = false;
|
||||
iter++;
|
||||
}
|
||||
}
|
||||
|
||||
void UserMessages::OnMessageEnd_Pre()
|
||||
{
|
||||
if (!m_InHook)
|
||||
{
|
||||
RETURN_META(MRES_IGNORED);
|
||||
}
|
||||
|
||||
MsgList *pList;
|
||||
MsgIter iter;
|
||||
ListenerInfo *pInfo;
|
||||
|
||||
ResultType res;
|
||||
bool intercepted = false;
|
||||
bool handled = false;
|
||||
|
||||
pList = &m_msgIntercepts[m_CurId];
|
||||
for (iter=pList->begin(); iter!=pList->end(); )
|
||||
{
|
||||
pInfo = (*iter);
|
||||
pInfo->IsHooked = true;
|
||||
res = pInfo->Callback->InterceptUserMessage(m_CurId, &m_InterceptBuffer, m_CurRecFilter);
|
||||
|
||||
intercepted = true;
|
||||
|
||||
switch (res)
|
||||
{
|
||||
case Pl_Stop:
|
||||
{
|
||||
if (pInfo->KillMe)
|
||||
{
|
||||
pList->erase(iter);
|
||||
m_FreeListeners.push(pInfo);
|
||||
_DecRefCounter();
|
||||
goto supercede;
|
||||
}
|
||||
pInfo->IsHooked = false;
|
||||
goto supercede;
|
||||
}
|
||||
case Pl_Handled:
|
||||
{
|
||||
handled = true;
|
||||
if (pInfo->KillMe)
|
||||
{
|
||||
iter = pList->erase(iter);
|
||||
m_FreeListeners.push(pInfo);
|
||||
_DecRefCounter();
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
if (pInfo->KillMe)
|
||||
{
|
||||
iter = pList->erase(iter);
|
||||
m_FreeListeners.push(pInfo);
|
||||
_DecRefCounter();
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
pInfo->IsHooked = false;
|
||||
iter++;
|
||||
}
|
||||
|
||||
if (handled)
|
||||
{
|
||||
goto supercede;
|
||||
}
|
||||
|
||||
if (intercepted)
|
||||
{
|
||||
bf_write *engine_bfw;
|
||||
|
||||
engine_bfw = ENGINE_CALL(UserMessageBegin)(m_CurRecFilter, m_CurId);
|
||||
m_ReadBuffer.StartReading(m_InterceptBuffer.GetBasePointer(), m_InterceptBuffer.GetNumBytesWritten());
|
||||
engine_bfw->WriteBitsFromBuffer(&m_ReadBuffer, m_InterceptBuffer.GetNumBitsWritten());
|
||||
ENGINE_CALL(MessageEnd)();
|
||||
}
|
||||
|
||||
pList = &m_msgHooks[m_CurId];
|
||||
for (iter=pList->begin(); iter!=pList->end(); )
|
||||
{
|
||||
pInfo = (*iter);
|
||||
pInfo->IsHooked = true;
|
||||
pInfo->Callback->OnUserMessage(m_CurId, m_OrigBuffer, m_CurRecFilter);
|
||||
|
||||
if (pInfo->KillMe)
|
||||
{
|
||||
iter = pList->erase(iter);
|
||||
m_FreeListeners.push(pInfo);
|
||||
_DecRefCounter();
|
||||
continue;
|
||||
}
|
||||
|
||||
pInfo->IsHooked = false;
|
||||
iter++;
|
||||
}
|
||||
|
||||
RETURN_META((intercepted) ? MRES_SUPERCEDE : MRES_IGNORED);
|
||||
supercede:
|
||||
m_InHook = false;
|
||||
m_BlockEndPost = true;
|
||||
RETURN_META(MRES_SUPERCEDE);
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
/**
|
||||
* vim: set ts=4 :
|
||||
* =============================================================================
|
||||
* SourceMod
|
||||
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
|
||||
* =============================================================================
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, version 3.0, as published by the
|
||||
* Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* As a special exception, AlliedModders LLC gives you permission to link the
|
||||
* code of this program (as well as its derivative works) to "Half-Life 2," the
|
||||
* "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
|
||||
* by the Valve Corporation. You must obey the GNU General Public License in
|
||||
* all respects for all other code used. Additionally, AlliedModders LLC grants
|
||||
* this exception to all derivative works. AlliedModders LLC defines further
|
||||
* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
|
||||
* or <http://www.sourcemod.net/license.php>.
|
||||
*
|
||||
* Version: $Id$
|
||||
*/
|
||||
|
||||
#ifndef _INCLUDE_SOURCEMOD_CUSERMESSAGES_H_
|
||||
#define _INCLUDE_SOURCEMOD_CUSERMESSAGES_H_
|
||||
|
||||
#include "ShareSys.h"
|
||||
#include <IUserMessages.h>
|
||||
#include "sourcemm_api.h"
|
||||
#include "sm_trie.h"
|
||||
#include "CellRecipientFilter.h"
|
||||
|
||||
using namespace SourceHook;
|
||||
using namespace SourceMod;
|
||||
|
||||
#define INVALID_MESSAGE_ID -1
|
||||
|
||||
struct ListenerInfo
|
||||
{
|
||||
IUserMessageListener *Callback;
|
||||
bool IsHooked;
|
||||
bool KillMe;
|
||||
};
|
||||
|
||||
typedef List<ListenerInfo *> MsgList;
|
||||
typedef List<ListenerInfo *>::iterator MsgIter;
|
||||
|
||||
class UserMessages :
|
||||
public IUserMessages,
|
||||
public SMGlobalClass
|
||||
{
|
||||
public:
|
||||
UserMessages();
|
||||
~UserMessages();
|
||||
public: //SMGlobalClass
|
||||
void OnSourceModStartup(bool late);
|
||||
void OnSourceModAllInitialized();
|
||||
void OnSourceModAllShutdown();
|
||||
public: //IUserMessages
|
||||
int GetMessageIndex(const char *msg);
|
||||
bool GetMessageName(int msgid, char *buffer, size_t maxlength) const;
|
||||
bool HookUserMessage(int msg_id, IUserMessageListener *pListener, bool intercept=false);
|
||||
bool UnhookUserMessage(int msg_id, IUserMessageListener *pListener, bool intercept=false);
|
||||
bf_write *StartMessage(int msg_id, const cell_t players[], unsigned int playersNum, int flags);
|
||||
bool EndMessage();
|
||||
public:
|
||||
bf_write *OnStartMessage_Pre(IRecipientFilter *filter, int msg_type);
|
||||
bf_write *OnStartMessage_Post(IRecipientFilter *filter, int msg_type);
|
||||
void OnMessageEnd_Pre();
|
||||
void OnMessageEnd_Post();
|
||||
private:
|
||||
void _DecRefCounter();
|
||||
private:
|
||||
List<ListenerInfo *> m_msgHooks[255];
|
||||
List<ListenerInfo *> m_msgIntercepts[255];
|
||||
CStack<ListenerInfo *> m_FreeListeners;
|
||||
unsigned char m_pBase[2500];
|
||||
IRecipientFilter *m_CurRecFilter;
|
||||
bf_write m_InterceptBuffer;
|
||||
bf_write *m_OrigBuffer;
|
||||
bf_read m_ReadBuffer;
|
||||
size_t m_HookCount;
|
||||
bool m_InHook;
|
||||
bool m_BlockEndPost;
|
||||
bool m_FallbackSearch;
|
||||
|
||||
Trie *m_Names;
|
||||
CellRecipientFilter m_CellRecFilter;
|
||||
bool m_InExec;
|
||||
int m_CurFlags;
|
||||
int m_CurId;
|
||||
};
|
||||
|
||||
extern UserMessages g_UserMsgs;
|
||||
|
||||
#endif //_INCLUDE_SOURCEMOD_CUSERMESSAGES_H_
|
||||
@@ -0,0 +1,175 @@
|
||||
/**
|
||||
* vim: set ts=4 :
|
||||
* =============================================================================
|
||||
* SourceMod
|
||||
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
|
||||
* =============================================================================
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, version 3.0, as published by the
|
||||
* Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* As a special exception, AlliedModders LLC gives you permission to link the
|
||||
* code of this program (as well as its derivative works) to "Half-Life 2," the
|
||||
* "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
|
||||
* by the Valve Corporation. You must obey the GNU General Public License in
|
||||
* all respects for all other code used. Additionally, AlliedModders LLC grants
|
||||
* this exception to all derivative works. AlliedModders LLC defines further
|
||||
* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
|
||||
* or <http://www.sourcemod.net/license.php>.
|
||||
*
|
||||
* Version: $Id$
|
||||
*/
|
||||
|
||||
#include "sm_globals.h"
|
||||
#include <sh_list.h>
|
||||
#include <convar.h>
|
||||
#include "concmd_cleaner.h"
|
||||
#include "sm_stringutil.h"
|
||||
#include "sourcemm_api.h"
|
||||
|
||||
#if defined ORANGEBOX_BUILD
|
||||
SH_DECL_HOOK1_void(ICvar, UnregisterConCommand, SH_NOATTRIB, 0, ConCommandBase *);
|
||||
#endif
|
||||
|
||||
using namespace SourceHook;
|
||||
|
||||
struct ConCommandInfo
|
||||
{
|
||||
ConCommandBase *pBase;
|
||||
IConCommandTracker *cls;
|
||||
char name[64];
|
||||
};
|
||||
|
||||
List<ConCommandInfo *> tracked_bases;
|
||||
|
||||
ConCommandBase *FindConCommandBase(const char *name);
|
||||
|
||||
class ConCommandCleaner : public SMGlobalClass
|
||||
{
|
||||
public:
|
||||
#if defined ORANGEBOX_BUILD
|
||||
void OnSourceModAllInitialized()
|
||||
{
|
||||
SH_ADD_HOOK_MEMFUNC(ICvar, UnregisterConCommand, icvar, this, &ConCommandCleaner::UnlinkConCommandBase, false);
|
||||
}
|
||||
|
||||
void OnSourceModShutdown()
|
||||
{
|
||||
SH_REMOVE_HOOK_MEMFUNC(ICvar, UnregisterConCommand, icvar, this, &ConCommandCleaner::UnlinkConCommandBase, false);
|
||||
}
|
||||
#endif
|
||||
|
||||
void UnlinkConCommandBase(ConCommandBase *pBase)
|
||||
{
|
||||
ConCommandInfo *pInfo;
|
||||
List<ConCommandInfo *>::iterator iter = tracked_bases.begin();
|
||||
|
||||
if (pBase)
|
||||
{
|
||||
while (iter != tracked_bases.end())
|
||||
{
|
||||
if ((*iter)->pBase == pBase)
|
||||
{
|
||||
pInfo = (*iter);
|
||||
iter = tracked_bases.erase(iter);
|
||||
pInfo->cls->OnUnlinkConCommandBase(pBase, pBase->GetName(), true);
|
||||
delete pInfo;
|
||||
}
|
||||
else
|
||||
{
|
||||
iter++;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
while (iter != tracked_bases.end())
|
||||
{
|
||||
/* This is just god-awful! */
|
||||
if (FindConCommandBase((*iter)->name) != (*iter)->pBase)
|
||||
{
|
||||
pInfo = (*iter);
|
||||
iter = tracked_bases.erase(iter);
|
||||
pInfo->cls->OnUnlinkConCommandBase(pBase, pInfo->name, false);
|
||||
delete pInfo;
|
||||
}
|
||||
else
|
||||
{
|
||||
iter++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AddTarget(ConCommandBase *pBase, IConCommandTracker *cls)
|
||||
{
|
||||
ConCommandInfo *info = new ConCommandInfo;
|
||||
|
||||
info->pBase = pBase;
|
||||
info->cls = cls;
|
||||
strncopy(info->name, pBase->GetName(), sizeof(info->name));
|
||||
|
||||
tracked_bases.push_back(info);
|
||||
}
|
||||
|
||||
void RemoveTarget(ConCommandBase *pBase, IConCommandTracker *cls)
|
||||
{
|
||||
List<ConCommandInfo *>::iterator iter;
|
||||
ConCommandInfo *pInfo;
|
||||
|
||||
iter = tracked_bases.begin();
|
||||
while (iter != tracked_bases.end())
|
||||
{
|
||||
pInfo = (*iter);
|
||||
if (pInfo->pBase == pBase && pInfo->cls == cls)
|
||||
{
|
||||
delete pInfo;
|
||||
iter = tracked_bases.erase(iter);
|
||||
}
|
||||
else
|
||||
{
|
||||
iter++;
|
||||
}
|
||||
}
|
||||
}
|
||||
} s_ConCmdTracker;
|
||||
|
||||
ConCommandBase *FindConCommandBase(const char *name)
|
||||
{
|
||||
const ConCommandBase *pBase = icvar->GetCommands();
|
||||
|
||||
while (pBase != NULL)
|
||||
{
|
||||
if (strcmp(pBase->GetName(), name) == 0)
|
||||
{
|
||||
return const_cast<ConCommandBase *>(pBase);
|
||||
}
|
||||
pBase = pBase->GetNext();
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void TrackConCommandBase(ConCommandBase *pBase, IConCommandTracker *me)
|
||||
{
|
||||
s_ConCmdTracker.AddTarget(pBase, me);
|
||||
}
|
||||
|
||||
void UntrackConCommandBase(ConCommandBase *pBase, IConCommandTracker *me)
|
||||
{
|
||||
s_ConCmdTracker.RemoveTarget(pBase, me);
|
||||
}
|
||||
|
||||
void Global_OnUnlinkConCommandBase(ConCommandBase *pBase)
|
||||
{
|
||||
s_ConCmdTracker.UnlinkConCommandBase(pBase);
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/**
|
||||
* vim: set ts=4 :
|
||||
* =============================================================================
|
||||
* SourceMod
|
||||
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
|
||||
* =============================================================================
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, version 3.0, as published by the
|
||||
* Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* As a special exception, AlliedModders LLC gives you permission to link the
|
||||
* code of this program (as well as its derivative works) to "Half-Life 2," the
|
||||
* "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
|
||||
* by the Valve Corporation. You must obey the GNU General Public License in
|
||||
* all respects for all other code used. Additionally, AlliedModders LLC grants
|
||||
* this exception to all derivative works. AlliedModders LLC defines further
|
||||
* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
|
||||
* or <http://www.sourcemod.net/license.php>.
|
||||
*
|
||||
* Version: $Id$
|
||||
*/
|
||||
|
||||
#ifndef _INCLUDE_CONCMD_TRACKER_H_
|
||||
#define _INCLUDE_CONCMD_TRACKER_H_
|
||||
|
||||
class IConCommandTracker
|
||||
{
|
||||
public:
|
||||
virtual void OnUnlinkConCommandBase(ConCommandBase *pBase, const char *name, bool is_read_safe) = 0;
|
||||
};
|
||||
|
||||
ConCommandBase *FindConCommandBase(const char *name);
|
||||
void TrackConCommandBase(ConCommandBase *pBase, IConCommandTracker *me);
|
||||
void UntrackConCommandBase(ConCommandBase *pBase, IConCommandTracker *me);
|
||||
void Global_OnUnlinkConCommandBase(ConCommandBase *pBase);
|
||||
|
||||
#endif //_INCLUDE_CONCMD_TRACKER_H_
|
||||
@@ -0,0 +1,551 @@
|
||||
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $Workfile: $
|
||||
// $Date: 2006-08-13 06:34:30 -0500 (Sun, 13 Aug 2006) $
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#ifndef CONVAR_H
|
||||
#define CONVAR_H
|
||||
#if _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include "tier0/dbg.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#define FORCEINLINE_CVAR FORCEINLINE
|
||||
#elif _LINUX
|
||||
#define FORCEINLINE_CVAR __inline__ FORCEINLINE
|
||||
#else
|
||||
#error "implement me"
|
||||
#endif
|
||||
|
||||
// The default, no flags at all
|
||||
#define FCVAR_NONE 0
|
||||
|
||||
// Command to ConVars and ConCommands
|
||||
// ConVar Systems
|
||||
#define FCVAR_UNREGISTERED (1<<0) // If this is set, don't add to linked list, etc.
|
||||
#define FCVAR_LAUNCHER (1<<1) // defined by launcher
|
||||
#define FCVAR_GAMEDLL (1<<2) // defined by the game DLL
|
||||
#define FCVAR_CLIENTDLL (1<<3) // defined by the client DLL
|
||||
#define FCVAR_MATERIAL_SYSTEM (1<<4) // Defined by the material system.
|
||||
#define FCVAR_DATACACHE (1<<19) // Defined by the datacache system.
|
||||
#define FCVAR_STUDIORENDER (1<<15) // Defined by the studiorender system.
|
||||
#define FCVAR_FILESYSTEM (1<<21) // Defined by the file system.
|
||||
#define FCVAR_PLUGIN (1<<18) // Defined by a 3rd party plugin.
|
||||
#define FCVAR_TOOLSYSTEM (1<<20) // Defined by an IToolSystem library
|
||||
#define FCVAR_SOUNDSYSTEM (1<<23) // Defined by the soundsystem library
|
||||
#define FCVAR_INPUTSYSTEM (1<<25) // Defined by the inputsystem dll
|
||||
#define FCVAR_NETWORKSYSTEM (1<<26) // Defined by the network system
|
||||
// NOTE!! if you add a cvar system, add it here too!!!!
|
||||
// the engine lacks a cvar flag, but needs it for xbox
|
||||
// an engine cvar is thus a cvar not marked with any other system
|
||||
#define FCVAR_NON_ENGINE ((FCVAR_LAUNCHER|FCVAR_GAMEDLL|FCVAR_CLIENTDLL|FCVAR_MATERIAL_SYSTEM|FCVAR_DATACACHE|FCVAR_STUDIORENDER|FCVAR_FILESYSTEM|FCVAR_PLUGIN|FCVAR_TOOLSYSTEM|FCVAR_SOUNDSYSTEM|FCVAR_INPUTSYSTEM|FCVAR_NETWORKSYSTEM))
|
||||
|
||||
// ConVar only
|
||||
#define FCVAR_PROTECTED (1<<5) // It's a server cvar, but we don't send the data since it's a password, etc. Sends 1 if it's not bland/zero, 0 otherwise as value
|
||||
#define FCVAR_SPONLY (1<<6) // This cvar cannot be changed by clients connected to a multiplayer server.
|
||||
#define FCVAR_ARCHIVE (1<<7) // set to cause it to be saved to vars.rc
|
||||
#define FCVAR_NOTIFY (1<<8) // notifies players when changed
|
||||
#define FCVAR_USERINFO (1<<9) // changes the client's info string
|
||||
#define FCVAR_CHEAT (1<<14) // Only useable in singleplayer / debug / multiplayer & sv_cheats
|
||||
|
||||
#define FCVAR_PRINTABLEONLY (1<<10) // This cvar's string cannot contain unprintable characters ( e.g., used for player name etc ).
|
||||
#define FCVAR_UNLOGGED (1<<11) // If this is a FCVAR_SERVER, don't log changes to the log file / console if we are creating a log
|
||||
#define FCVAR_NEVER_AS_STRING (1<<12) // never try to print that cvar
|
||||
|
||||
// It's a ConVar that's shared between the client and the server.
|
||||
// At signon, the values of all such ConVars are sent from the server to the client (skipped for local
|
||||
// client, of course )
|
||||
// If a change is requested it must come from the console (i.e., no remote client changes)
|
||||
// If a value is changed while a server is active, it's replicated to all connected clients
|
||||
#define FCVAR_REPLICATED (1<<13) // server setting enforced on clients, TODO rename to FCAR_SERVER at some time
|
||||
#define FCVAR_DEMO (1<<16) // record this cvar when starting a demo file
|
||||
#define FCVAR_DONTRECORD (1<<17) // don't record these command in demofiles
|
||||
|
||||
#define FCVAR_NOT_CONNECTED (1<<22) // cvar cannot be changed by a client that is connected to a server
|
||||
|
||||
#define FCVAR_ARCHIVE_XBOX (1<<24) // cvar written to config.cfg on the Xbox
|
||||
|
||||
|
||||
// #define FCVAR_AVAILABLE (1<<27)
|
||||
// #define FCVAR_AVAILABLE (1<<28)
|
||||
// #define FCVAR_AVAILABLE (1<<29)
|
||||
// #define FCVAR_AVAILABLE (1<<30)
|
||||
// #define FCVAR_AVAILABLE (1<<31)
|
||||
|
||||
|
||||
class ConVar;
|
||||
class ConCommand;
|
||||
class ConCommandBase;
|
||||
|
||||
// Any executable that wants to use ConVars need to implement one of
|
||||
// these to hook up access to console variables.
|
||||
class IConCommandBaseAccessor
|
||||
{
|
||||
public:
|
||||
// Flags is a combination of FCVAR flags in cvar.h.
|
||||
// hOut is filled in with a handle to the variable.
|
||||
virtual bool RegisterConCommandBase( ConCommandBase *pVar )=0;
|
||||
};
|
||||
|
||||
|
||||
// You don't have to instantiate one of these, just call its
|
||||
// OneTimeInit function when your executable is initializing.
|
||||
class ConCommandBaseMgr
|
||||
{
|
||||
public:
|
||||
// Call this ONCE when the executable starts up.
|
||||
static void OneTimeInit( IConCommandBaseAccessor *pAccessor );
|
||||
#ifdef _XBOX
|
||||
static bool Fixup( ConCommandBase* pConCommandBase );
|
||||
#ifndef _RETAIL
|
||||
static void PublishCommands( bool bForce );
|
||||
#endif
|
||||
#endif
|
||||
};
|
||||
|
||||
// Called when a ConVar changes value
|
||||
typedef void ( *FnChangeCallback )( ConVar *var, char const *pOldString );
|
||||
|
||||
// Called when a ConCommand needs to execute
|
||||
typedef void ( *FnCommandCallback )( void );
|
||||
|
||||
#define COMMAND_COMPLETION_MAXITEMS 64
|
||||
#define COMMAND_COMPLETION_ITEM_LENGTH 64
|
||||
|
||||
// Returns 0 to COMMAND_COMPLETION_MAXITEMS worth of completion strings
|
||||
typedef int ( *FnCommandCompletionCallback )( char const *partial, char commands[ COMMAND_COMPLETION_MAXITEMS ][ COMMAND_COMPLETION_ITEM_LENGTH ] );
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: The base console invoked command/cvar interface
|
||||
//-----------------------------------------------------------------------------
|
||||
class ConCommandBase
|
||||
{
|
||||
friend class ConCommandBaseMgr;
|
||||
friend class CCvar;
|
||||
friend class ConVar;
|
||||
friend class ConCommand;
|
||||
|
||||
public:
|
||||
ConCommandBase( void );
|
||||
ConCommandBase( char const *pName, char const *pHelpString = 0,
|
||||
int flags = 0 );
|
||||
|
||||
virtual ~ConCommandBase( void );
|
||||
|
||||
virtual bool IsCommand( void ) const;
|
||||
|
||||
// Check flag
|
||||
virtual bool IsBitSet( int flag ) const;
|
||||
// Set flag
|
||||
virtual void AddFlags( int flags );
|
||||
|
||||
// Return name of cvar
|
||||
virtual char const *GetName( void ) const;
|
||||
|
||||
// Return help text for cvar
|
||||
virtual char const *GetHelpText( void ) const;
|
||||
|
||||
// Deal with next pointer
|
||||
const ConCommandBase *GetNext( void ) const;
|
||||
void SetNext( ConCommandBase *next );
|
||||
|
||||
virtual bool IsRegistered( void ) const;
|
||||
|
||||
// Global methods
|
||||
static ConCommandBase const *GetCommands( void );
|
||||
static void AddToList( ConCommandBase *var );
|
||||
static void RemoveFlaggedCommands( int flag );
|
||||
static void RevertFlaggedCvars( int flag );
|
||||
static ConCommandBase const *FindCommand( char const *name );
|
||||
|
||||
protected:
|
||||
virtual void Create( char const *pName, char const *pHelpString = 0,
|
||||
int flags = 0 );
|
||||
|
||||
// Used internally by OneTimeInit to initialize.
|
||||
virtual void Init();
|
||||
|
||||
// Internal copy routine ( uses new operator from correct module )
|
||||
char *CopyString( char const *from );
|
||||
|
||||
// Next ConVar in chain
|
||||
ConCommandBase *m_pNext;
|
||||
|
||||
private:
|
||||
// Has the cvar been added to the global list?
|
||||
bool m_bRegistered;
|
||||
|
||||
// Static data
|
||||
char const *m_pszName;
|
||||
char const *m_pszHelpString;
|
||||
|
||||
// ConVar flags
|
||||
int m_nFlags;
|
||||
|
||||
protected:
|
||||
|
||||
// ConVars add themselves to this list for the executable. Then ConVarMgr::Init() runs through
|
||||
// all the console variables and registers them.
|
||||
static ConCommandBase *s_pConCommandBases;
|
||||
|
||||
// ConVars in this executable use this 'global' to access values.
|
||||
static IConCommandBaseAccessor *s_pAccessor;
|
||||
|
||||
public: // Hackalicous
|
||||
inline int GetFlags() const
|
||||
{
|
||||
return m_nFlags;
|
||||
}
|
||||
inline void SetFlags(int flags)
|
||||
{
|
||||
m_nFlags = flags;
|
||||
}
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: The console invoked command
|
||||
//-----------------------------------------------------------------------------
|
||||
class ConCommand : public ConCommandBase
|
||||
{
|
||||
friend class ConCommandBaseMgr;
|
||||
friend class CCvar;
|
||||
#ifdef _STATIC_LINKED
|
||||
friend class G_ConCommand;
|
||||
friend class C_ConCommand;
|
||||
friend class M_ConCommand;
|
||||
friend class S_ConCommand;
|
||||
friend class D_ConCommand;
|
||||
#endif
|
||||
|
||||
public:
|
||||
typedef ConCommandBase BaseClass;
|
||||
|
||||
ConCommand( void );
|
||||
ConCommand( char const *pName, FnCommandCallback callback,
|
||||
char const *pHelpString = 0, int flags = 0, FnCommandCompletionCallback completionFunc = 0 );
|
||||
|
||||
virtual ~ConCommand( void );
|
||||
|
||||
virtual bool IsCommand( void ) const;
|
||||
|
||||
virtual int AutoCompleteSuggest( char const *partial, char commands[ COMMAND_COMPLETION_MAXITEMS ][ COMMAND_COMPLETION_ITEM_LENGTH ] );
|
||||
|
||||
virtual bool CanAutoComplete( void );
|
||||
|
||||
// Invoke the function
|
||||
virtual void Dispatch( void );
|
||||
private:
|
||||
virtual void Create( char const *pName, FnCommandCallback callback,
|
||||
char const *pHelpString = 0, int flags = 0, FnCommandCompletionCallback completionFunc = 0 );
|
||||
|
||||
// Call this function when executing the command
|
||||
FnCommandCallback m_fnCommandCallback;
|
||||
|
||||
FnCommandCompletionCallback m_fnCompletionCallback;
|
||||
bool m_bHasCompletionCallback;
|
||||
public: // Hackalicous
|
||||
inline FnCommandCallback GetCallback() const
|
||||
{
|
||||
return m_fnCommandCallback;
|
||||
}
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: A console variable
|
||||
//-----------------------------------------------------------------------------
|
||||
class ConVar : public ConCommandBase
|
||||
{
|
||||
friend class ConCommandBaseMgr;
|
||||
friend class CCvar;
|
||||
friend class CDefaultCvar;
|
||||
#ifdef _STATIC_LINKED
|
||||
friend class G_ConVar;
|
||||
friend class C_ConVar;
|
||||
friend class M_ConVar;
|
||||
friend class S_ConVar;
|
||||
friend class D_ConVar;
|
||||
#endif
|
||||
|
||||
public:
|
||||
typedef ConCommandBase BaseClass;
|
||||
|
||||
ConVar( char const *pName, char const *pDefaultValue, int flags = 0);
|
||||
|
||||
ConVar( char const *pName, char const *pDefaultValue, int flags,
|
||||
char const *pHelpString );
|
||||
ConVar( char const *pName, char const *pDefaultValue, int flags,
|
||||
char const *pHelpString, bool bMin, float fMin, bool bMax, float fMax );
|
||||
ConVar( char const *pName, char const *pDefaultValue, int flags,
|
||||
char const *pHelpString, FnChangeCallback callback );
|
||||
ConVar( char const *pName, char const *pDefaultValue, int flags,
|
||||
char const *pHelpString, bool bMin, float fMin, bool bMax, float fMax,
|
||||
FnChangeCallback callback );
|
||||
|
||||
virtual ~ConVar( void );
|
||||
|
||||
virtual bool IsBitSet( int flag ) const;
|
||||
virtual char const* GetHelpText( void ) const;
|
||||
virtual bool IsRegistered( void ) const;
|
||||
virtual char const *GetName( void ) const;
|
||||
virtual void AddFlags( int flags );
|
||||
virtual bool IsCommand( void ) const;
|
||||
|
||||
// Install a change callback (there shouldn't already be one....)
|
||||
void InstallChangeCallback( FnChangeCallback callback );
|
||||
|
||||
// Retrieve value
|
||||
FORCEINLINE_CVAR float GetFloat( void ) const;
|
||||
FORCEINLINE_CVAR int GetInt( void ) const;
|
||||
FORCEINLINE_CVAR bool GetBool() const { return !!GetInt(); }
|
||||
FORCEINLINE_CVAR char const *GetString( void ) const;
|
||||
|
||||
// Any function that allocates/frees memory needs to be virtual or else you'll have crashes
|
||||
// from alloc/free across dll/exe boundaries.
|
||||
|
||||
// These just call into the IConCommandBaseAccessor to check flags and set the var (which ends up calling InternalSetValue).
|
||||
virtual void SetValue( char const *value );
|
||||
virtual void SetValue( float value );
|
||||
virtual void SetValue( int value );
|
||||
|
||||
// Reset to default value
|
||||
void Revert( void );
|
||||
|
||||
// True if it has a min/max setting
|
||||
bool GetMin( float& minVal ) const;
|
||||
bool GetMax( float& maxVal ) const;
|
||||
char const *GetDefault( void ) const;
|
||||
|
||||
static void RevertAll( void );
|
||||
private:
|
||||
// Called by CCvar when the value of a var is changing.
|
||||
virtual void InternalSetValue(char const *value);
|
||||
// For CVARs marked FCVAR_NEVER_AS_STRING
|
||||
virtual void InternalSetFloatValue( float fNewValue );
|
||||
virtual void InternalSetIntValue( int nValue );
|
||||
|
||||
virtual bool ClampValue( float& value );
|
||||
virtual void ChangeStringValue( char const *tempVal );
|
||||
|
||||
virtual void Create( char const *pName, char const *pDefaultValue, int flags = 0,
|
||||
char const *pHelpString = 0, bool bMin = false, float fMin = 0.0,
|
||||
bool bMax = false, float fMax = false, FnChangeCallback callback = 0 );
|
||||
|
||||
// Used internally by OneTimeInit to initialize.
|
||||
virtual void Init();
|
||||
|
||||
private:
|
||||
|
||||
// This either points to "this" or it points to the original declaration of a ConVar.
|
||||
// This allows ConVars to exist in separate modules, and they all use the first one to be declared.
|
||||
// m_pParent->m_pParent must equal m_pParent (ie: m_pParent must be the root, or original, ConVar).
|
||||
ConVar *m_pParent;
|
||||
|
||||
// Static data
|
||||
char const *m_pszDefaultValue;
|
||||
|
||||
// Value
|
||||
// Dynamically allocated
|
||||
char *m_pszString;
|
||||
int m_StringLength;
|
||||
|
||||
// Values
|
||||
float m_fValue;
|
||||
int m_nValue;
|
||||
|
||||
// Min/Max values
|
||||
bool m_bHasMin;
|
||||
float m_fMinVal;
|
||||
bool m_bHasMax;
|
||||
float m_fMaxVal;
|
||||
|
||||
// Call this function when ConVar changes
|
||||
FnChangeCallback m_fnChangeCallback;
|
||||
public: // Hackalicous
|
||||
inline FnChangeCallback GetCallback() const
|
||||
{
|
||||
return m_fnChangeCallback;
|
||||
}
|
||||
inline void SetMin(bool set, float min=0.0)
|
||||
{
|
||||
m_bHasMin = set;
|
||||
m_fMinVal = min;
|
||||
}
|
||||
inline void SetMax(bool set, float max=0.0)
|
||||
{
|
||||
m_bHasMax = set;
|
||||
m_fMaxVal = max;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Return ConVar value as a float
|
||||
// Output : float
|
||||
//-----------------------------------------------------------------------------
|
||||
FORCEINLINE_CVAR float ConVar::GetFloat( void ) const
|
||||
{
|
||||
return m_pParent->m_fValue;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Return ConVar value as an int
|
||||
// Output : int
|
||||
//-----------------------------------------------------------------------------
|
||||
FORCEINLINE_CVAR int ConVar::GetInt( void ) const
|
||||
{
|
||||
return m_pParent->m_nValue;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Return ConVar value as a string, return "" for bogus string pointer, etc.
|
||||
// Output : char const *
|
||||
//-----------------------------------------------------------------------------
|
||||
FORCEINLINE_CVAR char const *ConVar::GetString( void ) const
|
||||
{
|
||||
if ( m_nFlags & FCVAR_NEVER_AS_STRING )
|
||||
{
|
||||
return "FCVAR_NEVER_AS_STRING";
|
||||
}
|
||||
|
||||
return ( m_pParent->m_pszString ) ? m_pParent->m_pszString : "";
|
||||
}
|
||||
|
||||
|
||||
#ifdef _STATIC_LINKED
|
||||
// identifies subsystem via piggybacking constructors with flags
|
||||
class G_ConCommand : public ConCommand
|
||||
{
|
||||
public:
|
||||
G_ConCommand(char const *pName, FnCommandCallback callback, char const *pHelpString = 0, int flags = 0, FnCommandCompletionCallback completionFunc = 0 ) : ConCommand(pName, callback, pHelpString, flags|FCVAR_GAMEDLL, completionFunc) {}
|
||||
};
|
||||
|
||||
class C_ConCommand : public ConCommand
|
||||
{
|
||||
public:
|
||||
C_ConCommand(char const *pName, FnCommandCallback callback, char const *pHelpString = 0, int flags = 0, FnCommandCompletionCallback completionFunc = 0 ) : ConCommand(pName, callback, pHelpString, flags|FCVAR_CLIENTDLL, completionFunc) {}
|
||||
};
|
||||
|
||||
class M_ConCommand : public ConCommand
|
||||
{
|
||||
public:
|
||||
M_ConCommand(char const *pName, FnCommandCallback callback, char const *pHelpString = 0, int flags = 0, FnCommandCompletionCallback completionFunc = 0 ) : ConCommand(pName, callback, pHelpString, flags|FCVAR_MATERIAL_SYSTEM, completionFunc) {}
|
||||
};
|
||||
|
||||
class S_ConCommand : public ConCommand
|
||||
{
|
||||
public:
|
||||
S_ConCommand(char const *pName, FnCommandCallback callback, char const *pHelpString = 0, int flags = 0, FnCommandCompletionCallback completionFunc = 0 ) : ConCommand(pName, callback, pHelpString, flags|FCVAR_STUDIORENDER, completionFunc) {}
|
||||
};
|
||||
|
||||
class D_ConCommand : public ConCommand
|
||||
{
|
||||
public:
|
||||
D_ConCommand(char const *pName, FnCommandCallback callback, char const *pHelpString = 0, int flags = 0, FnCommandCompletionCallback completionFunc = 0 ) : ConCommand(pName, callback, pHelpString, flags|FCVAR_DATACACHE, completionFunc) {}
|
||||
};
|
||||
|
||||
typedef void ( *G_FnChangeCallback )( G_ConVar *var, char const *pOldString );
|
||||
typedef void ( *C_FnChangeCallback )( C_ConVar *var, char const *pOldString );
|
||||
typedef void ( *M_FnChangeCallback )( M_ConVar *var, char const *pOldString );
|
||||
typedef void ( *S_FnChangeCallback )( S_ConVar *var, char const *pOldString );
|
||||
typedef void ( *D_FnChangeCallback )( D_ConVar *var, char const *pOldString );
|
||||
|
||||
class G_ConVar : public ConVar
|
||||
{
|
||||
public:
|
||||
G_ConVar( char const *pName, char const *pDefaultValue, int flags = 0) : ConVar(pName, pDefaultValue, flags|FCVAR_GAMEDLL) {}
|
||||
G_ConVar( char const *pName, char const *pDefaultValue, int flags, char const *pHelpString ) : ConVar(pName, pDefaultValue, flags|FCVAR_GAMEDLL, pHelpString ) {}
|
||||
G_ConVar( char const *pName, char const *pDefaultValue, int flags, char const *pHelpString, bool bMin, float fMin, bool bMax, float fMax ) : ConVar(pName, pDefaultValue, flags|FCVAR_GAMEDLL, pHelpString, bMin, fMin, bMax, fMax) {}
|
||||
G_ConVar( char const *pName, char const *pDefaultValue, int flags, char const *pHelpString, G_FnChangeCallback callback ) : ConVar(pName, pDefaultValue, flags|FCVAR_GAMEDLL, pHelpString, (FnChangeCallback)callback ) {}
|
||||
G_ConVar( char const *pName, char const *pDefaultValue, int flags, char const *pHelpString, bool bMin, float fMin, bool bMax, float fMax, G_FnChangeCallback callback ) : ConVar(pName, pDefaultValue, flags|FCVAR_GAMEDLL, pHelpString, bMin, fMin, bMax, fMax, (FnChangeCallback)callback ) {}
|
||||
};
|
||||
|
||||
class C_ConVar : public ConVar
|
||||
{
|
||||
public:
|
||||
C_ConVar( char const *pName, char const *pDefaultValue, int flags = 0) : ConVar(pName, pDefaultValue, flags|FCVAR_CLIENTDLL) {}
|
||||
C_ConVar( char const *pName, char const *pDefaultValue, int flags, char const *pHelpString ) : ConVar(pName, pDefaultValue, flags|FCVAR_CLIENTDLL, pHelpString ) {}
|
||||
C_ConVar( char const *pName, char const *pDefaultValue, int flags, char const *pHelpString, bool bMin, float fMin, bool bMax, float fMax ) : ConVar(pName, pDefaultValue, flags|FCVAR_CLIENTDLL, pHelpString, bMin, fMin, bMax, fMax) {}
|
||||
C_ConVar( char const *pName, char const *pDefaultValue, int flags, char const *pHelpString, C_FnChangeCallback callback ) : ConVar(pName, pDefaultValue, flags|FCVAR_CLIENTDLL, pHelpString, (FnChangeCallback)callback ) {}
|
||||
C_ConVar( char const *pName, char const *pDefaultValue, int flags, char const *pHelpString, bool bMin, float fMin, bool bMax, float fMax, C_FnChangeCallback callback ) : ConVar(pName, pDefaultValue, flags|FCVAR_CLIENTDLL, pHelpString, bMin, fMin, bMax, fMax, (FnChangeCallback)callback ) {}
|
||||
};
|
||||
|
||||
class M_ConVar : public ConVar
|
||||
{
|
||||
public:
|
||||
M_ConVar( char const *pName, char const *pDefaultValue, int flags = 0) : ConVar(pName, pDefaultValue, flags|FCVAR_MATERIAL_SYSTEM) {}
|
||||
M_ConVar( char const *pName, char const *pDefaultValue, int flags, char const *pHelpString ) : ConVar(pName, pDefaultValue, flags|FCVAR_MATERIAL_SYSTEM, pHelpString ) {}
|
||||
M_ConVar( char const *pName, char const *pDefaultValue, int flags, char const *pHelpString, bool bMin, float fMin, bool bMax, float fMax ) : ConVar(pName, pDefaultValue, flags|FCVAR_MATERIAL_SYSTEM, pHelpString, bMin, fMin, bMax, fMax) {}
|
||||
M_ConVar( char const *pName, char const *pDefaultValue, int flags, char const *pHelpString, M_FnChangeCallback callback ) : ConVar(pName, pDefaultValue, flags|FCVAR_MATERIAL_SYSTEM, pHelpString, (FnChangeCallback)callback ) {}
|
||||
M_ConVar( char const *pName, char const *pDefaultValue, int flags, char const *pHelpString, bool bMin, float fMin, bool bMax, float fMax, M_FnChangeCallback callback ) : ConVar(pName, pDefaultValue, flags|FCVAR_MATERIAL_SYSTEM, pHelpString, bMin, fMin, bMax, fMax, (FnChangeCallback)callback ) {}
|
||||
};
|
||||
|
||||
class S_ConVar : public ConVar
|
||||
{
|
||||
public:
|
||||
S_ConVar( char const *pName, char const *pDefaultValue, int flags = 0) : ConVar(pName, pDefaultValue, flags|FCVAR_STUDIORENDER) {}
|
||||
S_ConVar( char const *pName, char const *pDefaultValue, int flags, char const *pHelpString ) : ConVar(pName, pDefaultValue, flags|FCVAR_STUDIORENDER, pHelpString ) {}
|
||||
S_ConVar( char const *pName, char const *pDefaultValue, int flags, char const *pHelpString, bool bMin, float fMin, bool bMax, float fMax ) : ConVar(pName, pDefaultValue, flags|FCVAR_STUDIORENDER, pHelpString, bMin, fMin, bMax, fMax) {}
|
||||
S_ConVar( char const *pName, char const *pDefaultValue, int flags, char const *pHelpString, M_FnChangeCallback callback ) : ConVar(pName, pDefaultValue, flags|FCVAR_STUDIORENDER, pHelpString, (FnChangeCallback)callback ) {}
|
||||
S_ConVar( char const *pName, char const *pDefaultValue, int flags, char const *pHelpString, bool bMin, float fMin, bool bMax, float fMax, S_FnChangeCallback callback ) : ConVar(pName, pDefaultValue, flags|FCVAR_STUDIORENDER, pHelpString, bMin, fMin, bMax, fMax, (FnChangeCallback)callback ) {}
|
||||
};
|
||||
|
||||
class D_ConVar : public ConVar
|
||||
{
|
||||
public:
|
||||
D_ConVar( char const *pName, char const *pDefaultValue, int flags = 0) : ConVar(pName, pDefaultValue, flags|FCVAR_DATACACHE) {}
|
||||
D_ConVar( char const *pName, char const *pDefaultValue, int flags, char const *pHelpString ) : ConVar(pName, pDefaultValue, flags|FCVAR_DATACACHE, pHelpString ) {}
|
||||
D_ConVar( char const *pName, char const *pDefaultValue, int flags, char const *pHelpString, bool bMin, float fMin, bool bMax, float fMax ) : ConVar(pName, pDefaultValue, flags|FCVAR_DATACACHE, pHelpString, bMin, fMin, bMax, fMax) {}
|
||||
D_ConVar( char const *pName, char const *pDefaultValue, int flags, char const *pHelpString, M_FnChangeCallback callback ) : ConVar(pName, pDefaultValue, flags|FCVAR_DATACACHE, pHelpString, (FnChangeCallback)callback ) {}
|
||||
D_ConVar( char const *pName, char const *pDefaultValue, int flags, char const *pHelpString, bool bMin, float fMin, bool bMax, float fMax, D_FnChangeCallback callback ) : ConVar(pName, pDefaultValue, flags|FCVAR_DATACACHE, pHelpString, bMin, fMin, bMax, fMax, (FnChangeCallback)callback ) {}
|
||||
};
|
||||
|
||||
// redirect these declarations to their specific subsystem
|
||||
#ifdef GAME_DLL
|
||||
#define ConCommand G_ConCommand
|
||||
#define ConVar G_ConVar
|
||||
#endif
|
||||
#ifdef CLIENT_DLL
|
||||
#define ConCommand C_ConCommand
|
||||
#define ConVar C_ConVar
|
||||
#endif
|
||||
#ifdef MATERIALSYSTEM_DLL
|
||||
#define ConCommand M_ConCommand
|
||||
#define ConVar M_ConVar
|
||||
#endif
|
||||
#ifdef STUDIORENDER_DLL
|
||||
#define ConCommand S_ConCommand
|
||||
#define ConVar S_ConVar
|
||||
#endif
|
||||
#ifdef DATACACHE_DLL
|
||||
#define ConCommand D_ConCommand
|
||||
#define ConVar D_ConVar
|
||||
#endif
|
||||
#endif // _STATIC_LINKED
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Utility to quicky generate a simple console command
|
||||
//-----------------------------------------------------------------------------
|
||||
#define CON_COMMAND( name, description ) \
|
||||
static void name(); \
|
||||
static ConCommand name##_command( #name, name, description ); \
|
||||
static void name()
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Utility to quicky generate a simple console command
|
||||
//-----------------------------------------------------------------------------
|
||||
#define CON_COMMAND_F( name, description, flags ) \
|
||||
static void name(); \
|
||||
static ConCommand name##_command( #name, name, description, flags ); \
|
||||
static void name()
|
||||
|
||||
|
||||
#endif // CONVAR_H
|
||||
@@ -0,0 +1,709 @@
|
||||
//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $Workfile: $
|
||||
// $Date: $
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
// $NoKeywords: $
|
||||
//===========================================================================//
|
||||
|
||||
#ifndef CONVAR_H
|
||||
#define CONVAR_H
|
||||
|
||||
#if _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include "tier0/dbg.h"
|
||||
#include "tier1/iconvar.h"
|
||||
#include "tier1/utlvector.h"
|
||||
#include "tier1/utlstring.h"
|
||||
#include "icvar.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#define FORCEINLINE_CVAR FORCEINLINE
|
||||
#elif _LINUX
|
||||
#define FORCEINLINE_CVAR inline
|
||||
#else
|
||||
#error "implement me"
|
||||
#endif
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Forward declarations
|
||||
//-----------------------------------------------------------------------------
|
||||
class ConVar;
|
||||
class CCommand;
|
||||
class ConCommand;
|
||||
class ConCommandBase;
|
||||
struct characterset_t;
|
||||
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Any executable that wants to use ConVars need to implement one of
|
||||
// these to hook up access to console variables.
|
||||
//-----------------------------------------------------------------------------
|
||||
class IConCommandBaseAccessor
|
||||
{
|
||||
public:
|
||||
// Flags is a combination of FCVAR flags in cvar.h.
|
||||
// hOut is filled in with a handle to the variable.
|
||||
virtual bool RegisterConCommandBase( ConCommandBase *pVar ) = 0;
|
||||
};
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Helper method for console development
|
||||
//-----------------------------------------------------------------------------
|
||||
#if defined( _X360 ) && !defined( _RETAIL )
|
||||
void ConVar_PublishToVXConsole();
|
||||
#endif
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Called when a ConCommand needs to execute
|
||||
//-----------------------------------------------------------------------------
|
||||
typedef void ( *FnCommandCallbackV1_t )( void );
|
||||
typedef void ( *FnCommandCallback_t )( const CCommand &command );
|
||||
|
||||
#define COMMAND_COMPLETION_MAXITEMS 64
|
||||
#define COMMAND_COMPLETION_ITEM_LENGTH 64
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Returns 0 to COMMAND_COMPLETION_MAXITEMS worth of completion strings
|
||||
//-----------------------------------------------------------------------------
|
||||
typedef int ( *FnCommandCompletionCallback )( const char *partial, char commands[ COMMAND_COMPLETION_MAXITEMS ][ COMMAND_COMPLETION_ITEM_LENGTH ] );
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Interface version
|
||||
//-----------------------------------------------------------------------------
|
||||
class ICommandCallback
|
||||
{
|
||||
public:
|
||||
virtual void CommandCallback( const CCommand &command ) = 0;
|
||||
};
|
||||
|
||||
class ICommandCompletionCallback
|
||||
{
|
||||
public:
|
||||
virtual int CommandCompletionCallback( const char *pPartial, CUtlVector< CUtlString > &commands ) = 0;
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: The base console invoked command/cvar interface
|
||||
//-----------------------------------------------------------------------------
|
||||
class ConCommandBase
|
||||
{
|
||||
friend class CCvar;
|
||||
friend class ConVar;
|
||||
friend class ConCommand;
|
||||
friend void ConVar_Register( int nCVarFlag, IConCommandBaseAccessor *pAccessor );
|
||||
friend void ConVar_PublishToVXConsole();
|
||||
|
||||
// FIXME: Remove when ConVar changes are done
|
||||
friend class CDefaultCvar;
|
||||
|
||||
public:
|
||||
ConCommandBase( void );
|
||||
ConCommandBase( const char *pName, const char *pHelpString = 0,
|
||||
int flags = 0 );
|
||||
|
||||
virtual ~ConCommandBase( void );
|
||||
|
||||
virtual bool IsCommand( void ) const;
|
||||
|
||||
// Check flag
|
||||
virtual bool IsFlagSet( int flag ) const;
|
||||
// Set flag
|
||||
virtual void AddFlags( int flags );
|
||||
|
||||
// Return name of cvar
|
||||
virtual const char *GetName( void ) const;
|
||||
|
||||
// Return help text for cvar
|
||||
virtual const char *GetHelpText( void ) const;
|
||||
|
||||
// Deal with next pointer
|
||||
const ConCommandBase *GetNext( void ) const;
|
||||
ConCommandBase *GetNext( void );
|
||||
|
||||
void SetNext(ConCommandBase *pBase)
|
||||
{
|
||||
m_pNext = pBase;
|
||||
}
|
||||
|
||||
virtual bool IsRegistered( void ) const;
|
||||
|
||||
// Returns the DLL identifier
|
||||
virtual CVarDLLIdentifier_t GetDLLIdentifier() const;
|
||||
|
||||
protected:
|
||||
virtual void Create( const char *pName, const char *pHelpString = 0,
|
||||
int flags = 0 );
|
||||
|
||||
// Used internally by OneTimeInit to initialize/shutdown
|
||||
virtual void Init();
|
||||
void Shutdown();
|
||||
|
||||
// Internal copy routine ( uses new operator from correct module )
|
||||
char *CopyString( const char *from );
|
||||
|
||||
private:
|
||||
// Next ConVar in chain
|
||||
// Prior to register, it points to the next convar in the DLL.
|
||||
// Once registered, though, m_pNext is reset to point to the next
|
||||
// convar in the global list
|
||||
ConCommandBase *m_pNext;
|
||||
|
||||
// Has the cvar been added to the global list?
|
||||
bool m_bRegistered;
|
||||
|
||||
// Static data
|
||||
const char *m_pszName;
|
||||
const char *m_pszHelpString;
|
||||
|
||||
// ConVar flags
|
||||
int m_nFlags;
|
||||
|
||||
protected:
|
||||
// ConVars add themselves to this list for the executable.
|
||||
// Then ConVar_Register runs through all the console variables
|
||||
// and registers them into a global list stored in vstdlib.dll
|
||||
static ConCommandBase *s_pConCommandBases;
|
||||
|
||||
// ConVars in this executable use this 'global' to access values.
|
||||
static IConCommandBaseAccessor *s_pAccessor;
|
||||
public: // Hackalicous
|
||||
inline int GetFlags() const
|
||||
{
|
||||
return m_nFlags;
|
||||
}
|
||||
inline void SetFlags(int flags)
|
||||
{
|
||||
m_nFlags = flags;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Command tokenizer
|
||||
//-----------------------------------------------------------------------------
|
||||
class CCommand
|
||||
{
|
||||
public:
|
||||
CCommand();
|
||||
CCommand( int nArgC, const char **ppArgV );
|
||||
bool Tokenize( const char *pCommand, characterset_t *pBreakSet = NULL );
|
||||
void Reset();
|
||||
|
||||
int ArgC() const;
|
||||
const char **ArgV() const;
|
||||
const char *ArgS() const; // All args that occur after the 0th arg, in string form
|
||||
const char *GetCommandString() const; // The entire command in string form, including the 0th arg
|
||||
const char *operator[]( int nIndex ) const; // Gets at arguments
|
||||
const char *Arg( int nIndex ) const; // Gets at arguments
|
||||
|
||||
// Helper functions to parse arguments to commands.
|
||||
const char* FindArg( const char *pName ) const;
|
||||
int FindArgInt( const char *pName, int nDefaultVal ) const;
|
||||
|
||||
static int MaxCommandLength();
|
||||
static characterset_t* DefaultBreakSet();
|
||||
|
||||
private:
|
||||
enum
|
||||
{
|
||||
COMMAND_MAX_ARGC = 64,
|
||||
COMMAND_MAX_LENGTH = 512,
|
||||
};
|
||||
|
||||
int m_nArgc;
|
||||
int m_nArgv0Size;
|
||||
char m_pArgSBuffer[ COMMAND_MAX_LENGTH ];
|
||||
char m_pArgvBuffer[ COMMAND_MAX_LENGTH ];
|
||||
const char* m_ppArgv[ COMMAND_MAX_ARGC ];
|
||||
};
|
||||
|
||||
inline int CCommand::MaxCommandLength()
|
||||
{
|
||||
return COMMAND_MAX_LENGTH - 1;
|
||||
}
|
||||
|
||||
inline int CCommand::ArgC() const
|
||||
{
|
||||
return m_nArgc;
|
||||
}
|
||||
|
||||
inline const char **CCommand::ArgV() const
|
||||
{
|
||||
return m_nArgc ? (const char**)m_ppArgv : NULL;
|
||||
}
|
||||
|
||||
inline const char *CCommand::ArgS() const
|
||||
{
|
||||
return m_nArgv0Size ? &m_pArgSBuffer[m_nArgv0Size] : "";
|
||||
}
|
||||
|
||||
inline const char *CCommand::GetCommandString() const
|
||||
{
|
||||
return m_nArgc ? m_pArgSBuffer : "";
|
||||
}
|
||||
|
||||
inline const char *CCommand::Arg( int nIndex ) const
|
||||
{
|
||||
// FIXME: Many command handlers appear to not be particularly careful
|
||||
// about checking for valid argc range. For now, we're going to
|
||||
// do the extra check and return an empty string if it's out of range
|
||||
if ( nIndex < 0 || nIndex >= m_nArgc )
|
||||
return "";
|
||||
return m_ppArgv[nIndex];
|
||||
}
|
||||
|
||||
inline const char *CCommand::operator[]( int nIndex ) const
|
||||
{
|
||||
return Arg( nIndex );
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: The console invoked command
|
||||
//-----------------------------------------------------------------------------
|
||||
class ConCommand : public ConCommandBase
|
||||
{
|
||||
friend class CCvar;
|
||||
|
||||
public:
|
||||
typedef ConCommandBase BaseClass;
|
||||
|
||||
ConCommand( const char *pName, FnCommandCallbackV1_t callback,
|
||||
const char *pHelpString = 0, int flags = 0, FnCommandCompletionCallback completionFunc = 0 );
|
||||
ConCommand( const char *pName, FnCommandCallback_t callback,
|
||||
const char *pHelpString = 0, int flags = 0, FnCommandCompletionCallback completionFunc = 0 );
|
||||
ConCommand( const char *pName, ICommandCallback *pCallback,
|
||||
const char *pHelpString = 0, int flags = 0, ICommandCompletionCallback *pCommandCompletionCallback = 0 );
|
||||
|
||||
virtual ~ConCommand( void );
|
||||
|
||||
virtual bool IsCommand( void ) const;
|
||||
|
||||
virtual int AutoCompleteSuggest( const char *partial, CUtlVector< CUtlString > &commands );
|
||||
|
||||
virtual bool CanAutoComplete( void );
|
||||
|
||||
// Invoke the function
|
||||
virtual void Dispatch( const CCommand &command );
|
||||
|
||||
private:
|
||||
// NOTE: To maintain backward compat, we have to be very careful:
|
||||
// All public virtual methods must appear in the same order always
|
||||
// since engine code will be calling into this code, which *does not match*
|
||||
// in the mod code; it's using slightly different, but compatible versions
|
||||
// of this class. Also: Be very careful about adding new fields to this class.
|
||||
// Those fields will not exist in the version of this class that is instanced
|
||||
// in mod code.
|
||||
|
||||
// Call this function when executing the command
|
||||
union
|
||||
{
|
||||
FnCommandCallbackV1_t m_fnCommandCallbackV1;
|
||||
FnCommandCallback_t m_fnCommandCallback;
|
||||
ICommandCallback *m_pCommandCallback;
|
||||
};
|
||||
|
||||
union
|
||||
{
|
||||
FnCommandCompletionCallback m_fnCompletionCallback;
|
||||
ICommandCompletionCallback *m_pCommandCompletionCallback;
|
||||
};
|
||||
|
||||
bool m_bHasCompletionCallback : 1;
|
||||
bool m_bUsingNewCommandCallback : 1;
|
||||
bool m_bUsingCommandCallbackInterface : 1;
|
||||
public: // Hackalicous
|
||||
inline FnCommandCallback_t GetCallback() const
|
||||
{
|
||||
return m_fnCommandCallback;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: A console variable
|
||||
//-----------------------------------------------------------------------------
|
||||
class ConVar : public ConCommandBase, public IConVar
|
||||
{
|
||||
friend class CCvar;
|
||||
friend class ConVarRef;
|
||||
|
||||
public:
|
||||
typedef ConCommandBase BaseClass;
|
||||
|
||||
ConVar( const char *pName, const char *pDefaultValue, int flags = 0);
|
||||
|
||||
ConVar( const char *pName, const char *pDefaultValue, int flags,
|
||||
const char *pHelpString );
|
||||
ConVar( const char *pName, const char *pDefaultValue, int flags,
|
||||
const char *pHelpString, bool bMin, float fMin, bool bMax, float fMax );
|
||||
ConVar( const char *pName, const char *pDefaultValue, int flags,
|
||||
const char *pHelpString, FnChangeCallback_t callback );
|
||||
ConVar( const char *pName, const char *pDefaultValue, int flags,
|
||||
const char *pHelpString, bool bMin, float fMin, bool bMax, float fMax,
|
||||
FnChangeCallback_t callback );
|
||||
|
||||
virtual ~ConVar( void );
|
||||
|
||||
virtual bool IsFlagSet( int flag ) const;
|
||||
virtual const char* GetHelpText( void ) const;
|
||||
virtual bool IsRegistered( void ) const;
|
||||
virtual const char *GetName( void ) const;
|
||||
virtual void AddFlags( int flags );
|
||||
virtual bool IsCommand( void ) const;
|
||||
|
||||
// Install a change callback (there shouldn't already be one....)
|
||||
void InstallChangeCallback( FnChangeCallback_t callback );
|
||||
|
||||
// Retrieve value
|
||||
FORCEINLINE_CVAR float GetFloat( void ) const;
|
||||
FORCEINLINE_CVAR int GetInt( void ) const;
|
||||
FORCEINLINE_CVAR bool GetBool() const { return !!GetInt(); }
|
||||
FORCEINLINE_CVAR char const *GetString( void ) const;
|
||||
|
||||
// Any function that allocates/frees memory needs to be virtual or else you'll have crashes
|
||||
// from alloc/free across dll/exe boundaries.
|
||||
|
||||
// These just call into the IConCommandBaseAccessor to check flags and set the var (which ends up calling InternalSetValue).
|
||||
virtual void SetValue( const char *value );
|
||||
virtual void SetValue( float value );
|
||||
virtual void SetValue( int value );
|
||||
|
||||
// Reset to default value
|
||||
void Revert( void );
|
||||
|
||||
// True if it has a min/max setting
|
||||
bool GetMin( float& minVal ) const;
|
||||
bool GetMax( float& maxVal ) const;
|
||||
const char *GetDefault( void ) const;
|
||||
|
||||
private:
|
||||
// Called by CCvar when the value of a var is changing.
|
||||
virtual void InternalSetValue(const char *value);
|
||||
// For CVARs marked FCVAR_NEVER_AS_STRING
|
||||
virtual void InternalSetFloatValue( float fNewValue );
|
||||
virtual void InternalSetIntValue( int nValue );
|
||||
|
||||
virtual bool ClampValue( float& value );
|
||||
virtual void ChangeStringValue( const char *tempVal, float flOldValue );
|
||||
|
||||
virtual void Create( const char *pName, const char *pDefaultValue, int flags = 0,
|
||||
const char *pHelpString = 0, bool bMin = false, float fMin = 0.0,
|
||||
bool bMax = false, float fMax = false, FnChangeCallback_t callback = 0 );
|
||||
|
||||
// Used internally by OneTimeInit to initialize.
|
||||
virtual void Init();
|
||||
|
||||
private:
|
||||
|
||||
// This either points to "this" or it points to the original declaration of a ConVar.
|
||||
// This allows ConVars to exist in separate modules, and they all use the first one to be declared.
|
||||
// m_pParent->m_pParent must equal m_pParent (ie: m_pParent must be the root, or original, ConVar).
|
||||
ConVar *m_pParent;
|
||||
|
||||
// Static data
|
||||
const char *m_pszDefaultValue;
|
||||
|
||||
// Value
|
||||
// Dynamically allocated
|
||||
char *m_pszString;
|
||||
int m_StringLength;
|
||||
|
||||
// Values
|
||||
float m_fValue;
|
||||
int m_nValue;
|
||||
|
||||
// Min/Max values
|
||||
bool m_bHasMin;
|
||||
float m_fMinVal;
|
||||
bool m_bHasMax;
|
||||
float m_fMaxVal;
|
||||
|
||||
// Call this function when ConVar changes
|
||||
FnChangeCallback_t m_fnChangeCallback;
|
||||
public: // Hackalicous
|
||||
inline FnChangeCallback_t GetCallback() const
|
||||
{
|
||||
return m_fnChangeCallback;
|
||||
}
|
||||
inline void SetMin(bool set, float min=0.0)
|
||||
{
|
||||
m_bHasMin = set;
|
||||
m_fMinVal = min;
|
||||
}
|
||||
inline void SetMax(bool set, float max=0.0)
|
||||
{
|
||||
m_bHasMax = set;
|
||||
m_fMaxVal = max;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Return ConVar value as a float
|
||||
// Output : float
|
||||
//-----------------------------------------------------------------------------
|
||||
FORCEINLINE_CVAR float ConVar::GetFloat( void ) const
|
||||
{
|
||||
return m_pParent->m_fValue;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Return ConVar value as an int
|
||||
// Output : int
|
||||
//-----------------------------------------------------------------------------
|
||||
FORCEINLINE_CVAR int ConVar::GetInt( void ) const
|
||||
{
|
||||
return m_pParent->m_nValue;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Return ConVar value as a string, return "" for bogus string pointer, etc.
|
||||
// Output : const char *
|
||||
//-----------------------------------------------------------------------------
|
||||
FORCEINLINE_CVAR const char *ConVar::GetString( void ) const
|
||||
{
|
||||
if ( m_nFlags & FCVAR_NEVER_AS_STRING )
|
||||
return "FCVAR_NEVER_AS_STRING";
|
||||
|
||||
return ( m_pParent->m_pszString ) ? m_pParent->m_pszString : "";
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Used to read/write convars that already exist (replaces the FindVar method)
|
||||
//-----------------------------------------------------------------------------
|
||||
class ConVarRef
|
||||
{
|
||||
public:
|
||||
ConVarRef( const char *pName );
|
||||
ConVarRef( const char *pName, bool bIgnoreMissing );
|
||||
ConVarRef( IConVar *pConVar );
|
||||
|
||||
void Init( const char *pName, bool bIgnoreMissing );
|
||||
bool IsValid() const;
|
||||
bool IsFlagSet( int nFlags ) const;
|
||||
IConVar *GetLinkedConVar();
|
||||
|
||||
// Get/Set value
|
||||
float GetFloat( void ) const;
|
||||
int GetInt( void ) const;
|
||||
bool GetBool() const { return !!GetInt(); }
|
||||
const char *GetString( void ) const;
|
||||
|
||||
void SetValue( const char *pValue );
|
||||
void SetValue( float flValue );
|
||||
void SetValue( int nValue );
|
||||
void SetValue( bool bValue );
|
||||
|
||||
const char *GetName() const;
|
||||
|
||||
const char *GetDefault() const;
|
||||
|
||||
private:
|
||||
// High-speed method to read convar data
|
||||
IConVar *m_pConVar;
|
||||
ConVar *m_pConVarState;
|
||||
};
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Did we find an existing convar of that name?
|
||||
//-----------------------------------------------------------------------------
|
||||
FORCEINLINE_CVAR bool ConVarRef::IsFlagSet( int nFlags ) const
|
||||
{
|
||||
return ( m_pConVar->IsFlagSet( nFlags ) != 0 );
|
||||
}
|
||||
|
||||
FORCEINLINE_CVAR IConVar *ConVarRef::GetLinkedConVar()
|
||||
{
|
||||
return m_pConVar;
|
||||
}
|
||||
|
||||
FORCEINLINE_CVAR const char *ConVarRef::GetName() const
|
||||
{
|
||||
return m_pConVar->GetName();
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Return ConVar value as a float
|
||||
//-----------------------------------------------------------------------------
|
||||
FORCEINLINE_CVAR float ConVarRef::GetFloat( void ) const
|
||||
{
|
||||
return m_pConVarState->m_fValue;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Return ConVar value as an int
|
||||
//-----------------------------------------------------------------------------
|
||||
FORCEINLINE_CVAR int ConVarRef::GetInt( void ) const
|
||||
{
|
||||
return m_pConVarState->m_nValue;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Return ConVar value as a string, return "" for bogus string pointer, etc.
|
||||
//-----------------------------------------------------------------------------
|
||||
FORCEINLINE_CVAR const char *ConVarRef::GetString( void ) const
|
||||
{
|
||||
Assert( !IsFlagSet( FCVAR_NEVER_AS_STRING ) );
|
||||
return m_pConVarState->m_pszString;
|
||||
}
|
||||
|
||||
|
||||
FORCEINLINE_CVAR void ConVarRef::SetValue( const char *pValue )
|
||||
{
|
||||
m_pConVar->SetValue( pValue );
|
||||
}
|
||||
|
||||
FORCEINLINE_CVAR void ConVarRef::SetValue( float flValue )
|
||||
{
|
||||
m_pConVar->SetValue( flValue );
|
||||
}
|
||||
|
||||
FORCEINLINE_CVAR void ConVarRef::SetValue( int nValue )
|
||||
{
|
||||
m_pConVar->SetValue( nValue );
|
||||
}
|
||||
|
||||
FORCEINLINE_CVAR void ConVarRef::SetValue( bool bValue )
|
||||
{
|
||||
m_pConVar->SetValue( bValue ? 1 : 0 );
|
||||
}
|
||||
|
||||
FORCEINLINE_CVAR const char *ConVarRef::GetDefault() const
|
||||
{
|
||||
return m_pConVarState->m_pszDefaultValue;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Called by the framework to register ConCommands with the ICVar
|
||||
//-----------------------------------------------------------------------------
|
||||
void ConVar_Register( int nCVarFlag = 0, IConCommandBaseAccessor *pAccessor = NULL );
|
||||
void ConVar_Unregister( );
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Utility methods
|
||||
//-----------------------------------------------------------------------------
|
||||
void ConVar_PrintFlags( const ConCommandBase *var );
|
||||
void ConVar_PrintDescription( const ConCommandBase *pVar );
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Utility class to quickly allow ConCommands to call member methods
|
||||
//-----------------------------------------------------------------------------
|
||||
#if defined _MSC_VER
|
||||
#pragma warning (disable : 4355 )
|
||||
#endif
|
||||
|
||||
template< class T >
|
||||
class CConCommandMemberAccessor : public ConCommand, public ICommandCallback, public ICommandCompletionCallback
|
||||
{
|
||||
typedef ConCommand BaseClass;
|
||||
typedef void ( T::*FnMemberCommandCallback_t )( const CCommand &command );
|
||||
typedef int ( T::*FnMemberCommandCompletionCallback_t )( const char *pPartial, CUtlVector< CUtlString > &commands );
|
||||
|
||||
public:
|
||||
CConCommandMemberAccessor( T* pOwner, const char *pName, FnMemberCommandCallback_t callback, const char *pHelpString = 0,
|
||||
int flags = 0, FnMemberCommandCompletionCallback_t completionFunc = 0 ) :
|
||||
BaseClass( pName, this, pHelpString, flags, ( completionFunc != 0 ) ? this : NULL )
|
||||
{
|
||||
m_pOwner = pOwner;
|
||||
m_Func = callback;
|
||||
m_CompletionFunc = completionFunc;
|
||||
}
|
||||
|
||||
~CConCommandMemberAccessor()
|
||||
{
|
||||
Shutdown();
|
||||
}
|
||||
|
||||
void SetOwner( T* pOwner )
|
||||
{
|
||||
m_pOwner = pOwner;
|
||||
}
|
||||
|
||||
virtual void CommandCallback( const CCommand &command )
|
||||
{
|
||||
Assert( m_pOwner && m_Func );
|
||||
(m_pOwner->*m_Func)( command );
|
||||
}
|
||||
|
||||
virtual int CommandCompletionCallback( const char *pPartial, CUtlVector< CUtlString > &commands )
|
||||
{
|
||||
Assert( m_pOwner && m_CompletionFunc );
|
||||
return (m_pOwner->*m_CompletionFunc)( pPartial, commands );
|
||||
}
|
||||
|
||||
private:
|
||||
T* m_pOwner;
|
||||
FnMemberCommandCallback_t m_Func;
|
||||
FnMemberCommandCompletionCallback_t m_CompletionFunc;
|
||||
};
|
||||
|
||||
#if defined _MSC_VER
|
||||
#pragma warning ( default : 4355 )
|
||||
#endif
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Utility macros to quicky generate a simple console command
|
||||
//-----------------------------------------------------------------------------
|
||||
#define CON_COMMAND( name, description ) \
|
||||
static void name( const CCommand &args ); \
|
||||
static ConCommand name##_command( #name, name, description ); \
|
||||
static void name( const CCommand &args )
|
||||
|
||||
#define CON_COMMAND_F( name, description, flags ) \
|
||||
static void name( const CCommand &args ); \
|
||||
static ConCommand name##_command( #name, name, description, flags ); \
|
||||
static void name( const CCommand &args )
|
||||
|
||||
#define CON_COMMAND_F_COMPLETION( name, description, flags, completion ) \
|
||||
static void name( const CCommand &args ); \
|
||||
static ConCommand name##_command( #name, name, description, flags, completion ); \
|
||||
static void name( const CCommand &args )
|
||||
|
||||
#define CON_COMMAND_EXTERN( name, _funcname, description ) \
|
||||
void _funcname( const CCommand &args ); \
|
||||
static ConCommand name##_command( #name, _funcname, description ); \
|
||||
void _funcname( const CCommand &args )
|
||||
|
||||
#define CON_COMMAND_EXTERN_F( name, _funcname, description, flags ) \
|
||||
void _funcname( const CCommand &args ); \
|
||||
static ConCommand name##_command( #name, _funcname, description, flags ); \
|
||||
void _funcname( const CCommand &args )
|
||||
|
||||
#define CON_COMMAND_MEMBER_F( _thisclass, name, _funcname, description, flags ) \
|
||||
void _funcname( const CCommand &args ); \
|
||||
friend class CCommandMemberInitializer_##_funcname; \
|
||||
class CCommandMemberInitializer_##_funcname \
|
||||
{ \
|
||||
public: \
|
||||
CCommandMemberInitializer_##_funcname() : m_ConCommandAccessor( NULL, name, &_thisclass::_funcname, description, flags ) \
|
||||
{ \
|
||||
m_ConCommandAccessor.SetOwner( GET_OUTER( _thisclass, m_##_funcname##_register ) ); \
|
||||
} \
|
||||
private: \
|
||||
CConCommandMemberAccessor< _thisclass > m_ConCommandAccessor; \
|
||||
}; \
|
||||
\
|
||||
CCommandMemberInitializer_##_funcname m_##_funcname##_register; \
|
||||
|
||||
|
||||
#endif // CONVAR_H
|
||||
@@ -0,0 +1,64 @@
|
||||
/**
|
||||
* vim: set ts=4 :
|
||||
* =============================================================================
|
||||
* SourceMod
|
||||
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
|
||||
* =============================================================================
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, version 3.0, as published by the
|
||||
* Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* As a special exception, AlliedModders LLC gives you permission to link the
|
||||
* code of this program (as well as its derivative works) to "Half-Life 2," the
|
||||
* "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
|
||||
* by the Valve Corporation. You must obey the GNU General Public License in
|
||||
* all respects for all other code used. Additionally, AlliedModders LLC grants
|
||||
* this exception to all derivative works. AlliedModders LLC defines further
|
||||
* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
|
||||
* or <http://www.sourcemod.net/license.php>.
|
||||
*
|
||||
* Version: $Id$
|
||||
*/
|
||||
#include "frame_hooks.h"
|
||||
#include "TimerSys.h"
|
||||
#include "Database.h"
|
||||
#include "HalfLife2.h"
|
||||
#include "MenuStyle_Valve.h"
|
||||
#include "MenuStyle_Radio.h"
|
||||
#include "PlayerManager.h"
|
||||
|
||||
float g_LastMenuTime = 0.0f;
|
||||
float g_LastAuthCheck = 0.0f;
|
||||
|
||||
void RunFrameHooks(bool simulating)
|
||||
{
|
||||
/* Frame based hooks */
|
||||
g_DBMan.RunFrame();
|
||||
g_HL2.ProcessFakeCliCmdQueue();
|
||||
g_HL2.ProcessDelayedKicks();
|
||||
g_SourceMod.ProcessGameFrameHooks(simulating);
|
||||
|
||||
float curtime = *g_pUniversalTime;
|
||||
|
||||
if (curtime - g_LastMenuTime >= 1.0f)
|
||||
{
|
||||
g_ValveMenuStyle.ProcessWatchList();
|
||||
g_RadioMenuStyle.ProcessWatchList();
|
||||
g_LastMenuTime = curtime;
|
||||
}
|
||||
|
||||
if (*g_NumPlayersToAuth && curtime - g_LastAuthCheck >= 0.7f)
|
||||
{
|
||||
g_Players.RunAuthChecks();
|
||||
g_LastAuthCheck = curtime;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/**
|
||||
* vim: set ts=4 :
|
||||
* =============================================================================
|
||||
* SourceMod
|
||||
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
|
||||
* =============================================================================
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, version 3.0, as published by the
|
||||
* Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* As a special exception, AlliedModders LLC gives you permission to link the
|
||||
* code of this program (as well as its derivative works) to "Half-Life 2," the
|
||||
* "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
|
||||
* by the Valve Corporation. You must obey the GNU General Public License in
|
||||
* all respects for all other code used. Additionally, AlliedModders LLC grants
|
||||
* this exception to all derivative works. AlliedModders LLC defines further
|
||||
* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
|
||||
* or <http://www.sourcemod.net/license.php>.
|
||||
*
|
||||
* Version: $Id$
|
||||
*/
|
||||
|
||||
#ifndef _INCLUDE_SOURCEMOD_FRAME_HOOKS_H_
|
||||
#define _INCLUDE_SOURCEMOD_FRAME_HOOKS_H_
|
||||
|
||||
void RunFrameHooks(bool simulating);
|
||||
|
||||
#endif //_INCLUDE_SOURCEMOD_FRAME_HOOKS_H_
|
||||
+485
@@ -0,0 +1,485 @@
|
||||
// MD5.CC - source code for the C++/object oriented translation and
|
||||
// modification of MD5.
|
||||
|
||||
// Translation and modification (c) 1995 by Mordechai T. Abzug
|
||||
|
||||
// This translation/ modification is provided "as is," without express or
|
||||
// implied warranty of any kind.
|
||||
|
||||
// The translator/ modifier does not claim (1) that MD5 will do what you think
|
||||
// it does; (2) that this translation/ modification is accurate; or (3) that
|
||||
// this software is "merchantible." (Language for this disclaimer partially
|
||||
// copied from the disclaimer below).
|
||||
|
||||
/* based on:
|
||||
|
||||
MD5C.C - RSA Data Security, Inc., MD5 message-digest algorithm
|
||||
MDDRIVER.C - test driver for MD2, MD4 and MD5
|
||||
|
||||
|
||||
Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All
|
||||
rights reserved.
|
||||
|
||||
License to copy and use this software is granted provided that it
|
||||
is identified as the "RSA Data Security, Inc. MD5 Message-Digest
|
||||
Algorithm" in all material mentioning or referencing this software
|
||||
or this function.
|
||||
|
||||
License is also granted to make and use derivative works provided
|
||||
that such works are identified as "derived from the RSA Data
|
||||
Security, Inc. MD5 Message-Digest Algorithm" in all material
|
||||
mentioning or referencing the derived work.
|
||||
|
||||
RSA Data Security, Inc. makes no representations concerning either
|
||||
the merchantability of this software or the suitability of this
|
||||
software for any particular purpose. It is provided "as is"
|
||||
without express or implied warranty of any kind.
|
||||
|
||||
These notices must be retained in any copies of any part of this
|
||||
documentation and/or software.
|
||||
|
||||
*/
|
||||
|
||||
#include "md5.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
// MD5 simple initialization method
|
||||
|
||||
MD5::MD5(){
|
||||
|
||||
init();
|
||||
|
||||
}
|
||||
|
||||
|
||||
// MD5 block update operation. Continues an MD5 message-digest
|
||||
// operation, processing another message block, and updating the
|
||||
// context.
|
||||
|
||||
void MD5::update (uint1 *input, uint4 input_length) {
|
||||
|
||||
uint4 input_index, buffer_index;
|
||||
uint4 buffer_space; // how much space is left in buffer
|
||||
|
||||
if (finalized){ // so we can't update!
|
||||
/*cerr << "MD5::update: Can't update a finalized digest!" << endl;*/
|
||||
return;
|
||||
}
|
||||
|
||||
// Compute number of bytes mod 64
|
||||
buffer_index = (unsigned int)((count[0] >> 3) & 0x3F);
|
||||
|
||||
// Update number of bits
|
||||
if ( (count[0] += ((uint4) input_length << 3))<((uint4) input_length << 3) )
|
||||
count[1]++;
|
||||
|
||||
count[1] += ((uint4)input_length >> 29);
|
||||
|
||||
|
||||
buffer_space = 64 - buffer_index; // how much space is left in buffer
|
||||
|
||||
// Transform as many times as possible.
|
||||
if (input_length >= buffer_space) { // ie. we have enough to fill the buffer
|
||||
// fill the rest of the buffer and transform
|
||||
memcpy (buffer + buffer_index, input, buffer_space);
|
||||
transform (buffer);
|
||||
|
||||
// now, transform each 64-byte piece of the input, bypassing the buffer
|
||||
for (input_index = buffer_space; input_index + 63 < input_length;
|
||||
input_index += 64)
|
||||
transform (input+input_index);
|
||||
|
||||
buffer_index = 0; // so we can buffer remaining
|
||||
}
|
||||
else
|
||||
input_index=0; // so we can buffer the whole input
|
||||
|
||||
|
||||
// and here we do the buffering:
|
||||
memcpy(buffer+buffer_index, input+input_index, input_length-input_index);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// MD5 update for files.
|
||||
// Like above, except that it works on files (and uses above as a primitive.)
|
||||
|
||||
void MD5::update(FILE *file){
|
||||
|
||||
unsigned char buffer[1024];
|
||||
int len;
|
||||
|
||||
while ((len=fread(buffer, 1, 1024, file)))
|
||||
update(buffer, len);
|
||||
|
||||
fclose (file);
|
||||
|
||||
}
|
||||
|
||||
|
||||
// MD5 finalization. Ends an MD5 message-digest operation, writing the
|
||||
// the message digest and zeroizing the context.
|
||||
|
||||
|
||||
void MD5::finalize (){
|
||||
|
||||
unsigned char bits[8];
|
||||
unsigned int index, padLen;
|
||||
static uint1 PADDING[64]={
|
||||
0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
||||
};
|
||||
|
||||
if (finalized){
|
||||
/* cerr << "MD5::finalize: Already finalized this digest!" << endl;*/
|
||||
return;
|
||||
}
|
||||
|
||||
// Save number of bits
|
||||
encode (bits, count, 8);
|
||||
|
||||
// Pad out to 56 mod 64.
|
||||
index = (uint4) ((count[0] >> 3) & 0x3f);
|
||||
padLen = (index < 56) ? (56 - index) : (120 - index);
|
||||
update (PADDING, padLen);
|
||||
|
||||
// Append length (before padding)
|
||||
update (bits, 8);
|
||||
|
||||
// Store state in digest
|
||||
encode (digest, state, 16);
|
||||
|
||||
// Zeroize sensitive information
|
||||
memset (buffer, 0, sizeof(*buffer));
|
||||
|
||||
finalized=1;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
MD5::MD5(FILE *file){
|
||||
|
||||
init(); // must be called be all constructors
|
||||
update(file);
|
||||
finalize ();
|
||||
}
|
||||
|
||||
unsigned char *MD5::raw_digest(){
|
||||
|
||||
uint1 *s = new uint1[16];
|
||||
|
||||
if (!finalized){
|
||||
/* cerr << "MD5::raw_digest: Can't get digest if you haven't "<<
|
||||
"finalized the digest!" <<endl;*/
|
||||
return ( (unsigned char*) "");
|
||||
}
|
||||
|
||||
memcpy(s, digest, 16);
|
||||
return s;
|
||||
}
|
||||
|
||||
unsigned char *MD5::raw_digest(unsigned char buffer[16])
|
||||
{
|
||||
if (!finalized)
|
||||
{
|
||||
return ( (unsigned char*) "");
|
||||
}
|
||||
|
||||
memcpy(buffer, digest, 16);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
|
||||
|
||||
char *MD5::hex_digest(){
|
||||
|
||||
int i;
|
||||
char *s= new char[33];
|
||||
|
||||
if (!finalized){
|
||||
/* cerr << "MD5::hex_digest: Can't get digest if you haven't "<<
|
||||
"finalized the digest!" <<endl;*/
|
||||
return "";
|
||||
}
|
||||
|
||||
for (i=0; i<16; i++)
|
||||
sprintf(s+i*2, "%02x", digest[i]);
|
||||
|
||||
s[32]='\0';
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
char *MD5::hex_digest(char buffer[33]){
|
||||
|
||||
int i;
|
||||
|
||||
if (!finalized)
|
||||
{
|
||||
/* cerr << "MD5::hex_digest: Can't get digest if you haven't "<<
|
||||
"finalized the digest!" <<endl;*/
|
||||
return "";
|
||||
}
|
||||
|
||||
for (i=0; i<16; i++)
|
||||
sprintf(buffer+i*2, "%02x", digest[i]);
|
||||
|
||||
buffer[32]='\0';
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
|
||||
// PRIVATE METHODS:
|
||||
|
||||
|
||||
|
||||
void MD5::init(){
|
||||
finalized=0; // we just started!
|
||||
|
||||
// Nothing counted, so count=0
|
||||
count[0] = 0;
|
||||
count[1] = 0;
|
||||
|
||||
// Load magic initialization constants.
|
||||
state[0] = 0x67452301;
|
||||
state[1] = 0xefcdab89;
|
||||
state[2] = 0x98badcfe;
|
||||
state[3] = 0x10325476;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Constants for MD5Transform routine.
|
||||
// Although we could use C++ style constants, defines are actually better,
|
||||
// since they let us easily evade scope clashes.
|
||||
|
||||
#define S11 7
|
||||
#define S12 12
|
||||
#define S13 17
|
||||
#define S14 22
|
||||
#define S21 5
|
||||
#define S22 9
|
||||
#define S23 14
|
||||
#define S24 20
|
||||
#define S31 4
|
||||
#define S32 11
|
||||
#define S33 16
|
||||
#define S34 23
|
||||
#define S41 6
|
||||
#define S42 10
|
||||
#define S43 15
|
||||
#define S44 21
|
||||
|
||||
|
||||
|
||||
|
||||
// MD5 basic transformation. Transforms state based on block.
|
||||
void MD5::transform (uint1 block[64]){
|
||||
|
||||
uint4 a = state[0], b = state[1], c = state[2], d = state[3], x[16];
|
||||
|
||||
decode (x, block, 64);
|
||||
|
||||
assert(!finalized); // not just a user error, since the method is private
|
||||
|
||||
/* Round 1 */
|
||||
FF (a, b, c, d, x[ 0], S11, 0xd76aa478); /* 1 */
|
||||
FF (d, a, b, c, x[ 1], S12, 0xe8c7b756); /* 2 */
|
||||
FF (c, d, a, b, x[ 2], S13, 0x242070db); /* 3 */
|
||||
FF (b, c, d, a, x[ 3], S14, 0xc1bdceee); /* 4 */
|
||||
FF (a, b, c, d, x[ 4], S11, 0xf57c0faf); /* 5 */
|
||||
FF (d, a, b, c, x[ 5], S12, 0x4787c62a); /* 6 */
|
||||
FF (c, d, a, b, x[ 6], S13, 0xa8304613); /* 7 */
|
||||
FF (b, c, d, a, x[ 7], S14, 0xfd469501); /* 8 */
|
||||
FF (a, b, c, d, x[ 8], S11, 0x698098d8); /* 9 */
|
||||
FF (d, a, b, c, x[ 9], S12, 0x8b44f7af); /* 10 */
|
||||
FF (c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */
|
||||
FF (b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */
|
||||
FF (a, b, c, d, x[12], S11, 0x6b901122); /* 13 */
|
||||
FF (d, a, b, c, x[13], S12, 0xfd987193); /* 14 */
|
||||
FF (c, d, a, b, x[14], S13, 0xa679438e); /* 15 */
|
||||
FF (b, c, d, a, x[15], S14, 0x49b40821); /* 16 */
|
||||
|
||||
/* Round 2 */
|
||||
GG (a, b, c, d, x[ 1], S21, 0xf61e2562); /* 17 */
|
||||
GG (d, a, b, c, x[ 6], S22, 0xc040b340); /* 18 */
|
||||
GG (c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */
|
||||
GG (b, c, d, a, x[ 0], S24, 0xe9b6c7aa); /* 20 */
|
||||
GG (a, b, c, d, x[ 5], S21, 0xd62f105d); /* 21 */
|
||||
GG (d, a, b, c, x[10], S22, 0x2441453); /* 22 */
|
||||
GG (c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */
|
||||
GG (b, c, d, a, x[ 4], S24, 0xe7d3fbc8); /* 24 */
|
||||
GG (a, b, c, d, x[ 9], S21, 0x21e1cde6); /* 25 */
|
||||
GG (d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */
|
||||
GG (c, d, a, b, x[ 3], S23, 0xf4d50d87); /* 27 */
|
||||
GG (b, c, d, a, x[ 8], S24, 0x455a14ed); /* 28 */
|
||||
GG (a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */
|
||||
GG (d, a, b, c, x[ 2], S22, 0xfcefa3f8); /* 30 */
|
||||
GG (c, d, a, b, x[ 7], S23, 0x676f02d9); /* 31 */
|
||||
GG (b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */
|
||||
|
||||
/* Round 3 */
|
||||
HH (a, b, c, d, x[ 5], S31, 0xfffa3942); /* 33 */
|
||||
HH (d, a, b, c, x[ 8], S32, 0x8771f681); /* 34 */
|
||||
HH (c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */
|
||||
HH (b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */
|
||||
HH (a, b, c, d, x[ 1], S31, 0xa4beea44); /* 37 */
|
||||
HH (d, a, b, c, x[ 4], S32, 0x4bdecfa9); /* 38 */
|
||||
HH (c, d, a, b, x[ 7], S33, 0xf6bb4b60); /* 39 */
|
||||
HH (b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */
|
||||
HH (a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */
|
||||
HH (d, a, b, c, x[ 0], S32, 0xeaa127fa); /* 42 */
|
||||
HH (c, d, a, b, x[ 3], S33, 0xd4ef3085); /* 43 */
|
||||
HH (b, c, d, a, x[ 6], S34, 0x4881d05); /* 44 */
|
||||
HH (a, b, c, d, x[ 9], S31, 0xd9d4d039); /* 45 */
|
||||
HH (d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */
|
||||
HH (c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */
|
||||
HH (b, c, d, a, x[ 2], S34, 0xc4ac5665); /* 48 */
|
||||
|
||||
/* Round 4 */
|
||||
II (a, b, c, d, x[ 0], S41, 0xf4292244); /* 49 */
|
||||
II (d, a, b, c, x[ 7], S42, 0x432aff97); /* 50 */
|
||||
II (c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */
|
||||
II (b, c, d, a, x[ 5], S44, 0xfc93a039); /* 52 */
|
||||
II (a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */
|
||||
II (d, a, b, c, x[ 3], S42, 0x8f0ccc92); /* 54 */
|
||||
II (c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */
|
||||
II (b, c, d, a, x[ 1], S44, 0x85845dd1); /* 56 */
|
||||
II (a, b, c, d, x[ 8], S41, 0x6fa87e4f); /* 57 */
|
||||
II (d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */
|
||||
II (c, d, a, b, x[ 6], S43, 0xa3014314); /* 59 */
|
||||
II (b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */
|
||||
II (a, b, c, d, x[ 4], S41, 0xf7537e82); /* 61 */
|
||||
II (d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */
|
||||
II (c, d, a, b, x[ 2], S43, 0x2ad7d2bb); /* 63 */
|
||||
II (b, c, d, a, x[ 9], S44, 0xeb86d391); /* 64 */
|
||||
|
||||
state[0] += a;
|
||||
state[1] += b;
|
||||
state[2] += c;
|
||||
state[3] += d;
|
||||
|
||||
// Zeroize sensitive information.
|
||||
memset ( (uint1 *) x, 0, sizeof(x));
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Encodes input (UINT4) into output (unsigned char). Assumes len is
|
||||
// a multiple of 4.
|
||||
void MD5::encode (uint1 *output, uint4 *input, uint4 len) {
|
||||
|
||||
unsigned int i, j;
|
||||
|
||||
for (i = 0, j = 0; j < len; i++, j += 4) {
|
||||
output[j] = (uint1) (input[i] & 0xff);
|
||||
output[j+1] = (uint1) ((input[i] >> 8) & 0xff);
|
||||
output[j+2] = (uint1) ((input[i] >> 16) & 0xff);
|
||||
output[j+3] = (uint1) ((input[i] >> 24) & 0xff);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// Decodes input (unsigned char) into output (UINT4). Assumes len is
|
||||
// a multiple of 4.
|
||||
void MD5::decode (uint4 *output, uint1 *input, uint4 len){
|
||||
|
||||
unsigned int i, j;
|
||||
|
||||
for (i = 0, j = 0; j < len; i++, j += 4)
|
||||
output[i] = ((uint4)input[j]) | (((uint4)input[j+1]) << 8) |
|
||||
(((uint4)input[j+2]) << 16) | (((uint4)input[j+3]) << 24);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Note: Replace "for loop" with standard memcpy if possible.
|
||||
void MD5::memcpy (uint1 *output, uint1 *input, uint4 len){
|
||||
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < len; i++)
|
||||
output[i] = input[i];
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Note: Replace "for loop" with standard memset if possible.
|
||||
void MD5::memset (uint1 *output, uint1 value, uint4 len){
|
||||
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < len; i++)
|
||||
output[i] = value;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ROTATE_LEFT rotates x left n bits.
|
||||
|
||||
inline unsigned int MD5::rotate_left (uint4 x, uint4 n){
|
||||
return (x << n) | (x >> (32-n)) ;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// F, G, H and I are basic MD5 functions.
|
||||
|
||||
inline unsigned int MD5::F (uint4 x, uint4 y, uint4 z){
|
||||
return (x & y) | (~x & z);
|
||||
}
|
||||
|
||||
inline unsigned int MD5::G (uint4 x, uint4 y, uint4 z){
|
||||
return (x & z) | (y & ~z);
|
||||
}
|
||||
|
||||
inline unsigned int MD5::H (uint4 x, uint4 y, uint4 z){
|
||||
return x ^ y ^ z;
|
||||
}
|
||||
|
||||
inline unsigned int MD5::I (uint4 x, uint4 y, uint4 z){
|
||||
return y ^ (x | ~z);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4.
|
||||
// Rotation is separate from addition to prevent recomputation.
|
||||
|
||||
|
||||
inline void MD5::FF(uint4& a, uint4 b, uint4 c, uint4 d, uint4 x,
|
||||
uint4 s, uint4 ac){
|
||||
a += F(b, c, d) + x + ac;
|
||||
a = rotate_left (a, s) +b;
|
||||
}
|
||||
|
||||
inline void MD5::GG(uint4& a, uint4 b, uint4 c, uint4 d, uint4 x,
|
||||
uint4 s, uint4 ac){
|
||||
a += G(b, c, d) + x + ac;
|
||||
a = rotate_left (a, s) +b;
|
||||
}
|
||||
|
||||
inline void MD5::HH(uint4& a, uint4 b, uint4 c, uint4 d, uint4 x,
|
||||
uint4 s, uint4 ac){
|
||||
a += H(b, c, d) + x + ac;
|
||||
a = rotate_left (a, s) +b;
|
||||
}
|
||||
|
||||
inline void MD5::II(uint4& a, uint4 b, uint4 c, uint4 d, uint4 x,
|
||||
uint4 s, uint4 ac){
|
||||
a += I(b, c, d) + x + ac;
|
||||
a = rotate_left (a, s) +b;
|
||||
}
|
||||
+106
@@ -0,0 +1,106 @@
|
||||
// MD5.CC - source code for the C++/object oriented translation and
|
||||
// modification of MD5.
|
||||
|
||||
// Translation and modification (c) 1995 by Mordechai T. Abzug
|
||||
|
||||
// This translation/ modification is provided "as is," without express or
|
||||
// implied warranty of any kind.
|
||||
|
||||
// The translator/ modifier does not claim (1) that MD5 will do what you think
|
||||
// it does; (2) that this translation/ modification is accurate; or (3) that
|
||||
// this software is "merchantible." (Language for this disclaimer partially
|
||||
// copied from the disclaimer below).
|
||||
|
||||
/* based on:
|
||||
|
||||
MD5.H - header file for MD5C.C
|
||||
MDDRIVER.C - test driver for MD2, MD4 and MD5
|
||||
|
||||
Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All
|
||||
rights reserved.
|
||||
|
||||
License to copy and use this software is granted provided that it
|
||||
is identified as the "RSA Data Security, Inc. MD5 Message-Digest
|
||||
Algorithm" in all material mentioning or referencing this software
|
||||
or this function.
|
||||
|
||||
License is also granted to make and use derivative works provided
|
||||
that such works are identified as "derived from the RSA Data
|
||||
Security, Inc. MD5 Message-Digest Algorithm" in all material
|
||||
mentioning or referencing the derived work.
|
||||
|
||||
RSA Data Security, Inc. makes no representations concerning either
|
||||
the merchantability of this software or the suitability of this
|
||||
software for any particular purpose. It is provided "as is"
|
||||
without express or implied warranty of any kind.
|
||||
|
||||
These notices must be retained in any copies of any part of this
|
||||
documentation and/or software.
|
||||
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
//#include <fstream.h>
|
||||
//#include <iostream.h>
|
||||
|
||||
class MD5 {
|
||||
|
||||
public:
|
||||
// methods for controlled operation:
|
||||
MD5 (); // simple initializer
|
||||
void update (unsigned char *input, unsigned int input_length);
|
||||
void update (FILE *file);
|
||||
void finalize ();
|
||||
|
||||
// constructors for special circumstances. All these constructors finalize
|
||||
// the MD5 context.
|
||||
MD5 (unsigned char *string); // digest string, finalize
|
||||
MD5 (FILE *file); // digest file, close, finalize
|
||||
|
||||
// methods to acquire finalized result
|
||||
unsigned char *raw_digest (); // digest as a 16-byte binary array
|
||||
unsigned char *raw_digest(unsigned char buffer[16]);
|
||||
char * hex_digest (); // digest as a 33-byte ascii-hex string
|
||||
char * hex_digest (char buffer[33]); //same as above, passing buffer
|
||||
|
||||
|
||||
|
||||
private:
|
||||
|
||||
// first, some types:
|
||||
typedef unsigned int uint4; // assumes integer is 4 words long
|
||||
typedef unsigned short int uint2; // assumes short integer is 2 words long
|
||||
typedef unsigned char uint1; // assumes char is 1 word long
|
||||
|
||||
// next, the private data:
|
||||
uint4 state[4];
|
||||
uint4 count[2]; // number of *bits*, mod 2^64
|
||||
uint1 buffer[64]; // input buffer
|
||||
uint1 digest[16];
|
||||
uint1 finalized;
|
||||
|
||||
// last, the private methods, mostly static:
|
||||
void init (); // called by all constructors
|
||||
void transform (uint1 *buffer); // does the real update work. Note
|
||||
// that length is implied to be 64.
|
||||
|
||||
static void encode (uint1 *dest, uint4 *src, uint4 length);
|
||||
static void decode (uint4 *dest, uint1 *src, uint4 length);
|
||||
static void memcpy (uint1 *dest, uint1 *src, uint4 length);
|
||||
static void memset (uint1 *start, uint1 val, uint4 length);
|
||||
|
||||
static inline uint4 rotate_left (uint4 x, uint4 n);
|
||||
static inline uint4 F (uint4 x, uint4 y, uint4 z);
|
||||
static inline uint4 G (uint4 x, uint4 y, uint4 z);
|
||||
static inline uint4 H (uint4 x, uint4 y, uint4 z);
|
||||
static inline uint4 I (uint4 x, uint4 y, uint4 z);
|
||||
static inline void FF (uint4& a, uint4 b, uint4 c, uint4 d, uint4 x,
|
||||
uint4 s, uint4 ac);
|
||||
static inline void GG (uint4& a, uint4 b, uint4 c, uint4 d, uint4 x,
|
||||
uint4 s, uint4 ac);
|
||||
static inline void HH (uint4& a, uint4 b, uint4 c, uint4 d, uint4 x,
|
||||
uint4 s, uint4 ac);
|
||||
static inline void II (uint4& a, uint4 b, uint4 c, uint4 d, uint4 x,
|
||||
uint4 s, uint4 ac);
|
||||
|
||||
};
|
||||
@@ -0,0 +1,41 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 9.00
|
||||
# Visual Studio 2005
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "sourcemod_mm", "sourcemod_mm.vcproj", "{E39527CD-7CAB-4420-97CC-DA1B93B260BC}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
CrazyDebug - Episode 1|Win32 = CrazyDebug - Episode 1|Win32
|
||||
CrazyDebug - Old Metamod|Win32 = CrazyDebug - Old Metamod|Win32
|
||||
CrazyDebug - Orange Box|Win32 = CrazyDebug - Orange Box|Win32
|
||||
Debug - Episode 1|Win32 = Debug - Episode 1|Win32
|
||||
Debug - Old Metamod|Win32 = Debug - Old Metamod|Win32
|
||||
Debug - Orange Box|Win32 = Debug - Orange Box|Win32
|
||||
Release - Episode 1|Win32 = Release - Episode 1|Win32
|
||||
Release - Old Metamod|Win32 = Release - Old Metamod|Win32
|
||||
Release - Orange Box|Win32 = Release - Orange Box|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{E39527CD-7CAB-4420-97CC-DA1B93B260BC}.CrazyDebug - Episode 1|Win32.ActiveCfg = CrazyDebug - Episode 1|Win32
|
||||
{E39527CD-7CAB-4420-97CC-DA1B93B260BC}.CrazyDebug - Episode 1|Win32.Build.0 = CrazyDebug - Episode 1|Win32
|
||||
{E39527CD-7CAB-4420-97CC-DA1B93B260BC}.CrazyDebug - Old Metamod|Win32.ActiveCfg = CrazyDebug - Old Metamod|Win32
|
||||
{E39527CD-7CAB-4420-97CC-DA1B93B260BC}.CrazyDebug - Old Metamod|Win32.Build.0 = CrazyDebug - Old Metamod|Win32
|
||||
{E39527CD-7CAB-4420-97CC-DA1B93B260BC}.CrazyDebug - Orange Box|Win32.ActiveCfg = CrazyDebug - Orange Box|Win32
|
||||
{E39527CD-7CAB-4420-97CC-DA1B93B260BC}.CrazyDebug - Orange Box|Win32.Build.0 = CrazyDebug - Orange Box|Win32
|
||||
{E39527CD-7CAB-4420-97CC-DA1B93B260BC}.Debug - Episode 1|Win32.ActiveCfg = Debug - Episode 1|Win32
|
||||
{E39527CD-7CAB-4420-97CC-DA1B93B260BC}.Debug - Episode 1|Win32.Build.0 = Debug - Episode 1|Win32
|
||||
{E39527CD-7CAB-4420-97CC-DA1B93B260BC}.Debug - Old Metamod|Win32.ActiveCfg = Debug - Old Metamod|Win32
|
||||
{E39527CD-7CAB-4420-97CC-DA1B93B260BC}.Debug - Old Metamod|Win32.Build.0 = Debug - Old Metamod|Win32
|
||||
{E39527CD-7CAB-4420-97CC-DA1B93B260BC}.Debug - Orange Box|Win32.ActiveCfg = Debug - Orange Box|Win32
|
||||
{E39527CD-7CAB-4420-97CC-DA1B93B260BC}.Debug - Orange Box|Win32.Build.0 = Debug - Orange Box|Win32
|
||||
{E39527CD-7CAB-4420-97CC-DA1B93B260BC}.Release - Episode 1|Win32.ActiveCfg = Release - Episode 1|Win32
|
||||
{E39527CD-7CAB-4420-97CC-DA1B93B260BC}.Release - Episode 1|Win32.Build.0 = Release - Episode 1|Win32
|
||||
{E39527CD-7CAB-4420-97CC-DA1B93B260BC}.Release - Old Metamod|Win32.ActiveCfg = Release - Old Metamod|Win32
|
||||
{E39527CD-7CAB-4420-97CC-DA1B93B260BC}.Release - Old Metamod|Win32.Build.0 = Release - Old Metamod|Win32
|
||||
{E39527CD-7CAB-4420-97CC-DA1B93B260BC}.Release - Orange Box|Win32.ActiveCfg = Release - Orange Box|Win32
|
||||
{E39527CD-7CAB-4420-97CC-DA1B93B260BC}.Release - Orange Box|Win32.Build.0 = Release - Orange Box|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,43 @@
|
||||
/**
|
||||
* vim: set ts=4 :
|
||||
* =============================================================================
|
||||
* SourceMod
|
||||
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
|
||||
* =============================================================================
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, version 3.0, as published by the
|
||||
* Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* As a special exception, AlliedModders LLC gives you permission to link the
|
||||
* code of this program (as well as its derivative works) to "Half-Life 2," the
|
||||
* "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
|
||||
* by the Valve Corporation. You must obey the GNU General Public License in
|
||||
* all respects for all other code used. Additionally, AlliedModders LLC grants
|
||||
* this exception to all derivative works. AlliedModders LLC defines further
|
||||
* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
|
||||
* or <http://www.sourcemod.net/license.php>.
|
||||
*
|
||||
* Version: $Id$
|
||||
*/
|
||||
|
||||
#include "sm_autonatives.h"
|
||||
#include "PluginSys.h"
|
||||
#include "NativeOwner.h"
|
||||
|
||||
CNativeOwner g_CoreNatives;
|
||||
|
||||
CNativeOwner *g_pCoreNatives = &g_CoreNatives;
|
||||
|
||||
void CoreNativesToAdd::OnSourceModAllInitialized()
|
||||
{
|
||||
g_CoreNatives.AddNatives(m_NativeList);
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/**
|
||||
* vim: set ts=4 :
|
||||
* =============================================================================
|
||||
* SourceMod
|
||||
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
|
||||
* =============================================================================
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, version 3.0, as published by the
|
||||
* Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* As a special exception, AlliedModders LLC gives you permission to link the
|
||||
* code of this program (as well as its derivative works) to "Half-Life 2," the
|
||||
* "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
|
||||
* by the Valve Corporation. You must obey the GNU General Public License in
|
||||
* all respects for all other code used. Additionally, AlliedModders LLC grants
|
||||
* this exception to all derivative works. AlliedModders LLC defines further
|
||||
* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
|
||||
* or <http://www.sourcemod.net/license.php>.
|
||||
*
|
||||
* Version: $Id$
|
||||
*/
|
||||
|
||||
#ifndef _INCLUDE_SOURCEMOD_CORE_AUTONATIVES_H_
|
||||
#define _INCLUDE_SOURCEMOD_CORE_AUTONATIVES_H_
|
||||
|
||||
#include "sm_globals.h"
|
||||
|
||||
#define REGISTER_NATIVES(globname) \
|
||||
extern sp_nativeinfo_t globNatives##globname[]; \
|
||||
CoreNativesToAdd globNativesCls##globname(globNatives##globname); \
|
||||
sp_nativeinfo_t globNatives##globname[] =
|
||||
|
||||
class CoreNativesToAdd : public SMGlobalClass
|
||||
{
|
||||
public:
|
||||
CoreNativesToAdd(sp_nativeinfo_t *pList)
|
||||
: m_NativeList(pList)
|
||||
{
|
||||
}
|
||||
void OnSourceModAllInitialized();
|
||||
sp_nativeinfo_t *m_NativeList;
|
||||
};
|
||||
|
||||
class CNativeOwner;
|
||||
|
||||
extern CNativeOwner *g_pCoreNatives;
|
||||
|
||||
#endif //_INCLUDE_SOURCEMOD_CORE_AUTONATIVES_H_
|
||||
@@ -0,0 +1,96 @@
|
||||
/**
|
||||
* vim: set ts=4 :
|
||||
* =============================================================================
|
||||
* SourceMod
|
||||
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
|
||||
* =============================================================================
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, version 3.0, as published by the
|
||||
* Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* As a special exception, AlliedModders LLC gives you permission to link the
|
||||
* code of this program (as well as its derivative works) to "Half-Life 2," the
|
||||
* "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
|
||||
* by the Valve Corporation. You must obey the GNU General Public License in
|
||||
* all respects for all other code used. Additionally, AlliedModders LLC grants
|
||||
* this exception to all derivative works. AlliedModders LLC defines further
|
||||
* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
|
||||
* or <http://www.sourcemod.net/license.php>.
|
||||
*
|
||||
* Version: $Id$
|
||||
*/
|
||||
|
||||
#include "sm_crc32.h"
|
||||
|
||||
/**
|
||||
* Polynomial: 0x04C11DB7
|
||||
*/
|
||||
const unsigned int CRCTable[256] = {
|
||||
0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
|
||||
0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
|
||||
0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,
|
||||
0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
|
||||
0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
|
||||
0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
|
||||
0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c,
|
||||
0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
|
||||
0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423,
|
||||
0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
|
||||
0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106,
|
||||
0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
|
||||
0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d,
|
||||
0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
|
||||
0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
|
||||
0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
|
||||
0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7,
|
||||
0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
|
||||
0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa,
|
||||
0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
|
||||
0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
|
||||
0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
|
||||
0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84,
|
||||
0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
|
||||
0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
|
||||
0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
|
||||
0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e,
|
||||
0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
|
||||
0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55,
|
||||
0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
|
||||
0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28,
|
||||
0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
|
||||
0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f,
|
||||
0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
|
||||
0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
|
||||
0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
|
||||
0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69,
|
||||
0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
|
||||
0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc,
|
||||
0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
|
||||
0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693,
|
||||
0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
|
||||
0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d};
|
||||
|
||||
unsigned int UTIL_CRC32(const void *pdata, size_t data_length)
|
||||
{
|
||||
unsigned char *data = (unsigned char *)pdata;
|
||||
unsigned int crc = 0xFFFFFFFF;
|
||||
unsigned char c;
|
||||
|
||||
for (size_t i=0; i<data_length; i++, data++)
|
||||
{
|
||||
c = (unsigned char)((crc ^ *data) & 0xFF);
|
||||
crc = CRCTable[c] ^ (crc >> 8);
|
||||
}
|
||||
|
||||
return crc;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
/**
|
||||
* vim: set ts=4 :
|
||||
* =============================================================================
|
||||
* SourceMod
|
||||
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
|
||||
* =============================================================================
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, version 3.0, as published by the
|
||||
* Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* As a special exception, AlliedModders LLC gives you permission to link the
|
||||
* code of this program (as well as its derivative works) to "Half-Life 2," the
|
||||
* "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
|
||||
* by the Valve Corporation. You must obey the GNU General Public License in
|
||||
* all respects for all other code used. Additionally, AlliedModders LLC grants
|
||||
* this exception to all derivative works. AlliedModders LLC defines further
|
||||
* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
|
||||
* or <http://www.sourcemod.net/license.php>.
|
||||
*
|
||||
* Version: $Id$
|
||||
*/
|
||||
|
||||
#ifndef _INCLUDE_SOURCEMOD_CRC32_H_
|
||||
#define _INCLUDE_SOURCEMOD_CRC32_H_
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
unsigned int UTIL_CRC32(const void *data, size_t data_length);
|
||||
|
||||
#endif //_INCLUDE_SOURCEMOD_CRC32_H_
|
||||
|
||||
@@ -0,0 +1,218 @@
|
||||
/**
|
||||
* vim: set ts=4 :
|
||||
* =============================================================================
|
||||
* SourceMod
|
||||
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
|
||||
* =============================================================================
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, version 3.0, as published by the
|
||||
* Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* As a special exception, AlliedModders LLC gives you permission to link the
|
||||
* code of this program (as well as its derivative works) to "Half-Life 2," the
|
||||
* "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
|
||||
* by the Valve Corporation. You must obey the GNU General Public License in
|
||||
* all respects for all other code used. Additionally, AlliedModders LLC grants
|
||||
* this exception to all derivative works. AlliedModders LLC defines further
|
||||
* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
|
||||
* or <http://www.sourcemod.net/license.php>.
|
||||
*
|
||||
* Version: $Id$
|
||||
*/
|
||||
|
||||
#ifndef _INCLUDE_SOURCEMOD_FASTLINK_H_
|
||||
#define _INCLUDE_SOURCEMOD_FASTLINK_H_
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
template <typename T>
|
||||
class FastLink
|
||||
{
|
||||
friend class iterator;
|
||||
public:
|
||||
struct FastLinkNode
|
||||
{
|
||||
unsigned int prev;
|
||||
unsigned int next;
|
||||
unsigned int freeNode;
|
||||
T obj;
|
||||
};
|
||||
public:
|
||||
FastLink(unsigned int maxsize) : m_Size(0), m_FirstLink(0), m_FreeNodes(0), m_LastLink(0), m_UsableIndex(0)
|
||||
{
|
||||
m_MaxSize = maxsize;
|
||||
m_Nodes = new FastLinkNode[m_MaxSize+1];
|
||||
}
|
||||
public:
|
||||
bool push_back(const T & obj)
|
||||
{
|
||||
unsigned int new_node = GetFreeIndex();
|
||||
if (!new_node)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
m_Nodes[new_node].obj = obj;
|
||||
m_Nodes[new_node].next = 0;
|
||||
if (!m_FirstLink)
|
||||
{
|
||||
m_Nodes[new_node].prev = 0;
|
||||
m_FirstLink = new_node;
|
||||
m_LastLink = new_node;
|
||||
} else {
|
||||
m_Nodes[new_node].prev = m_LastLink;
|
||||
m_Nodes[m_LastLink].next = new_node;
|
||||
m_LastLink = new_node;
|
||||
}
|
||||
m_Size++;
|
||||
return true;
|
||||
}
|
||||
size_t size() const
|
||||
{
|
||||
return m_Size;
|
||||
}
|
||||
private:
|
||||
unsigned int GetFreeIndex()
|
||||
{
|
||||
if (m_FreeNodes)
|
||||
{
|
||||
return m_Nodes[m_FreeNodes--].freeNode;
|
||||
} else {
|
||||
if (m_UsableIndex >= m_MaxSize)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
m_UsableIndex++;
|
||||
return m_UsableIndex;
|
||||
}
|
||||
}
|
||||
public:
|
||||
class iterator
|
||||
{
|
||||
friend class FastLink;
|
||||
public:
|
||||
iterator()
|
||||
{
|
||||
link = NULL;
|
||||
position = 0;
|
||||
}
|
||||
iterator(const FastLink *_link, unsigned int _position)
|
||||
{
|
||||
link = _link;
|
||||
position = _position;
|
||||
}
|
||||
public:
|
||||
bool operator ==(const iterator &where) const
|
||||
{
|
||||
return (link == where.link && position == where.position);
|
||||
}
|
||||
bool operator !=(const iterator &where) const
|
||||
{
|
||||
return (link != where.link || position != where.position);
|
||||
}
|
||||
T & operator *()
|
||||
{
|
||||
return link->m_Nodes[position].obj;
|
||||
}
|
||||
const T & operator *() const
|
||||
{
|
||||
return link->m_Nodes[position].obj;
|
||||
}
|
||||
iterator & operator++()
|
||||
{
|
||||
position = link->m_Nodes[position].next;
|
||||
return *this;
|
||||
}
|
||||
private:
|
||||
const FastLink *link;
|
||||
unsigned int position;
|
||||
};
|
||||
public:
|
||||
iterator begin() const
|
||||
{
|
||||
return iterator(this, m_FirstLink);
|
||||
}
|
||||
iterator end() const
|
||||
{
|
||||
return iterator(this, 0);
|
||||
}
|
||||
void remove(const T & obj)
|
||||
{
|
||||
for (iterator iter=begin(); iter!=end(); ++iter)
|
||||
{
|
||||
if ((*iter) == obj)
|
||||
{
|
||||
erase(iter);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
iterator erase(const iterator &where)
|
||||
{
|
||||
/* Whoa, this better be right! */
|
||||
assert(where.link == this);
|
||||
|
||||
iterator iter(where.link, where.position);
|
||||
++iter;
|
||||
|
||||
unsigned int index = where.position;
|
||||
|
||||
/* Each case is different, we have no sentinel.
|
||||
* CASE: We're the HEAD AND the TAIL */
|
||||
if (index == m_FirstLink
|
||||
&& index == m_LastLink)
|
||||
{
|
||||
m_FirstLink = 0;
|
||||
m_LastLink = 0;
|
||||
}
|
||||
/* We're the HEAD */
|
||||
else if (index == m_FirstLink)
|
||||
{
|
||||
m_FirstLink = m_Nodes[index].next;
|
||||
m_Nodes[m_FirstLink].prev = 0;
|
||||
}
|
||||
/* We're the TAIL */
|
||||
else if (index == m_LastLink)
|
||||
{
|
||||
m_LastLink = m_Nodes[index].prev;
|
||||
m_Nodes[m_LastLink].next = 0;
|
||||
}
|
||||
/* We're in the middle! */
|
||||
else
|
||||
{
|
||||
/* Patch forward reference */
|
||||
m_Nodes[m_Nodes[index].next].prev = m_Nodes[index].prev;
|
||||
/* Patch backward reference */
|
||||
m_Nodes[m_Nodes[index].prev].next = m_Nodes[index].next;
|
||||
}
|
||||
|
||||
/* Add us to the free list */
|
||||
m_Nodes[++m_FreeNodes].freeNode = index;
|
||||
|
||||
m_Size--;
|
||||
|
||||
return iter;
|
||||
}
|
||||
#if defined MENU_DEBUG
|
||||
public:
|
||||
#else
|
||||
private:
|
||||
#endif
|
||||
size_t m_Size;
|
||||
unsigned int m_FirstLink;
|
||||
unsigned int m_FreeNodes;
|
||||
unsigned int m_LastLink;
|
||||
unsigned int m_MaxSize;
|
||||
unsigned int m_UsableIndex;
|
||||
FastLinkNode *m_Nodes;
|
||||
};
|
||||
|
||||
#endif //_INCLUDE_SOURCEMOD_FASTLINK_H_
|
||||
@@ -0,0 +1,181 @@
|
||||
/**
|
||||
* vim: set ts=4 :
|
||||
* =============================================================================
|
||||
* SourceMod
|
||||
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
|
||||
* =============================================================================
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, version 3.0, as published by the
|
||||
* Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* As a special exception, AlliedModders LLC gives you permission to link the
|
||||
* code of this program (as well as its derivative works) to "Half-Life 2," the
|
||||
* "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
|
||||
* by the Valve Corporation. You must obey the GNU General Public License in
|
||||
* all respects for all other code used. Additionally, AlliedModders LLC grants
|
||||
* this exception to all derivative works. AlliedModders LLC defines further
|
||||
* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
|
||||
* or <http://www.sourcemod.net/license.php>.
|
||||
*
|
||||
* Version: $Id$
|
||||
*/
|
||||
|
||||
#ifndef _INCLUDE_SOURCEMOD_GLOBALS_H_
|
||||
#define _INCLUDE_SOURCEMOD_GLOBALS_H_
|
||||
|
||||
/**
|
||||
* @file Contains global headers that most files in SourceMod will need.
|
||||
*/
|
||||
|
||||
#include <sp_vm_types.h>
|
||||
#include <sp_vm_api.h>
|
||||
#include "sm_platform.h"
|
||||
#include <IShareSys.h>
|
||||
|
||||
using namespace SourcePawn;
|
||||
using namespace SourceMod;
|
||||
|
||||
class IServerPluginCallbacks;
|
||||
|
||||
/**
|
||||
* @brief Lists result codes possible from attempting to set a core configuration option.
|
||||
*/
|
||||
enum ConfigResult
|
||||
{
|
||||
ConfigResult_Accept = 0, /**< Config option was successfully set */
|
||||
ConfigResult_Reject = 1, /**< Config option was given an invalid value and was rejected */
|
||||
ConfigResult_Ignore = 2 /**< Config option was invalid, but ignored */
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Lists possible sources of a config option change
|
||||
*/
|
||||
enum ConfigSource
|
||||
{
|
||||
ConfigSource_File = 0, /**< Config option was set from config file (core.cfg) */
|
||||
ConfigSource_Console = 1, /**< Config option was set from console command (sm config) */
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Any class deriving from this will be automatically initiated/shutdown by SourceMod
|
||||
*/
|
||||
class SMGlobalClass
|
||||
{
|
||||
friend class SourceMod_Core;
|
||||
friend class SourceModBase;
|
||||
friend class CoreConfig;
|
||||
friend class CExtensionManager;
|
||||
public:
|
||||
SMGlobalClass();
|
||||
public:
|
||||
/**
|
||||
* @brief Called when SourceMod is initially loading
|
||||
*/
|
||||
virtual void OnSourceModStartup(bool late)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Called after all global classes have been started up
|
||||
*/
|
||||
virtual void OnSourceModAllInitialized()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Called after all global classes have initialized
|
||||
*/
|
||||
virtual void OnSourceModAllInitialized_Post()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Called when SourceMod is shutting down
|
||||
*/
|
||||
virtual void OnSourceModShutdown()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Called after SourceMod is completely shut down
|
||||
*/
|
||||
virtual void OnSourceModAllShutdown()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Called when a core config option is changed.
|
||||
* @note This is called once BEFORE OnSourceModStartup() when SourceMod is loading
|
||||
* @note It can then be called again when the 'sm config' command is used
|
||||
*/
|
||||
virtual ConfigResult OnSourceModConfigChanged(const char *key,
|
||||
const char *value,
|
||||
ConfigSource source,
|
||||
char *error,
|
||||
size_t maxlength)
|
||||
{
|
||||
return ConfigResult_Ignore;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Called when the level changes.
|
||||
*/
|
||||
virtual void OnSourceModLevelChange(const char *mapName)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Called when the level ends.
|
||||
*/
|
||||
virtual void OnSourceModLevelEnd()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Called after plugins are loaded on mapchange.
|
||||
*/
|
||||
virtual void OnSourceModPluginsLoaded()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Called when SourceMod receives a pointer to IServerPluginCallbacks from SourceMM
|
||||
*/
|
||||
virtual void OnSourceModVSPReceived()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Called once all MM:S plugins are loaded.
|
||||
*/
|
||||
virtual void OnSourceModGameInitialized()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Called when an identity is dropped (right now, extensions only)
|
||||
*/
|
||||
virtual void OnSourceModIdentityDropped(IdentityToken_t *pToken)
|
||||
{
|
||||
}
|
||||
private:
|
||||
SMGlobalClass *m_pGlobalClassNext;
|
||||
static SMGlobalClass *head;
|
||||
};
|
||||
|
||||
extern ISourcePawnEngine *g_pSourcePawn;
|
||||
extern IVirtualMachine *g_pVM;
|
||||
extern IdentityToken_t *g_pCoreIdent;
|
||||
|
||||
#include "sm_autonatives.h"
|
||||
|
||||
#endif //_INCLUDE_SOURCEMOD_GLOBALS_H_
|
||||
@@ -0,0 +1,112 @@
|
||||
/**
|
||||
* vim: set ts=4 :
|
||||
* =============================================================================
|
||||
* SourceMod
|
||||
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
|
||||
* =============================================================================
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, version 3.0, as published by the
|
||||
* Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* As a special exception, AlliedModders LLC gives you permission to link the
|
||||
* code of this program (as well as its derivative works) to "Half-Life 2," the
|
||||
* "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
|
||||
* by the Valve Corporation. You must obey the GNU General Public License in
|
||||
* all respects for all other code used. Additionally, AlliedModders LLC grants
|
||||
* this exception to all derivative works. AlliedModders LLC defines further
|
||||
* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
|
||||
* or <http://www.sourcemod.net/license.php>.
|
||||
*
|
||||
* Version: $Id$
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <malloc.h>
|
||||
#include "sm_memtable.h"
|
||||
|
||||
BaseMemTable::BaseMemTable(unsigned int init_size)
|
||||
{
|
||||
membase = (unsigned char *)malloc(init_size);
|
||||
size = init_size;
|
||||
tail = 0;
|
||||
}
|
||||
|
||||
BaseMemTable::~BaseMemTable()
|
||||
{
|
||||
free(membase);
|
||||
membase = NULL;
|
||||
}
|
||||
|
||||
int BaseMemTable::CreateMem(unsigned int addsize, void **addr)
|
||||
{
|
||||
int idx = (int)tail;
|
||||
|
||||
while (tail + addsize >= size)
|
||||
{
|
||||
size *= 2;
|
||||
membase = (unsigned char *)realloc(membase, size);
|
||||
}
|
||||
|
||||
tail += addsize;
|
||||
|
||||
if (addr)
|
||||
{
|
||||
*addr = (void *)&membase[idx];
|
||||
}
|
||||
|
||||
return idx;
|
||||
}
|
||||
|
||||
void *BaseMemTable::GetAddress(int index)
|
||||
{
|
||||
if (index < 0 || (unsigned int)index >= tail)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return &membase[index];
|
||||
}
|
||||
|
||||
void BaseMemTable::Reset()
|
||||
{
|
||||
tail = 0;
|
||||
}
|
||||
|
||||
BaseStringTable::BaseStringTable(unsigned int init_size) : m_table(init_size)
|
||||
{
|
||||
}
|
||||
|
||||
BaseStringTable::~BaseStringTable()
|
||||
{
|
||||
}
|
||||
|
||||
int BaseStringTable::AddString(const char *string)
|
||||
{
|
||||
size_t len = strlen(string) + 1;
|
||||
int idx;
|
||||
char *addr;
|
||||
|
||||
idx = m_table.CreateMem(len, (void **)&addr);
|
||||
strcpy(addr, string);
|
||||
|
||||
return idx;
|
||||
}
|
||||
|
||||
/*const char *BaseStringTable::GetString(int str)
|
||||
{
|
||||
return (const char *)m_table.GetAddress(str);
|
||||
}*/
|
||||
|
||||
void BaseStringTable::Reset()
|
||||
{
|
||||
m_table.Reset();
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
/**
|
||||
* vim: set ts=4 :
|
||||
* =============================================================================
|
||||
* SourceMod
|
||||
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
|
||||
* =============================================================================
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, version 3.0, as published by the
|
||||
* Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* As a special exception, AlliedModders LLC gives you permission to link the
|
||||
* code of this program (as well as its derivative works) to "Half-Life 2," the
|
||||
* "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
|
||||
* by the Valve Corporation. You must obey the GNU General Public License in
|
||||
* all respects for all other code used. Additionally, AlliedModders LLC grants
|
||||
* this exception to all derivative works. AlliedModders LLC defines further
|
||||
* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
|
||||
* or <http://www.sourcemod.net/license.php>.
|
||||
*
|
||||
* Version: $Id$
|
||||
*/
|
||||
|
||||
#ifndef _INCLUDE_SOURCEMOD_CORE_STRINGTABLE_H_
|
||||
#define _INCLUDE_SOURCEMOD_CORE_STRINGTABLE_H_
|
||||
|
||||
class BaseMemTable
|
||||
{
|
||||
public:
|
||||
BaseMemTable(unsigned int init_size);
|
||||
~BaseMemTable();
|
||||
public:
|
||||
/**
|
||||
* Allocates 'size' bytes of memory.
|
||||
* Optionally outputs the address through 'addr'.
|
||||
* Returns an index >= 0 on success, < 0 on failure.
|
||||
*/
|
||||
int CreateMem(unsigned int size, void **addr);
|
||||
|
||||
/**
|
||||
* Given an index into the memory table, returns its address.
|
||||
* Returns NULL if invalid.
|
||||
*/
|
||||
void *GetAddress(int index);
|
||||
|
||||
/**
|
||||
* Scraps the memory table. For caching purposes, the memory
|
||||
* is not freed, however subsequent calls to CreateMem() will
|
||||
* begin at the first index again.
|
||||
*/
|
||||
void Reset();
|
||||
|
||||
inline unsigned int GetMemUsage()
|
||||
{
|
||||
return size;
|
||||
}
|
||||
|
||||
inline unsigned int GetActualMemUsed()
|
||||
{
|
||||
return tail;
|
||||
}
|
||||
|
||||
private:
|
||||
unsigned char *membase;
|
||||
unsigned int size;
|
||||
unsigned int tail;
|
||||
};
|
||||
|
||||
class BaseStringTable
|
||||
{
|
||||
public:
|
||||
BaseStringTable(unsigned int init_size);
|
||||
~BaseStringTable();
|
||||
public:
|
||||
/**
|
||||
* Adds a string to the string table and returns its index.
|
||||
*/
|
||||
int AddString(const char *string);
|
||||
|
||||
/**
|
||||
* Given an index into the string table, returns the associated string.
|
||||
*/
|
||||
inline const char *GetString(int str)
|
||||
{
|
||||
return (const char *)m_table.GetAddress(str);
|
||||
}
|
||||
|
||||
/**
|
||||
* Scraps the string table. For caching purposes, the memory
|
||||
* is not freed, however subsequent calls to AddString() will
|
||||
* begin at the first index again.
|
||||
*/
|
||||
void Reset();
|
||||
|
||||
/**
|
||||
* Returns the parent BaseMemTable that this string table uses.
|
||||
*/
|
||||
inline BaseMemTable *GetMemTable()
|
||||
{
|
||||
return &m_table;
|
||||
}
|
||||
private:
|
||||
BaseMemTable m_table;
|
||||
};
|
||||
|
||||
#endif //_INCLUDE_SOURCEMOD_CORE_STRINGTABLE_H_
|
||||
+333
@@ -0,0 +1,333 @@
|
||||
/**
|
||||
* vim: set ts=4 :
|
||||
* =============================================================================
|
||||
* SourceMod
|
||||
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
|
||||
* =============================================================================
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, version 3.0, as published by the
|
||||
* Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* As a special exception, AlliedModders LLC gives you permission to link the
|
||||
* code of this program (as well as its derivative works) to "Half-Life 2," the
|
||||
* "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
|
||||
* by the Valve Corporation. You must obey the GNU General Public License in
|
||||
* all respects for all other code used. Additionally, AlliedModders LLC grants
|
||||
* this exception to all derivative works. AlliedModders LLC defines further
|
||||
* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
|
||||
* or <http://www.sourcemod.net/license.php>.
|
||||
*
|
||||
* Version: $Id$
|
||||
*/
|
||||
|
||||
#ifndef _INCLUDE_SM_QUEUE_H
|
||||
#define _INCLUDE_SM_QUEUE_H
|
||||
|
||||
#include <new>
|
||||
#include <malloc.h>
|
||||
#include <sh_stack.h>
|
||||
|
||||
using namespace SourceHook;
|
||||
|
||||
/*
|
||||
A circular, doubly-linked List with one sentinel node
|
||||
|
||||
Empty:
|
||||
m_Head = sentinel
|
||||
m_Head->next = m_Head;
|
||||
m_Head->prev = m_Head;
|
||||
One element:
|
||||
m_Head = sentinel
|
||||
m_Head->next = node1
|
||||
m_Head->prev = node1
|
||||
node1->next = m_Head
|
||||
node1->prev = m_Head
|
||||
Two elements:
|
||||
m_Head = sentinel
|
||||
m_Head->next = node1
|
||||
m_Head->prev = node2
|
||||
node1->next = node2
|
||||
node1->prev = m_Head
|
||||
node2->next = m_Head
|
||||
node2->prev = node1
|
||||
*/
|
||||
template <class T>
|
||||
class Queue
|
||||
{
|
||||
public:
|
||||
class iterator;
|
||||
friend class iterator;
|
||||
class QueueNode
|
||||
{
|
||||
public:
|
||||
T obj;
|
||||
QueueNode *next;
|
||||
QueueNode *prev;
|
||||
};
|
||||
private:
|
||||
// Initializes the sentinel node.
|
||||
QueueNode *_Initialize()
|
||||
{
|
||||
QueueNode *n = (QueueNode *)malloc(sizeof(QueueNode));
|
||||
n->next = n;
|
||||
n->prev = n;
|
||||
return n;
|
||||
}
|
||||
public:
|
||||
Queue() : m_Head(_Initialize()), m_Size(0)
|
||||
{
|
||||
}
|
||||
|
||||
Queue(const Queue &src) : m_Head(_Initialize()), m_Size(0)
|
||||
{
|
||||
iterator iter;
|
||||
for (iter=src.begin(); iter!=src.end(); iter++)
|
||||
{
|
||||
push_back( (*iter) );
|
||||
}
|
||||
}
|
||||
|
||||
~Queue()
|
||||
{
|
||||
clear();
|
||||
|
||||
// Don't forget to free the sentinel
|
||||
if (m_Head)
|
||||
{
|
||||
free(m_Head);
|
||||
m_Head = NULL;
|
||||
}
|
||||
|
||||
while (!m_FreeNodes.empty())
|
||||
{
|
||||
free(m_FreeNodes.front());
|
||||
m_FreeNodes.pop();
|
||||
}
|
||||
}
|
||||
|
||||
void push(const T &obj)
|
||||
{
|
||||
QueueNode *node;
|
||||
|
||||
if (m_FreeNodes.empty())
|
||||
{
|
||||
node = (QueueNode *)malloc(sizeof(QueueNode));
|
||||
} else {
|
||||
node = m_FreeNodes.front();
|
||||
m_FreeNodes.pop();
|
||||
}
|
||||
|
||||
/* Copy the object */
|
||||
new (&node->obj) T(obj);
|
||||
|
||||
/* Install into the Queue */
|
||||
node->prev = m_Head->prev;
|
||||
node->next = m_Head;
|
||||
m_Head->prev->next = node;
|
||||
m_Head->prev = node;
|
||||
|
||||
m_Size++;
|
||||
}
|
||||
|
||||
size_t size() const
|
||||
{
|
||||
return m_Size;
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
QueueNode *node = m_Head->next;
|
||||
QueueNode *temp;
|
||||
m_Head->next = m_Head;
|
||||
m_Head->prev = m_Head;
|
||||
|
||||
// Iterate through the nodes until we find g_Head (the sentinel) again
|
||||
while (node != m_Head)
|
||||
{
|
||||
temp = node->next;
|
||||
node->obj.~T();
|
||||
m_FreeNodes.push(node);
|
||||
node = temp;
|
||||
}
|
||||
m_Size = 0;
|
||||
}
|
||||
|
||||
bool empty() const
|
||||
{
|
||||
return (m_Size == 0);
|
||||
}
|
||||
|
||||
private:
|
||||
QueueNode *m_Head;
|
||||
size_t m_Size;
|
||||
CStack<QueueNode *> m_FreeNodes;
|
||||
public:
|
||||
class iterator
|
||||
{
|
||||
friend class Queue;
|
||||
public:
|
||||
iterator()
|
||||
{
|
||||
m_This = NULL;
|
||||
}
|
||||
iterator(const Queue &src)
|
||||
{
|
||||
m_This = src.m_Head;
|
||||
}
|
||||
iterator(QueueNode *n) : m_This(n)
|
||||
{
|
||||
}
|
||||
iterator(const iterator &where)
|
||||
{
|
||||
m_This = where.m_This;
|
||||
}
|
||||
//pre decrement
|
||||
iterator & operator--()
|
||||
{
|
||||
if (m_This)
|
||||
m_This = m_This->prev;
|
||||
return *this;
|
||||
}
|
||||
//post decrement
|
||||
iterator operator--(int)
|
||||
{
|
||||
iterator old(*this);
|
||||
if (m_This)
|
||||
m_This = m_This->prev;
|
||||
return old;
|
||||
}
|
||||
|
||||
//pre increment
|
||||
iterator & operator++()
|
||||
{
|
||||
if (m_This)
|
||||
m_This = m_This->next;
|
||||
return *this;
|
||||
}
|
||||
//post increment
|
||||
iterator operator++(int)
|
||||
{
|
||||
iterator old(*this);
|
||||
if (m_This)
|
||||
m_This = m_This->next;
|
||||
return old;
|
||||
}
|
||||
|
||||
const T & operator * () const
|
||||
{
|
||||
return m_This->obj;
|
||||
}
|
||||
T & operator * ()
|
||||
{
|
||||
return m_This->obj;
|
||||
}
|
||||
|
||||
T * operator -> ()
|
||||
{
|
||||
return &(m_This->obj);
|
||||
}
|
||||
const T * operator -> () const
|
||||
{
|
||||
return &(m_This->obj);
|
||||
}
|
||||
|
||||
bool operator != (const iterator &where) const
|
||||
{
|
||||
return (m_This != where.m_This);
|
||||
}
|
||||
bool operator ==(const iterator &where) const
|
||||
{
|
||||
return (m_This == where.m_This);
|
||||
}
|
||||
private:
|
||||
QueueNode *m_This;
|
||||
};
|
||||
public:
|
||||
iterator begin() const
|
||||
{
|
||||
return iterator(m_Head->next);
|
||||
}
|
||||
|
||||
iterator end() const
|
||||
{
|
||||
return iterator(m_Head);
|
||||
}
|
||||
|
||||
iterator erase(iterator &where)
|
||||
{
|
||||
QueueNode *pNode = where.m_This;
|
||||
iterator iter(where);
|
||||
iter++;
|
||||
|
||||
|
||||
// Works for all cases: empty Queue, erasing first element, erasing tail, erasing in the middle...
|
||||
pNode->prev->next = pNode->next;
|
||||
pNode->next->prev = pNode->prev;
|
||||
|
||||
pNode->obj.~T();
|
||||
m_FreeNodes.push(pNode);
|
||||
m_Size--;
|
||||
|
||||
return iter;
|
||||
}
|
||||
public:
|
||||
void remove(const T & obj)
|
||||
{
|
||||
iterator b;
|
||||
for (b=begin(); b!=end(); b++)
|
||||
{
|
||||
if ( (*b) == obj )
|
||||
{
|
||||
erase( b );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
template <typename U>
|
||||
iterator find(const U & equ) const
|
||||
{
|
||||
iterator iter;
|
||||
for (iter=begin(); iter!=end(); iter++)
|
||||
{
|
||||
if ( (*iter) == equ )
|
||||
{
|
||||
return iter;
|
||||
}
|
||||
}
|
||||
return end();
|
||||
}
|
||||
Queue & operator =(const Queue &src)
|
||||
{
|
||||
clear();
|
||||
iterator iter;
|
||||
for (iter=src.begin(); iter!=src.end(); iter++)
|
||||
{
|
||||
push_back( (*iter) );
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
public:
|
||||
T & first() const
|
||||
{
|
||||
iterator i = begin();
|
||||
|
||||
return (*i);
|
||||
}
|
||||
|
||||
void pop()
|
||||
{
|
||||
iterator iter = begin();
|
||||
erase(iter);
|
||||
}
|
||||
};
|
||||
|
||||
#endif //_INCLUDE_SM_QUEUE_H
|
||||
@@ -0,0 +1,66 @@
|
||||
/**
|
||||
* vim: set ts=4 :
|
||||
* =============================================================================
|
||||
* SourceMod
|
||||
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
|
||||
* =============================================================================
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, version 3.0, as published by the
|
||||
* Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* As a special exception, AlliedModders LLC gives you permission to link the
|
||||
* code of this program (as well as its derivative works) to "Half-Life 2," the
|
||||
* "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
|
||||
* by the Valve Corporation. You must obey the GNU General Public License in
|
||||
* all respects for all other code used. Additionally, AlliedModders LLC grants
|
||||
* this exception to all derivative works. AlliedModders LLC defines further
|
||||
* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
|
||||
* or <http://www.sourcemod.net/license.php>.
|
||||
*
|
||||
* Version: $Id$
|
||||
*/
|
||||
|
||||
#include "sm_queue.h"
|
||||
#include <IDBDriver.h>
|
||||
|
||||
template <class T>
|
||||
class PrioQueue
|
||||
{
|
||||
private:
|
||||
Queue<T> m_HighQueue;
|
||||
Queue<T> m_NormalQueue;
|
||||
Queue<T> m_LowQueue;
|
||||
public:
|
||||
inline Queue<T> & GetQueue(PrioQueueLevel level)
|
||||
{
|
||||
if (level == PrioQueue_High)
|
||||
{
|
||||
return m_HighQueue;
|
||||
} else if (level == PrioQueue_Normal) {
|
||||
return m_NormalQueue;
|
||||
} else {
|
||||
return m_LowQueue;
|
||||
}
|
||||
}
|
||||
inline Queue<T> & GetLikelyQueue()
|
||||
{
|
||||
if (!m_HighQueue.empty())
|
||||
{
|
||||
return m_HighQueue;
|
||||
}
|
||||
if (!m_NormalQueue.empty())
|
||||
{
|
||||
return m_NormalQueue;
|
||||
}
|
||||
return m_LowQueue;
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,358 @@
|
||||
/**
|
||||
* vim: set ts=4 :
|
||||
* =============================================================================
|
||||
* SourceMod
|
||||
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
|
||||
* =============================================================================
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, version 3.0, as published by the
|
||||
* Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* As a special exception, AlliedModders LLC gives you permission to link the
|
||||
* code of this program (as well as its derivative works) to "Half-Life 2," the
|
||||
* "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
|
||||
* by the Valve Corporation. You must obey the GNU General Public License in
|
||||
* all respects for all other code used. Additionally, AlliedModders LLC grants
|
||||
* this exception to all derivative works. AlliedModders LLC defines further
|
||||
* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
|
||||
* or <http://www.sourcemod.net/license.php>.
|
||||
*
|
||||
* Version: $Id$
|
||||
*/
|
||||
|
||||
#include "sm_srvcmds.h"
|
||||
#include "sm_version.h"
|
||||
#include "sm_stringutil.h"
|
||||
#include "HandleSys.h"
|
||||
#include "CoreConfig.h"
|
||||
#include "ConVarManager.h"
|
||||
#include "ShareSys.h"
|
||||
|
||||
RootConsoleMenu g_RootMenu;
|
||||
|
||||
ConVar sourcemod_version("sourcemod_version", SVN_FULL_VERSION, FCVAR_SPONLY|FCVAR_REPLICATED|FCVAR_NOTIFY, "SourceMod Version");
|
||||
|
||||
RootConsoleMenu::RootConsoleMenu()
|
||||
{
|
||||
m_pCommands = sm_trie_create();
|
||||
m_CfgExecDone = false;
|
||||
}
|
||||
|
||||
RootConsoleMenu::~RootConsoleMenu()
|
||||
{
|
||||
sm_trie_destroy(m_pCommands);
|
||||
|
||||
List<ConsoleEntry *>::iterator iter;
|
||||
for (iter=m_Menu.begin(); iter!=m_Menu.end(); iter++)
|
||||
{
|
||||
delete (*iter);
|
||||
}
|
||||
m_Menu.clear();
|
||||
}
|
||||
|
||||
void RootConsoleMenu::OnSourceModStartup(bool late)
|
||||
{
|
||||
#if defined ORANGEBOX_BUILD
|
||||
g_pCVar = icvar;
|
||||
#endif
|
||||
CONVAR_REGISTER(this);
|
||||
AddRootConsoleCommand("version", "Display version information", this);
|
||||
AddRootConsoleCommand("credits", "Display credits listing", this);
|
||||
}
|
||||
|
||||
void RootConsoleMenu::OnSourceModAllInitialized()
|
||||
{
|
||||
g_ShareSys.AddInterface(NULL, this);
|
||||
}
|
||||
|
||||
void RootConsoleMenu::OnSourceModShutdown()
|
||||
{
|
||||
RemoveRootConsoleCommand("credits", this);
|
||||
RemoveRootConsoleCommand("version", this);
|
||||
}
|
||||
|
||||
bool RootConsoleMenu::RegisterConCommandBase(ConCommandBase *pCommand)
|
||||
{
|
||||
META_REGCVAR(pCommand);
|
||||
|
||||
/* Override values of convars created by SourceMod convar manager if specified on command line */
|
||||
const char *cmdLineValue = icvar->GetCommandLineValue(pCommand->GetName());
|
||||
if (cmdLineValue && !pCommand->IsCommand())
|
||||
{
|
||||
static_cast<ConVar *>(pCommand)->SetValue(cmdLineValue);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void RootConsoleMenu::ConsolePrint(const char *fmt, ...)
|
||||
{
|
||||
char buffer[512];
|
||||
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
size_t len = vsnprintf(buffer, sizeof(buffer), fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
if (len >= sizeof(buffer) - 1)
|
||||
{
|
||||
buffer[510] = '\n';
|
||||
buffer[511] = '\0';
|
||||
} else {
|
||||
buffer[len++] = '\n';
|
||||
buffer[len] = '\0';
|
||||
}
|
||||
|
||||
META_CONPRINT(buffer);
|
||||
}
|
||||
|
||||
bool RootConsoleMenu::AddRootConsoleCommand(const char *cmd, const char *text, IRootConsoleCommand *pHandler)
|
||||
{
|
||||
if (sm_trie_retrieve(m_pCommands, cmd, NULL))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
sm_trie_insert(m_pCommands, cmd, pHandler);
|
||||
|
||||
/* Sort this into the menu */
|
||||
List<ConsoleEntry *>::iterator iter = m_Menu.begin();
|
||||
ConsoleEntry *pEntry;
|
||||
bool inserted = false;
|
||||
while (iter != m_Menu.end())
|
||||
{
|
||||
pEntry = (*iter);
|
||||
if (strcmp(cmd, pEntry->command.c_str()) < 0)
|
||||
{
|
||||
ConsoleEntry *pNew = new ConsoleEntry;
|
||||
pNew->command.assign(cmd);
|
||||
pNew->description.assign(text);
|
||||
m_Menu.insert(iter, pNew);
|
||||
inserted = true;
|
||||
break;
|
||||
}
|
||||
iter++;
|
||||
}
|
||||
|
||||
if (!inserted)
|
||||
{
|
||||
ConsoleEntry *pNew = new ConsoleEntry;
|
||||
pNew->command.assign(cmd);
|
||||
pNew->description.assign(text);
|
||||
m_Menu.push_back(pNew);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RootConsoleMenu::RemoveRootConsoleCommand(const char *cmd, IRootConsoleCommand *pHandler)
|
||||
{
|
||||
/* Sanity tests */
|
||||
IRootConsoleCommand *object;
|
||||
if (sm_trie_retrieve(m_pCommands, cmd, (void **)&object))
|
||||
{
|
||||
if (object != pHandler)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
sm_trie_delete(m_pCommands, cmd);
|
||||
|
||||
List<ConsoleEntry *>::iterator iter;
|
||||
ConsoleEntry *pEntry;
|
||||
for (iter=m_Menu.begin(); iter!=m_Menu.end(); iter++)
|
||||
{
|
||||
pEntry = (*iter);
|
||||
if (pEntry->command.compare(cmd) == 0)
|
||||
{
|
||||
delete pEntry;
|
||||
m_Menu.erase(iter);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void RootConsoleMenu::DrawGenericOption(const char *cmd, const char *text)
|
||||
{
|
||||
char buffer[255];
|
||||
size_t len, cmdlen = strlen(cmd);
|
||||
|
||||
len = UTIL_Format(buffer, sizeof(buffer), " %s", cmd);
|
||||
if (cmdlen < 16)
|
||||
{
|
||||
size_t num = 16 - cmdlen;
|
||||
for (size_t i = 0; i < num; i++)
|
||||
{
|
||||
buffer[len++] = ' ';
|
||||
}
|
||||
len += snprintf(&buffer[len], sizeof(buffer) - len, " - %s", text);
|
||||
ConsolePrint("%s", buffer);
|
||||
}
|
||||
}
|
||||
|
||||
const char *RootConsoleMenu::GetInterfaceName()
|
||||
{
|
||||
return SMINTERFACE_ROOTCONSOLE_NAME;
|
||||
}
|
||||
|
||||
unsigned int RootConsoleMenu::GetInterfaceVersion()
|
||||
{
|
||||
return SMINTERFACE_ROOTCONSOLE_VERSION;
|
||||
}
|
||||
|
||||
void RootConsoleMenu::GotRootCmd(const CCommand &cmd)
|
||||
{
|
||||
unsigned int argnum = cmd.ArgC();
|
||||
|
||||
if (argnum >= 2)
|
||||
{
|
||||
const char *cmdname = cmd.Arg(1);
|
||||
if (strcmp(cmdname, "internal") == 0)
|
||||
{
|
||||
if (argnum >= 3)
|
||||
{
|
||||
const char *arg = cmd.Arg(2);
|
||||
if (strcmp(arg, "1") == 0)
|
||||
{
|
||||
SM_ConfigsExecuted_Global();
|
||||
}
|
||||
else if (strcmp(arg, "2") == 0)
|
||||
{
|
||||
if (argnum >= 4)
|
||||
{
|
||||
SM_ConfigsExecuted_Plugin(atoi(cmd.Arg(3)));
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
IRootConsoleCommand *pHandler;
|
||||
if (sm_trie_retrieve(m_pCommands, cmdname, (void **)&pHandler))
|
||||
{
|
||||
pHandler->OnRootConsoleCommand(cmdname, cmd);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
ConsolePrint("SourceMod Menu:");
|
||||
ConsolePrint("Usage: sm <command> [arguments]");
|
||||
|
||||
List<ConsoleEntry *>::iterator iter;
|
||||
ConsoleEntry *pEntry;
|
||||
for (iter=m_Menu.begin(); iter!=m_Menu.end(); iter++)
|
||||
{
|
||||
pEntry = (*iter);
|
||||
DrawGenericOption(pEntry->command.c_str(), pEntry->description.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void RootConsoleMenu::OnRootConsoleCommand(const char *cmdname, const CCommand &command)
|
||||
{
|
||||
if (strcmp(cmdname, "credits") == 0)
|
||||
{
|
||||
ConsolePrint(" SourceMod was developed by AlliedModders, LLC.");
|
||||
ConsolePrint(" Development would not have been possible without the following people:");
|
||||
ConsolePrint(" David \"BAILOPAN\" Anderson, lead developer");
|
||||
ConsolePrint(" Borja \"faluco\" Ferrer, core developer");
|
||||
ConsolePrint(" Scott \"Damaged Soul\" Ehlert, core developer");
|
||||
ConsolePrint(" Matt \"pRED\" Woodrow, core developer");
|
||||
ConsolePrint(" Michael \"ferret\" McKoy, plugin developer");
|
||||
ConsolePrint(" Pavol \"PM OnoTo\" Marko, SourceHook developer");
|
||||
ConsolePrint(" Special thanks to Viper of GameConnect");
|
||||
ConsolePrint(" Special thanks to Mani of Mani-Admin-Plugin");
|
||||
ConsolePrint(" http://www.sourcemod.net/");
|
||||
}
|
||||
else if (strcmp(cmdname, "version") == 0)
|
||||
{
|
||||
ConsolePrint(" SourceMod Version Information:");
|
||||
ConsolePrint(" SourceMod Version: %s", SVN_FULL_VERSION);
|
||||
ConsolePrint(" JIT Version: %s, %s", g_pVM->GetVMName(), g_pVM->GetVersionString());
|
||||
ConsolePrint(" JIT Settings: %s", g_pVM->GetCPUOptimizations());
|
||||
ConsolePrint(" Compiled on: %s %s", __DATE__, __TIME__);
|
||||
ConsolePrint(" http://www.sourcemod.net/");
|
||||
}
|
||||
}
|
||||
|
||||
CON_COMMAND(sm, "SourceMod Menu")
|
||||
{
|
||||
#if !defined ORANGEBOX_BUILD
|
||||
CCommand args;
|
||||
#endif
|
||||
g_RootMenu.GotRootCmd(args);
|
||||
}
|
||||
|
||||
FILE *g_pHndlLog = NULL;
|
||||
|
||||
void write_handles_to_log(const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
vfprintf(g_pHndlLog, fmt, ap);
|
||||
fprintf(g_pHndlLog, "\n");
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void write_handles_to_game(const char *fmt, ...)
|
||||
{
|
||||
size_t len;
|
||||
va_list ap;
|
||||
char buffer[1024];
|
||||
|
||||
va_start(ap, fmt);
|
||||
len = UTIL_FormatArgs(buffer, sizeof(buffer)-2, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
buffer[len] = '\n';
|
||||
buffer[len+1] = '\0';
|
||||
|
||||
engine->LogPrint(buffer);
|
||||
}
|
||||
|
||||
CON_COMMAND(sm_dump_handles, "Dumps Handle usage to a file for finding Handle leaks")
|
||||
{
|
||||
#if !defined ORANGEBOX_BUILD
|
||||
CCommand args;
|
||||
#endif
|
||||
if (args.ArgC() < 2)
|
||||
{
|
||||
g_RootMenu.ConsolePrint("Usage: sm_dump_handles <file> or <log> for game logs");
|
||||
return;
|
||||
}
|
||||
|
||||
if (strcmp(args.Arg(1), "log") != 0)
|
||||
{
|
||||
const char *arg = args.Arg(1);
|
||||
FILE *fp = fopen(arg, "wt");
|
||||
if (!fp)
|
||||
{
|
||||
g_RootMenu.ConsolePrint("Could not find file \"%s\"", arg);
|
||||
return;
|
||||
}
|
||||
|
||||
g_pHndlLog = fp;
|
||||
g_HandleSys.Dump(write_handles_to_log);
|
||||
g_pHndlLog = NULL;
|
||||
|
||||
fclose(fp);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_HandleSys.Dump(write_handles_to_game);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
/**
|
||||
* vim: set ts=4 :
|
||||
* =============================================================================
|
||||
* SourceMod
|
||||
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
|
||||
* =============================================================================
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, version 3.0, as published by the
|
||||
* Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* As a special exception, AlliedModders LLC gives you permission to link the
|
||||
* code of this program (as well as its derivative works) to "Half-Life 2," the
|
||||
* "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
|
||||
* by the Valve Corporation. You must obey the GNU General Public License in
|
||||
* all respects for all other code used. Additionally, AlliedModders LLC grants
|
||||
* this exception to all derivative works. AlliedModders LLC defines further
|
||||
* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
|
||||
* or <http://www.sourcemod.net/license.php>.
|
||||
*
|
||||
* Version: $Id$
|
||||
*/
|
||||
|
||||
#ifndef _INCLUDE_SOURCEMOD_SERVERCOMMANDS_H_
|
||||
#define _INCLUDE_SOURCEMOD_SERVERCOMMANDS_H_
|
||||
|
||||
#include "sourcemod.h"
|
||||
#include <IRootConsoleMenu.h>
|
||||
#include "PluginSys.h"
|
||||
#include "sourcemm_api.h"
|
||||
#include <sh_list.h>
|
||||
#include <sh_string.h>
|
||||
#include <compat_wrappers.h>
|
||||
#include "sm_trie.h"
|
||||
|
||||
using namespace SourceMod;
|
||||
using namespace SourceHook;
|
||||
|
||||
struct ConsoleEntry
|
||||
{
|
||||
String command;
|
||||
String description;
|
||||
};
|
||||
|
||||
class RootConsoleMenu :
|
||||
public IConCommandBaseAccessor,
|
||||
public SMGlobalClass,
|
||||
public IRootConsoleCommand,
|
||||
public IRootConsole
|
||||
{
|
||||
public:
|
||||
RootConsoleMenu();
|
||||
~RootConsoleMenu();
|
||||
public:
|
||||
const char *GetInterfaceName();
|
||||
unsigned int GetInterfaceVersion();
|
||||
public: //IConCommandBaseAccessor
|
||||
bool RegisterConCommandBase(ConCommandBase *pCommand);
|
||||
public: //SMGlobalClass
|
||||
void OnSourceModStartup(bool late);
|
||||
void OnSourceModAllInitialized();
|
||||
void OnSourceModShutdown();
|
||||
public: //IRootConsoleCommand
|
||||
void OnRootConsoleCommand(const char *cmdname, const CCommand &command);
|
||||
public: //IRootConsole
|
||||
bool AddRootConsoleCommand(const char *cmd, const char *text, IRootConsoleCommand *pHandler);
|
||||
bool RemoveRootConsoleCommand(const char *cmd, IRootConsoleCommand *pHandler);
|
||||
void ConsolePrint(const char *fmt, ...);
|
||||
void DrawGenericOption(const char *cmd, const char *text);
|
||||
public:
|
||||
void GotRootCmd(const CCommand &cmd);
|
||||
private:
|
||||
bool m_CfgExecDone;
|
||||
Trie *m_pCommands;
|
||||
List<ConsoleEntry *> m_Menu;
|
||||
};
|
||||
|
||||
extern RootConsoleMenu g_RootMenu;
|
||||
|
||||
#endif //_INCLUDE_SOURCEMOD_SERVERCOMMANDS_H_
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,64 @@
|
||||
/**
|
||||
* vim: set ts=4 :
|
||||
* =============================================================================
|
||||
* SourceMod
|
||||
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
|
||||
* =============================================================================
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, version 3.0, as published by the
|
||||
* Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* As a special exception, AlliedModders LLC gives you permission to link the
|
||||
* code of this program (as well as its derivative works) to "Half-Life 2," the
|
||||
* "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
|
||||
* by the Valve Corporation. You must obey the GNU General Public License in
|
||||
* all respects for all other code used. Additionally, AlliedModders LLC grants
|
||||
* this exception to all derivative works. AlliedModders LLC defines further
|
||||
* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
|
||||
* or <http://www.sourcemod.net/license.php>.
|
||||
*
|
||||
* Version: $Id$
|
||||
*/
|
||||
|
||||
#ifndef _INCLUDE_SOURCEMOD_STRINGUTIL_H_
|
||||
#define _INCLUDE_SOURCEMOD_STRINGUTIL_H_
|
||||
|
||||
#include <math.h>
|
||||
#include <sp_vm_api.h>
|
||||
#include <sp_typeutil.h>
|
||||
#include <ITranslator.h>
|
||||
|
||||
using namespace SourcePawn;
|
||||
using namespace SourceMod;
|
||||
|
||||
#define IS_STR_FILLED(var) (var[0] != '\0')
|
||||
|
||||
size_t atcprintf(char *buffer, size_t maxlen, const char *format, IPluginContext *pCtx, const cell_t *params, int *param);
|
||||
const char *stristr(const char *str, const char *substr);
|
||||
unsigned int strncopy(char *dest, const char *src, size_t count);
|
||||
bool gnprintf(char *buffer,
|
||||
size_t maxlen,
|
||||
const char *format,
|
||||
IPhraseCollection *pPhrases,
|
||||
void **params,
|
||||
unsigned int numparams,
|
||||
unsigned int &curparam,
|
||||
size_t *pOutLength,
|
||||
const char **pFailPhrase);
|
||||
size_t UTIL_Format(char *buffer, size_t maxlength, const char *fmt, ...);
|
||||
size_t UTIL_FormatArgs(char *buffer, size_t maxlength, const char *fmt, va_list ap);
|
||||
char *sm_strdup(const char *str);
|
||||
unsigned int UTIL_ReplaceAll(char *subject, size_t maxlength, const char *search, const char *replace);
|
||||
char *UTIL_ReplaceEx(char *subject, size_t maxLen, const char *search, size_t searchLen, const char *replace, size_t replaceLen);
|
||||
char *UTIL_TrimWhitespace(char *str, size_t &len);
|
||||
|
||||
#endif // _INCLUDE_SOURCEMOD_STRINGUTIL_H_
|
||||
@@ -0,0 +1,121 @@
|
||||
/**
|
||||
* vim: set ts=4 :
|
||||
* =============================================================================
|
||||
* SourceMod
|
||||
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
|
||||
* =============================================================================
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, version 3.0, as published by the
|
||||
* Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* As a special exception, AlliedModders LLC gives you permission to link the
|
||||
* code of this program (as well as its derivative works) to "Half-Life 2," the
|
||||
* "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
|
||||
* by the Valve Corporation. You must obey the GNU General Public License in
|
||||
* all respects for all other code used. Additionally, AlliedModders LLC grants
|
||||
* this exception to all derivative works. AlliedModders LLC defines further
|
||||
* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
|
||||
* or <http://www.sourcemod.net/license.php>.
|
||||
*
|
||||
* Version: $Id$
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <sm_trie_tpl.h>
|
||||
#include "sm_trie.h"
|
||||
|
||||
struct Trie
|
||||
{
|
||||
KTrie<void *> k;
|
||||
};
|
||||
|
||||
Trie *sm_trie_create()
|
||||
{
|
||||
return new Trie;
|
||||
}
|
||||
|
||||
void sm_trie_destroy(Trie *trie)
|
||||
{
|
||||
delete trie;
|
||||
}
|
||||
|
||||
bool sm_trie_insert(Trie *trie, const char *key, void *value)
|
||||
{
|
||||
return trie->k.insert(key, value);
|
||||
}
|
||||
|
||||
bool sm_trie_replace(Trie *trie, const char *key, void *value)
|
||||
{
|
||||
return trie->k.replace(key, value);
|
||||
}
|
||||
|
||||
bool sm_trie_retrieve(Trie *trie, const char *key, void **value)
|
||||
{
|
||||
void **pValue = trie->k.retrieve(key);
|
||||
|
||||
if (!pValue)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (value)
|
||||
{
|
||||
*value = *pValue;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool sm_trie_delete(Trie *trie, const char *key)
|
||||
{
|
||||
return trie->k.remove(key);
|
||||
}
|
||||
|
||||
void sm_trie_clear(Trie *trie)
|
||||
{
|
||||
trie->k.clear();
|
||||
}
|
||||
|
||||
size_t sm_trie_mem_usage(Trie *trie)
|
||||
{
|
||||
return trie->k.mem_usage();
|
||||
}
|
||||
|
||||
struct trie_iter_data
|
||||
{
|
||||
SM_TRIE_BAD_ITERATOR iter;
|
||||
void *ptr;
|
||||
Trie *pTrie;
|
||||
};
|
||||
|
||||
void our_trie_iterator(KTrie<void *> *pTrie, const char *name, void *& obj, void *data)
|
||||
{
|
||||
trie_iter_data *our_iter;
|
||||
|
||||
our_iter = (trie_iter_data *)data;
|
||||
our_iter->iter(our_iter->pTrie, name, &obj, our_iter->ptr);
|
||||
}
|
||||
|
||||
void sm_trie_bad_iterator(Trie *trie,
|
||||
char *buffer,
|
||||
size_t maxlength,
|
||||
SM_TRIE_BAD_ITERATOR iter,
|
||||
void *data)
|
||||
{
|
||||
trie_iter_data our_iter;
|
||||
|
||||
our_iter.iter = iter;
|
||||
our_iter.ptr = data;
|
||||
our_iter.pTrie = trie;
|
||||
trie->k.bad_iterator(buffer, maxlength, &our_iter, our_trie_iterator);
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/**
|
||||
* vim: set ts=4 :
|
||||
* =============================================================================
|
||||
* SourceMod
|
||||
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
|
||||
* =============================================================================
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, version 3.0, as published by the
|
||||
* Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* As a special exception, AlliedModders LLC gives you permission to link the
|
||||
* code of this program (as well as its derivative works) to "Half-Life 2," the
|
||||
* "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
|
||||
* by the Valve Corporation. You must obey the GNU General Public License in
|
||||
* all respects for all other code used. Additionally, AlliedModders LLC grants
|
||||
* this exception to all derivative works. AlliedModders LLC defines further
|
||||
* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
|
||||
* or <http://www.sourcemod.net/license.php>.
|
||||
*
|
||||
* Version: $Id$
|
||||
*/
|
||||
|
||||
#ifndef _INCLUDE_SOURCEMOD_SIMPLE_TRIE_H_
|
||||
#define _INCLUDE_SOURCEMOD_SIMPLE_TRIE_H_
|
||||
|
||||
struct Trie;
|
||||
|
||||
typedef void (*SM_TRIE_BAD_ITERATOR)(Trie *pTrie, const char *key, void **value, void *data);
|
||||
|
||||
Trie *sm_trie_create();
|
||||
void sm_trie_destroy(Trie *trie);
|
||||
bool sm_trie_insert(Trie *trie, const char *key, void *value);
|
||||
bool sm_trie_replace(Trie *trie, const char *key, void *value);
|
||||
bool sm_trie_retrieve(Trie *trie, const char *key, void **value);
|
||||
bool sm_trie_delete(Trie *trie, const char *key);
|
||||
void sm_trie_clear(Trie *trie);
|
||||
size_t sm_trie_mem_usage(Trie *trie);
|
||||
void sm_trie_bad_iterator(Trie *trie,
|
||||
char *buffer,
|
||||
size_t maxlength,
|
||||
SM_TRIE_BAD_ITERATOR iter,
|
||||
void *data);
|
||||
|
||||
#endif //_INCLUDE_SOURCEMOD_SIMPLE_TRIE_H_
|
||||
@@ -0,0 +1,46 @@
|
||||
/**
|
||||
* vim: set ts=4 :
|
||||
* =============================================================================
|
||||
* SourceMod
|
||||
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
|
||||
* =============================================================================
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, version 3.0, as published by the
|
||||
* Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* As a special exception, AlliedModders LLC gives you permission to link the
|
||||
* code of this program (as well as its derivative works) to "Half-Life 2," the
|
||||
* "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
|
||||
* by the Valve Corporation. You must obey the GNU General Public License in
|
||||
* all respects for all other code used. Additionally, AlliedModders LLC grants
|
||||
* this exception to all derivative works. AlliedModders LLC defines further
|
||||
* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
|
||||
* or <http://www.sourcemod.net/license.php>.
|
||||
*
|
||||
* Version: $Id$
|
||||
*/
|
||||
|
||||
/**
|
||||
* Autogenerated by build scripts
|
||||
*/
|
||||
|
||||
#ifndef _INCLUDE_SOURCEMOD_VERSION_H_
|
||||
#define _INCLUDE_SOURCEMOD_VERSION_H_
|
||||
|
||||
/**
|
||||
* @file Contains SourceMod version information.
|
||||
*/
|
||||
|
||||
#define SVN_FULL_VERSION "1.1.0-svn"
|
||||
#define SVN_FILE_VERSION 1,1,0,2230
|
||||
|
||||
#endif //_INCLUDE_SOURCEMOD_VERSION_H_
|
||||
@@ -0,0 +1,46 @@
|
||||
/**
|
||||
* vim: set ts=4 :
|
||||
* =============================================================================
|
||||
* SourceMod
|
||||
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
|
||||
* =============================================================================
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, version 3.0, as published by the
|
||||
* Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* As a special exception, AlliedModders LLC gives you permission to link the
|
||||
* code of this program (as well as its derivative works) to "Half-Life 2," the
|
||||
* "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
|
||||
* by the Valve Corporation. You must obey the GNU General Public License in
|
||||
* all respects for all other code used. Additionally, AlliedModders LLC grants
|
||||
* this exception to all derivative works. AlliedModders LLC defines further
|
||||
* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
|
||||
* or <http://www.sourcemod.net/license.php>.
|
||||
*
|
||||
* Version: $Id$
|
||||
*/
|
||||
|
||||
/**
|
||||
* Autogenerated by build scripts
|
||||
*/
|
||||
|
||||
#ifndef _INCLUDE_SOURCEMOD_VERSION_H_
|
||||
#define _INCLUDE_SOURCEMOD_VERSION_H_
|
||||
|
||||
/**
|
||||
* @file Contains SourceMod version information.
|
||||
*/
|
||||
|
||||
#define SVN_FULL_VERSION "$PMAJOR$.$PMINOR$.$PREVISION$.$GLOBAL_BUILD$"
|
||||
#define SVN_FILE_VERSION $PMAJOR$,$PMINOR$,$PREVISION$,$GLOBAL_BUILD$
|
||||
|
||||
#endif //_INCLUDE_SOURCEMOD_VERSION_H_
|
||||
@@ -0,0 +1,572 @@
|
||||
/**
|
||||
* vim: set ts=4 :
|
||||
* =============================================================================
|
||||
* SourceMod
|
||||
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
|
||||
* =============================================================================
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, version 3.0, as published by the
|
||||
* Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* As a special exception, AlliedModders LLC gives you permission to link the
|
||||
* code of this program (as well as its derivative works) to "Half-Life 2," the
|
||||
* "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
|
||||
* by the Valve Corporation. You must obey the GNU General Public License in
|
||||
* all respects for all other code used. Additionally, AlliedModders LLC grants
|
||||
* this exception to all derivative works. AlliedModders LLC defines further
|
||||
* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
|
||||
* or <http://www.sourcemod.net/license.php>.
|
||||
*
|
||||
* Version: $Id$
|
||||
*/
|
||||
|
||||
#include "sm_globals.h"
|
||||
#include "AdminCache.h"
|
||||
#include "ForwardSys.h"
|
||||
|
||||
static cell_t DumpAdminCache(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
g_Admins.DumpAdminCache((AdminCachePart)params[1], (params[2] == 1) ? true : false);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static cell_t AddCommandOverride(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
char *str;
|
||||
|
||||
pContext->LocalToString(params[1], &str);
|
||||
g_Admins.AddCommandOverride(str, (OverrideType)params[2], (AdminFlag)params[3]);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static cell_t GetCommandOverride(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
char *cmd;
|
||||
FlagBits flags;
|
||||
|
||||
pContext->LocalToString(params[1], &cmd);
|
||||
|
||||
if (!g_Admins.GetCommandOverride(cmd, (OverrideType)params[2], &flags))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
cell_t *addr;
|
||||
pContext->LocalToPhysAddr(params[3], &addr);
|
||||
|
||||
*addr = (cell_t)flags;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static cell_t UnsetCommandOverride(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
char *cmd;
|
||||
|
||||
pContext->LocalToString(params[1], &cmd);
|
||||
|
||||
g_Admins.UnsetCommandOverride(cmd, (OverrideType)params[2]);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static cell_t CreateAdmGroup(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
char *str;
|
||||
|
||||
pContext->LocalToString(params[1], &str);
|
||||
|
||||
return g_Admins.AddGroup(str);
|
||||
}
|
||||
|
||||
static cell_t FindAdmGroup(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
char *str;
|
||||
|
||||
pContext->LocalToString(params[1], &str);
|
||||
|
||||
return g_Admins.FindGroupByName(str);
|
||||
}
|
||||
|
||||
static cell_t SetAdmGroupAddFlag(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
GroupId id = (GroupId)params[1];
|
||||
AdminFlag flag = (AdminFlag)params[2];
|
||||
bool enabled = params[3] ? 1 : 0;
|
||||
|
||||
g_Admins.SetGroupAddFlag(id, flag, enabled);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static cell_t GetAdmGroupAddFlag(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
GroupId id = (GroupId)params[1];
|
||||
AdminFlag flag = (AdminFlag)params[2];
|
||||
|
||||
return g_Admins.GetGroupAddFlag(id, flag) ? 1 : 0;
|
||||
}
|
||||
|
||||
static cell_t SetAdmGroupImmunity(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
GroupId id = (GroupId)params[1];
|
||||
ImmunityType type = (ImmunityType)params[2];
|
||||
bool enabled = params[3] ? 1 : 0;
|
||||
|
||||
g_Admins.SetGroupGenericImmunity(id, type, enabled);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static cell_t GetAdmGroupImmunity(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
GroupId id = (GroupId)params[1];
|
||||
ImmunityType type = (ImmunityType)params[2];
|
||||
|
||||
return g_Admins.GetGroupGenericImmunity(id, type) ? 1 : 0;
|
||||
}
|
||||
|
||||
static cell_t SetAdmGroupImmuneFrom(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
GroupId id = (GroupId)params[1];
|
||||
GroupId other_id = (GroupId)params[2];
|
||||
|
||||
g_Admins.AddGroupImmunity(id, other_id);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static cell_t GetAdmGroupImmuneCount(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
GroupId id = (GroupId)params[1];
|
||||
|
||||
return g_Admins.GetGroupImmunityCount(id);
|
||||
}
|
||||
|
||||
static cell_t GetAdmGroupImmuneFrom(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
GroupId id = (GroupId)params[1];
|
||||
|
||||
return g_Admins.GetGroupImmunity(id, params[2]);
|
||||
}
|
||||
|
||||
static cell_t AddAdmGroupCmdOverride(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
GroupId id = (GroupId)params[1];
|
||||
OverrideType type = (OverrideType)params[3];
|
||||
OverrideRule rule = (OverrideRule)params[4];
|
||||
char *cmd;
|
||||
|
||||
pContext->LocalToString(params[2], &cmd);
|
||||
|
||||
g_Admins.AddGroupCommandOverride(id, cmd, type, rule);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static cell_t GetAdmGroupCmdOverride(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
GroupId id = (GroupId)params[1];
|
||||
OverrideType type = (OverrideType)params[3];
|
||||
OverrideRule rule;
|
||||
char *cmd;
|
||||
|
||||
pContext->LocalToString(params[2], &cmd);
|
||||
|
||||
if (!g_Admins.GetGroupCommandOverride(id, cmd, type, &rule))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
cell_t *addr;
|
||||
pContext->LocalToPhysAddr(params[4], &addr);
|
||||
*addr = (cell_t)rule;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static cell_t GetAdmGroupAddFlags(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
GroupId id = (GroupId)params[1];
|
||||
return g_Admins.GetGroupAddFlags(id);
|
||||
}
|
||||
|
||||
static cell_t RegisterAuthIdentType(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
char *type;
|
||||
pContext->LocalToString(params[1], &type);
|
||||
|
||||
g_Admins.RegisterAuthIdentType(type);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static cell_t CreateAdmin(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
char *admin;
|
||||
pContext->LocalToString(params[1], &admin);
|
||||
|
||||
if (admin[0] == '\0')
|
||||
{
|
||||
admin = NULL;
|
||||
}
|
||||
|
||||
return g_Admins.CreateAdmin(admin);
|
||||
}
|
||||
|
||||
static cell_t GetAdminUsername(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
AdminId id = params[1];
|
||||
size_t written;
|
||||
const char *name = g_Admins.GetAdminName(id);
|
||||
|
||||
if (!name)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
pContext->StringToLocalUTF8(params[2], params[3], name, &written);
|
||||
|
||||
return written;
|
||||
}
|
||||
|
||||
static cell_t BindAdminIdentity(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
AdminId id = params[1];
|
||||
char *auth, *ident;
|
||||
|
||||
pContext->LocalToString(params[2], &auth);
|
||||
pContext->LocalToString(params[3], &ident);
|
||||
|
||||
return g_Admins.BindAdminIdentity(id, auth, ident);
|
||||
}
|
||||
|
||||
static cell_t SetAdminFlag(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
AdminId id = params[1];
|
||||
AdminFlag flag = (AdminFlag)params[2];
|
||||
bool enabled = params[3] ? true : false;
|
||||
|
||||
g_Admins.SetAdminFlag(id, flag, enabled);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static cell_t GetAdminFlag(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
AdminId id = params[1];
|
||||
AdminFlag flag = (AdminFlag)params[2];
|
||||
AccessMode mode = (AccessMode)params[3];
|
||||
|
||||
return g_Admins.GetAdminFlag(id, flag, mode);
|
||||
}
|
||||
|
||||
static cell_t GetAdminFlags(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
AdminId id = params[1];
|
||||
AccessMode mode = (AccessMode)params[2];
|
||||
|
||||
return g_Admins.GetAdminFlags(id, mode);
|
||||
}
|
||||
|
||||
static cell_t AdminInheritGroup(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
AdminId id = params[1];
|
||||
GroupId gid = params[2];
|
||||
|
||||
return g_Admins.AdminInheritGroup(id, gid);
|
||||
}
|
||||
|
||||
static cell_t GetAdminGroupCount(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
AdminId id = params[1];
|
||||
|
||||
return g_Admins.GetAdminGroupCount(id);
|
||||
}
|
||||
|
||||
static cell_t GetAdminGroup(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
AdminId id = params[1];
|
||||
unsigned int index = params[2];
|
||||
const char *name;
|
||||
GroupId gid;
|
||||
|
||||
if ((gid=g_Admins.GetAdminGroup(id, index, &name)) == INVALID_GROUP_ID)
|
||||
{
|
||||
return gid;
|
||||
}
|
||||
|
||||
if (name == NULL)
|
||||
{
|
||||
name = "";
|
||||
}
|
||||
|
||||
pContext->StringToLocalUTF8(params[3], params[4], name, NULL);
|
||||
|
||||
return gid;
|
||||
}
|
||||
|
||||
static cell_t SetAdminPassword(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
AdminId id = params[1];
|
||||
char *password;
|
||||
|
||||
pContext->LocalToString(params[2], &password);
|
||||
|
||||
g_Admins.SetAdminPassword(id, password);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static cell_t GetAdminPassword(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
AdminId id = params[1];
|
||||
const char *password = g_Admins.GetAdminPassword(id);
|
||||
|
||||
if (!password)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
pContext->StringToLocalUTF8(params[2], params[3], password, NULL);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static cell_t FindAdminByIdentity(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
char *auth, *ident;
|
||||
|
||||
pContext->LocalToString(params[1], &auth);
|
||||
pContext->LocalToString(params[2], &ident);
|
||||
|
||||
return g_Admins.FindAdminByIdentity(auth, ident);
|
||||
}
|
||||
|
||||
static cell_t RemoveAdmin(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
AdminId id = params[1];
|
||||
|
||||
return g_Admins.InvalidateAdmin(id);
|
||||
}
|
||||
|
||||
static cell_t FlagBitsToBitArray(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
FlagBits bits = (FlagBits)params[1];
|
||||
bool array[AdminFlags_TOTAL];
|
||||
cell_t *addr;
|
||||
unsigned int numWr = g_Admins.FlagBitsToBitArray(bits, array, AdminFlags_TOTAL);
|
||||
|
||||
pContext->LocalToPhysAddr(params[2], &addr);
|
||||
|
||||
unsigned int i;
|
||||
for (i=0; i<numWr && i<(unsigned int)params[3]; i++)
|
||||
{
|
||||
addr[i] = array[i] ? 1 : 0;
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
static cell_t FlagBitArrayToBits(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
bool array[AdminFlags_TOTAL];
|
||||
unsigned int num = ((unsigned int)params[2] > AdminFlags_TOTAL ? params[2] : AdminFlags_TOTAL);
|
||||
cell_t *addr;
|
||||
|
||||
pContext->LocalToPhysAddr(params[1], &addr);
|
||||
|
||||
for (unsigned int i=0; i<num; i++)
|
||||
{
|
||||
array[i] = addr[i] ? true : false;
|
||||
}
|
||||
|
||||
return g_Admins.FlagBitArrayToBits(array, num);
|
||||
}
|
||||
|
||||
static cell_t FlagArrayToBits(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
cell_t *addr;
|
||||
pContext->LocalToPhysAddr(params[1], &addr);
|
||||
|
||||
if (sizeof(AdminFlag) == sizeof(cell_t))
|
||||
{
|
||||
return g_Admins.FlagArrayToBits((const AdminFlag *)addr, params[2]);
|
||||
} else {
|
||||
AdminFlag flags[AdminFlags_TOTAL];
|
||||
unsigned int num = ((unsigned)params[2] > AdminFlags_TOTAL ? AdminFlags_TOTAL : params[2]);
|
||||
|
||||
for (unsigned int i=0; i<num; i++)
|
||||
{
|
||||
flags[i] = (AdminFlag)addr[i];
|
||||
}
|
||||
return g_Admins.FlagArrayToBits(flags, num);
|
||||
}
|
||||
}
|
||||
|
||||
static cell_t FlagBitsToArray(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
cell_t *addr;
|
||||
pContext->LocalToPhysAddr(params[2], &addr);
|
||||
|
||||
if (sizeof(AdminFlag) == sizeof(cell_t))
|
||||
{
|
||||
return g_Admins.FlagBitsToArray(params[1], (AdminFlag *)addr, params[3]);
|
||||
} else {
|
||||
AdminFlag flags[AdminFlags_TOTAL];
|
||||
unsigned int num = ((unsigned)params[2] > AdminFlags_TOTAL ? AdminFlags_TOTAL : params[2]);
|
||||
num = g_Admins.FlagBitsToArray(params[1], flags, num);
|
||||
|
||||
for (unsigned int i=0; i<num; i++)
|
||||
{
|
||||
addr[i] = flags[i];
|
||||
}
|
||||
|
||||
return num;
|
||||
}
|
||||
}
|
||||
|
||||
static cell_t CanAdminTarget(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
return g_Admins.CanAdminTarget(params[1], params[2]) ? 1 : 0;
|
||||
}
|
||||
|
||||
static cell_t CreateAuthMethod(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
char *name;
|
||||
pContext->LocalToString(params[1], &name);
|
||||
g_Admins.RegisterAuthIdentType(name);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static cell_t SetAdmGroupImmunityLevel(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
return g_Admins.SetGroupImmunityLevel(params[1], params[2]);
|
||||
}
|
||||
|
||||
static cell_t GetAdmGroupImmunityLevel(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
return g_Admins.GetGroupImmunityLevel(params[1]);
|
||||
}
|
||||
|
||||
static cell_t SetAdminImmunityLevel(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
return g_Admins.SetAdminImmunityLevel(params[1], params[2]);
|
||||
}
|
||||
|
||||
static cell_t GetAdminImmunityLevel(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
return g_Admins.GetAdminImmunityLevel(params[1]);
|
||||
}
|
||||
|
||||
static cell_t FindFlagByName(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
char *flag;
|
||||
pContext->LocalToString(params[1], &flag);
|
||||
|
||||
cell_t *addr;
|
||||
pContext->LocalToPhysAddr(params[2], &addr);
|
||||
|
||||
AdminFlag admflag;
|
||||
if (!g_Admins.FindFlag(flag, &admflag))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
*addr = (cell_t)admflag;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static cell_t FindFlagByChar(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
cell_t *addr;
|
||||
pContext->LocalToPhysAddr(params[2], &addr);
|
||||
|
||||
AdminFlag admflag;
|
||||
if (!g_Admins.FindFlag((char)params[1], &admflag))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
*addr = (cell_t)admflag;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static cell_t ReadFlagString(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
char *flag;
|
||||
pContext->LocalToString(params[1], &flag);
|
||||
|
||||
cell_t *addr;
|
||||
pContext->LocalToPhysAddr(params[2], &addr);
|
||||
|
||||
const char *end = flag;
|
||||
FlagBits bits = g_Admins.ReadFlagString(flag, &end);
|
||||
|
||||
*addr = end - flag;
|
||||
|
||||
return bits;
|
||||
}
|
||||
|
||||
REGISTER_NATIVES(adminNatives)
|
||||
{
|
||||
{"DumpAdminCache", DumpAdminCache},
|
||||
{"AddCommandOverride", AddCommandOverride},
|
||||
{"GetCommandOverride", GetCommandOverride},
|
||||
{"UnsetCommandOverride", UnsetCommandOverride},
|
||||
{"CreateAdmGroup", CreateAdmGroup},
|
||||
{"FindAdmGroup", FindAdmGroup},
|
||||
{"SetAdmGroupAddFlag", SetAdmGroupAddFlag},
|
||||
{"GetAdmGroupAddFlag", GetAdmGroupAddFlag},
|
||||
{"SetAdmGroupImmunity", SetAdmGroupImmunity},
|
||||
{"GetAdmGroupImmunity", GetAdmGroupImmunity},
|
||||
{"SetAdmGroupImmuneFrom", SetAdmGroupImmuneFrom},
|
||||
{"GetAdmGroupImmuneFrom", GetAdmGroupImmuneFrom},
|
||||
{"GetAdmGroupImmuneCount", GetAdmGroupImmuneCount},
|
||||
{"AddAdmGroupCmdOverride", AddAdmGroupCmdOverride},
|
||||
{"GetAdmGroupCmdOverride", GetAdmGroupCmdOverride},
|
||||
{"GetAdmGroupAddFlags", GetAdmGroupAddFlags},
|
||||
{"RegisterAuthIdentType", RegisterAuthIdentType},
|
||||
{"CreateAdmin", CreateAdmin},
|
||||
{"GetAdminUsername", GetAdminUsername},
|
||||
{"BindAdminIdentity", BindAdminIdentity},
|
||||
{"SetAdminFlag", SetAdminFlag},
|
||||
{"GetAdminFlag", GetAdminFlag},
|
||||
{"GetAdminFlags", GetAdminFlags},
|
||||
{"AdminInheritGroup", AdminInheritGroup},
|
||||
{"GetAdminGroupCount", GetAdminGroupCount},
|
||||
{"GetAdminGroup", GetAdminGroup},
|
||||
{"SetAdminPassword", SetAdminPassword},
|
||||
{"GetAdminPassword", GetAdminPassword},
|
||||
{"FindAdminByIdentity", FindAdminByIdentity},
|
||||
{"RemoveAdmin", RemoveAdmin},
|
||||
{"FlagBitsToBitArray", FlagBitsToBitArray},
|
||||
{"FlagBitArrayToBits", FlagBitArrayToBits},
|
||||
{"FlagArrayToBits", FlagArrayToBits},
|
||||
{"FlagBitsToArray", FlagBitsToArray},
|
||||
{"CanAdminTarget", CanAdminTarget},
|
||||
{"CreateAuthMethod", CreateAuthMethod},
|
||||
{"FindFlagByName", FindFlagByName},
|
||||
{"FindFlagByChar", FindFlagByChar},
|
||||
{"ReadFlagString", ReadFlagString},
|
||||
{"GetAdmGroupImmunityLevel",GetAdmGroupImmunityLevel},
|
||||
{"SetAdmGroupImmunityLevel",SetAdmGroupImmunityLevel},
|
||||
{"GetAdminImmunityLevel", GetAdminImmunityLevel},
|
||||
{"SetAdminImmunityLevel", SetAdminImmunityLevel},
|
||||
/* -------------------------------------------------- */
|
||||
{NULL, NULL},
|
||||
};
|
||||
|
||||
@@ -0,0 +1,588 @@
|
||||
/**
|
||||
* vim: set ts=4 :
|
||||
* =============================================================================
|
||||
* SourceMod
|
||||
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
|
||||
* =============================================================================
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, version 3.0, as published by the
|
||||
* Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* As a special exception, AlliedModders LLC gives you permission to link the
|
||||
* code of this program (as well as its derivative works) to "Half-Life 2," the
|
||||
* "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
|
||||
* by the Valve Corporation. You must obey the GNU General Public License in
|
||||
* all respects for all other code used. Additionally, AlliedModders LLC grants
|
||||
* this exception to all derivative works. AlliedModders LLC defines further
|
||||
* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
|
||||
* or <http://www.sourcemod.net/license.php>.
|
||||
*
|
||||
* Version: $Id$
|
||||
*/
|
||||
|
||||
#include <malloc.h>
|
||||
#include "sm_globals.h"
|
||||
#include "sm_stringutil.h"
|
||||
#include "CellArray.h"
|
||||
#include "HandleSys.h"
|
||||
|
||||
HandleType_t htCellArray;
|
||||
|
||||
class CellArrayHelpers :
|
||||
public SMGlobalClass,
|
||||
public IHandleTypeDispatch
|
||||
{
|
||||
public: //SMGlobalClass
|
||||
void OnSourceModAllInitialized()
|
||||
{
|
||||
htCellArray = g_HandleSys.CreateType("CellArray", this, 0, NULL, NULL, g_pCoreIdent, NULL);
|
||||
}
|
||||
void OnSourceModShutdown()
|
||||
{
|
||||
g_HandleSys.RemoveType(htCellArray, g_pCoreIdent);
|
||||
}
|
||||
public: //IHandleTypeDispatch
|
||||
void OnHandleDestroy(HandleType_t type, void *object)
|
||||
{
|
||||
CellArray *array = (CellArray *)object;
|
||||
delete array;
|
||||
}
|
||||
bool GetHandleApproxSize(HandleType_t type, void *object, unsigned int *pSize)
|
||||
{
|
||||
CellArray *pArray = (CellArray *)object;
|
||||
*pSize = sizeof(CellArray) + pArray->mem_usage();
|
||||
return true;
|
||||
}
|
||||
} s_CellArrayHelpers;
|
||||
|
||||
static cell_t CreateArray(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
if (!params[1])
|
||||
{
|
||||
return pContext->ThrowNativeError("Invalid block size (must be > 0)");
|
||||
}
|
||||
|
||||
CellArray *array = new CellArray(params[1]);
|
||||
|
||||
if (params[2])
|
||||
{
|
||||
array->resize(params[2]);
|
||||
}
|
||||
|
||||
Handle_t hndl = g_HandleSys.CreateHandle(htCellArray, array, pContext->GetIdentity(), g_pCoreIdent, NULL);
|
||||
if (!hndl)
|
||||
{
|
||||
delete array;
|
||||
}
|
||||
|
||||
return hndl;
|
||||
}
|
||||
|
||||
static cell_t ClearArray(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
CellArray *array;
|
||||
HandleError err;
|
||||
HandleSecurity sec(pContext->GetIdentity(), g_pCoreIdent);
|
||||
|
||||
if ((err = g_HandleSys.ReadHandle(params[1], htCellArray, &sec, (void **)&array))
|
||||
!= HandleError_None)
|
||||
{
|
||||
return pContext->ThrowNativeError("Invalid Handle %x (error: %d)", params[1], err);
|
||||
}
|
||||
|
||||
array->clear();
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static cell_t ResizeArray(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
CellArray *array;
|
||||
HandleError err;
|
||||
HandleSecurity sec(pContext->GetIdentity(), g_pCoreIdent);
|
||||
|
||||
if ((err = g_HandleSys.ReadHandle(params[1], htCellArray, &sec, (void **)&array))
|
||||
!= HandleError_None)
|
||||
{
|
||||
return pContext->ThrowNativeError("Invalid Handle %x (error: %d)", params[1], err);
|
||||
}
|
||||
|
||||
if (!array->resize(params[2]))
|
||||
{
|
||||
return pContext->ThrowNativeError("Unable to resize array to \"%u\"", params[2]);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static cell_t GetArraySize(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
CellArray *array;
|
||||
HandleError err;
|
||||
HandleSecurity sec(pContext->GetIdentity(), g_pCoreIdent);
|
||||
|
||||
if ((err = g_HandleSys.ReadHandle(params[1], htCellArray, &sec, (void **)&array))
|
||||
!= HandleError_None)
|
||||
{
|
||||
return pContext->ThrowNativeError("Invalid Handle %x (error: %d)", params[1], err);
|
||||
}
|
||||
|
||||
return (cell_t)array->size();
|
||||
}
|
||||
|
||||
static cell_t PushArrayCell(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
CellArray *array;
|
||||
HandleError err;
|
||||
HandleSecurity sec(pContext->GetIdentity(), g_pCoreIdent);
|
||||
|
||||
if ((err = g_HandleSys.ReadHandle(params[1], htCellArray, &sec, (void **)&array))
|
||||
!= HandleError_None)
|
||||
{
|
||||
return pContext->ThrowNativeError("Invalid Handle %x (error: %d)", params[1], err);
|
||||
}
|
||||
|
||||
cell_t *blk = array->push();
|
||||
if (!blk)
|
||||
{
|
||||
return pContext->ThrowNativeError("Failed to grow array");
|
||||
}
|
||||
|
||||
*blk = params[2];
|
||||
|
||||
return (cell_t)(array->size() - 1);
|
||||
}
|
||||
|
||||
static cell_t PushArrayString(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
CellArray *array;
|
||||
HandleError err;
|
||||
HandleSecurity sec(pContext->GetIdentity(), g_pCoreIdent);
|
||||
|
||||
if ((err = g_HandleSys.ReadHandle(params[1], htCellArray, &sec, (void **)&array))
|
||||
!= HandleError_None)
|
||||
{
|
||||
return pContext->ThrowNativeError("Invalid Handle %x (error: %d)", params[1], err);
|
||||
}
|
||||
|
||||
cell_t *blk = array->push();
|
||||
if (!blk)
|
||||
{
|
||||
return pContext->ThrowNativeError("Failed to grow array");
|
||||
}
|
||||
|
||||
char *str;
|
||||
pContext->LocalToString(params[2], &str);
|
||||
|
||||
strncopy((char *)blk, str, array->blocksize() * sizeof(cell_t));
|
||||
|
||||
return (cell_t)(array->size() - 1);
|
||||
}
|
||||
|
||||
static cell_t PushArrayArray(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
CellArray *array;
|
||||
HandleError err;
|
||||
HandleSecurity sec(pContext->GetIdentity(), g_pCoreIdent);
|
||||
|
||||
if ((err = g_HandleSys.ReadHandle(params[1], htCellArray, &sec, (void **)&array))
|
||||
!= HandleError_None)
|
||||
{
|
||||
return pContext->ThrowNativeError("Invalid Handle %x (error: %d)", params[1], err);
|
||||
}
|
||||
|
||||
cell_t *blk = array->push();
|
||||
if (!blk)
|
||||
{
|
||||
return pContext->ThrowNativeError("Failed to grow array");
|
||||
}
|
||||
|
||||
cell_t *addr;
|
||||
pContext->LocalToPhysAddr(params[2], &addr);
|
||||
|
||||
size_t indexes = array->blocksize();
|
||||
if (params[3] != -1 && (size_t)params[3] <= array->blocksize())
|
||||
{
|
||||
indexes = params[3];
|
||||
}
|
||||
|
||||
memcpy(blk, addr, sizeof(cell_t) * indexes);
|
||||
|
||||
return (cell_t)(array->size() - 1);
|
||||
}
|
||||
|
||||
static cell_t GetArrayCell(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
CellArray *array;
|
||||
HandleError err;
|
||||
HandleSecurity sec(pContext->GetIdentity(), g_pCoreIdent);
|
||||
|
||||
if ((err = g_HandleSys.ReadHandle(params[1], htCellArray, &sec, (void **)&array))
|
||||
!= HandleError_None)
|
||||
{
|
||||
return pContext->ThrowNativeError("Invalid Handle %x (error: %d)", params[1], err);
|
||||
}
|
||||
|
||||
size_t idx = (size_t)params[2];
|
||||
if (idx >= array->size())
|
||||
{
|
||||
return pContext->ThrowNativeError("Invalid index %d (count: %d)", idx, array->size());
|
||||
}
|
||||
|
||||
cell_t *blk = array->at(idx);
|
||||
|
||||
idx = (size_t)params[3];
|
||||
if (params[4] == 0)
|
||||
{
|
||||
if (idx >= array->blocksize())
|
||||
{
|
||||
return pContext->ThrowNativeError("Invalid block %d (blocksize: %d)", idx, array->blocksize());
|
||||
}
|
||||
return blk[idx];
|
||||
} else {
|
||||
if (idx >= array->blocksize() * 4)
|
||||
{
|
||||
return pContext->ThrowNativeError("Invalid byte %d (blocksize: %d bytes)", idx, array->blocksize() * 4);
|
||||
}
|
||||
return (cell_t)*((char *)blk + idx);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static cell_t GetArrayString(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
CellArray *array;
|
||||
HandleError err;
|
||||
HandleSecurity sec(pContext->GetIdentity(), g_pCoreIdent);
|
||||
|
||||
if ((err = g_HandleSys.ReadHandle(params[1], htCellArray, &sec, (void **)&array))
|
||||
!= HandleError_None)
|
||||
{
|
||||
return pContext->ThrowNativeError("Invalid Handle %x (error: %d)", params[1], err);
|
||||
}
|
||||
|
||||
size_t idx = (size_t)params[2];
|
||||
if (idx >= array->size())
|
||||
{
|
||||
return pContext->ThrowNativeError("Invalid index %d (count: %d)", idx, array->size());
|
||||
}
|
||||
|
||||
cell_t *blk = array->at(idx);
|
||||
size_t numWritten = 0;
|
||||
|
||||
pContext->StringToLocalUTF8(params[3], params[4], (char *)blk, &numWritten);
|
||||
|
||||
return numWritten;
|
||||
}
|
||||
|
||||
static cell_t GetArrayArray(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
CellArray *array;
|
||||
HandleError err;
|
||||
HandleSecurity sec(pContext->GetIdentity(), g_pCoreIdent);
|
||||
|
||||
if ((err = g_HandleSys.ReadHandle(params[1], htCellArray, &sec, (void **)&array))
|
||||
!= HandleError_None)
|
||||
{
|
||||
return pContext->ThrowNativeError("Invalid Handle %x (error: %d)", params[1], err);
|
||||
}
|
||||
|
||||
size_t idx = (size_t)params[2];
|
||||
if (idx >= array->size())
|
||||
{
|
||||
return pContext->ThrowNativeError("Invalid index %d (count: %d)", idx, array->size());
|
||||
}
|
||||
|
||||
cell_t *blk = array->at(idx);
|
||||
size_t indexes = array->blocksize();
|
||||
if (params[4] != -1 && (size_t)params[4] <= array->blocksize())
|
||||
{
|
||||
indexes = params[4];
|
||||
}
|
||||
|
||||
cell_t *addr;
|
||||
pContext->LocalToPhysAddr(params[3], &addr);
|
||||
|
||||
memcpy(addr, blk, sizeof(cell_t) * indexes);
|
||||
|
||||
return indexes;
|
||||
}
|
||||
|
||||
static cell_t SetArrayCell(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
CellArray *array;
|
||||
HandleError err;
|
||||
HandleSecurity sec(pContext->GetIdentity(), g_pCoreIdent);
|
||||
|
||||
if ((err = g_HandleSys.ReadHandle(params[1], htCellArray, &sec, (void **)&array))
|
||||
!= HandleError_None)
|
||||
{
|
||||
return pContext->ThrowNativeError("Invalid Handle %x (error: %d)", params[1], err);
|
||||
}
|
||||
|
||||
size_t idx = (size_t)params[2];
|
||||
if (idx >= array->size())
|
||||
{
|
||||
return pContext->ThrowNativeError("Invalid index %d (count: %d)", idx, array->size());
|
||||
}
|
||||
|
||||
cell_t *blk = array->at(idx);
|
||||
|
||||
idx = (size_t)params[4];
|
||||
if (params[5] == 0)
|
||||
{
|
||||
if (idx >= array->blocksize())
|
||||
{
|
||||
return pContext->ThrowNativeError("Invalid block %d (blocksize: %d)", idx, array->blocksize());
|
||||
}
|
||||
blk[idx] = params[3];
|
||||
} else {
|
||||
if (idx >= array->blocksize() * 4)
|
||||
{
|
||||
return pContext->ThrowNativeError("Invalid byte %d (blocksize: %d bytes)", idx, array->blocksize() * 4);
|
||||
}
|
||||
*((char *)blk + idx) = (char)params[3];
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static cell_t SetArrayString(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
CellArray *array;
|
||||
HandleError err;
|
||||
HandleSecurity sec(pContext->GetIdentity(), g_pCoreIdent);
|
||||
|
||||
if ((err = g_HandleSys.ReadHandle(params[1], htCellArray, &sec, (void **)&array))
|
||||
!= HandleError_None)
|
||||
{
|
||||
return pContext->ThrowNativeError("Invalid Handle %x (error: %d)", params[1], err);
|
||||
}
|
||||
|
||||
size_t idx = (size_t)params[2];
|
||||
if (idx >= array->size())
|
||||
{
|
||||
return pContext->ThrowNativeError("Invalid index %d (count: %d)", idx, array->size());
|
||||
}
|
||||
|
||||
cell_t *blk = array->at(idx);
|
||||
|
||||
char *str;
|
||||
pContext->LocalToString(params[3], &str);
|
||||
|
||||
return strncopy((char *)blk, str, array->blocksize() * sizeof(cell_t));
|
||||
}
|
||||
|
||||
static cell_t SetArrayArray(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
CellArray *array;
|
||||
HandleError err;
|
||||
HandleSecurity sec(pContext->GetIdentity(), g_pCoreIdent);
|
||||
|
||||
if ((err = g_HandleSys.ReadHandle(params[1], htCellArray, &sec, (void **)&array))
|
||||
!= HandleError_None)
|
||||
{
|
||||
return pContext->ThrowNativeError("Invalid Handle %x (error: %d)", params[1], err);
|
||||
}
|
||||
|
||||
size_t idx = (size_t)params[2];
|
||||
if (idx >= array->size())
|
||||
{
|
||||
return pContext->ThrowNativeError("Invalid index %d (count: %d)", idx, array->size());
|
||||
}
|
||||
|
||||
cell_t *blk = array->at(idx);
|
||||
size_t indexes = array->blocksize();
|
||||
if (params[4] != -1 && (size_t)params[4] <= array->blocksize())
|
||||
{
|
||||
indexes = params[4];
|
||||
}
|
||||
|
||||
cell_t *addr;
|
||||
pContext->LocalToPhysAddr(params[3], &addr);
|
||||
|
||||
memcpy(blk, addr, sizeof(cell_t) * indexes);
|
||||
|
||||
return indexes;
|
||||
}
|
||||
|
||||
static cell_t ShiftArrayUp(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
CellArray *array;
|
||||
HandleError err;
|
||||
HandleSecurity sec(pContext->GetIdentity(), g_pCoreIdent);
|
||||
|
||||
if ((err = g_HandleSys.ReadHandle(params[1], htCellArray, &sec, (void **)&array))
|
||||
!= HandleError_None)
|
||||
{
|
||||
return pContext->ThrowNativeError("Invalid Handle %x (error: %d)", params[1], err);
|
||||
}
|
||||
|
||||
size_t idx = (size_t)params[2];
|
||||
if (idx >= array->size())
|
||||
{
|
||||
return pContext->ThrowNativeError("Invalid index %d (count: %d)", idx, array->size());
|
||||
}
|
||||
|
||||
array->insert_at(idx);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static cell_t RemoveFromArray(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
CellArray *array;
|
||||
HandleError err;
|
||||
HandleSecurity sec(pContext->GetIdentity(), g_pCoreIdent);
|
||||
|
||||
if ((err = g_HandleSys.ReadHandle(params[1], htCellArray, &sec, (void **)&array))
|
||||
!= HandleError_None)
|
||||
{
|
||||
return pContext->ThrowNativeError("Invalid Handle %x (error: %d)", params[1], err);
|
||||
}
|
||||
|
||||
size_t idx = (size_t)params[2];
|
||||
if (idx >= array->size())
|
||||
{
|
||||
return pContext->ThrowNativeError("Invalid index %d (count: %d)", idx, array->size());
|
||||
}
|
||||
|
||||
array->remove(idx);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static cell_t SwapArrayItems(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
CellArray *array;
|
||||
HandleError err;
|
||||
HandleSecurity sec(pContext->GetIdentity(), g_pCoreIdent);
|
||||
|
||||
if ((err = g_HandleSys.ReadHandle(params[1], htCellArray, &sec, (void **)&array))
|
||||
!= HandleError_None)
|
||||
{
|
||||
return pContext->ThrowNativeError("Invalid Handle %x (error: %d)", params[1], err);
|
||||
}
|
||||
|
||||
size_t idx1 = (size_t)params[2];
|
||||
size_t idx2 = (size_t)params[3];
|
||||
if (idx1 >= array->size())
|
||||
{
|
||||
return pContext->ThrowNativeError("Invalid index %d (count: %d)", idx1, array->size());
|
||||
}
|
||||
if (idx2 >= array->size())
|
||||
{
|
||||
return pContext->ThrowNativeError("Invalid index %d (count: %d)", idx2, array->size());
|
||||
}
|
||||
|
||||
array->swap(idx1, idx2);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static cell_t CloneArray(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
CellArray *oldArray;
|
||||
HandleError err;
|
||||
HandleSecurity sec(pContext->GetIdentity(), g_pCoreIdent);
|
||||
|
||||
if ((err = g_HandleSys.ReadHandle(params[1], htCellArray, &sec, (void **)&oldArray))
|
||||
!= HandleError_None)
|
||||
{
|
||||
return pContext->ThrowNativeError("Invalid Handle %x (error: %d)", params[1], err);
|
||||
}
|
||||
|
||||
CellArray *array = oldArray->clone();
|
||||
|
||||
Handle_t hndl = g_HandleSys.CreateHandle(htCellArray, array, pContext->GetIdentity(), g_pCoreIdent, NULL);
|
||||
if (!hndl)
|
||||
{
|
||||
delete array;
|
||||
}
|
||||
|
||||
return hndl;
|
||||
}
|
||||
|
||||
static cell_t FindStringInArray(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
CellArray *array;
|
||||
HandleError err;
|
||||
HandleSecurity sec(pContext->GetIdentity(), g_pCoreIdent);
|
||||
|
||||
if ((err = g_HandleSys.ReadHandle(params[1], htCellArray, &sec, (void **)&array))
|
||||
!= HandleError_None)
|
||||
{
|
||||
return pContext->ThrowNativeError("Invalid Handle %x (error: %d)", params[1], err);
|
||||
}
|
||||
|
||||
char *str;
|
||||
pContext->LocalToString(params[2], &str);
|
||||
|
||||
for (unsigned int i = 0; i < array->size(); i++)
|
||||
{
|
||||
const char *array_str = (const char *)array->at(i);
|
||||
if (strcmp(str, array_str) == 0)
|
||||
{
|
||||
return (cell_t) i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static cell_t FindValueInArray(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
CellArray *array;
|
||||
HandleError err;
|
||||
HandleSecurity sec(pContext->GetIdentity(), g_pCoreIdent);
|
||||
|
||||
if ((err = g_HandleSys.ReadHandle(params[1], htCellArray, &sec, (void **)&array))
|
||||
!= HandleError_None)
|
||||
{
|
||||
return pContext->ThrowNativeError("Invalid Handle %x (error: %d)", params[1], err);
|
||||
}
|
||||
|
||||
for (unsigned int i = 0; i < array->size(); i++)
|
||||
{
|
||||
if (params[2] == *array->at(i))
|
||||
{
|
||||
return (cell_t) i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
REGISTER_NATIVES(cellArrayNatives)
|
||||
{
|
||||
{"ClearArray", ClearArray},
|
||||
{"CreateArray", CreateArray},
|
||||
{"GetArrayArray", GetArrayArray},
|
||||
{"GetArrayCell", GetArrayCell},
|
||||
{"GetArraySize", GetArraySize},
|
||||
{"GetArrayString", GetArrayString},
|
||||
{"ResizeArray", ResizeArray},
|
||||
{"PushArrayArray", PushArrayArray},
|
||||
{"PushArrayCell", PushArrayCell},
|
||||
{"PushArrayString", PushArrayString},
|
||||
{"RemoveFromArray", RemoveFromArray},
|
||||
{"SetArrayCell", SetArrayCell},
|
||||
{"SetArrayString", SetArrayString},
|
||||
{"SetArrayArray", SetArrayArray},
|
||||
{"ShiftArrayUp", ShiftArrayUp},
|
||||
{"SwapArrayItems", SwapArrayItems},
|
||||
{"CloneArray", CloneArray},
|
||||
{"FindStringInArray", FindStringInArray},
|
||||
{"FindValueInArray", FindValueInArray},
|
||||
{NULL, NULL},
|
||||
};
|
||||
@@ -0,0 +1,309 @@
|
||||
/**
|
||||
* vim: set ts=4 :
|
||||
* =============================================================================
|
||||
* SourceMod
|
||||
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
|
||||
* =============================================================================
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, version 3.0, as published by the
|
||||
* Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* As a special exception, AlliedModders LLC gives you permission to link the
|
||||
* code of this program (as well as its derivative works) to "Half-Life 2," the
|
||||
* "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
|
||||
* by the Valve Corporation. You must obey the GNU General Public License in
|
||||
* all respects for all other code used. Additionally, AlliedModders LLC grants
|
||||
* this exception to all derivative works. AlliedModders LLC defines further
|
||||
* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
|
||||
* or <http://www.sourcemod.net/license.php>.
|
||||
*
|
||||
* Version: $Id$
|
||||
*/
|
||||
|
||||
#include <malloc.h>
|
||||
#include "sm_globals.h"
|
||||
#include "sm_stringutil.h"
|
||||
#include "CellArray.h"
|
||||
#include "HandleSys.h"
|
||||
|
||||
HandleType_t htCellStack;
|
||||
|
||||
class CellStackHelpers :
|
||||
public SMGlobalClass,
|
||||
public IHandleTypeDispatch
|
||||
{
|
||||
public: //SMGlobalClass
|
||||
void OnSourceModAllInitialized()
|
||||
{
|
||||
htCellStack = g_HandleSys.CreateType("CellStack", this, 0, NULL, NULL, g_pCoreIdent, NULL);
|
||||
}
|
||||
void OnSourceModShutdown()
|
||||
{
|
||||
g_HandleSys.RemoveType(htCellStack, g_pCoreIdent);
|
||||
}
|
||||
public: //IHandleTypeDispatch
|
||||
void OnHandleDestroy(HandleType_t type, void *object)
|
||||
{
|
||||
CellArray *array = (CellArray *)object;
|
||||
delete array;
|
||||
}
|
||||
bool GetHandleApproxSize(HandleType_t type, void *object, unsigned int *pSize)
|
||||
{
|
||||
CellArray *pArray = (CellArray *)object;
|
||||
*pSize = sizeof(CellArray) + pArray->mem_usage();
|
||||
return true;
|
||||
}
|
||||
} s_CellStackHelpers;
|
||||
|
||||
static cell_t CreateStack(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
if (params[1] < 1)
|
||||
{
|
||||
return pContext->ThrowNativeError("Invalid block size (must be > 0)");
|
||||
}
|
||||
|
||||
CellArray *array = new CellArray(params[1]);
|
||||
|
||||
Handle_t hndl = g_HandleSys.CreateHandle(htCellStack, array, pContext->GetIdentity(), g_pCoreIdent, NULL);
|
||||
if (!hndl)
|
||||
{
|
||||
delete array;
|
||||
}
|
||||
|
||||
return hndl;
|
||||
}
|
||||
|
||||
static cell_t PushStackCell(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
CellArray *array;
|
||||
HandleError err;
|
||||
HandleSecurity sec(pContext->GetIdentity(), g_pCoreIdent);
|
||||
|
||||
if ((err = g_HandleSys.ReadHandle(params[1], htCellStack, &sec, (void **)&array))
|
||||
!= HandleError_None)
|
||||
{
|
||||
return pContext->ThrowNativeError("Invalid Handle %x (error: %d)", params[1], err);
|
||||
}
|
||||
|
||||
cell_t *blk = array->push();
|
||||
if (!blk)
|
||||
{
|
||||
return pContext->ThrowNativeError("Failed to grow stack");
|
||||
}
|
||||
|
||||
*blk = params[2];
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static cell_t PushStackString(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
CellArray *array;
|
||||
HandleError err;
|
||||
HandleSecurity sec(pContext->GetIdentity(), g_pCoreIdent);
|
||||
|
||||
if ((err = g_HandleSys.ReadHandle(params[1], htCellStack, &sec, (void **)&array))
|
||||
!= HandleError_None)
|
||||
{
|
||||
return pContext->ThrowNativeError("Invalid Handle %x (error: %d)", params[1], err);
|
||||
}
|
||||
|
||||
cell_t *blk = array->push();
|
||||
if (!blk)
|
||||
{
|
||||
return pContext->ThrowNativeError("Failed to grow stack");
|
||||
}
|
||||
|
||||
char *str;
|
||||
pContext->LocalToString(params[2], &str);
|
||||
|
||||
strncopy((char *)blk, str, array->blocksize() * sizeof(cell_t));
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static cell_t PushStackArray(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
CellArray *array;
|
||||
HandleError err;
|
||||
HandleSecurity sec(pContext->GetIdentity(), g_pCoreIdent);
|
||||
|
||||
if ((err = g_HandleSys.ReadHandle(params[1], htCellStack, &sec, (void **)&array))
|
||||
!= HandleError_None)
|
||||
{
|
||||
return pContext->ThrowNativeError("Invalid Handle %x (error: %d)", params[1], err);
|
||||
}
|
||||
|
||||
cell_t *blk = array->push();
|
||||
if (!blk)
|
||||
{
|
||||
return pContext->ThrowNativeError("Failed to grow array");
|
||||
}
|
||||
|
||||
cell_t *addr;
|
||||
pContext->LocalToPhysAddr(params[2], &addr);
|
||||
|
||||
size_t indexes = array->blocksize();
|
||||
if (params[3] != -1 && (size_t)params[3] <= array->blocksize())
|
||||
{
|
||||
indexes = params[3];
|
||||
}
|
||||
|
||||
memcpy(blk, addr, sizeof(cell_t) * indexes);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static cell_t PopStackCell(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
size_t idx;
|
||||
HandleError err;
|
||||
CellArray *array;
|
||||
cell_t *blk, *buffer;
|
||||
HandleSecurity sec(pContext->GetIdentity(), g_pCoreIdent);
|
||||
|
||||
if ((err = g_HandleSys.ReadHandle(params[1], htCellStack, &sec, (void **)&array))
|
||||
!= HandleError_None)
|
||||
{
|
||||
return pContext->ThrowNativeError("Invalid Handle %x (error: %d)", params[1], err);
|
||||
}
|
||||
|
||||
if (array->size() == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
pContext->LocalToPhysAddr(params[2], &buffer);
|
||||
|
||||
blk = array->at(array->size() - 1);
|
||||
idx = (size_t)params[3];
|
||||
|
||||
if (params[4] == 0)
|
||||
{
|
||||
if (idx >= array->blocksize())
|
||||
{
|
||||
return pContext->ThrowNativeError("Invalid block %d (blocksize: %d)", idx, array->blocksize());
|
||||
}
|
||||
*buffer = blk[idx];
|
||||
}
|
||||
else
|
||||
{
|
||||
if (idx >= array->blocksize() * 4)
|
||||
{
|
||||
return pContext->ThrowNativeError("Invalid byte %d (blocksize: %d bytes)", idx, array->blocksize() * 4);
|
||||
}
|
||||
*buffer = (cell_t)*((char *)blk + idx);
|
||||
}
|
||||
|
||||
array->remove(array->size() - 1);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static cell_t PopStackString(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
HandleError err;
|
||||
CellArray *array;
|
||||
size_t idx, numWritten;
|
||||
cell_t *blk, *pWritten;
|
||||
HandleSecurity sec(pContext->GetIdentity(), g_pCoreIdent);
|
||||
|
||||
if ((err = g_HandleSys.ReadHandle(params[1], htCellStack, &sec, (void **)&array))
|
||||
!= HandleError_None)
|
||||
{
|
||||
return pContext->ThrowNativeError("Invalid Handle %x (error: %d)", params[1], err);
|
||||
}
|
||||
|
||||
if (array->size() == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
idx = array->size() - 1;
|
||||
blk = array->at(idx);
|
||||
pContext->StringToLocalUTF8(params[2], params[3], (char *)blk, &numWritten);
|
||||
pContext->LocalToPhysAddr(params[4], &pWritten);
|
||||
*pWritten = (cell_t)numWritten;
|
||||
array->remove(idx);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static cell_t PopStackArray(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
size_t idx;
|
||||
cell_t *blk;
|
||||
cell_t *addr;
|
||||
size_t indexes;
|
||||
HandleError err;
|
||||
CellArray *array;
|
||||
HandleSecurity sec(pContext->GetIdentity(), g_pCoreIdent);
|
||||
|
||||
if ((err = g_HandleSys.ReadHandle(params[1], htCellStack, &sec, (void **)&array))
|
||||
!= HandleError_None)
|
||||
{
|
||||
return pContext->ThrowNativeError("Invalid Handle %x (error: %d)", params[1], err);
|
||||
}
|
||||
|
||||
if (array->size() == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
idx = array->size() - 1;
|
||||
blk = array->at(idx);
|
||||
indexes = array->blocksize();
|
||||
|
||||
if (params[3] != -1 && (size_t)params[3] <= array->blocksize())
|
||||
{
|
||||
indexes = params[3];
|
||||
}
|
||||
|
||||
pContext->LocalToPhysAddr(params[2], &addr);
|
||||
memcpy(addr, blk, sizeof(cell_t) * indexes);
|
||||
array->remove(idx);
|
||||
|
||||
return indexes;
|
||||
}
|
||||
|
||||
static cell_t IsStackEmpty(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
HandleError err;
|
||||
CellArray *array;
|
||||
HandleSecurity sec(pContext->GetIdentity(), g_pCoreIdent);
|
||||
|
||||
if ((err = g_HandleSys.ReadHandle(params[1], htCellStack, &sec, (void **)&array))
|
||||
!= HandleError_None)
|
||||
{
|
||||
return pContext->ThrowNativeError("Invalid Handle %x (error: %d)", params[1], err);
|
||||
}
|
||||
|
||||
if (array->size() == 0)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
REGISTER_NATIVES(cellStackNatives)
|
||||
{
|
||||
{"CreateStack", CreateStack},
|
||||
{"IsStackEmpty", IsStackEmpty},
|
||||
{"PopStackArray", PopStackArray},
|
||||
{"PopStackCell", PopStackCell},
|
||||
{"PopStackString", PopStackString},
|
||||
{"PushStackArray", PushStackArray},
|
||||
{"PushStackCell", PushStackCell},
|
||||
{"PushStackString", PushStackString},
|
||||
{NULL, NULL},
|
||||
};
|
||||
@@ -0,0 +1,532 @@
|
||||
/**
|
||||
* vim: set ts=4 :
|
||||
* =============================================================================
|
||||
* SourceMod
|
||||
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
|
||||
* =============================================================================
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, version 3.0, as published by the
|
||||
* Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* As a special exception, AlliedModders LLC gives you permission to link the
|
||||
* code of this program (as well as its derivative works) to "Half-Life 2," the
|
||||
* "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
|
||||
* by the Valve Corporation. You must obey the GNU General Public License in
|
||||
* all respects for all other code used. Additionally, AlliedModders LLC grants
|
||||
* this exception to all derivative works. AlliedModders LLC defines further
|
||||
* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
|
||||
* or <http://www.sourcemod.net/license.php>.
|
||||
*
|
||||
* Version: $Id$
|
||||
*/
|
||||
|
||||
#include <malloc.h>
|
||||
#include "sm_globals.h"
|
||||
#include "sm_stringutil.h"
|
||||
#include "HandleSys.h"
|
||||
#include "sm_trie_tpl.h"
|
||||
|
||||
HandleType_t htCellTrie;
|
||||
|
||||
enum TrieNodeType
|
||||
{
|
||||
TrieNode_Cell,
|
||||
TrieNode_CellArray,
|
||||
TrieNode_String,
|
||||
};
|
||||
|
||||
struct SmartTrieNode
|
||||
{
|
||||
SmartTrieNode()
|
||||
{
|
||||
ptr = NULL;
|
||||
type = TrieNode_Cell;
|
||||
}
|
||||
SmartTrieNode(const SmartTrieNode &obj)
|
||||
{
|
||||
type = obj.type;
|
||||
ptr = obj.ptr;
|
||||
data = obj.data;
|
||||
data_len = obj.data_len;
|
||||
}
|
||||
SmartTrieNode & operator =(const SmartTrieNode &src)
|
||||
{
|
||||
type = src.type;
|
||||
ptr = src.ptr;
|
||||
data = src.data;
|
||||
data_len = src.data_len;
|
||||
return *this;
|
||||
}
|
||||
TrieNodeType type;
|
||||
cell_t *ptr;
|
||||
cell_t data;
|
||||
cell_t data_len;
|
||||
};
|
||||
|
||||
struct CellTrie
|
||||
{
|
||||
KTrie<SmartTrieNode> trie;
|
||||
cell_t mem_usage;
|
||||
};
|
||||
|
||||
class TrieHelpers :
|
||||
public SMGlobalClass,
|
||||
public IHandleTypeDispatch
|
||||
{
|
||||
public: //SMGlobalClass
|
||||
void OnSourceModAllInitialized()
|
||||
{
|
||||
htCellTrie = g_HandleSys.CreateType("Trie", this, 0, NULL, NULL, g_pCoreIdent, NULL);
|
||||
}
|
||||
void OnSourceModShutdown()
|
||||
{
|
||||
g_HandleSys.RemoveType(htCellTrie, g_pCoreIdent);
|
||||
}
|
||||
public: //IHandleTypeDispatch
|
||||
static void DestroySmartTrieNode(SmartTrieNode *pNode)
|
||||
{
|
||||
free(pNode->ptr);
|
||||
}
|
||||
void OnHandleDestroy(HandleType_t type, void *object)
|
||||
{
|
||||
CellTrie *pTrie = (CellTrie *)object;
|
||||
|
||||
pTrie->trie.run_destructor(DestroySmartTrieNode);
|
||||
|
||||
delete pTrie;
|
||||
}
|
||||
bool GetHandleApproxSize(HandleType_t type, void *object, unsigned int *pSize)
|
||||
{
|
||||
CellTrie *pArray = (CellTrie *)object;
|
||||
*pSize = sizeof(CellTrie) + pArray->mem_usage + pArray->trie.mem_usage();
|
||||
return true;
|
||||
}
|
||||
} s_CellTrieHelpers;
|
||||
|
||||
static cell_t CreateTrie(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
CellTrie *pTrie = new CellTrie;
|
||||
Handle_t hndl;
|
||||
|
||||
pTrie->mem_usage = 0;
|
||||
|
||||
if ((hndl = g_HandleSys.CreateHandle(htCellTrie, pTrie, pContext->GetIdentity(), g_pCoreIdent, NULL))
|
||||
== BAD_HANDLE)
|
||||
{
|
||||
delete pTrie;
|
||||
return BAD_HANDLE;
|
||||
}
|
||||
|
||||
return hndl;
|
||||
}
|
||||
|
||||
static void UpdateNodeCells(CellTrie *pTrie, SmartTrieNode *pData, const cell_t *cells, cell_t num_cells)
|
||||
{
|
||||
if (num_cells == 1)
|
||||
{
|
||||
pData->data = *cells;
|
||||
pData->type = TrieNode_Cell;
|
||||
}
|
||||
else
|
||||
{
|
||||
pData->type = TrieNode_CellArray;
|
||||
if (pData->ptr == NULL)
|
||||
{
|
||||
pData->ptr = (cell_t *)malloc(num_cells * sizeof(cell_t));
|
||||
pData->data_len = num_cells;
|
||||
pTrie->mem_usage += (pData->data_len * sizeof(cell_t));
|
||||
}
|
||||
else if (pData->data_len < num_cells)
|
||||
{
|
||||
pData->ptr = (cell_t *)realloc(pData->ptr, num_cells * sizeof(cell_t));
|
||||
pTrie->mem_usage += (num_cells - pData->data_len) * sizeof(cell_t);
|
||||
pData->data_len = num_cells;
|
||||
}
|
||||
if (num_cells != 0)
|
||||
{
|
||||
memcpy(pData->ptr, cells, sizeof(cell_t) * num_cells);
|
||||
}
|
||||
pData->data = num_cells;
|
||||
}
|
||||
}
|
||||
|
||||
static void UpdateNodeString(CellTrie *pTrie, SmartTrieNode *pData, const char *str)
|
||||
{
|
||||
size_t len = strlen(str);
|
||||
cell_t num_cells = (len + sizeof(cell_t)) / sizeof(cell_t);
|
||||
|
||||
if (pData->ptr == NULL)
|
||||
{
|
||||
pData->ptr = (cell_t *)malloc(num_cells * sizeof(cell_t));
|
||||
pData->data_len = num_cells;
|
||||
pTrie->mem_usage += (pData->data_len * sizeof(cell_t));
|
||||
}
|
||||
else if (pData->data_len < num_cells)
|
||||
{
|
||||
pData->ptr = (cell_t *)realloc(pData->ptr, num_cells * sizeof(cell_t));
|
||||
pTrie->mem_usage += (num_cells - pData->data_len) * sizeof(cell_t);
|
||||
pData->data_len = num_cells;
|
||||
}
|
||||
|
||||
strcpy((char *)pData->ptr, str);
|
||||
pData->data = len;
|
||||
pData->type = TrieNode_String;
|
||||
}
|
||||
|
||||
static cell_t SetTrieValue(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
Handle_t hndl;
|
||||
CellTrie *pTrie;
|
||||
HandleError err;
|
||||
HandleSecurity sec = HandleSecurity(pContext->GetIdentity(), g_pCoreIdent);
|
||||
|
||||
hndl = params[1];
|
||||
|
||||
if ((err = g_HandleSys.ReadHandle(hndl, htCellTrie, &sec, (void **)&pTrie))
|
||||
!= HandleError_None)
|
||||
{
|
||||
return pContext->ThrowNativeError("Invalid Handle %x (error %d)", hndl, err);
|
||||
}
|
||||
|
||||
char *key;
|
||||
pContext->LocalToString(params[2], &key);
|
||||
|
||||
SmartTrieNode *pNode;
|
||||
if ((pNode = pTrie->trie.retrieve(key)) == NULL)
|
||||
{
|
||||
SmartTrieNode node;
|
||||
UpdateNodeCells(pTrie, &node, ¶ms[3], 1);
|
||||
return pTrie->trie.insert(key, node) ? 1 : 0;
|
||||
}
|
||||
|
||||
if (!params[4])
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
UpdateNodeCells(pTrie, pNode, ¶ms[3], 1);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static cell_t SetTrieArray(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
Handle_t hndl;
|
||||
CellTrie *pTrie;
|
||||
HandleError err;
|
||||
HandleSecurity sec = HandleSecurity(pContext->GetIdentity(), g_pCoreIdent);
|
||||
|
||||
hndl = params[1];
|
||||
|
||||
if ((err = g_HandleSys.ReadHandle(hndl, htCellTrie, &sec, (void **)&pTrie))
|
||||
!= HandleError_None)
|
||||
{
|
||||
return pContext->ThrowNativeError("Invalid Handle %x (error %d)", hndl, err);
|
||||
}
|
||||
|
||||
if (params[4] < 0)
|
||||
{
|
||||
return pContext->ThrowNativeError("Invalid array size: %d", params[4]);
|
||||
}
|
||||
|
||||
char *key;
|
||||
cell_t *array;
|
||||
pContext->LocalToString(params[2], &key);
|
||||
pContext->LocalToPhysAddr(params[3], &array);
|
||||
|
||||
SmartTrieNode *pNode;
|
||||
if ((pNode = pTrie->trie.retrieve(key)) == NULL)
|
||||
{
|
||||
SmartTrieNode node;
|
||||
UpdateNodeCells(pTrie, &node, array, params[4]);
|
||||
if (!pTrie->trie.insert(key, node))
|
||||
{
|
||||
free(node.ptr);
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!params[4])
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
UpdateNodeCells(pTrie, pNode, array, params[4]);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static cell_t SetTrieString(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
Handle_t hndl;
|
||||
CellTrie *pTrie;
|
||||
HandleError err;
|
||||
HandleSecurity sec = HandleSecurity(pContext->GetIdentity(), g_pCoreIdent);
|
||||
|
||||
hndl = params[1];
|
||||
|
||||
if ((err = g_HandleSys.ReadHandle(hndl, htCellTrie, &sec, (void **)&pTrie))
|
||||
!= HandleError_None)
|
||||
{
|
||||
return pContext->ThrowNativeError("Invalid Handle %x (error %d)", hndl, err);
|
||||
}
|
||||
|
||||
char *key, *val;
|
||||
pContext->LocalToString(params[2], &key);
|
||||
pContext->LocalToString(params[3], &val);
|
||||
|
||||
SmartTrieNode *pNode;
|
||||
if ((pNode = pTrie->trie.retrieve(key)) == NULL)
|
||||
{
|
||||
SmartTrieNode node;
|
||||
UpdateNodeString(pTrie, &node, val);
|
||||
if (!pTrie->trie.insert(key, node))
|
||||
{
|
||||
free(node.ptr);
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!params[4])
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
UpdateNodeString(pTrie, pNode, val);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static cell_t RemoveFromTrie(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
Handle_t hndl;
|
||||
CellTrie *pTrie;
|
||||
HandleError err;
|
||||
HandleSecurity sec = HandleSecurity(pContext->GetIdentity(), g_pCoreIdent);
|
||||
|
||||
hndl = params[1];
|
||||
|
||||
if ((err = g_HandleSys.ReadHandle(hndl, htCellTrie, &sec, (void **)&pTrie))
|
||||
!= HandleError_None)
|
||||
{
|
||||
return pContext->ThrowNativeError("Invalid Handle %x (error %d)", hndl, err);
|
||||
}
|
||||
|
||||
char *key;
|
||||
pContext->LocalToString(params[2], &key);
|
||||
|
||||
SmartTrieNode *pNode;
|
||||
if ((pNode = pTrie->trie.retrieve(key)) == NULL)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
free(pNode->ptr);
|
||||
pNode->ptr = NULL;
|
||||
|
||||
return pTrie->trie.remove(key) ? 1 : 0;
|
||||
}
|
||||
|
||||
static cell_t ClearTrie(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
Handle_t hndl;
|
||||
CellTrie *pTrie;
|
||||
HandleError err;
|
||||
HandleSecurity sec = HandleSecurity(pContext->GetIdentity(), g_pCoreIdent);
|
||||
|
||||
hndl = params[1];
|
||||
|
||||
if ((err = g_HandleSys.ReadHandle(hndl, htCellTrie, &sec, (void **)&pTrie))
|
||||
!= HandleError_None)
|
||||
{
|
||||
return pContext->ThrowNativeError("Invalid Handle %x (error %d)", hndl, err);
|
||||
}
|
||||
|
||||
pTrie->trie.run_destructor(TrieHelpers::DestroySmartTrieNode);
|
||||
pTrie->trie.clear();
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static cell_t GetTrieValue(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
Handle_t hndl;
|
||||
CellTrie *pTrie;
|
||||
HandleError err;
|
||||
HandleSecurity sec = HandleSecurity(pContext->GetIdentity(), g_pCoreIdent);
|
||||
|
||||
hndl = params[1];
|
||||
|
||||
if ((err = g_HandleSys.ReadHandle(hndl, htCellTrie, &sec, (void **)&pTrie))
|
||||
!= HandleError_None)
|
||||
{
|
||||
return pContext->ThrowNativeError("Invalid Handle %x (error %d)", hndl, err);
|
||||
}
|
||||
|
||||
char *key;
|
||||
cell_t *pValue;
|
||||
pContext->LocalToString(params[2], &key);
|
||||
pContext->LocalToPhysAddr(params[3], &pValue);
|
||||
|
||||
SmartTrieNode *pNode;
|
||||
if ((pNode = pTrie->trie.retrieve(key)) == NULL)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (pNode->type == TrieNode_Cell)
|
||||
{
|
||||
*pValue = pNode->data;
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static cell_t GetTrieArray(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
Handle_t hndl;
|
||||
CellTrie *pTrie;
|
||||
HandleError err;
|
||||
HandleSecurity sec = HandleSecurity(pContext->GetIdentity(), g_pCoreIdent);
|
||||
|
||||
hndl = params[1];
|
||||
|
||||
if ((err = g_HandleSys.ReadHandle(hndl, htCellTrie, &sec, (void **)&pTrie))
|
||||
!= HandleError_None)
|
||||
{
|
||||
return pContext->ThrowNativeError("Invalid Handle %x (error %d)", hndl, err);
|
||||
}
|
||||
|
||||
if (params[4] < 0)
|
||||
{
|
||||
return pContext->ThrowNativeError("Invalid array size: %d", params[4]);
|
||||
}
|
||||
|
||||
char *key;
|
||||
cell_t *pValue, *pSize;
|
||||
pContext->LocalToString(params[2], &key);
|
||||
pContext->LocalToPhysAddr(params[3], &pValue);
|
||||
pContext->LocalToPhysAddr(params[5], &pSize);
|
||||
|
||||
SmartTrieNode *pNode;
|
||||
if ((pNode = pTrie->trie.retrieve(key)) == NULL
|
||||
|| pNode->type != TrieNode_CellArray)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (pNode->ptr == NULL)
|
||||
{
|
||||
*pSize = 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (pNode->data > params[4])
|
||||
{
|
||||
*pSize = params[4];
|
||||
}
|
||||
else if (params[4] != 0)
|
||||
{
|
||||
*pSize = pNode->data;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
memcpy(pValue, pNode->ptr, sizeof(cell_t) * pSize[0]);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static cell_t GetTrieString(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
Handle_t hndl;
|
||||
CellTrie *pTrie;
|
||||
HandleError err;
|
||||
HandleSecurity sec = HandleSecurity(pContext->GetIdentity(), g_pCoreIdent);
|
||||
|
||||
hndl = params[1];
|
||||
|
||||
if ((err = g_HandleSys.ReadHandle(hndl, htCellTrie, &sec, (void **)&pTrie))
|
||||
!= HandleError_None)
|
||||
{
|
||||
return pContext->ThrowNativeError("Invalid Handle %x (error %d)", hndl, err);
|
||||
}
|
||||
|
||||
if (params[4] < 0)
|
||||
{
|
||||
return pContext->ThrowNativeError("Invalid buffer size: %d", params[4]);
|
||||
}
|
||||
|
||||
char *key;
|
||||
cell_t *pSize;
|
||||
pContext->LocalToString(params[2], &key);
|
||||
pContext->LocalToPhysAddr(params[5], &pSize);
|
||||
|
||||
SmartTrieNode *pNode;
|
||||
if ((pNode = pTrie->trie.retrieve(key)) == NULL
|
||||
|| pNode->type != TrieNode_String)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (pNode->ptr == NULL)
|
||||
{
|
||||
*pSize = 0;
|
||||
pContext->StringToLocal(params[3], params[4], "");
|
||||
return 1;
|
||||
}
|
||||
|
||||
size_t written;
|
||||
pContext->StringToLocalUTF8(params[3], params[4], (char *)pNode->ptr, &written);
|
||||
|
||||
*pSize = (cell_t)written;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static cell_t GetTrieSize(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
Handle_t hndl;
|
||||
CellTrie *pTrie;
|
||||
HandleError err;
|
||||
HandleSecurity sec = HandleSecurity(pContext->GetIdentity(), g_pCoreIdent);
|
||||
|
||||
hndl = params[1];
|
||||
|
||||
if ((err = g_HandleSys.ReadHandle(hndl, htCellTrie, &sec, (void **)&pTrie))
|
||||
!= HandleError_None)
|
||||
{
|
||||
return pContext->ThrowNativeError("Invalid Handle %x (error %d)", hndl, err);
|
||||
}
|
||||
|
||||
return pTrie->trie.size();
|
||||
}
|
||||
|
||||
REGISTER_NATIVES(trieNatives)
|
||||
{
|
||||
{"ClearTrie", ClearTrie},
|
||||
{"CreateTrie", CreateTrie},
|
||||
{"GetTrieArray", GetTrieArray},
|
||||
{"GetTrieString", GetTrieString},
|
||||
{"GetTrieValue", GetTrieValue},
|
||||
{"RemoveFromTrie", RemoveFromTrie},
|
||||
{"SetTrieArray", SetTrieArray},
|
||||
{"SetTrieString", SetTrieString},
|
||||
{"SetTrieValue", SetTrieValue},
|
||||
{"GetTrieSize", GetTrieSize},
|
||||
{NULL, NULL},
|
||||
};
|
||||
@@ -0,0 +1,401 @@
|
||||
/**
|
||||
* vim: set ts=4 :
|
||||
* =============================================================================
|
||||
* SourceMod
|
||||
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
|
||||
* =============================================================================
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, version 3.0, as published by the
|
||||
* Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* As a special exception, AlliedModders LLC gives you permission to link the
|
||||
* code of this program (as well as its derivative works) to "Half-Life 2," the
|
||||
* "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
|
||||
* by the Valve Corporation. You must obey the GNU General Public License in
|
||||
* all respects for all other code used. Additionally, AlliedModders LLC grants
|
||||
* this exception to all derivative works. AlliedModders LLC defines further
|
||||
* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
|
||||
* or <http://www.sourcemod.net/license.php>.
|
||||
*
|
||||
* Version: $Id$
|
||||
*/
|
||||
|
||||
#include "sm_globals.h"
|
||||
#include "sm_stringutil.h"
|
||||
#include "HalfLife2.h"
|
||||
#include "PlayerManager.h"
|
||||
#include "ForwardSys.h"
|
||||
|
||||
#define BANFLAG_AUTO (1<<0) /**< Auto-detects whether to ban by steamid or IP */
|
||||
#define BANFLAG_IP (1<<1) /**< Always ban by IP address */
|
||||
#define BANFLAG_AUTHID (1<<2) /**< Ban by SteamID */
|
||||
#define BANFLAG_NOKICK (1<<3) /**< Does not kick the client */
|
||||
#define BANFLAG_NOWRITE (1<<4) /**< Ban is not written to SourceDS's files if permanent */
|
||||
|
||||
IForward *g_pOnBanClient = NULL;
|
||||
IForward *g_pOnBanIdentity = NULL;
|
||||
IForward *g_pOnRemoveBan = NULL;
|
||||
|
||||
class BanNativeHelpers : public SMGlobalClass
|
||||
{
|
||||
public:
|
||||
void OnSourceModAllInitialized()
|
||||
{
|
||||
g_pOnBanClient = g_Forwards.CreateForward(
|
||||
"OnBanClient",
|
||||
ET_Event,
|
||||
7,
|
||||
NULL,
|
||||
Param_Cell,
|
||||
Param_Cell,
|
||||
Param_Cell,
|
||||
Param_String,
|
||||
Param_String,
|
||||
Param_String,
|
||||
Param_Cell);
|
||||
g_pOnBanIdentity = g_Forwards.CreateForward(
|
||||
"OnBanIdentity",
|
||||
ET_Event,
|
||||
6,
|
||||
NULL,
|
||||
Param_String,
|
||||
Param_Cell,
|
||||
Param_Cell,
|
||||
Param_String,
|
||||
Param_String,
|
||||
Param_Cell);
|
||||
g_pOnRemoveBan = g_Forwards.CreateForward(
|
||||
"OnRemoveBan",
|
||||
ET_Event,
|
||||
4,
|
||||
NULL,
|
||||
Param_String,
|
||||
Param_Cell,
|
||||
Param_String,
|
||||
Param_Cell);
|
||||
}
|
||||
void OnSourceModShutdown()
|
||||
{
|
||||
g_Forwards.ReleaseForward(g_pOnBanClient);
|
||||
g_Forwards.ReleaseForward(g_pOnBanIdentity);
|
||||
g_Forwards.ReleaseForward(g_pOnRemoveBan);
|
||||
|
||||
g_pOnBanClient = NULL;
|
||||
g_pOnBanIdentity = NULL;
|
||||
g_pOnRemoveBan = NULL;
|
||||
}
|
||||
} s_BanNativeHelpers;
|
||||
|
||||
|
||||
static cell_t BanIdentity(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
char *r_identity, *ban_reason, *ban_cmd;
|
||||
int ban_time, ban_flags, ban_source;
|
||||
|
||||
pContext->LocalToString(params[1], &r_identity);
|
||||
pContext->LocalToString(params[4], &ban_reason);
|
||||
pContext->LocalToString(params[5], &ban_cmd);
|
||||
ban_time = params[2];
|
||||
ban_flags = params[3];
|
||||
ban_source = params[6];
|
||||
|
||||
/* Make sure we can ban by one of the two methods! */
|
||||
bool ban_by_ip = ((ban_flags & BANFLAG_IP) == BANFLAG_IP);
|
||||
if (!ban_by_ip && ((ban_flags & BANFLAG_AUTHID) != BANFLAG_AUTHID))
|
||||
{
|
||||
return pContext->ThrowNativeError("No valid ban flags specified");
|
||||
}
|
||||
|
||||
/* Sanitize the input */
|
||||
char identity[64];
|
||||
strncopy(identity, r_identity, sizeof(identity));
|
||||
UTIL_ReplaceAll(identity, sizeof(identity), ";", "");
|
||||
|
||||
cell_t handled = 0;
|
||||
if (ban_cmd[0] != '\0' && g_pOnBanIdentity->GetFunctionCount() > 0)
|
||||
{
|
||||
g_pOnBanIdentity->PushString(identity);
|
||||
g_pOnBanIdentity->PushCell(ban_time);
|
||||
g_pOnBanIdentity->PushCell(ban_flags);
|
||||
g_pOnBanIdentity->PushString(ban_reason);
|
||||
g_pOnBanIdentity->PushString(ban_cmd);
|
||||
g_pOnBanIdentity->PushCell(ban_source);
|
||||
g_pOnBanIdentity->Execute(&handled);
|
||||
}
|
||||
|
||||
if (!handled)
|
||||
{
|
||||
bool write_ban = ((ban_flags & BANFLAG_NOWRITE) != BANFLAG_NOWRITE);
|
||||
|
||||
char command[256];
|
||||
if (ban_by_ip)
|
||||
{
|
||||
UTIL_Format(
|
||||
command,
|
||||
sizeof(command),
|
||||
"addip %d %s\n",
|
||||
ban_time,
|
||||
identity);
|
||||
engine->ServerCommand(command);
|
||||
|
||||
if (write_ban && ban_time == 0)
|
||||
{
|
||||
engine->ServerCommand("writeip\n");
|
||||
}
|
||||
}
|
||||
else if (!g_HL2.IsLANServer())
|
||||
{
|
||||
UTIL_Format(
|
||||
command,
|
||||
sizeof(command),
|
||||
"banid %d %s\n",
|
||||
ban_time,
|
||||
identity);
|
||||
engine->ServerCommand(command);
|
||||
|
||||
if (write_ban && ban_time == 0)
|
||||
{
|
||||
engine->ServerCommand("writeid\n");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static cell_t RemoveBan(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
char *r_identity, *ban_cmd;
|
||||
int ban_flags, ban_source;
|
||||
|
||||
pContext->LocalToString(params[1], &r_identity);
|
||||
pContext->LocalToString(params[3], &ban_cmd);
|
||||
ban_flags = params[2];
|
||||
ban_source = params[4];
|
||||
|
||||
/* Make sure we can ban by one of the two methods! */
|
||||
bool ban_by_ip = ((ban_flags & BANFLAG_IP) == BANFLAG_IP);
|
||||
if (!ban_by_ip && ((ban_flags & BANFLAG_AUTHID) != BANFLAG_AUTHID))
|
||||
{
|
||||
return pContext->ThrowNativeError("No valid ban flags specified");
|
||||
}
|
||||
|
||||
char identity[64];
|
||||
strncopy(identity, r_identity, sizeof(identity));
|
||||
UTIL_ReplaceAll(identity, sizeof(identity), ";", "");
|
||||
|
||||
cell_t handled = 0;
|
||||
if (ban_cmd[0] != '\0' && g_pOnRemoveBan->GetFunctionCount() > 0)
|
||||
{
|
||||
g_pOnRemoveBan->PushString(identity);
|
||||
g_pOnRemoveBan->PushCell(ban_flags);
|
||||
g_pOnRemoveBan->PushString(ban_cmd);
|
||||
g_pOnRemoveBan->PushCell(ban_source);
|
||||
g_pOnRemoveBan->Execute(&handled);
|
||||
}
|
||||
|
||||
char command[256];
|
||||
if (ban_by_ip)
|
||||
{
|
||||
if (!handled)
|
||||
{
|
||||
UTIL_Format(
|
||||
command,
|
||||
sizeof(command),
|
||||
"removeip %s\n",
|
||||
identity);
|
||||
engine->ServerCommand(command);
|
||||
engine->ServerCommand("writeip\n");
|
||||
}
|
||||
}
|
||||
else if (!g_HL2.IsLANServer())
|
||||
{
|
||||
if (!handled)
|
||||
{
|
||||
UTIL_Format(
|
||||
command,
|
||||
sizeof(command),
|
||||
"removeid %s\n",
|
||||
identity);
|
||||
engine->ServerCommand(command);
|
||||
engine->ServerCommand("writeid\n");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static cell_t BanClient(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
const char *kick_message;
|
||||
char *ban_reason, *ban_cmd;
|
||||
int client, ban_flags, ban_source, ban_time;
|
||||
|
||||
client = params[1];
|
||||
|
||||
CPlayer *pPlayer = g_Players.GetPlayerByIndex(client);
|
||||
if (!pPlayer || !pPlayer->IsConnected())
|
||||
{
|
||||
return pContext->ThrowNativeError("Client index %d is invalid", client);
|
||||
}
|
||||
|
||||
if (pPlayer->IsFakeClient())
|
||||
{
|
||||
return pContext->ThrowNativeError("Cannot ban fake client %d", client);
|
||||
}
|
||||
|
||||
pContext->LocalToString(params[4], &ban_reason);
|
||||
pContext->LocalToString(params[5], (char **)&kick_message);
|
||||
pContext->LocalToString(params[6], &ban_cmd);
|
||||
ban_time = params[2];
|
||||
ban_flags = params[3];
|
||||
ban_source = params[7];
|
||||
|
||||
/* Check how we should ban the player */
|
||||
if ((ban_flags & BANFLAG_AUTO) == BANFLAG_AUTO)
|
||||
{
|
||||
if (g_HL2.IsLANServer() || !pPlayer->IsAuthorized())
|
||||
{
|
||||
ban_flags |= BANFLAG_IP;
|
||||
ban_flags &= ~BANFLAG_AUTHID;
|
||||
}
|
||||
else
|
||||
{
|
||||
ban_flags |= BANFLAG_AUTHID;
|
||||
ban_flags &= ~BANFLAG_IP;
|
||||
}
|
||||
}
|
||||
else if ((ban_flags & BANFLAG_IP) == BANFLAG_IP)
|
||||
{
|
||||
ban_flags |= BANFLAG_IP;
|
||||
ban_flags &= ~BANFLAG_AUTHID;
|
||||
}
|
||||
else if ((ban_flags & BANFLAG_AUTHID) == BANFLAG_AUTHID)
|
||||
{
|
||||
if (pPlayer->IsAuthorized())
|
||||
{
|
||||
ban_flags |= BANFLAG_AUTHID;
|
||||
ban_flags &= ~BANFLAG_IP;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return pContext->ThrowNativeError("No valid ban method flags specified");
|
||||
}
|
||||
|
||||
cell_t handled = 0;
|
||||
if (ban_cmd[0] != '\0' && g_pOnBanClient->GetFunctionCount() > 0)
|
||||
{
|
||||
g_pOnBanClient->PushCell(client);
|
||||
g_pOnBanClient->PushCell(ban_time);
|
||||
g_pOnBanClient->PushCell(ban_flags);
|
||||
g_pOnBanClient->PushString(ban_reason);
|
||||
g_pOnBanClient->PushString(kick_message);
|
||||
g_pOnBanClient->PushString(ban_cmd);
|
||||
g_pOnBanClient->PushCell(ban_source);
|
||||
g_pOnBanClient->Execute(&handled);
|
||||
}
|
||||
|
||||
/* Sanitize the kick message */
|
||||
if (kick_message[0] == '\0')
|
||||
{
|
||||
kick_message = "Kicked";
|
||||
}
|
||||
|
||||
if (!handled)
|
||||
{
|
||||
if ((ban_flags & BANFLAG_IP) == BANFLAG_IP)
|
||||
{
|
||||
/* Get the IP address and strip the port */
|
||||
char ip[24], *ptr;
|
||||
strncopy(ip, pPlayer->GetIPAddress(), sizeof(ip));
|
||||
if ((ptr = strchr(ip, ':')) != NULL)
|
||||
{
|
||||
*ptr = '\0';
|
||||
}
|
||||
|
||||
/* Tell the server to ban the ip */
|
||||
char command[256];
|
||||
UTIL_Format(
|
||||
command,
|
||||
sizeof(command),
|
||||
"addip %d %s\n",
|
||||
ban_time,
|
||||
ip);
|
||||
|
||||
/* Kick, then ban */
|
||||
if ((ban_flags & BANFLAG_NOKICK) != BANFLAG_NOKICK)
|
||||
{
|
||||
pPlayer->Kick(kick_message);
|
||||
}
|
||||
engine->ServerCommand(command);
|
||||
|
||||
/* Physically write the ban */
|
||||
if ((ban_time == 0) && ((ban_flags & BANFLAG_NOWRITE) != BANFLAG_NOWRITE))
|
||||
{
|
||||
engine->ServerCommand("writeip\n");
|
||||
}
|
||||
}
|
||||
else if ((ban_flags & BANFLAG_AUTHID) == BANFLAG_AUTHID)
|
||||
{
|
||||
/* Tell the server to ban the auth string */
|
||||
char command[256];
|
||||
UTIL_Format(
|
||||
command,
|
||||
sizeof(command),
|
||||
"banid %d %s\n",
|
||||
ban_time,
|
||||
pPlayer->GetAuthString());
|
||||
|
||||
/* Kick, then ban */
|
||||
if ((ban_flags & BANFLAG_NOKICK) != BANFLAG_NOKICK)
|
||||
{
|
||||
pPlayer->Kick(kick_message);
|
||||
}
|
||||
engine->ServerCommand(command);
|
||||
|
||||
/* Physically write the ban if it's permanent */
|
||||
if ((ban_time == 0) && ((ban_flags & BANFLAG_NOWRITE) != BANFLAG_NOWRITE))
|
||||
{
|
||||
engine->ServerCommand("writeid\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
else if ((ban_flags & BANFLAG_NOKICK) != BANFLAG_NOKICK)
|
||||
{
|
||||
pPlayer->Kick(kick_message);
|
||||
}
|
||||
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
REGISTER_NATIVES(banNatives)
|
||||
{
|
||||
{"BanClient", BanClient},
|
||||
{"BanIdentity", BanIdentity},
|
||||
{"RemoveBan", RemoveBan},
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
@@ -0,0 +1,705 @@
|
||||
/**
|
||||
* vim: set ts=4 :
|
||||
* =============================================================================
|
||||
* SourceMod
|
||||
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
|
||||
* =============================================================================
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, version 3.0, as published by the
|
||||
* Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* As a special exception, AlliedModders LLC gives you permission to link the
|
||||
* code of this program (as well as its derivative works) to "Half-Life 2," the
|
||||
* "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
|
||||
* by the Valve Corporation. You must obey the GNU General Public License in
|
||||
* all respects for all other code used. Additionally, AlliedModders LLC grants
|
||||
* this exception to all derivative works. AlliedModders LLC defines further
|
||||
* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
|
||||
* or <http://www.sourcemod.net/license.php>.
|
||||
*
|
||||
* Version: $Id$
|
||||
*/
|
||||
|
||||
#include "sourcemod.h"
|
||||
#include "HandleSys.h"
|
||||
#include <bitbuf.h>
|
||||
#include <vector.h>
|
||||
|
||||
static cell_t smn_BfWriteBool(IPluginContext *pCtx, const cell_t *params)
|
||||
{
|
||||
Handle_t hndl = static_cast<Handle_t>(params[1]);
|
||||
HandleError herr;
|
||||
HandleSecurity sec;
|
||||
bf_write *pBitBuf;
|
||||
|
||||
sec.pOwner = NULL;
|
||||
sec.pIdentity = g_pCoreIdent;
|
||||
|
||||
if ((herr=g_HandleSys.ReadHandle(hndl, g_WrBitBufType, &sec, (void **)&pBitBuf))
|
||||
!= HandleError_None)
|
||||
{
|
||||
return pCtx->ThrowNativeError("Invalid bit buffer handle %x (error %d)", hndl, herr);
|
||||
}
|
||||
|
||||
pBitBuf->WriteOneBit(params[2]);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static cell_t smn_BfWriteByte(IPluginContext *pCtx, const cell_t *params)
|
||||
{
|
||||
Handle_t hndl = static_cast<Handle_t>(params[1]);
|
||||
HandleError herr;
|
||||
HandleSecurity sec;
|
||||
bf_write *pBitBuf;
|
||||
|
||||
sec.pOwner = NULL;
|
||||
sec.pIdentity = g_pCoreIdent;
|
||||
|
||||
if ((herr=g_HandleSys.ReadHandle(hndl, g_WrBitBufType, &sec, (void **)&pBitBuf))
|
||||
!= HandleError_None)
|
||||
{
|
||||
return pCtx->ThrowNativeError("Invalid bit buffer handle %x (error %d)", hndl, herr);
|
||||
}
|
||||
|
||||
pBitBuf->WriteByte(params[2]);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static cell_t smn_BfWriteChar(IPluginContext *pCtx, const cell_t *params)
|
||||
{
|
||||
Handle_t hndl = static_cast<Handle_t>(params[1]);
|
||||
HandleError herr;
|
||||
HandleSecurity sec;
|
||||
bf_write *pBitBuf;
|
||||
|
||||
sec.pOwner = NULL;
|
||||
sec.pIdentity = g_pCoreIdent;
|
||||
|
||||
if ((herr=g_HandleSys.ReadHandle(hndl, g_WrBitBufType, &sec, (void **)&pBitBuf))
|
||||
!= HandleError_None)
|
||||
{
|
||||
return pCtx->ThrowNativeError("Invalid bit buffer handle %x (error %d)", hndl, herr);
|
||||
}
|
||||
|
||||
pBitBuf->WriteChar(params[2]);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static cell_t smn_BfWriteShort(IPluginContext *pCtx, const cell_t *params)
|
||||
{
|
||||
Handle_t hndl = static_cast<Handle_t>(params[1]);
|
||||
HandleError herr;
|
||||
HandleSecurity sec;
|
||||
bf_write *pBitBuf;
|
||||
|
||||
sec.pOwner = NULL;
|
||||
sec.pIdentity = g_pCoreIdent;
|
||||
|
||||
if ((herr=g_HandleSys.ReadHandle(hndl, g_WrBitBufType, &sec, (void **)&pBitBuf))
|
||||
!= HandleError_None)
|
||||
{
|
||||
return pCtx->ThrowNativeError("Invalid bit buffer handle %x (error %d)", hndl, herr);
|
||||
}
|
||||
|
||||
pBitBuf->WriteShort(params[2]);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static cell_t smn_BfWriteWord(IPluginContext *pCtx, const cell_t *params)
|
||||
{
|
||||
Handle_t hndl = static_cast<Handle_t>(params[1]);
|
||||
HandleError herr;
|
||||
HandleSecurity sec;
|
||||
bf_write *pBitBuf;
|
||||
|
||||
sec.pOwner = NULL;
|
||||
sec.pIdentity = g_pCoreIdent;
|
||||
|
||||
if ((herr=g_HandleSys.ReadHandle(hndl, g_WrBitBufType, &sec, (void **)&pBitBuf))
|
||||
!= HandleError_None)
|
||||
{
|
||||
return pCtx->ThrowNativeError("Invalid bit buffer handle %x (error %d)", hndl, herr);
|
||||
}
|
||||
|
||||
pBitBuf->WriteWord(params[2]);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static cell_t smn_BfWriteNum(IPluginContext *pCtx, const cell_t *params)
|
||||
{
|
||||
Handle_t hndl = static_cast<Handle_t>(params[1]);
|
||||
HandleError herr;
|
||||
HandleSecurity sec;
|
||||
bf_write *pBitBuf;
|
||||
|
||||
sec.pOwner = NULL;
|
||||
sec.pIdentity = g_pCoreIdent;
|
||||
|
||||
if ((herr=g_HandleSys.ReadHandle(hndl, g_WrBitBufType, &sec, (void **)&pBitBuf))
|
||||
!= HandleError_None)
|
||||
{
|
||||
return pCtx->ThrowNativeError("Invalid bit buffer handle %x (error %d)", hndl, herr);
|
||||
}
|
||||
|
||||
pBitBuf->WriteLong(static_cast<long>(params[2]));
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static cell_t smn_BfWriteFloat(IPluginContext *pCtx, const cell_t *params)
|
||||
{
|
||||
Handle_t hndl = static_cast<Handle_t>(params[1]);
|
||||
HandleError herr;
|
||||
HandleSecurity sec;
|
||||
bf_write *pBitBuf;
|
||||
|
||||
sec.pOwner = NULL;
|
||||
sec.pIdentity = g_pCoreIdent;
|
||||
|
||||
if ((herr=g_HandleSys.ReadHandle(hndl, g_WrBitBufType, &sec, (void **)&pBitBuf))
|
||||
!= HandleError_None)
|
||||
{
|
||||
return pCtx->ThrowNativeError("Invalid bit buffer handle %x (error %d)", hndl, herr);
|
||||
}
|
||||
|
||||
pBitBuf->WriteFloat(sp_ctof(params[2]));
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static cell_t smn_BfWriteString(IPluginContext *pCtx, const cell_t *params)
|
||||
{
|
||||
Handle_t hndl = static_cast<Handle_t>(params[1]);
|
||||
HandleError herr;
|
||||
HandleSecurity sec;
|
||||
bf_write *pBitBuf;
|
||||
int err;
|
||||
|
||||
sec.pOwner = NULL;
|
||||
sec.pIdentity = g_pCoreIdent;
|
||||
|
||||
if ((herr=g_HandleSys.ReadHandle(hndl, g_WrBitBufType, &sec, (void **)&pBitBuf))
|
||||
!= HandleError_None)
|
||||
{
|
||||
return pCtx->ThrowNativeError("Invalid bit buffer handle %x (error %d)", hndl, herr);
|
||||
}
|
||||
|
||||
char *str;
|
||||
if ((err=pCtx->LocalToString(params[2], &str)) != SP_ERROR_NONE)
|
||||
{
|
||||
pCtx->ThrowNativeErrorEx(err, NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
pBitBuf->WriteString(str);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static cell_t smn_BfWriteEntity(IPluginContext *pCtx, const cell_t *params)
|
||||
{
|
||||
Handle_t hndl = static_cast<Handle_t>(params[1]);
|
||||
HandleError herr;
|
||||
HandleSecurity sec;
|
||||
bf_write *pBitBuf;
|
||||
|
||||
sec.pOwner = NULL;
|
||||
sec.pIdentity = g_pCoreIdent;
|
||||
|
||||
if ((herr=g_HandleSys.ReadHandle(hndl, g_WrBitBufType, &sec, (void **)&pBitBuf))
|
||||
!= HandleError_None)
|
||||
{
|
||||
return pCtx->ThrowNativeError("Invalid bit buffer handle %x (error %d)", hndl, herr);
|
||||
}
|
||||
|
||||
pBitBuf->WriteShort(params[2]);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static cell_t smn_BfWriteAngle(IPluginContext *pCtx, const cell_t *params)
|
||||
{
|
||||
Handle_t hndl = static_cast<Handle_t>(params[1]);
|
||||
HandleError herr;
|
||||
HandleSecurity sec;
|
||||
bf_write *pBitBuf;
|
||||
|
||||
sec.pOwner = NULL;
|
||||
sec.pIdentity = g_pCoreIdent;
|
||||
|
||||
if ((herr=g_HandleSys.ReadHandle(hndl, g_WrBitBufType, &sec, (void **)&pBitBuf))
|
||||
!= HandleError_None)
|
||||
{
|
||||
return pCtx->ThrowNativeError("Invalid bit buffer handle %x (error %d)", hndl, herr);
|
||||
}
|
||||
|
||||
pBitBuf->WriteBitAngle(sp_ctof(params[2]), params[3]);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static cell_t smn_BfWriteCoord(IPluginContext *pCtx, const cell_t *params)
|
||||
{
|
||||
Handle_t hndl = static_cast<Handle_t>(params[1]);
|
||||
HandleError herr;
|
||||
HandleSecurity sec;
|
||||
bf_write *pBitBuf;
|
||||
|
||||
sec.pOwner = NULL;
|
||||
sec.pIdentity = g_pCoreIdent;
|
||||
|
||||
if ((herr=g_HandleSys.ReadHandle(hndl, g_WrBitBufType, &sec, (void **)&pBitBuf))
|
||||
!= HandleError_None)
|
||||
{
|
||||
return pCtx->ThrowNativeError("Invalid bit buffer handle %x (error %d)", hndl, herr);
|
||||
}
|
||||
|
||||
pBitBuf->WriteBitCoord(sp_ctof(params[2]));
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static cell_t smn_BfWriteVecCoord(IPluginContext *pCtx, const cell_t *params)
|
||||
{
|
||||
Handle_t hndl = static_cast<Handle_t>(params[1]);
|
||||
HandleError herr;
|
||||
HandleSecurity sec;
|
||||
bf_write *pBitBuf;
|
||||
|
||||
sec.pOwner = NULL;
|
||||
sec.pIdentity = g_pCoreIdent;
|
||||
|
||||
if ((herr=g_HandleSys.ReadHandle(hndl, g_WrBitBufType, &sec, (void **)&pBitBuf))
|
||||
!= HandleError_None)
|
||||
{
|
||||
return pCtx->ThrowNativeError("Invalid bit buffer handle %x (error %d)", hndl, herr);
|
||||
}
|
||||
|
||||
cell_t *pVec;
|
||||
pCtx->LocalToPhysAddr(params[2], &pVec);
|
||||
Vector vec(sp_ctof(pVec[0]), sp_ctof(pVec[1]), sp_ctof(pVec[2]));
|
||||
pBitBuf->WriteBitVec3Coord(vec);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static cell_t smn_BfWriteVecNormal(IPluginContext *pCtx, const cell_t *params)
|
||||
{
|
||||
Handle_t hndl = static_cast<Handle_t>(params[1]);
|
||||
HandleError herr;
|
||||
HandleSecurity sec;
|
||||
bf_write *pBitBuf;
|
||||
|
||||
sec.pOwner = NULL;
|
||||
sec.pIdentity = g_pCoreIdent;
|
||||
|
||||
if ((herr=g_HandleSys.ReadHandle(hndl, g_WrBitBufType, &sec, (void **)&pBitBuf))
|
||||
!= HandleError_None)
|
||||
{
|
||||
return pCtx->ThrowNativeError("Invalid bit buffer handle %x (error %d)", hndl, herr);
|
||||
}
|
||||
|
||||
cell_t *pVec;
|
||||
pCtx->LocalToPhysAddr(params[2], &pVec);
|
||||
Vector vec(sp_ctof(pVec[0]), sp_ctof(pVec[1]), sp_ctof(pVec[2]));
|
||||
pBitBuf->WriteBitVec3Normal(vec);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static cell_t smn_BfWriteAngles(IPluginContext *pCtx, const cell_t *params)
|
||||
{
|
||||
Handle_t hndl = static_cast<Handle_t>(params[1]);
|
||||
HandleError herr;
|
||||
HandleSecurity sec;
|
||||
bf_write *pBitBuf;
|
||||
|
||||
sec.pOwner = NULL;
|
||||
sec.pIdentity = g_pCoreIdent;
|
||||
|
||||
if ((herr=g_HandleSys.ReadHandle(hndl, g_WrBitBufType, &sec, (void **)&pBitBuf))
|
||||
!= HandleError_None)
|
||||
{
|
||||
return pCtx->ThrowNativeError("Invalid bit buffer handle %x (error %d)", hndl, herr);
|
||||
}
|
||||
|
||||
cell_t *pAng;
|
||||
pCtx->LocalToPhysAddr(params[2], &pAng);
|
||||
QAngle ang(sp_ctof(pAng[0]), sp_ctof(pAng[1]), sp_ctof(pAng[2]));
|
||||
pBitBuf->WriteBitAngles(ang);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static cell_t smn_BfReadBool(IPluginContext *pCtx, const cell_t *params)
|
||||
{
|
||||
Handle_t hndl = static_cast<Handle_t>(params[1]);
|
||||
HandleError herr;
|
||||
HandleSecurity sec;
|
||||
bf_read *pBitBuf;
|
||||
|
||||
sec.pOwner = NULL;
|
||||
sec.pIdentity = g_pCoreIdent;
|
||||
|
||||
if ((herr=g_HandleSys.ReadHandle(hndl, g_RdBitBufType, &sec, (void **)&pBitBuf))
|
||||
!= HandleError_None)
|
||||
{
|
||||
return pCtx->ThrowNativeError("Invalid bit buffer handle %x (error %d)", hndl, herr);
|
||||
}
|
||||
|
||||
return pBitBuf->ReadOneBit() ? 1 : 0;
|
||||
}
|
||||
|
||||
static cell_t smn_BfReadByte(IPluginContext *pCtx, const cell_t *params)
|
||||
{
|
||||
Handle_t hndl = static_cast<Handle_t>(params[1]);
|
||||
HandleError herr;
|
||||
HandleSecurity sec;
|
||||
bf_read *pBitBuf;
|
||||
|
||||
sec.pOwner = NULL;
|
||||
sec.pIdentity = g_pCoreIdent;
|
||||
|
||||
if ((herr=g_HandleSys.ReadHandle(hndl, g_RdBitBufType, &sec, (void **)&pBitBuf))
|
||||
!= HandleError_None)
|
||||
{
|
||||
return pCtx->ThrowNativeError("Invalid bit buffer handle %x (error %d)", hndl, herr);
|
||||
}
|
||||
|
||||
return pBitBuf->ReadByte();
|
||||
}
|
||||
|
||||
static cell_t smn_BfReadChar(IPluginContext *pCtx, const cell_t *params)
|
||||
{
|
||||
Handle_t hndl = static_cast<Handle_t>(params[1]);
|
||||
HandleError herr;
|
||||
HandleSecurity sec;
|
||||
bf_read *pBitBuf;
|
||||
|
||||
sec.pOwner = NULL;
|
||||
sec.pIdentity = g_pCoreIdent;
|
||||
|
||||
if ((herr=g_HandleSys.ReadHandle(hndl, g_RdBitBufType, &sec, (void **)&pBitBuf))
|
||||
!= HandleError_None)
|
||||
{
|
||||
return pCtx->ThrowNativeError("Invalid bit buffer handle %x (error %d)", hndl, herr);
|
||||
}
|
||||
|
||||
return pBitBuf->ReadChar();
|
||||
}
|
||||
|
||||
static cell_t smn_BfReadShort(IPluginContext *pCtx, const cell_t *params)
|
||||
{
|
||||
Handle_t hndl = static_cast<Handle_t>(params[1]);
|
||||
HandleError herr;
|
||||
HandleSecurity sec;
|
||||
bf_read *pBitBuf;
|
||||
|
||||
sec.pOwner = NULL;
|
||||
sec.pIdentity = g_pCoreIdent;
|
||||
|
||||
if ((herr=g_HandleSys.ReadHandle(hndl, g_RdBitBufType, &sec, (void **)&pBitBuf))
|
||||
!= HandleError_None)
|
||||
{
|
||||
return pCtx->ThrowNativeError("Invalid bit buffer handle %x (error %d)", hndl, herr);
|
||||
}
|
||||
|
||||
return pBitBuf->ReadShort();
|
||||
}
|
||||
|
||||
static cell_t smn_BfReadWord(IPluginContext *pCtx, const cell_t *params)
|
||||
{
|
||||
Handle_t hndl = static_cast<Handle_t>(params[1]);
|
||||
HandleError herr;
|
||||
HandleSecurity sec;
|
||||
bf_read *pBitBuf;
|
||||
|
||||
sec.pOwner = NULL;
|
||||
sec.pIdentity = g_pCoreIdent;
|
||||
|
||||
if ((herr=g_HandleSys.ReadHandle(hndl, g_RdBitBufType, &sec, (void **)&pBitBuf))
|
||||
!= HandleError_None)
|
||||
{
|
||||
return pCtx->ThrowNativeError("Invalid bit buffer handle %x (error %d)", hndl, herr);
|
||||
}
|
||||
|
||||
return pBitBuf->ReadWord();
|
||||
}
|
||||
|
||||
static cell_t smn_BfReadNum(IPluginContext *pCtx, const cell_t *params)
|
||||
{
|
||||
Handle_t hndl = static_cast<Handle_t>(params[1]);
|
||||
HandleError herr;
|
||||
HandleSecurity sec;
|
||||
bf_read *pBitBuf;
|
||||
|
||||
sec.pOwner = NULL;
|
||||
sec.pIdentity = g_pCoreIdent;
|
||||
|
||||
if ((herr=g_HandleSys.ReadHandle(hndl, g_RdBitBufType, &sec, (void **)&pBitBuf))
|
||||
!= HandleError_None)
|
||||
{
|
||||
return pCtx->ThrowNativeError("Invalid bit buffer handle %x (error %d)", hndl, herr);
|
||||
}
|
||||
|
||||
return static_cast<cell_t>(pBitBuf->ReadLong());
|
||||
}
|
||||
|
||||
static cell_t smn_BfReadFloat(IPluginContext *pCtx, const cell_t *params)
|
||||
{
|
||||
Handle_t hndl = static_cast<Handle_t>(params[1]);
|
||||
HandleError herr;
|
||||
HandleSecurity sec;
|
||||
bf_read *pBitBuf;
|
||||
|
||||
sec.pOwner = NULL;
|
||||
sec.pIdentity = g_pCoreIdent;
|
||||
|
||||
if ((herr=g_HandleSys.ReadHandle(hndl, g_RdBitBufType, &sec, (void **)&pBitBuf))
|
||||
!= HandleError_None)
|
||||
{
|
||||
return pCtx->ThrowNativeError("Invalid bit buffer handle %x (error %d)", hndl, herr);
|
||||
}
|
||||
|
||||
return sp_ftoc(pBitBuf->ReadFloat());
|
||||
}
|
||||
|
||||
static cell_t smn_BfReadString(IPluginContext *pCtx, const cell_t *params)
|
||||
{
|
||||
Handle_t hndl = static_cast<Handle_t>(params[1]);
|
||||
HandleError herr;
|
||||
HandleSecurity sec;
|
||||
bf_read *pBitBuf;
|
||||
int numChars = 0;
|
||||
char *buf;
|
||||
|
||||
sec.pOwner = NULL;
|
||||
sec.pIdentity = g_pCoreIdent;
|
||||
|
||||
if ((herr=g_HandleSys.ReadHandle(hndl, g_RdBitBufType, &sec, (void **)&pBitBuf))
|
||||
!= HandleError_None)
|
||||
{
|
||||
return pCtx->ThrowNativeError("Invalid bit buffer handle %x (error %d)", hndl, herr);
|
||||
}
|
||||
|
||||
pCtx->LocalToPhysAddr(params[2], (cell_t **)&buf);
|
||||
pBitBuf->ReadString(buf, params[3], params[4] ? true : false, &numChars);
|
||||
|
||||
if (pBitBuf->IsOverflowed())
|
||||
{
|
||||
return -numChars - 1;
|
||||
}
|
||||
|
||||
return numChars;
|
||||
}
|
||||
|
||||
static cell_t smn_BfReadEntity(IPluginContext *pCtx, const cell_t *params)
|
||||
{
|
||||
Handle_t hndl = static_cast<Handle_t>(params[1]);
|
||||
HandleError herr;
|
||||
HandleSecurity sec;
|
||||
bf_read *pBitBuf;
|
||||
|
||||
sec.pOwner = NULL;
|
||||
sec.pIdentity = g_pCoreIdent;
|
||||
|
||||
if ((herr=g_HandleSys.ReadHandle(hndl, g_RdBitBufType, &sec, (void **)&pBitBuf))
|
||||
!= HandleError_None)
|
||||
{
|
||||
return pCtx->ThrowNativeError("Invalid bit buffer handle %x (error %d)", hndl, herr);
|
||||
}
|
||||
|
||||
return pBitBuf->ReadShort();
|
||||
}
|
||||
|
||||
static cell_t smn_BfReadAngle(IPluginContext *pCtx, const cell_t *params)
|
||||
{
|
||||
Handle_t hndl = static_cast<Handle_t>(params[1]);
|
||||
HandleError herr;
|
||||
HandleSecurity sec;
|
||||
bf_read *pBitBuf;
|
||||
|
||||
sec.pOwner = NULL;
|
||||
sec.pIdentity = g_pCoreIdent;
|
||||
|
||||
if ((herr=g_HandleSys.ReadHandle(hndl, g_RdBitBufType, &sec, (void **)&pBitBuf))
|
||||
!= HandleError_None)
|
||||
{
|
||||
return pCtx->ThrowNativeError("Invalid bit buffer handle %x (error %d)", hndl, herr);
|
||||
}
|
||||
|
||||
return sp_ftoc(pBitBuf->ReadBitAngle(params[2]));
|
||||
}
|
||||
|
||||
static cell_t smn_BfReadCoord(IPluginContext *pCtx, const cell_t *params)
|
||||
{
|
||||
Handle_t hndl = static_cast<Handle_t>(params[1]);
|
||||
HandleError herr;
|
||||
HandleSecurity sec;
|
||||
bf_read *pBitBuf;
|
||||
|
||||
sec.pOwner = NULL;
|
||||
sec.pIdentity = g_pCoreIdent;
|
||||
|
||||
if ((herr=g_HandleSys.ReadHandle(hndl, g_RdBitBufType, &sec, (void **)&pBitBuf))
|
||||
!= HandleError_None)
|
||||
{
|
||||
return pCtx->ThrowNativeError("Invalid bit buffer handle %x (error %d)", hndl, herr);
|
||||
}
|
||||
|
||||
return sp_ftoc(pBitBuf->ReadBitCoord());
|
||||
}
|
||||
|
||||
static cell_t smn_BfReadVecCoord(IPluginContext *pCtx, const cell_t *params)
|
||||
{
|
||||
Handle_t hndl = static_cast<Handle_t>(params[1]);
|
||||
HandleError herr;
|
||||
HandleSecurity sec;
|
||||
bf_read *pBitBuf;
|
||||
|
||||
sec.pOwner = NULL;
|
||||
sec.pIdentity = g_pCoreIdent;
|
||||
|
||||
if ((herr=g_HandleSys.ReadHandle(hndl, g_RdBitBufType, &sec, (void **)&pBitBuf))
|
||||
!= HandleError_None)
|
||||
{
|
||||
return pCtx->ThrowNativeError("Invalid bit buffer handle %x (error %d)", hndl, herr);
|
||||
}
|
||||
|
||||
cell_t *pVec;
|
||||
pCtx->LocalToPhysAddr(params[2], &pVec);
|
||||
|
||||
Vector vec;
|
||||
pBitBuf->ReadBitVec3Coord(vec);
|
||||
|
||||
pVec[0] = sp_ftoc(vec.x);
|
||||
pVec[1] = sp_ftoc(vec.y);
|
||||
pVec[2] = sp_ftoc(vec.z);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static cell_t smn_BfReadVecNormal(IPluginContext *pCtx, const cell_t *params)
|
||||
{
|
||||
Handle_t hndl = static_cast<Handle_t>(params[1]);
|
||||
HandleError herr;
|
||||
HandleSecurity sec;
|
||||
bf_read *pBitBuf;
|
||||
|
||||
sec.pOwner = NULL;
|
||||
sec.pIdentity = g_pCoreIdent;
|
||||
|
||||
if ((herr=g_HandleSys.ReadHandle(hndl, g_RdBitBufType, &sec, (void **)&pBitBuf))
|
||||
!= HandleError_None)
|
||||
{
|
||||
return pCtx->ThrowNativeError("Invalid bit buffer handle %x (error %d)", hndl, herr);
|
||||
}
|
||||
|
||||
cell_t *pVec;
|
||||
pCtx->LocalToPhysAddr(params[2], &pVec);
|
||||
|
||||
Vector vec;
|
||||
pBitBuf->ReadBitVec3Normal(vec);
|
||||
|
||||
pVec[0] = sp_ftoc(vec.x);
|
||||
pVec[1] = sp_ftoc(vec.y);
|
||||
pVec[2] = sp_ftoc(vec.z);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static cell_t smn_BfReadAngles(IPluginContext *pCtx, const cell_t *params)
|
||||
{
|
||||
Handle_t hndl = static_cast<Handle_t>(params[1]);
|
||||
HandleError herr;
|
||||
HandleSecurity sec;
|
||||
bf_read *pBitBuf;
|
||||
|
||||
sec.pOwner = NULL;
|
||||
sec.pIdentity = g_pCoreIdent;
|
||||
|
||||
if ((herr=g_HandleSys.ReadHandle(hndl, g_RdBitBufType, &sec, (void **)&pBitBuf))
|
||||
!= HandleError_None)
|
||||
{
|
||||
return pCtx->ThrowNativeError("Invalid bit buffer handle %x (error %d)", hndl, herr);
|
||||
}
|
||||
|
||||
cell_t *pAng;
|
||||
pCtx->LocalToPhysAddr(params[2], &pAng);
|
||||
|
||||
QAngle ang;
|
||||
pBitBuf->ReadBitAngles(ang);
|
||||
|
||||
pAng[0] = sp_ftoc(ang.x);
|
||||
pAng[1] = sp_ftoc(ang.y);
|
||||
pAng[2] = sp_ftoc(ang.z);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static cell_t smn_BfGetNumBytesLeft(IPluginContext *pCtx, const cell_t *params)
|
||||
{
|
||||
Handle_t hndl = static_cast<Handle_t>(params[1]);
|
||||
HandleError herr;
|
||||
HandleSecurity sec;
|
||||
bf_read *pBitBuf;
|
||||
|
||||
sec.pOwner = NULL;
|
||||
sec.pIdentity = g_pCoreIdent;
|
||||
|
||||
if ((herr=g_HandleSys.ReadHandle(hndl, g_RdBitBufType, &sec, (void **)&pBitBuf))
|
||||
!= HandleError_None)
|
||||
{
|
||||
return pCtx->ThrowNativeError("Invalid bit buffer handle %x (error %d)", hndl, herr);
|
||||
}
|
||||
|
||||
return pBitBuf->GetNumBitsLeft() >> 3;
|
||||
}
|
||||
|
||||
REGISTER_NATIVES(bitbufnatives)
|
||||
{
|
||||
{"BfWriteBool", smn_BfWriteBool},
|
||||
{"BfWriteByte", smn_BfWriteByte},
|
||||
{"BfWriteChar", smn_BfWriteChar},
|
||||
{"BfWriteShort", smn_BfWriteShort},
|
||||
{"BfWriteWord", smn_BfWriteWord},
|
||||
{"BfWriteNum", smn_BfWriteNum},
|
||||
{"BfWriteFloat", smn_BfWriteFloat},
|
||||
{"BfWriteString", smn_BfWriteString},
|
||||
{"BfWriteEntity", smn_BfWriteEntity},
|
||||
{"BfWriteAngle", smn_BfWriteAngle},
|
||||
{"BfWriteCoord", smn_BfWriteCoord},
|
||||
{"BfWriteVecCoord", smn_BfWriteVecCoord},
|
||||
{"BfWriteVecNormal", smn_BfWriteVecNormal},
|
||||
{"BfWriteAngles", smn_BfWriteAngles},
|
||||
{"BfReadBool", smn_BfReadBool},
|
||||
{"BfReadByte", smn_BfReadByte},
|
||||
{"BfReadChar", smn_BfReadChar},
|
||||
{"BfReadShort", smn_BfReadShort},
|
||||
{"BfReadWord", smn_BfReadWord},
|
||||
{"BfReadNum", smn_BfReadNum},
|
||||
{"BfReadFloat", smn_BfReadFloat},
|
||||
{"BfReadString", smn_BfReadString},
|
||||
{"BfReadEntity", smn_BfReadEntity},
|
||||
{"BfReadAngle", smn_BfReadAngle},
|
||||
{"BfReadCoord", smn_BfReadCoord},
|
||||
{"BfReadVecCoord", smn_BfReadVecCoord},
|
||||
{"BfReadVecNormal", smn_BfReadVecNormal},
|
||||
{"BfReadAngles", smn_BfReadAngles},
|
||||
{"BfGetNumBytesLeft", smn_BfGetNumBytesLeft},
|
||||
{NULL, NULL}
|
||||
};
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,648 @@
|
||||
/**
|
||||
* vim: set ts=4 :
|
||||
* =============================================================================
|
||||
* SourceMod
|
||||
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
|
||||
* =============================================================================
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, version 3.0, as published by the
|
||||
* Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* As a special exception, AlliedModders LLC gives you permission to link the
|
||||
* code of this program (as well as its derivative works) to "Half-Life 2," the
|
||||
* "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
|
||||
* by the Valve Corporation. You must obey the GNU General Public License in
|
||||
* all respects for all other code used. Additionally, AlliedModders LLC grants
|
||||
* this exception to all derivative works. AlliedModders LLC defines further
|
||||
* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
|
||||
* or <http://www.sourcemod.net/license.php>.
|
||||
*
|
||||
* Version: $Id$
|
||||
*/
|
||||
|
||||
#include <time.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include "sm_stringutil.h"
|
||||
#include "sm_globals.h"
|
||||
#include "sourcemod.h"
|
||||
#include "PluginSys.h"
|
||||
#include "HandleSys.h"
|
||||
#include "LibrarySys.h"
|
||||
#include "TimerSys.h"
|
||||
#include "ForwardSys.h"
|
||||
#include "Logger.h"
|
||||
#include "ExtensionSys.h"
|
||||
|
||||
#if defined PLATFORM_WINDOWS
|
||||
#include <windows.h>
|
||||
#elif defined PLATFORM_LINUX
|
||||
#include <limits.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/times.h>
|
||||
#endif
|
||||
|
||||
HandleType_t g_PlIter;
|
||||
ConVar sm_datetime_format("sm_datetime_format", "%m/%d/%Y - %H:%M:%S", 0, "Default formatting time rules");
|
||||
IForward *g_OnLogAction = NULL;
|
||||
|
||||
class CoreNativeHelpers :
|
||||
public SMGlobalClass,
|
||||
public IHandleTypeDispatch
|
||||
{
|
||||
public:
|
||||
void OnSourceModAllInitialized()
|
||||
{
|
||||
HandleAccess hacc;
|
||||
g_HandleSys.InitAccessDefaults(NULL, &hacc);
|
||||
hacc.access[HandleAccess_Clone] = HANDLE_RESTRICT_IDENTITY|HANDLE_RESTRICT_OWNER;
|
||||
|
||||
g_PlIter = g_HandleSys.CreateType("PluginIterator", this, 0, NULL, NULL, g_pCoreIdent, NULL);
|
||||
|
||||
g_OnLogAction = g_Forwards.CreateForward("OnLogAction",
|
||||
ET_Hook,
|
||||
5,
|
||||
NULL,
|
||||
Param_Cell,
|
||||
Param_Cell,
|
||||
Param_Cell,
|
||||
Param_Cell,
|
||||
Param_String);
|
||||
}
|
||||
void OnHandleDestroy(HandleType_t type, void *object)
|
||||
{
|
||||
IPluginIterator *iter = (IPluginIterator *)object;
|
||||
iter->Release();
|
||||
}
|
||||
void OnSourceModShutdown()
|
||||
{
|
||||
g_Forwards.ReleaseForward(g_OnLogAction);
|
||||
g_HandleSys.RemoveType(g_PlIter, g_pCoreIdent);
|
||||
}
|
||||
} g_CoreNativeHelpers;
|
||||
|
||||
void LogAction(Handle_t hndl, int type, int client, int target, const char *message)
|
||||
{
|
||||
if (g_OnLogAction->GetFunctionCount())
|
||||
{
|
||||
cell_t result = 0;
|
||||
g_OnLogAction->PushCell(hndl);
|
||||
g_OnLogAction->PushCell(type);
|
||||
g_OnLogAction->PushCell(client);
|
||||
g_OnLogAction->PushCell(target);
|
||||
g_OnLogAction->PushString(message);
|
||||
g_OnLogAction->Execute(&result);
|
||||
|
||||
if (result >= (ResultType)Pl_Handled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const char *logtag = "SM";
|
||||
if (type == 2)
|
||||
{
|
||||
HandleError err;
|
||||
IPlugin *pPlugin = g_PluginSys.PluginFromHandle(hndl, &err);
|
||||
if (pPlugin)
|
||||
{
|
||||
logtag = pPlugin->GetFilename();
|
||||
}
|
||||
}
|
||||
|
||||
g_Logger.LogMessage("[%s] %s", logtag, message);
|
||||
}
|
||||
|
||||
static cell_t ThrowError(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
char buffer[512];
|
||||
|
||||
g_SourceMod.SetGlobalTarget(SOURCEMOD_SERVER_LANGUAGE);
|
||||
|
||||
g_SourceMod.FormatString(buffer, sizeof(buffer), pContext, params, 1);
|
||||
|
||||
if (pContext->GetContext()->n_err == SP_ERROR_NONE)
|
||||
{
|
||||
pContext->ThrowNativeErrorEx(SP_ERROR_ABORTED, "%s", buffer);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static cell_t GetTime(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
time_t t = GetAdjustedTime();
|
||||
cell_t *addr;
|
||||
pContext->LocalToPhysAddr(params[1], &addr);
|
||||
|
||||
*(time_t *)addr = t;
|
||||
|
||||
return static_cast<cell_t>(t);
|
||||
}
|
||||
|
||||
#if defined SUBPLATFORM_SECURECRT
|
||||
void _ignore_invalid_parameter(
|
||||
const wchar_t * expression,
|
||||
const wchar_t * function,
|
||||
const wchar_t * file,
|
||||
unsigned int line,
|
||||
uintptr_t pReserved
|
||||
)
|
||||
{
|
||||
/* Wow we don't care, thanks Microsoft. */
|
||||
}
|
||||
#endif
|
||||
|
||||
static cell_t FormatTime(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
char *format, *buffer;
|
||||
pContext->LocalToString(params[1], &buffer);
|
||||
pContext->LocalToStringNULL(params[3], &format);
|
||||
|
||||
if (format == NULL)
|
||||
{
|
||||
format = const_cast<char *>(sm_datetime_format.GetString());
|
||||
}
|
||||
|
||||
#if defined SUBPLATFORM_SECURECRT
|
||||
_invalid_parameter_handler handler = _set_invalid_parameter_handler(_ignore_invalid_parameter);
|
||||
#endif
|
||||
|
||||
time_t t = (params[4] == -1) ? GetAdjustedTime() : (time_t)params[4];
|
||||
size_t written = strftime(buffer, params[2], format, localtime(&t));
|
||||
|
||||
#if defined SUBPLATFORM_SECURECRT
|
||||
_set_invalid_parameter_handler(handler);
|
||||
#endif
|
||||
|
||||
if (params[2] && format[0] != '\0' && !written)
|
||||
{
|
||||
pContext->ThrowNativeError("Invalid time format or buffer too small");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static cell_t GetPluginIterator(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
IPluginIterator *iter = g_PluginSys.GetPluginIterator();
|
||||
|
||||
Handle_t hndl = g_HandleSys.CreateHandle(g_PlIter, iter, pContext->GetIdentity(), g_pCoreIdent, NULL);
|
||||
|
||||
if (hndl == BAD_HANDLE)
|
||||
{
|
||||
iter->Release();
|
||||
}
|
||||
|
||||
return hndl;
|
||||
}
|
||||
|
||||
static cell_t MorePlugins(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
Handle_t hndl = (Handle_t)params[1];
|
||||
HandleError err;
|
||||
IPluginIterator *pIter;
|
||||
|
||||
HandleSecurity sec;
|
||||
sec.pIdentity = g_pCoreIdent;
|
||||
sec.pOwner = pContext->GetIdentity();
|
||||
|
||||
if ((err=g_HandleSys.ReadHandle(hndl, g_PlIter, &sec, (void **)&pIter)) != HandleError_None)
|
||||
{
|
||||
return pContext->ThrowNativeError("Could not read Handle %x (error %d)", hndl, err);
|
||||
}
|
||||
|
||||
return pIter->MorePlugins() ? 1 : 0;
|
||||
}
|
||||
|
||||
static cell_t ReadPlugin(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
Handle_t hndl = (Handle_t)params[1];
|
||||
HandleError err;
|
||||
IPluginIterator *pIter;
|
||||
|
||||
HandleSecurity sec;
|
||||
sec.pIdentity = g_pCoreIdent;
|
||||
sec.pOwner = pContext->GetIdentity();
|
||||
|
||||
if ((err=g_HandleSys.ReadHandle(hndl, g_PlIter, &sec, (void **)&pIter)) != HandleError_None)
|
||||
{
|
||||
return pContext->ThrowNativeError("Could not read Handle %x (error %d)", hndl, err);
|
||||
}
|
||||
|
||||
CPlugin *pPlugin = (CPlugin *)pIter->GetPlugin();
|
||||
if (!pPlugin)
|
||||
{
|
||||
return BAD_HANDLE;
|
||||
}
|
||||
|
||||
pIter->NextPlugin();
|
||||
|
||||
return pPlugin->GetMyHandle();
|
||||
}
|
||||
|
||||
CPlugin *GetPluginFromHandle(IPluginContext *pContext, Handle_t hndl)
|
||||
{
|
||||
if (hndl == BAD_HANDLE)
|
||||
{
|
||||
return g_PluginSys.GetPluginByCtx(pContext->GetContext());
|
||||
} else {
|
||||
HandleError err;
|
||||
CPlugin *pPlugin = (CPlugin *)g_PluginSys.PluginFromHandle(hndl, &err);
|
||||
if (!pPlugin)
|
||||
{
|
||||
pContext->ThrowNativeError("Could not read Handle %x (error %d)", hndl, err);
|
||||
}
|
||||
return pPlugin;
|
||||
}
|
||||
}
|
||||
|
||||
static cell_t GetPluginStatus(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
CPlugin *pPlugin = GetPluginFromHandle(pContext, params[1]);
|
||||
if (!pPlugin)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return pPlugin->GetStatus();
|
||||
}
|
||||
|
||||
static cell_t GetPluginFilename(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
CPlugin *pPlugin = GetPluginFromHandle(pContext, params[1]);
|
||||
if (!pPlugin)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
pContext->StringToLocalUTF8(params[2], params[3], pPlugin->GetFilename(), NULL);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static cell_t IsPluginDebugging(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
CPlugin *pPlugin = GetPluginFromHandle(pContext, params[1]);
|
||||
if (!pPlugin)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return pPlugin->IsDebugging() ? 1 : 0;
|
||||
}
|
||||
|
||||
/* Local to plugins only */
|
||||
enum PluginInfo
|
||||
{
|
||||
PlInfo_Name, /**< Plugin name */
|
||||
PlInfo_Author, /**< Plugin author */
|
||||
PlInfo_Description, /**< Plugin description */
|
||||
PlInfo_Version, /**< Plugin verison */
|
||||
PlInfo_URL, /**< Plugin URL */
|
||||
};
|
||||
|
||||
static cell_t GetPluginInfo(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
CPlugin *pPlugin = GetPluginFromHandle(pContext, params[1]);
|
||||
if (!pPlugin)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
const sm_plugininfo_t *info = pPlugin->GetPublicInfo();
|
||||
|
||||
if (!info)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
const char *str = NULL;
|
||||
|
||||
switch ((PluginInfo)params[2])
|
||||
{
|
||||
case PlInfo_Name:
|
||||
{
|
||||
str = info->name;
|
||||
break;
|
||||
}
|
||||
case PlInfo_Author:
|
||||
{
|
||||
str = info->author;
|
||||
break;
|
||||
}
|
||||
case PlInfo_Description:
|
||||
{
|
||||
str = info->description;
|
||||
break;
|
||||
}
|
||||
case PlInfo_Version:
|
||||
{
|
||||
str = info->version;
|
||||
break;
|
||||
}
|
||||
case PlInfo_URL:
|
||||
{
|
||||
str = info->url;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!str || str[0] == '\0')
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
pContext->StringToLocalUTF8(params[3], params[4], str, NULL);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static cell_t SetFailState(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
char *str;
|
||||
CPlugin *pPlugin;
|
||||
|
||||
pContext->LocalToString(params[1], &str);
|
||||
pPlugin = g_PluginSys.GetPluginByCtx(pContext->GetContext());
|
||||
|
||||
if (params[0] == 1)
|
||||
{
|
||||
pPlugin->SetErrorState(Plugin_Error, "%s", str);
|
||||
|
||||
return pContext->ThrowNativeErrorEx(SP_ERROR_ABORTED, "%s", str);
|
||||
}
|
||||
else
|
||||
{
|
||||
char buffer[2048];
|
||||
|
||||
g_SourceMod.FormatString(buffer, sizeof(buffer), pContext, params, 1);
|
||||
if (pContext->GetContext()->n_err != SP_ERROR_NONE)
|
||||
{
|
||||
pPlugin->SetErrorState(Plugin_Error, "%s", str);
|
||||
return pContext->ThrowNativeErrorEx(SP_ERROR_ABORTED, "Formatting error (%s)", str);
|
||||
}
|
||||
else
|
||||
{
|
||||
pPlugin->SetErrorState(Plugin_Error, "%s", buffer);
|
||||
return pContext->ThrowNativeErrorEx(SP_ERROR_ABORTED, "%s", buffer);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static cell_t GetSysTickCount(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
#if defined PLATFORM_WINDOWS
|
||||
return (cell_t)GetTickCount();
|
||||
#elif defined PLATFORM_LINUX
|
||||
tms tm;
|
||||
clock_t ticks = times(&tm);
|
||||
long ticks_per_sec = sysconf(_SC_CLK_TCK);
|
||||
double fticks = (double)ticks / (double)ticks_per_sec;
|
||||
fticks *= 1000.0f;
|
||||
if (fticks > INT_MAX)
|
||||
{
|
||||
double r = (int)(fticks / INT_MAX) * (double)INT_MAX;
|
||||
fticks -= r;
|
||||
}
|
||||
return (cell_t)fticks;
|
||||
#endif
|
||||
}
|
||||
|
||||
static cell_t AutoExecConfig(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
CPlugin *plugin = g_PluginSys.GetPluginByCtx(pContext->GetContext());
|
||||
|
||||
char *cfg, *folder;
|
||||
pContext->LocalToString(params[2], &cfg);
|
||||
pContext->LocalToString(params[3], &folder);
|
||||
|
||||
if (cfg[0] == '\0')
|
||||
{
|
||||
static char temp_str[255];
|
||||
static char temp_file[PLATFORM_MAX_PATH];
|
||||
char *ptr;
|
||||
|
||||
g_LibSys.GetFileFromPath(temp_str, sizeof(temp_str), plugin->GetFilename());
|
||||
if ((ptr = strstr(temp_str, ".smx")) != NULL)
|
||||
{
|
||||
*ptr = '\0';
|
||||
}
|
||||
|
||||
/* We have the raw filename! */
|
||||
UTIL_Format(temp_file, sizeof(temp_file), "plugin.%s", temp_str);
|
||||
cfg = temp_file;
|
||||
}
|
||||
|
||||
plugin->AddConfig(params[1] ? true : false, cfg, folder);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static cell_t MarkNativeAsOptional(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
char *name;
|
||||
uint32_t idx;
|
||||
sp_context_t *ctx = pContext->GetContext();
|
||||
|
||||
pContext->LocalToString(params[1], &name);
|
||||
if (pContext->FindNativeByName(name, &idx) != SP_ERROR_NONE)
|
||||
{
|
||||
/* Oops! This HAS to silently fail! */
|
||||
return 0;
|
||||
}
|
||||
|
||||
ctx->natives[idx].flags |= SP_NTVFLAG_OPTIONAL;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static cell_t RegPluginLibrary(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
char *name;
|
||||
CPlugin *pl = g_PluginSys.GetPluginByCtx(pContext->GetContext());
|
||||
|
||||
pContext->LocalToString(params[1], &name);
|
||||
|
||||
pl->AddLibrary(name);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static cell_t LibraryExists(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
char *str;
|
||||
pContext->LocalToString(params[1], &str);
|
||||
|
||||
if (g_PluginSys.LibraryExists(str))
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (g_Extensions.LibraryExists(str))
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static cell_t sm_LogAction(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
char buffer[2048];
|
||||
g_SourceMod.SetGlobalTarget(SOURCEMOD_SERVER_LANGUAGE);
|
||||
g_SourceMod.FormatString(buffer, sizeof(buffer), pContext, params, 3);
|
||||
|
||||
if (pContext->GetContext()->n_err != SP_ERROR_NONE)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
CPlugin *pPlugin = g_PluginSys.GetPluginByCtx(pContext->GetContext());
|
||||
|
||||
LogAction(pPlugin->GetMyHandle(), 2, params[1], params[2], buffer);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static cell_t LogToFile(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
char *file;
|
||||
pContext->LocalToString(params[1], &file);
|
||||
|
||||
char path[PLATFORM_MAX_PATH];
|
||||
g_SourceMod.BuildPath(Path_Game, path, sizeof(path), "%s", file);
|
||||
|
||||
FILE *fp = fopen(path, "at");
|
||||
if (!fp)
|
||||
{
|
||||
return pContext->ThrowNativeError("Could not open file \"%s\"", path);
|
||||
}
|
||||
|
||||
char buffer[2048];
|
||||
g_SourceMod.SetGlobalTarget(SOURCEMOD_SERVER_LANGUAGE);
|
||||
g_SourceMod.FormatString(buffer, sizeof(buffer), pContext, params, 2);
|
||||
|
||||
if (pContext->GetContext()->n_err != SP_ERROR_NONE)
|
||||
{
|
||||
fclose(fp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
CPlugin *pPlugin = g_PluginSys.GetPluginByCtx(pContext->GetContext());
|
||||
|
||||
g_Logger.LogToOpenFile(fp, "[%s] %s", pPlugin->GetFilename(), buffer);
|
||||
|
||||
fclose(fp);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static cell_t LogToFileEx(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
char *file;
|
||||
pContext->LocalToString(params[1], &file);
|
||||
|
||||
char path[PLATFORM_MAX_PATH];
|
||||
g_SourceMod.BuildPath(Path_Game, path, sizeof(path), "%s", file);
|
||||
|
||||
FILE *fp = fopen(path, "at");
|
||||
if (!fp)
|
||||
{
|
||||
return pContext->ThrowNativeError("Could not open file \"%s\"", path);
|
||||
}
|
||||
|
||||
char buffer[2048];
|
||||
g_SourceMod.SetGlobalTarget(SOURCEMOD_SERVER_LANGUAGE);
|
||||
g_SourceMod.FormatString(buffer, sizeof(buffer), pContext, params, 2);
|
||||
|
||||
if (pContext->GetContext()->n_err != SP_ERROR_NONE)
|
||||
{
|
||||
fclose(fp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
g_Logger.LogToOpenFile(fp, "%s", buffer);
|
||||
|
||||
fclose(fp);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static cell_t GetExtensionFileStatus(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
char *str;
|
||||
pContext->LocalToString(params[1], &str);
|
||||
|
||||
IExtension *pExtension = g_Extensions.FindExtensionByFile(str);
|
||||
|
||||
if (!pExtension)
|
||||
{
|
||||
return -2;
|
||||
}
|
||||
|
||||
if (!pExtension->IsLoaded())
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
char *error;
|
||||
pContext->LocalToString(params[2], &error);
|
||||
if (!pExtension->IsRunning(error, params[3]))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static cell_t FindPluginByNumber(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
CPlugin *pPlugin = g_PluginSys.GetPluginByOrder(params[1]);
|
||||
|
||||
if (pPlugin == NULL)
|
||||
{
|
||||
return BAD_HANDLE;
|
||||
}
|
||||
|
||||
return pPlugin->GetMyHandle();
|
||||
}
|
||||
|
||||
REGISTER_NATIVES(coreNatives)
|
||||
{
|
||||
{"AutoExecConfig", AutoExecConfig},
|
||||
{"GetPluginFilename", GetPluginFilename},
|
||||
{"GetPluginInfo", GetPluginInfo},
|
||||
{"GetPluginIterator", GetPluginIterator},
|
||||
{"GetPluginStatus", GetPluginStatus},
|
||||
{"GetSysTickCount", GetSysTickCount},
|
||||
{"GetTime", GetTime},
|
||||
{"IsPluginDebugging", IsPluginDebugging},
|
||||
{"MorePlugins", MorePlugins},
|
||||
{"ReadPlugin", ReadPlugin},
|
||||
{"ThrowError", ThrowError},
|
||||
{"SetFailState", SetFailState},
|
||||
{"FormatTime", FormatTime},
|
||||
{"MarkNativeAsOptional", MarkNativeAsOptional},
|
||||
{"RegPluginLibrary", RegPluginLibrary},
|
||||
{"LibraryExists", LibraryExists},
|
||||
{"LogAction", sm_LogAction},
|
||||
{"LogToFile", LogToFile},
|
||||
{"LogToFileEx", LogToFileEx},
|
||||
{"GetExtensionFileStatus", GetExtensionFileStatus},
|
||||
{"FindPluginByNumber", FindPluginByNumber},
|
||||
{NULL, NULL},
|
||||
};
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user