2008-03-30 09:00:22 +02:00
|
|
|
/**
|
Implement a new stack and error handling model for the SourcePawn VM.
This has three major changes to SourcePawn. First, the API now supports the concept of "exceptions". The exception state is a global property of an instance of the SourcePawn VM. Exceptions can be caught or suppressed. Many places in SourceMod have been updated to check exceptions instead of errors.
The new API obsoletes major parts of the embedder API - all but one method of invoking functions is obsoleted, and the debug interface has been scrapped. Extensions using the native API will not be affected, however, ThrowNativeError has been deprecated in favor of ReportError.
Second, the SourcePawn concept of a "stack" has been unified at the API level. A stack frame iterator now iterates over all SourcePawn invocations, rather than the topmost plugin. This makes error handling more consistent and removes another dependency on context-per-plugin.
Finally, the implementation of stack frames has been changed dramatically. Rather than maintain a complicated and expensive return pointer stack, we now rely on the implicit one provided by the CPU. The stack frame iterator now walks the JIT stack directly. This removes many unnecessary bookkeeping instructions from the generated code, in particular making the CALL instruction 40% faster.
These changes required some fair surgery to the JIT. Its error paths are now slightly more complicated, as they have to throw an exception rather than return an error code. In addition, any path that can throw an exception is now responsible for creating an "exit frame", which exists to tell the stack frame iterator about transitions from the JIT to the VM.
2015-02-27 09:32:44 +01:00
|
|
|
* vim: set ts=4 sw=4 tw=99 noet :
|
2008-03-30 09:00:22 +02:00
|
|
|
* =============================================================================
|
|
|
|
* SourceMod
|
2010-07-28 00:32:32 +02:00
|
|
|
* Copyright (C) 2004-2010 AlliedModders LLC. All rights reserved.
|
2008-03-30 09:00:22 +02:00
|
|
|
* =============================================================================
|
|
|
|
*
|
|
|
|
* 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 "sourcemod.h"
|
|
|
|
#include "sourcemm_api.h"
|
|
|
|
#include "PlayerManager.h"
|
|
|
|
#include "HalfLife2.h"
|
|
|
|
|
2014-11-09 16:59:10 +01:00
|
|
|
#include <vstdlib/random.h>
|
|
|
|
|
2008-03-30 09:00:22 +02:00
|
|
|
static cell_t SetRandomSeed(IPluginContext *pContext, const cell_t *params)
|
|
|
|
{
|
2014-11-09 16:59:10 +01:00
|
|
|
::RandomSeed(params[1]);
|
2008-03-30 09:00:22 +02:00
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static cell_t GetRandomFloat(IPluginContext *pContext, const cell_t *params)
|
|
|
|
{
|
|
|
|
float fMin = sp_ctof(params[1]);
|
|
|
|
float fMax = sp_ctof(params[2]);
|
|
|
|
|
2014-11-09 16:59:10 +01:00
|
|
|
float fRandom = ::RandomFloat(fMin, fMax);
|
2008-03-30 09:00:22 +02:00
|
|
|
|
|
|
|
return sp_ftoc(fRandom);
|
|
|
|
}
|
|
|
|
|
|
|
|
static cell_t GetRandomInt(IPluginContext *pContext, const cell_t *params)
|
|
|
|
{
|
2014-11-09 16:59:10 +01:00
|
|
|
return ::RandomInt(params[1], params[2]);
|
2008-03-30 09:00:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static cell_t IsMapValid(IPluginContext *pContext, const cell_t *params)
|
|
|
|
{
|
|
|
|
char *map;
|
|
|
|
pContext->LocalToString(params[1], &map);
|
|
|
|
|
2013-02-15 01:28:12 +01:00
|
|
|
return g_HL2.IsMapValid(map);
|
2008-03-30 09:00:22 +02:00
|
|
|
}
|
|
|
|
|
2015-06-27 19:10:47 +02:00
|
|
|
static cell_t FindMap(IPluginContext *pContext, const cell_t *params)
|
|
|
|
{
|
|
|
|
char *pMapname;
|
|
|
|
pContext->LocalToString(params[1], &pMapname);
|
|
|
|
|
|
|
|
cell_t size = params[2];
|
|
|
|
|
2015-06-28 01:58:14 +02:00
|
|
|
return static_cast<cell_t>(g_HL2.FindMap(pMapname, size));
|
2015-06-27 19:10:47 +02:00
|
|
|
}
|
|
|
|
|
2008-03-30 09:00:22 +02:00
|
|
|
static cell_t IsDedicatedServer(IPluginContext *pContext, const cell_t *params)
|
|
|
|
{
|
|
|
|
return engine->IsDedicatedServer();
|
|
|
|
}
|
|
|
|
|
|
|
|
static cell_t GetEngineTime(IPluginContext *pContext, const cell_t *params)
|
|
|
|
{
|
2013-07-21 17:53:56 +02:00
|
|
|
#if SOURCE_ENGINE >= SE_NUCLEARDAWN
|
2009-10-30 02:29:45 +01:00
|
|
|
float fTime = Plat_FloatTime();
|
2009-10-30 02:34:34 +01:00
|
|
|
#else
|
|
|
|
float fTime = engine->Time();
|
|
|
|
#endif
|
2009-10-30 02:29:45 +01:00
|
|
|
|
2008-03-30 09:00:22 +02:00
|
|
|
return sp_ftoc(fTime);
|
|
|
|
}
|
|
|
|
|
|
|
|
static cell_t GetGameTime(IPluginContext *pContext, const cell_t *params)
|
|
|
|
{
|
|
|
|
return sp_ftoc(gpGlobals->curtime);
|
|
|
|
}
|
|
|
|
|
2012-05-30 15:13:43 +02:00
|
|
|
static cell_t GetGameTickCount(IPluginContext *pContext, const cell_t *params)
|
|
|
|
{
|
|
|
|
return gpGlobals->tickcount;
|
|
|
|
}
|
|
|
|
|
2008-03-30 09:00:22 +02:00
|
|
|
static cell_t CreateFakeClient(IPluginContext *pContext, const cell_t *params)
|
|
|
|
{
|
2011-10-12 04:50:21 +02:00
|
|
|
if (!g_SourceMod.IsMapRunning())
|
|
|
|
{
|
|
|
|
return pContext->ThrowNativeError("Cannot create fakeclient when no map is active");
|
|
|
|
}
|
|
|
|
|
2008-03-30 09:00:22 +02:00
|
|
|
char *netname;
|
|
|
|
|
|
|
|
pContext->LocalToString(params[1], &netname);
|
|
|
|
|
2013-05-13 21:18:12 +02:00
|
|
|
#if SOURCE_ENGINE == SE_DOTA
|
2013-07-12 08:31:41 +02:00
|
|
|
int index = engine->CreateFakeClient(netname).Get();
|
2013-05-13 21:18:12 +02:00
|
|
|
|
|
|
|
if (index == -1)
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return index;
|
|
|
|
#else
|
2008-03-30 09:00:22 +02:00
|
|
|
edict_t *pEdict = engine->CreateFakeClient(netname);
|
|
|
|
|
|
|
|
/* :TODO: does the engine fire forwards for us and whatnot? no idea... */
|
|
|
|
|
|
|
|
if (!pEdict)
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2008-11-14 16:18:30 +01:00
|
|
|
return IndexOfEdict(pEdict);
|
2013-05-13 21:18:12 +02:00
|
|
|
#endif
|
2008-03-30 09:00:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static cell_t SetFakeClientConVar(IPluginContext *pContext, const cell_t *params)
|
|
|
|
{
|
|
|
|
CPlayer *pPlayer = g_Players.GetPlayerByIndex(params[1]);
|
|
|
|
|
|
|
|
if (!pPlayer)
|
|
|
|
{
|
|
|
|
return pContext->ThrowNativeError("Client index %d is invalid", params[1]);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!pPlayer->IsConnected())
|
|
|
|
{
|
|
|
|
return pContext->ThrowNativeError("Client %d is not connected", params[1]);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!pPlayer->IsFakeClient())
|
|
|
|
{
|
|
|
|
return pContext->ThrowNativeError("Client %d is not a fake client", params[1]);
|
|
|
|
}
|
|
|
|
|
|
|
|
char *cvar, *value;
|
|
|
|
|
|
|
|
pContext->LocalToString(params[2], &cvar);
|
|
|
|
pContext->LocalToString(params[3], &value);
|
|
|
|
|
2013-05-13 21:18:12 +02:00
|
|
|
#if SOURCE_ENGINE == SE_DOTA
|
|
|
|
engine->SetFakeClientConVarValue(pPlayer->GetIndex(), cvar, value);
|
|
|
|
#else
|
2008-03-30 09:00:22 +02:00
|
|
|
engine->SetFakeClientConVarValue(pPlayer->GetEdict(), cvar, value);
|
2013-05-13 21:18:12 +02:00
|
|
|
#endif
|
2008-03-30 09:00:22 +02:00
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static cell_t GetGameDescription(IPluginContext *pContext, const cell_t *params)
|
|
|
|
{
|
|
|
|
const char *description;
|
|
|
|
|
|
|
|
if (params[3])
|
|
|
|
{
|
|
|
|
description = gamedll->GetGameDescription();
|
|
|
|
} else {
|
|
|
|
description = SERVER_CALL(GetGameDescription)();
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t numBytes;
|
|
|
|
|
|
|
|
pContext->StringToLocalUTF8(params[1], params[2], description, &numBytes);
|
|
|
|
|
|
|
|
return numBytes;
|
|
|
|
}
|
|
|
|
|
|
|
|
static cell_t GetGameFolderName(IPluginContext *pContext, const cell_t *params)
|
|
|
|
{
|
|
|
|
const char *name = g_SourceMod.GetGameFolderName();
|
|
|
|
size_t numBytes;
|
|
|
|
|
|
|
|
pContext->StringToLocalUTF8(params[1], params[2], name, &numBytes);
|
|
|
|
|
|
|
|
return numBytes;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Useless comment to bump the build */
|
|
|
|
static cell_t GetCurrentMap(IPluginContext *pContext, const cell_t *params)
|
|
|
|
{
|
|
|
|
size_t bytes;
|
|
|
|
pContext->StringToLocalUTF8(params[1], params[2], STRING(gpGlobals->mapname), &bytes);
|
|
|
|
return bytes;
|
|
|
|
}
|
|
|
|
|
|
|
|
static cell_t PrecacheModel(IPluginContext *pContext, const cell_t *params)
|
|
|
|
{
|
|
|
|
char *model;
|
|
|
|
pContext->LocalToString(params[1], &model);
|
|
|
|
|
|
|
|
return engine->PrecacheModel(model, params[2] ? true : false);
|
|
|
|
}
|
|
|
|
|
|
|
|
static cell_t PrecacheSentenceFile(IPluginContext *pContext, const cell_t *params)
|
|
|
|
{
|
|
|
|
char *sentencefile;
|
|
|
|
pContext->LocalToString(params[1], &sentencefile);
|
|
|
|
|
|
|
|
return engine->PrecacheSentenceFile(sentencefile, params[2] ? true : false);
|
|
|
|
}
|
|
|
|
|
|
|
|
static cell_t PrecacheDecal(IPluginContext *pContext, const cell_t *params)
|
|
|
|
{
|
|
|
|
char *decal;
|
|
|
|
pContext->LocalToString(params[1], &decal);
|
|
|
|
|
|
|
|
return engine->PrecacheDecal(decal, params[2] ? true : false);
|
|
|
|
}
|
|
|
|
|
|
|
|
static cell_t PrecacheGeneric(IPluginContext *pContext, const cell_t *params)
|
|
|
|
{
|
|
|
|
char *generic;
|
|
|
|
pContext->LocalToString(params[1], &generic);
|
|
|
|
|
|
|
|
return engine->PrecacheGeneric(generic, params[2] ? true : false);
|
|
|
|
}
|
|
|
|
|
|
|
|
static cell_t IsModelPrecached(IPluginContext *pContext, const cell_t *params)
|
|
|
|
{
|
|
|
|
char *model;
|
|
|
|
pContext->LocalToString(params[1], &model);
|
|
|
|
|
|
|
|
return engine->IsModelPrecached(model) ? 1 : 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static cell_t IsDecalPrecached(IPluginContext *pContext, const cell_t *params)
|
|
|
|
{
|
|
|
|
char *decal;
|
|
|
|
pContext->LocalToString(params[1], &decal);
|
|
|
|
|
|
|
|
return engine->IsDecalPrecached(decal) ? 1 : 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static cell_t IsGenericPrecached(IPluginContext *pContext, const cell_t *params)
|
|
|
|
{
|
|
|
|
char *generic;
|
|
|
|
pContext->LocalToString(params[1], &generic);
|
|
|
|
|
|
|
|
return engine->IsGenericPrecached(generic) ? 1 : 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static cell_t PrecacheSound(IPluginContext *pContext, const cell_t *params)
|
|
|
|
{
|
|
|
|
char *sample;
|
|
|
|
pContext->LocalToString(params[1], &sample);
|
|
|
|
|
|
|
|
return enginesound->PrecacheSound(sample, params[2] ? true : false) ? 1 : 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static cell_t IsSoundPrecached(IPluginContext *pContext, const cell_t *params)
|
|
|
|
{
|
|
|
|
char *sample;
|
|
|
|
pContext->LocalToString(params[1], &sample);
|
|
|
|
|
|
|
|
return enginesound->IsSoundPrecached(sample) ? 1 : 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static cell_t smn_CreateDialog(IPluginContext *pContext, const cell_t *params)
|
|
|
|
{
|
2013-03-19 16:19:19 +01:00
|
|
|
#if SOURCE_ENGINE == SE_DOTA
|
|
|
|
return pContext->ThrowNativeError("CreateDialog is not supported on this game");
|
|
|
|
#else
|
2008-03-30 09:00:22 +02:00
|
|
|
KeyValues *pKV;
|
|
|
|
HandleError herr;
|
|
|
|
Handle_t hndl = static_cast<Handle_t>(params[2]);
|
|
|
|
CPlayer *pPlayer = g_Players.GetPlayerByIndex(params[1]);
|
|
|
|
|
|
|
|
if (!pPlayer)
|
|
|
|
{
|
|
|
|
return pContext->ThrowNativeError("Client index %d is invalid", params[1]);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!pPlayer->IsInGame())
|
|
|
|
{
|
|
|
|
return pContext->ThrowNativeError("Client %d is not in game", params[1]);
|
|
|
|
}
|
|
|
|
|
|
|
|
pKV = g_SourceMod.ReadKeyValuesHandle(hndl, &herr, true);
|
|
|
|
if (herr != HandleError_None)
|
|
|
|
{
|
|
|
|
return pContext->ThrowNativeError("Invalid key value handle %x (error %d)", hndl, herr);
|
|
|
|
}
|
|
|
|
|
|
|
|
serverpluginhelpers->CreateMessage(pPlayer->GetEdict(),
|
|
|
|
static_cast<DIALOG_TYPE>(params[3]),
|
|
|
|
pKV,
|
|
|
|
vsp_interface);
|
|
|
|
|
|
|
|
return 1;
|
2013-03-19 16:19:19 +01:00
|
|
|
#endif // DOTA
|
2008-03-30 09:00:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static cell_t PrintToChat(IPluginContext *pContext, const cell_t *params)
|
|
|
|
{
|
|
|
|
int client = params[1];
|
|
|
|
CPlayer *pPlayer = g_Players.GetPlayerByIndex(client);
|
|
|
|
|
|
|
|
if (!pPlayer)
|
|
|
|
{
|
|
|
|
return pContext->ThrowNativeError("Client index %d is invalid", client);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!pPlayer->IsInGame())
|
|
|
|
{
|
|
|
|
return pContext->ThrowNativeError("Client %d is not in game", client);
|
|
|
|
}
|
|
|
|
|
|
|
|
g_SourceMod.SetGlobalTarget(client);
|
|
|
|
|
2015-05-07 15:38:25 +02:00
|
|
|
char buffer[254];
|
2008-03-30 09:00:22 +02:00
|
|
|
|
|
|
|
{
|
Implement a new stack and error handling model for the SourcePawn VM.
This has three major changes to SourcePawn. First, the API now supports the concept of "exceptions". The exception state is a global property of an instance of the SourcePawn VM. Exceptions can be caught or suppressed. Many places in SourceMod have been updated to check exceptions instead of errors.
The new API obsoletes major parts of the embedder API - all but one method of invoking functions is obsoleted, and the debug interface has been scrapped. Extensions using the native API will not be affected, however, ThrowNativeError has been deprecated in favor of ReportError.
Second, the SourcePawn concept of a "stack" has been unified at the API level. A stack frame iterator now iterates over all SourcePawn invocations, rather than the topmost plugin. This makes error handling more consistent and removes another dependency on context-per-plugin.
Finally, the implementation of stack frames has been changed dramatically. Rather than maintain a complicated and expensive return pointer stack, we now rely on the implicit one provided by the CPU. The stack frame iterator now walks the JIT stack directly. This removes many unnecessary bookkeeping instructions from the generated code, in particular making the CALL instruction 40% faster.
These changes required some fair surgery to the JIT. Its error paths are now slightly more complicated, as they have to throw an exception rather than return an error code. In addition, any path that can throw an exception is now responsible for creating an "exit frame", which exists to tell the stack frame iterator about transitions from the JIT to the VM.
2015-02-27 09:32:44 +01:00
|
|
|
DetectExceptions eh(pContext);
|
|
|
|
g_SourceMod.FormatString(buffer, sizeof(buffer), pContext, params, 2);
|
|
|
|
if (eh.HasException())
|
|
|
|
return 0;
|
2008-03-30 09:00:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!g_HL2.TextMsg(client, HUD_PRINTTALK, buffer))
|
|
|
|
{
|
|
|
|
return pContext->ThrowNativeError("Could not send a usermessage");
|
|
|
|
}
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static cell_t PrintCenterText(IPluginContext *pContext, const cell_t *params)
|
|
|
|
{
|
|
|
|
int client = params[1];
|
|
|
|
CPlayer *pPlayer = g_Players.GetPlayerByIndex(client);
|
|
|
|
|
|
|
|
if (!pPlayer)
|
|
|
|
{
|
|
|
|
return pContext->ThrowNativeError("Client index %d is invalid", client);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!pPlayer->IsInGame())
|
|
|
|
{
|
|
|
|
return pContext->ThrowNativeError("Client %d is not in game", client);
|
|
|
|
}
|
|
|
|
|
|
|
|
g_SourceMod.SetGlobalTarget(client);
|
|
|
|
|
2015-05-09 15:22:07 +02:00
|
|
|
char buffer[254];
|
Implement a new stack and error handling model for the SourcePawn VM.
This has three major changes to SourcePawn. First, the API now supports the concept of "exceptions". The exception state is a global property of an instance of the SourcePawn VM. Exceptions can be caught or suppressed. Many places in SourceMod have been updated to check exceptions instead of errors.
The new API obsoletes major parts of the embedder API - all but one method of invoking functions is obsoleted, and the debug interface has been scrapped. Extensions using the native API will not be affected, however, ThrowNativeError has been deprecated in favor of ReportError.
Second, the SourcePawn concept of a "stack" has been unified at the API level. A stack frame iterator now iterates over all SourcePawn invocations, rather than the topmost plugin. This makes error handling more consistent and removes another dependency on context-per-plugin.
Finally, the implementation of stack frames has been changed dramatically. Rather than maintain a complicated and expensive return pointer stack, we now rely on the implicit one provided by the CPU. The stack frame iterator now walks the JIT stack directly. This removes many unnecessary bookkeeping instructions from the generated code, in particular making the CALL instruction 40% faster.
These changes required some fair surgery to the JIT. Its error paths are now slightly more complicated, as they have to throw an exception rather than return an error code. In addition, any path that can throw an exception is now responsible for creating an "exit frame", which exists to tell the stack frame iterator about transitions from the JIT to the VM.
2015-02-27 09:32:44 +01:00
|
|
|
|
2008-03-30 09:00:22 +02:00
|
|
|
{
|
Implement a new stack and error handling model for the SourcePawn VM.
This has three major changes to SourcePawn. First, the API now supports the concept of "exceptions". The exception state is a global property of an instance of the SourcePawn VM. Exceptions can be caught or suppressed. Many places in SourceMod have been updated to check exceptions instead of errors.
The new API obsoletes major parts of the embedder API - all but one method of invoking functions is obsoleted, and the debug interface has been scrapped. Extensions using the native API will not be affected, however, ThrowNativeError has been deprecated in favor of ReportError.
Second, the SourcePawn concept of a "stack" has been unified at the API level. A stack frame iterator now iterates over all SourcePawn invocations, rather than the topmost plugin. This makes error handling more consistent and removes another dependency on context-per-plugin.
Finally, the implementation of stack frames has been changed dramatically. Rather than maintain a complicated and expensive return pointer stack, we now rely on the implicit one provided by the CPU. The stack frame iterator now walks the JIT stack directly. This removes many unnecessary bookkeeping instructions from the generated code, in particular making the CALL instruction 40% faster.
These changes required some fair surgery to the JIT. Its error paths are now slightly more complicated, as they have to throw an exception rather than return an error code. In addition, any path that can throw an exception is now responsible for creating an "exit frame", which exists to tell the stack frame iterator about transitions from the JIT to the VM.
2015-02-27 09:32:44 +01:00
|
|
|
DetectExceptions eh(pContext);
|
|
|
|
g_SourceMod.FormatString(buffer, sizeof(buffer), pContext, params, 2);
|
|
|
|
if (eh.HasException())
|
|
|
|
return 0;
|
2008-03-30 09:00:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!g_HL2.TextMsg(client, HUD_PRINTCENTER, buffer))
|
|
|
|
{
|
|
|
|
return pContext->ThrowNativeError("Could not send a usermessage");
|
|
|
|
}
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static cell_t PrintHintText(IPluginContext *pContext, const cell_t *params)
|
|
|
|
{
|
|
|
|
int client = params[1];
|
|
|
|
CPlayer *pPlayer = g_Players.GetPlayerByIndex(client);
|
|
|
|
|
|
|
|
if (!pPlayer)
|
|
|
|
{
|
|
|
|
return pContext->ThrowNativeError("Client index %d is invalid", client);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!pPlayer->IsInGame())
|
|
|
|
{
|
|
|
|
return pContext->ThrowNativeError("Client %d is not in game", client);
|
|
|
|
}
|
|
|
|
|
|
|
|
g_SourceMod.SetGlobalTarget(client);
|
|
|
|
|
2015-05-09 15:22:07 +02:00
|
|
|
char buffer[254];
|
2008-03-30 09:00:22 +02:00
|
|
|
{
|
Implement a new stack and error handling model for the SourcePawn VM.
This has three major changes to SourcePawn. First, the API now supports the concept of "exceptions". The exception state is a global property of an instance of the SourcePawn VM. Exceptions can be caught or suppressed. Many places in SourceMod have been updated to check exceptions instead of errors.
The new API obsoletes major parts of the embedder API - all but one method of invoking functions is obsoleted, and the debug interface has been scrapped. Extensions using the native API will not be affected, however, ThrowNativeError has been deprecated in favor of ReportError.
Second, the SourcePawn concept of a "stack" has been unified at the API level. A stack frame iterator now iterates over all SourcePawn invocations, rather than the topmost plugin. This makes error handling more consistent and removes another dependency on context-per-plugin.
Finally, the implementation of stack frames has been changed dramatically. Rather than maintain a complicated and expensive return pointer stack, we now rely on the implicit one provided by the CPU. The stack frame iterator now walks the JIT stack directly. This removes many unnecessary bookkeeping instructions from the generated code, in particular making the CALL instruction 40% faster.
These changes required some fair surgery to the JIT. Its error paths are now slightly more complicated, as they have to throw an exception rather than return an error code. In addition, any path that can throw an exception is now responsible for creating an "exit frame", which exists to tell the stack frame iterator about transitions from the JIT to the VM.
2015-02-27 09:32:44 +01:00
|
|
|
DetectExceptions eh(pContext);
|
|
|
|
g_SourceMod.FormatString(buffer, sizeof(buffer), pContext, params, 2);
|
|
|
|
if (eh.HasException())
|
|
|
|
return 0;
|
2008-03-30 09:00:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!g_HL2.HintTextMsg(client, buffer))
|
|
|
|
{
|
|
|
|
return pContext->ThrowNativeError("Could not send a usermessage");
|
|
|
|
}
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static cell_t ShowVGUIPanel(IPluginContext *pContext, const cell_t *params)
|
|
|
|
{
|
|
|
|
HandleError herr;
|
|
|
|
char *name;
|
|
|
|
KeyValues *pKV = NULL;
|
|
|
|
int client = params[1];
|
|
|
|
Handle_t hndl = static_cast<Handle_t>(params[3]);
|
|
|
|
CPlayer *pPlayer = g_Players.GetPlayerByIndex(client);
|
|
|
|
|
|
|
|
if (!pPlayer)
|
|
|
|
{
|
|
|
|
return pContext->ThrowNativeError("Client index %d is invalid", client);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!pPlayer->IsInGame())
|
|
|
|
{
|
|
|
|
return pContext->ThrowNativeError("Client %d is not in game", client);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (hndl != BAD_HANDLE)
|
|
|
|
{
|
|
|
|
pKV = g_SourceMod.ReadKeyValuesHandle(hndl, &herr, true);
|
|
|
|
if (herr != HandleError_None)
|
|
|
|
{
|
|
|
|
return pContext->ThrowNativeError("Invalid key value handle %x (error %d)", hndl, herr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pContext->LocalToString(params[2], &name);
|
|
|
|
|
|
|
|
if (!g_HL2.ShowVGUIMenu(client, name, pKV, (params[4]) ? true : false))
|
|
|
|
{
|
|
|
|
return pContext->ThrowNativeError("Could not send a usermessage");
|
|
|
|
}
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static cell_t smn_IsPlayerAlive(IPluginContext *pContext, const cell_t *params)
|
|
|
|
{
|
|
|
|
CPlayer *player = g_Players.GetPlayerByIndex(params[1]);
|
|
|
|
if (player == NULL)
|
|
|
|
{
|
|
|
|
return pContext->ThrowNativeError("Invalid client index %d", params[1]);
|
|
|
|
}
|
|
|
|
else if (!player->IsInGame())
|
|
|
|
{
|
|
|
|
return pContext->ThrowNativeError("Client %d is not in game", params[1]);
|
|
|
|
}
|
|
|
|
|
|
|
|
unsigned int state = player->GetLifeState();
|
|
|
|
|
|
|
|
if (state == PLAYER_LIFE_UNKNOWN)
|
|
|
|
{
|
|
|
|
return pContext->ThrowNativeError("\"IsPlayerAlive\" not supported by this mod");
|
|
|
|
}
|
|
|
|
else if (state == PLAYER_LIFE_ALIVE)
|
|
|
|
{
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static cell_t GuessSDKVersion(IPluginContext *pContext, const cell_t *params)
|
|
|
|
{
|
2009-10-29 09:33:57 +01:00
|
|
|
#if defined METAMOD_PLAPI_VERSION || PLAPI_VERSION >= 11
|
2008-03-30 09:00:22 +02:00
|
|
|
int version = g_SMAPI->GetSourceEngineBuild();
|
|
|
|
|
2009-10-29 09:33:57 +01:00
|
|
|
switch (version)
|
2008-03-30 09:00:22 +02:00
|
|
|
{
|
2009-10-29 09:33:57 +01:00
|
|
|
case SOURCE_ENGINE_ORIGINAL:
|
2008-03-30 09:00:22 +02:00
|
|
|
return 10;
|
2009-10-29 09:33:57 +01:00
|
|
|
case SOURCE_ENGINE_EPISODEONE:
|
2008-03-30 09:00:22 +02:00
|
|
|
return 20;
|
2009-10-29 09:33:57 +01:00
|
|
|
|
2009-10-30 04:34:00 +01:00
|
|
|
# if defined METAMOD_PLAPI_VERSION
|
2009-10-29 09:33:57 +01:00
|
|
|
/* Newer games. */
|
|
|
|
case SOURCE_ENGINE_DARKMESSIAH:
|
|
|
|
return 15;
|
|
|
|
case SOURCE_ENGINE_ORANGEBOX:
|
2008-03-30 09:00:22 +02:00
|
|
|
return 30;
|
2011-01-12 05:35:58 +01:00
|
|
|
case SOURCE_ENGINE_BLOODYGOODTIME:
|
|
|
|
return 32;
|
2011-08-21 00:58:37 +02:00
|
|
|
case SOURCE_ENGINE_EYE:
|
2011-10-29 01:09:40 +02:00
|
|
|
return 33;
|
2012-08-21 03:53:59 +02:00
|
|
|
case SOURCE_ENGINE_CSS:
|
|
|
|
return 34;
|
2013-08-16 17:47:35 +02:00
|
|
|
case SOURCE_ENGINE_ORANGEBOXVALVE_DEPRECATED:
|
2013-07-21 17:53:56 +02:00
|
|
|
case SOURCE_ENGINE_HL2DM:
|
|
|
|
case SOURCE_ENGINE_DODS:
|
|
|
|
case SOURCE_ENGINE_TF2:
|
2015-05-07 03:12:13 +02:00
|
|
|
case SOURCE_ENGINE_BMS:
|
2013-10-13 20:14:51 +02:00
|
|
|
case SOURCE_ENGINE_SDK2013:
|
2010-01-09 20:33:19 +01:00
|
|
|
return 35;
|
2009-10-29 09:33:57 +01:00
|
|
|
case SOURCE_ENGINE_LEFT4DEAD:
|
2009-05-17 01:32:21 +02:00
|
|
|
return 40;
|
2013-07-21 17:53:56 +02:00
|
|
|
case SOURCE_ENGINE_NUCLEARDAWN:
|
2014-03-29 19:14:00 +01:00
|
|
|
case SOURCE_ENGINE_CONTAGION:
|
2009-10-30 02:34:34 +01:00
|
|
|
case SOURCE_ENGINE_LEFT4DEAD2:
|
|
|
|
return 50;
|
2010-07-28 00:32:32 +02:00
|
|
|
case SOURCE_ENGINE_ALIENSWARM:
|
|
|
|
return 60;
|
2012-05-27 22:08:03 +02:00
|
|
|
case SOURCE_ENGINE_PORTAL2:
|
|
|
|
return 70;
|
|
|
|
case SOURCE_ENGINE_CSGO:
|
|
|
|
return 80;
|
2013-03-19 16:18:44 +01:00
|
|
|
case SOURCE_ENGINE_DOTA:
|
|
|
|
return 90;
|
2009-10-29 09:33:57 +01:00
|
|
|
# endif
|
2009-05-17 01:32:21 +02:00
|
|
|
}
|
2008-03-30 09:00:22 +02:00
|
|
|
#else
|
|
|
|
if (g_HL2.IsOriginalEngine())
|
|
|
|
{
|
|
|
|
return 10;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return 20;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-07-21 17:53:56 +02:00
|
|
|
static cell_t GetEngineVersion(IPluginContext *pContext, const cell_t *params)
|
|
|
|
{
|
2013-10-30 00:00:21 +01:00
|
|
|
int engineVer = g_SMAPI->GetSourceEngineBuild();
|
|
|
|
#if defined METAMOD_PLAPI_VERSION
|
|
|
|
if (engineVer == SOURCE_ENGINE_ORANGEBOXVALVE_DEPRECATED)
|
|
|
|
{
|
|
|
|
const char *gamedir = g_SourceMod.GetGameFolderName();
|
|
|
|
if (strcmp(gamedir, "tf") == 0)
|
|
|
|
return SOURCE_ENGINE_TF2;
|
|
|
|
else if (strcmp(gamedir, "cstrike") == 0)
|
|
|
|
return SOURCE_ENGINE_CSS;
|
|
|
|
else if (strcmp(gamedir, "dod") == 0)
|
|
|
|
return SOURCE_ENGINE_DODS;
|
|
|
|
else if (strcmp(gamedir, "hl2mp") == 0)
|
|
|
|
return SOURCE_ENGINE_HL2DM;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
return engineVer;
|
2013-07-21 17:53:56 +02:00
|
|
|
}
|
|
|
|
|
2009-07-24 02:34:31 +02:00
|
|
|
static cell_t IndexToReference(IPluginContext *pContext, const cell_t *params)
|
|
|
|
{
|
|
|
|
if (params[1] >= NUM_ENT_ENTRIES || params[1] < 0)
|
|
|
|
{
|
|
|
|
return pContext->ThrowNativeError("Invalid entity index %i", params[1]);
|
|
|
|
}
|
|
|
|
|
|
|
|
return g_HL2.IndexToReference(params[1]);
|
|
|
|
}
|
|
|
|
|
|
|
|
static cell_t ReferenceToIndex(IPluginContext *pContext, const cell_t *params)
|
|
|
|
{
|
|
|
|
return g_HL2.ReferenceToIndex(params[1]);
|
|
|
|
}
|
|
|
|
|
|
|
|
static cell_t ReferenceToBCompatRef(IPluginContext *pContext, const cell_t *params)
|
|
|
|
{
|
|
|
|
return g_HL2.ReferenceToBCompatRef(params[1]);
|
|
|
|
}
|
|
|
|
|
2014-12-30 17:38:06 +01:00
|
|
|
// Must match ClientRangeType enum in halflife.inc
|
|
|
|
enum class ClientRangeType : cell_t
|
|
|
|
{
|
|
|
|
Visibility = 0,
|
|
|
|
Audibility,
|
|
|
|
};
|
|
|
|
|
|
|
|
static cell_t GetClientsInRange(IPluginContext *pContext, const cell_t *params)
|
|
|
|
{
|
|
|
|
cell_t *origin;
|
|
|
|
pContext->LocalToPhysAddr(params[1], &origin);
|
|
|
|
|
|
|
|
Vector vOrigin(sp_ctof(origin[0]), sp_ctof(origin[1]), sp_ctof(origin[2]));
|
|
|
|
|
|
|
|
ClientRangeType rangeType = (ClientRangeType) params[2];
|
|
|
|
|
|
|
|
CBitVec<ABSOLUTE_PLAYER_LIMIT> players;
|
|
|
|
engine->Message_DetermineMulticastRecipients(rangeType == ClientRangeType::Audibility, vOrigin, players);
|
|
|
|
|
|
|
|
cell_t *outPlayers;
|
|
|
|
pContext->LocalToPhysAddr(params[3], &outPlayers);
|
|
|
|
|
|
|
|
int maxPlayers = params[4];
|
|
|
|
int curPlayers = 0;
|
|
|
|
|
|
|
|
int index = players.FindNextSetBit(0);
|
|
|
|
while (index > -1 && curPlayers < maxPlayers)
|
|
|
|
{
|
|
|
|
int entidx = index + 1;
|
|
|
|
CPlayer *pPlayer = g_Players.GetPlayerByIndex(entidx);
|
|
|
|
if (pPlayer && pPlayer->IsInGame())
|
|
|
|
{
|
|
|
|
outPlayers[curPlayers++] = entidx;
|
|
|
|
}
|
|
|
|
|
|
|
|
index = players.FindNextSetBit(index + 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
return curPlayers;
|
|
|
|
}
|
|
|
|
|
2008-03-30 09:00:22 +02:00
|
|
|
REGISTER_NATIVES(halflifeNatives)
|
|
|
|
{
|
|
|
|
{"CreateFakeClient", CreateFakeClient},
|
|
|
|
{"GetCurrentMap", GetCurrentMap},
|
|
|
|
{"GetEngineTime", GetEngineTime},
|
|
|
|
{"GetGameDescription", GetGameDescription},
|
|
|
|
{"GetGameFolderName", GetGameFolderName},
|
|
|
|
{"GetGameTime", GetGameTime},
|
2012-05-30 15:13:43 +02:00
|
|
|
{"GetGameTickCount", GetGameTickCount},
|
2008-03-30 09:00:22 +02:00
|
|
|
{"GetRandomFloat", GetRandomFloat},
|
|
|
|
{"GetRandomInt", GetRandomInt},
|
|
|
|
{"IsDedicatedServer", IsDedicatedServer},
|
|
|
|
{"IsMapValid", IsMapValid},
|
2015-06-27 19:10:47 +02:00
|
|
|
{"FindMap", FindMap},
|
2008-03-30 09:00:22 +02:00
|
|
|
{"SetFakeClientConVar", SetFakeClientConVar},
|
|
|
|
{"SetRandomSeed", SetRandomSeed},
|
|
|
|
{"PrecacheModel", PrecacheModel},
|
|
|
|
{"PrecacheSentenceFile", PrecacheSentenceFile},
|
|
|
|
{"PrecacheDecal", PrecacheDecal},
|
|
|
|
{"PrecacheGeneric", PrecacheGeneric},
|
|
|
|
{"IsModelPrecached", IsModelPrecached},
|
|
|
|
{"IsDecalPrecached", IsDecalPrecached},
|
|
|
|
{"IsGenericPrecached", IsGenericPrecached},
|
|
|
|
{"PrecacheSound", PrecacheSound},
|
|
|
|
{"IsSoundPrecached", IsSoundPrecached},
|
|
|
|
{"CreateDialog", smn_CreateDialog},
|
|
|
|
{"PrintToChat", PrintToChat},
|
|
|
|
{"PrintCenterText", PrintCenterText},
|
|
|
|
{"PrintHintText", PrintHintText},
|
|
|
|
{"ShowVGUIPanel", ShowVGUIPanel},
|
|
|
|
{"IsPlayerAlive", smn_IsPlayerAlive},
|
|
|
|
{"GuessSDKVersion", GuessSDKVersion},
|
2013-07-21 17:53:56 +02:00
|
|
|
{"GetEngineVersion", GetEngineVersion},
|
2009-07-24 02:34:31 +02:00
|
|
|
{"EntIndexToEntRef", IndexToReference},
|
|
|
|
{"EntRefToEntIndex", ReferenceToIndex},
|
|
|
|
{"MakeCompatEntRef", ReferenceToBCompatRef},
|
2014-12-30 17:38:06 +01:00
|
|
|
{"GetClientsInRange", GetClientsInRange},
|
2008-03-30 09:00:22 +02:00
|
|
|
{NULL, NULL},
|
|
|
|
};
|