2016-02-29 09:59:09 +01:00
|
|
|
/**
|
|
|
|
* vim: set ts=4 :
|
|
|
|
* =============================================================================
|
|
|
|
* SourceMod SourceTV Manager Extension
|
|
|
|
* Copyright (C) 2004-2016 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 "extension.h"
|
2016-03-03 09:13:59 +01:00
|
|
|
#include "natives.h"
|
2016-03-06 12:16:38 +01:00
|
|
|
#include "hltvserverwrapper.h"
|
2016-02-29 09:59:09 +01:00
|
|
|
|
2016-03-01 02:19:19 +01:00
|
|
|
#define TICK_INTERVAL (gpGlobals->interval_per_tick)
|
|
|
|
#define TIME_TO_TICKS( dt ) ( (int)( 0.5f + (float)(dt) / TICK_INTERVAL ) )
|
|
|
|
|
2016-02-29 09:59:09 +01:00
|
|
|
extern const sp_nativeinfo_t sourcetv_natives[];
|
|
|
|
|
2016-11-14 10:01:44 +01:00
|
|
|
// Print to client consoles
|
2016-03-03 09:13:59 +01:00
|
|
|
SH_DECL_MANUALHOOK0_void_vafmt(CBaseServer_BroadcastPrintf, 0, 0, 0);
|
2016-11-14 10:09:03 +01:00
|
|
|
// For print to chat
|
2016-11-14 10:01:44 +01:00
|
|
|
SH_DECL_MANUALHOOK1_void(CBaseClient_FireGameEvent, 0, 0, 0, IGameEvent *);
|
2016-03-03 09:13:59 +01:00
|
|
|
|
2016-11-14 10:09:03 +01:00
|
|
|
bool g_bHasBroadcastPrintfOffset = false;
|
2016-11-14 10:01:44 +01:00
|
|
|
bool g_bHasClientFireGameEventOffset = false;
|
2016-11-14 10:09:03 +01:00
|
|
|
|
|
|
|
void SetupNativeCalls()
|
2016-03-03 09:13:59 +01:00
|
|
|
{
|
|
|
|
int offset = -1;
|
|
|
|
if (!g_pGameConf->GetOffset("CBaseServer::BroadcastPrintf", &offset) || offset == -1)
|
|
|
|
{
|
|
|
|
smutils->LogError(myself, "Failed to get CBaseServer::BroadcastPrintf offset.");
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
SH_MANUALHOOK_RECONFIGURE(CBaseServer_BroadcastPrintf, offset, 0, 0);
|
2016-11-14 10:09:03 +01:00
|
|
|
g_bHasBroadcastPrintfOffset = true;
|
2016-03-03 09:13:59 +01:00
|
|
|
}
|
2016-11-14 10:01:44 +01:00
|
|
|
|
|
|
|
offset = -1;
|
|
|
|
if (!g_pGameConf->GetOffset("CBaseClient::FireGameEvent", &offset) || offset == -1)
|
|
|
|
{
|
|
|
|
smutils->LogError(myself, "Failed to get CBaseClient::FireGameEvent offset.");
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
SH_MANUALHOOK_RECONFIGURE(CBaseClient_FireGameEvent, offset, 0, 0);
|
|
|
|
g_bHasClientFireGameEventOffset = true;
|
|
|
|
}
|
2016-03-03 09:13:59 +01:00
|
|
|
}
|
|
|
|
|
2016-03-01 15:00:27 +01:00
|
|
|
// native SourceTV_GetServerInstanceCount();
|
|
|
|
static cell_t Native_GetServerInstanceCount(IPluginContext *pContext, const cell_t *params)
|
2016-02-29 09:59:09 +01:00
|
|
|
{
|
2016-02-29 14:55:23 +01:00
|
|
|
#if SOURCE_ENGINE == SE_CSGO
|
2016-02-29 09:59:09 +01:00
|
|
|
return hltvdirector->GetHLTVServerCount();
|
2016-02-29 14:55:23 +01:00
|
|
|
#else
|
|
|
|
return hltvdirector->GetHLTVServer() ? 1 : 0;
|
|
|
|
#endif
|
2016-02-29 09:59:09 +01:00
|
|
|
}
|
|
|
|
|
2016-03-01 15:00:27 +01:00
|
|
|
// native SourceTV_SelectServerInstance();
|
|
|
|
static cell_t Native_SelectServerInstance(IPluginContext *pContext, const cell_t *params)
|
2016-02-29 09:59:09 +01:00
|
|
|
{
|
2016-02-29 14:55:23 +01:00
|
|
|
#if SOURCE_ENGINE == SE_CSGO
|
2016-02-29 09:59:09 +01:00
|
|
|
if (params[1] < 0 || params[1] >= hltvdirector->GetHLTVServerCount())
|
|
|
|
{
|
|
|
|
pContext->ReportError("Invalid HLTV server instance number (%d).", params[1]);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
g_STVManager.SelectSourceTVServer(hltvdirector->GetHLTVServer(params[1]));
|
2016-02-29 14:55:23 +01:00
|
|
|
#else
|
|
|
|
if (params[1] != 0 || !hltvdirector->GetHLTVServer())
|
|
|
|
{
|
|
|
|
pContext->ReportError("Invalid HLTV server instance number (%d).", params[1]);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
// There only is one hltv server.
|
|
|
|
g_STVManager.SelectSourceTVServer(hltvdirector->GetHLTVServer());
|
|
|
|
#endif
|
2016-02-29 09:59:09 +01:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-03-01 15:00:27 +01:00
|
|
|
// native SourceTV_GetSelectedServerInstance();
|
|
|
|
static cell_t Native_GetSelectedServerInstance(IPluginContext *pContext, const cell_t *params)
|
2016-02-29 09:59:09 +01:00
|
|
|
{
|
|
|
|
if (hltvserver == nullptr)
|
|
|
|
return -1;
|
|
|
|
|
2016-03-06 12:16:38 +01:00
|
|
|
return hltvserver->GetInstanceNumber();
|
|
|
|
}
|
2016-02-29 09:59:09 +01:00
|
|
|
|
2016-03-06 12:16:38 +01:00
|
|
|
// native SourceTV_IsActive();
|
|
|
|
static cell_t Native_IsActive(IPluginContext *pContext, const cell_t *params)
|
|
|
|
{
|
|
|
|
if (hltvserver == nullptr)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
return hltvserver->GetBaseServer()->IsActive();
|
2016-02-29 09:59:09 +01:00
|
|
|
}
|
|
|
|
|
2016-03-03 12:28:49 +01:00
|
|
|
// native SourceTV_IsMasterProxy();
|
|
|
|
static cell_t Native_IsMasterProxy(IPluginContext *pContext, const cell_t *params)
|
|
|
|
{
|
|
|
|
if (hltvserver == nullptr)
|
|
|
|
return 0;
|
|
|
|
|
2016-03-06 12:16:38 +01:00
|
|
|
return hltvserver->GetHLTVServer()->IsMasterProxy();
|
2016-03-03 12:28:49 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// native bool:SourceTV_GetServerIP(String:ip[], maxlen);
|
|
|
|
static cell_t Native_GetServerIP(IPluginContext *pContext, const cell_t *params)
|
|
|
|
{
|
|
|
|
if (hltvserver == nullptr)
|
|
|
|
return 0;
|
|
|
|
|
2016-03-06 12:16:38 +01:00
|
|
|
const netadr_t *adr = hltvserver->GetHLTVServer()->GetRelayAddress();
|
2016-03-03 12:28:49 +01:00
|
|
|
|
|
|
|
char buf[16];
|
|
|
|
V_snprintf(buf, sizeof(buf), "%d.%d.%d.%d", adr->ip[0], adr->ip[1], adr->ip[2], adr->ip[3]);
|
|
|
|
pContext->StringToLocalUTF8(params[1], static_cast<size_t>(params[2]), buf, NULL);
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// native SourceTV_GetServerPort();
|
|
|
|
static cell_t Native_GetServerPort(IPluginContext *pContext, const cell_t *params)
|
|
|
|
{
|
|
|
|
if (hltvserver == nullptr)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
return hltvserver->GetBaseServer()->GetUDPPort();
|
|
|
|
}
|
|
|
|
|
2016-02-29 09:59:09 +01:00
|
|
|
// native SourceTV_GetBotIndex();
|
|
|
|
static cell_t Native_GetBotIndex(IPluginContext *pContext, const cell_t *params)
|
|
|
|
{
|
|
|
|
if (hltvserver == nullptr)
|
|
|
|
return 0;
|
|
|
|
|
2016-03-06 12:16:38 +01:00
|
|
|
return hltvserver->GetHLTVServer()->GetHLTVSlot() + 1;
|
2016-02-29 09:59:09 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// native bool:SourceTV_GetLocalStats(&proxies, &slots, &specs);
|
|
|
|
static cell_t Native_GetLocalStats(IPluginContext *pContext, const cell_t *params)
|
|
|
|
{
|
|
|
|
if (hltvserver == nullptr)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
int proxies, slots, specs;
|
2016-03-06 12:16:38 +01:00
|
|
|
hltvserver->GetHLTVServer()->GetLocalStats(proxies, slots, specs);
|
2016-02-29 09:59:09 +01:00
|
|
|
|
|
|
|
cell_t *plProxies, *plSlots, *plSpecs;
|
|
|
|
pContext->LocalToPhysAddr(params[1], &plProxies);
|
|
|
|
pContext->LocalToPhysAddr(params[2], &plSlots);
|
|
|
|
pContext->LocalToPhysAddr(params[3], &plSpecs);
|
|
|
|
|
|
|
|
*plProxies = proxies;
|
|
|
|
*plSlots = slots;
|
|
|
|
*plSpecs = specs;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2016-03-01 15:00:27 +01:00
|
|
|
// native bool:SourceTV_GetGlobalStats(&proxies, &slots, &specs);
|
|
|
|
static cell_t Native_GetGlobalStats(IPluginContext *pContext, const cell_t *params)
|
|
|
|
{
|
|
|
|
if (hltvserver == nullptr)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
int proxies, slots, specs;
|
2016-03-06 12:16:38 +01:00
|
|
|
hltvserver->GetHLTVServer()->GetGlobalStats(proxies, slots, specs);
|
2016-03-01 15:00:27 +01:00
|
|
|
|
|
|
|
cell_t *plProxies, *plSlots, *plSpecs;
|
|
|
|
pContext->LocalToPhysAddr(params[1], &plProxies);
|
|
|
|
pContext->LocalToPhysAddr(params[2], &plSlots);
|
|
|
|
pContext->LocalToPhysAddr(params[3], &plSpecs);
|
|
|
|
|
|
|
|
*plProxies = proxies;
|
|
|
|
*plSlots = slots;
|
|
|
|
*plSpecs = specs;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2016-02-29 09:59:09 +01:00
|
|
|
// native SourceTV_GetBroadcastTick();
|
|
|
|
static cell_t Native_GetBroadcastTick(IPluginContext *pContext, const cell_t *params)
|
|
|
|
{
|
|
|
|
if (hltvserver == nullptr)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
return hltvdirector->GetDirectorTick();
|
|
|
|
}
|
|
|
|
|
|
|
|
// native Float:SourceTV_GetDelay();
|
|
|
|
static cell_t Native_GetDelay(IPluginContext *pContext, const cell_t *params)
|
|
|
|
{
|
|
|
|
if (hltvserver == nullptr)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
return sp_ftoc(hltvdirector->GetDelay());
|
|
|
|
}
|
|
|
|
|
2016-03-03 04:13:21 +01:00
|
|
|
static bool BroadcastEventLocal(IHLTVServer *server, IGameEvent *event, bool bReliable)
|
|
|
|
{
|
|
|
|
static ICallWrapper *pBroadcastEventLocal = nullptr;
|
|
|
|
|
|
|
|
if (!pBroadcastEventLocal)
|
|
|
|
{
|
|
|
|
void *addr = nullptr;
|
|
|
|
if (!g_pGameConf->GetMemSig("CHLTVServer::BroadcastEventLocal", &addr) || !addr)
|
|
|
|
{
|
|
|
|
smutils->LogError(myself, "Failed to get CHLTVServer::BroadcastEventLocal signature.");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2019-04-09 18:00:58 +02:00
|
|
|
if (!bintools)
|
|
|
|
return false;
|
|
|
|
|
2016-03-03 04:13:21 +01:00
|
|
|
PassInfo pass[2];
|
|
|
|
pass[0].flags = PASSFLAG_BYVAL;
|
|
|
|
pass[0].type = PassType_Basic;
|
|
|
|
pass[0].size = sizeof(IGameEvent *);
|
|
|
|
pass[1].flags = PASSFLAG_BYVAL;
|
|
|
|
pass[1].type = PassType_Basic;
|
|
|
|
pass[1].size = sizeof(bool);
|
|
|
|
|
|
|
|
pBroadcastEventLocal = bintools->CreateCall(addr, CallConv_ThisCall, NULL, pass, 2);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pBroadcastEventLocal)
|
|
|
|
{
|
|
|
|
unsigned char vstk[sizeof(void *) + sizeof(IGameEvent *) + sizeof(bool)];
|
|
|
|
unsigned char *vptr = vstk;
|
2016-11-14 10:09:30 +01:00
|
|
|
|
|
|
|
IServer *iserver = server->GetBaseServer();
|
|
|
|
*(void **)vptr = (void *)((intptr_t)iserver - 8);
|
2016-03-03 04:13:21 +01:00
|
|
|
vptr += sizeof(void *);
|
|
|
|
*(IGameEvent **)vptr = event;
|
2016-11-14 10:09:30 +01:00
|
|
|
vptr += sizeof(IGameEvent *);
|
2016-03-03 04:13:21 +01:00
|
|
|
*(bool *)vptr = bReliable;
|
|
|
|
|
|
|
|
pBroadcastEventLocal->Execute(vstk, NULL);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// native bool:SourceTV_BroadcastScreenMessage(bool:bLocalOnly, const String:format[], any:...);
|
2016-03-01 15:00:27 +01:00
|
|
|
static cell_t Native_BroadcastScreenMessage(IPluginContext *pContext, const cell_t *params)
|
2016-02-29 09:59:09 +01:00
|
|
|
{
|
|
|
|
if (hltvserver == nullptr)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
char buffer[1024];
|
|
|
|
size_t len;
|
|
|
|
{
|
|
|
|
DetectExceptions eh(pContext);
|
2016-03-03 04:13:21 +01:00
|
|
|
len = smutils->FormatString(buffer, sizeof(buffer), pContext, params, 2);
|
2016-02-29 09:59:09 +01:00
|
|
|
if (eh.HasException())
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
IGameEvent *msg = gameevents->CreateEvent("hltv_message", true);
|
|
|
|
if (!msg)
|
|
|
|
return 0;
|
|
|
|
|
2016-07-28 17:01:58 +02:00
|
|
|
#if SOURCE_ENGINE == SE_CSGO
|
|
|
|
wchar_t wBuffer[1024];
|
|
|
|
V_UTF8ToUnicode(buffer, wBuffer, sizeof(wBuffer));
|
|
|
|
msg->SetWString("text", wBuffer);
|
|
|
|
#else
|
2016-02-29 09:59:09 +01:00
|
|
|
msg->SetString("text", buffer);
|
2016-07-28 17:01:58 +02:00
|
|
|
#endif
|
2016-03-03 04:13:21 +01:00
|
|
|
|
|
|
|
int ret = 1;
|
|
|
|
bool bLocalOnly = params[1] != 0;
|
2016-03-12 16:51:38 +01:00
|
|
|
if (!bLocalOnly)
|
2016-03-06 12:16:38 +01:00
|
|
|
hltvserver->GetHLTVServer()->BroadcastEvent(msg);
|
2016-03-03 04:13:21 +01:00
|
|
|
else
|
2016-03-06 12:16:38 +01:00
|
|
|
ret = BroadcastEventLocal(hltvserver->GetHLTVServer(), msg, false);
|
2016-03-03 04:13:21 +01:00
|
|
|
|
2016-02-29 09:59:09 +01:00
|
|
|
gameevents->FreeEvent(msg);
|
|
|
|
|
2016-03-03 04:13:21 +01:00
|
|
|
return ret;
|
2016-02-29 09:59:09 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// native bool:SourceTV_BroadcastConsoleMessage(const String:format[], any:...);
|
|
|
|
static cell_t Native_BroadcastConsoleMessage(IPluginContext *pContext, const cell_t *params)
|
|
|
|
{
|
|
|
|
if (hltvserver == nullptr)
|
|
|
|
return 0;
|
|
|
|
|
2016-11-14 10:09:03 +01:00
|
|
|
if (!g_bHasBroadcastPrintfOffset)
|
2016-03-03 09:13:59 +01:00
|
|
|
return 0;
|
2016-02-29 09:59:09 +01:00
|
|
|
|
|
|
|
char buffer[1024];
|
|
|
|
size_t len;
|
|
|
|
{
|
|
|
|
DetectExceptions eh(pContext);
|
2016-03-03 04:13:56 +01:00
|
|
|
len = smutils->FormatString(buffer, sizeof(buffer), pContext, params, 1);
|
2016-02-29 09:59:09 +01:00
|
|
|
if (eh.HasException())
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-03-03 09:13:59 +01:00
|
|
|
SH_MCALL(hltvserver->GetBaseServer(), CBaseServer_BroadcastPrintf)("%s\n", buffer);
|
2016-02-29 09:59:09 +01:00
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2016-03-03 04:13:21 +01:00
|
|
|
// native bool:SourceTV_BroadcastChatMessage(bool:bLocalOnly, const String:format[], any:...);
|
|
|
|
static cell_t Native_BroadcastChatMessage(IPluginContext *pContext, const cell_t *params)
|
|
|
|
{
|
|
|
|
if (hltvserver == nullptr)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
char buffer[1024];
|
|
|
|
size_t len;
|
|
|
|
{
|
|
|
|
DetectExceptions eh(pContext);
|
|
|
|
len = smutils->FormatString(buffer, sizeof(buffer), pContext, params, 2);
|
|
|
|
if (eh.HasException())
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
IGameEvent *msg = gameevents->CreateEvent("hltv_chat", true);
|
|
|
|
if (!msg)
|
|
|
|
return 0;
|
|
|
|
|
2016-07-28 17:01:58 +02:00
|
|
|
#if SOURCE_ENGINE == SE_CSGO
|
|
|
|
wchar_t wBuffer[1024];
|
|
|
|
V_UTF8ToUnicode(buffer, wBuffer, sizeof(wBuffer));
|
|
|
|
msg->SetWString("text", wBuffer);
|
|
|
|
#else
|
2016-03-03 04:13:21 +01:00
|
|
|
msg->SetString("text", buffer);
|
2016-07-28 17:01:58 +02:00
|
|
|
#endif
|
2016-03-03 04:13:21 +01:00
|
|
|
|
|
|
|
int ret = 1;
|
|
|
|
bool bLocalOnly = params[1] != 0;
|
2016-03-12 16:51:38 +01:00
|
|
|
if (!bLocalOnly)
|
2016-03-06 12:16:38 +01:00
|
|
|
hltvserver->GetHLTVServer()->BroadcastEvent(msg);
|
2016-03-03 04:13:21 +01:00
|
|
|
else
|
2016-03-06 12:16:38 +01:00
|
|
|
ret = BroadcastEventLocal(hltvserver->GetHLTVServer(), msg, false);
|
2016-03-03 04:13:21 +01:00
|
|
|
|
|
|
|
gameevents->FreeEvent(msg);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2016-02-29 09:59:09 +01:00
|
|
|
// native SourceTV_GetViewEntity();
|
|
|
|
static cell_t Native_GetViewEntity(IPluginContext *pContext, const cell_t *params)
|
|
|
|
{
|
|
|
|
return hltvdirector->GetPVSEntity();
|
|
|
|
}
|
|
|
|
|
2016-03-01 02:19:19 +01:00
|
|
|
// native SourceTV_GetViewOrigin();
|
|
|
|
static cell_t Native_GetViewOrigin(IPluginContext *pContext, const cell_t *params)
|
2016-02-29 09:59:09 +01:00
|
|
|
{
|
|
|
|
Vector pvs = hltvdirector->GetPVSOrigin();
|
|
|
|
|
|
|
|
cell_t *addr;
|
|
|
|
pContext->LocalToPhysAddr(params[1], &addr);
|
|
|
|
addr[0] = sp_ftoc(pvs.x);
|
|
|
|
addr[1] = sp_ftoc(pvs.y);
|
|
|
|
addr[2] = sp_ftoc(pvs.z);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-03-01 02:19:19 +01:00
|
|
|
// native bool:SourceTV_ForceFixedCameraShot(Float:pos[], Float:angle[], iTarget, Float:fov, Float:fDuration);
|
|
|
|
static cell_t Native_ForceFixedCameraShot(IPluginContext *pContext, const cell_t *params)
|
|
|
|
{
|
|
|
|
if (hltvserver == nullptr)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
// Validate entities.
|
|
|
|
if (params[3] != 0 && !gamehelpers->ReferenceToEntity(params[3]))
|
|
|
|
{
|
|
|
|
pContext->ReportError("Invalid target (%d - %d)", gamehelpers->ReferenceToIndex(params[3]), params[3]);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
cell_t *pos;
|
|
|
|
pContext->LocalToPhysAddr(params[1], &pos);
|
|
|
|
|
|
|
|
cell_t *angle;
|
|
|
|
pContext->LocalToPhysAddr(params[2], &angle);
|
|
|
|
|
|
|
|
// Update director state like it would do itself.
|
|
|
|
g_HLTVDirectorWrapper.SetPVSEntity(0);
|
|
|
|
Vector vPos(sp_ctof(pos[0]), sp_ctof(pos[1]), sp_ctof(pos[2]));
|
|
|
|
g_HLTVDirectorWrapper.SetPVSOrigin(vPos);
|
|
|
|
|
|
|
|
IGameEvent *shot = gameevents->CreateEvent("hltv_fixed", true);
|
|
|
|
if (!shot)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
shot->SetInt("posx", vPos.x);
|
|
|
|
shot->SetInt("posy", vPos.y);
|
|
|
|
shot->SetInt("posz", vPos.z);
|
|
|
|
shot->SetInt("theta", sp_ctof(angle[0]));
|
|
|
|
shot->SetInt("phi", sp_ctof(angle[1]));
|
2016-07-28 17:01:58 +02:00
|
|
|
shot->SetInt("target", params[3] ? gamehelpers->ReferenceToIndex(params[3]) : DIRECTOR_NO_TARGET);
|
2016-03-01 02:19:19 +01:00
|
|
|
shot->SetFloat("fov", sp_ctof(params[4]));
|
|
|
|
|
2016-03-06 12:16:38 +01:00
|
|
|
hltvserver->GetHLTVServer()->BroadcastEvent(shot);
|
2016-03-01 02:19:19 +01:00
|
|
|
gameevents->FreeEvent(shot);
|
|
|
|
|
|
|
|
// Prevent auto director from changing shots until we allow it to again.
|
|
|
|
g_HLTVDirectorWrapper.SetNextThinkTick(hltvdirector->GetDirectorTick() + TIME_TO_TICKS(sp_ctof(params[5])));
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// native bool:SourceTV_ForceChaseCameraShot(iTarget1, iTarget2, distance, phi, theta, bool:bInEye, Float:fDuration);
|
|
|
|
static cell_t Native_ForceChaseCameraShot(IPluginContext *pContext, const cell_t *params)
|
|
|
|
{
|
|
|
|
if (hltvserver == nullptr)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
// Validate entities.
|
|
|
|
if (!gamehelpers->ReferenceToEntity(params[1]))
|
|
|
|
{
|
|
|
|
pContext->ReportError("Invalid target1 (%d - %d)", gamehelpers->ReferenceToIndex(params[1]), params[1]);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (params[2] != 0 && !gamehelpers->ReferenceToEntity(params[2]))
|
|
|
|
{
|
|
|
|
pContext->ReportError("Invalid target2 (%d - %d)", gamehelpers->ReferenceToIndex(params[2]), params[2]);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
IGameEvent *shot = gameevents->CreateEvent("hltv_chase", true);
|
|
|
|
if (!shot)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
shot->SetInt("target1", gamehelpers->ReferenceToIndex(params[1]));
|
2016-07-28 17:01:58 +02:00
|
|
|
shot->SetInt("target2", params[2] ? gamehelpers->ReferenceToIndex(params[2]) : DIRECTOR_NO_TARGET);
|
2016-03-01 02:19:19 +01:00
|
|
|
shot->SetInt("distance", params[3]);
|
|
|
|
shot->SetInt("phi", params[4]); // hi/low
|
|
|
|
shot->SetInt("theta", params[5]); // left/right
|
|
|
|
shot->SetInt("ineye", params[6] ? 1 : 0);
|
|
|
|
|
|
|
|
// Update director state
|
|
|
|
g_HLTVDirectorWrapper.SetPVSEntity(gamehelpers->ReferenceToIndex(params[1]));
|
|
|
|
|
2016-03-06 12:16:38 +01:00
|
|
|
hltvserver->GetHLTVServer()->BroadcastEvent(shot);
|
2016-03-01 02:19:19 +01:00
|
|
|
gameevents->FreeEvent(shot);
|
|
|
|
|
|
|
|
// Prevent auto director from changing shots until we allow it to again.
|
|
|
|
g_HLTVDirectorWrapper.SetNextThinkTick(hltvdirector->GetDirectorTick() + TIME_TO_TICKS(sp_ctof(params[7])));
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2016-02-29 09:59:09 +01:00
|
|
|
// native bool:SourceTV_IsRecording();
|
|
|
|
static cell_t Native_IsRecording(IPluginContext *pContext, const cell_t *params)
|
|
|
|
{
|
2016-04-28 15:45:20 +02:00
|
|
|
if (!hltvserver || !hltvserver->GetDemoRecorder())
|
|
|
|
return 0;
|
2016-03-06 12:16:38 +01:00
|
|
|
return hltvserver->GetDemoRecorder()->IsRecording();
|
2016-02-29 09:59:09 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Checks in COM_IsValidPath in the engine
|
|
|
|
static bool IsValidPath(const char *path)
|
|
|
|
{
|
|
|
|
return strlen(path) > 0 && !strstr(path, "\\\\") && !strstr(path, ":") && !strstr(path, "..") && !strstr(path, "\n") && !strstr(path, "\r");
|
|
|
|
}
|
|
|
|
|
|
|
|
// native bool:SourceTV_StartRecording(const String:sFilename[]);
|
|
|
|
static cell_t Native_StartRecording(IPluginContext *pContext, const cell_t *params)
|
|
|
|
{
|
2016-03-06 12:16:38 +01:00
|
|
|
if (hltvserver == nullptr)
|
2016-02-29 09:59:09 +01:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
// SourceTV is not active.
|
|
|
|
if (!hltvserver->GetBaseServer()->IsActive())
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
// Only SourceTV Master can record demos instantly
|
2016-03-06 12:16:38 +01:00
|
|
|
if (!hltvserver->GetHLTVServer()->IsMasterProxy())
|
2016-02-29 09:59:09 +01:00
|
|
|
return 0;
|
2016-04-28 15:45:20 +02:00
|
|
|
|
|
|
|
// Failed to find the IDemoRecorder instance
|
|
|
|
if (!hltvserver->GetDemoRecorder())
|
|
|
|
return 0;
|
2016-02-29 09:59:09 +01:00
|
|
|
|
|
|
|
// already recording
|
2016-03-06 12:16:38 +01:00
|
|
|
if (hltvserver->GetDemoRecorder()->IsRecording())
|
2016-02-29 09:59:09 +01:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
char *pFile;
|
|
|
|
pContext->LocalToString(params[1], &pFile);
|
|
|
|
|
|
|
|
// Invalid path.
|
|
|
|
if (!IsValidPath(pFile))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
// Make sure there is a '.dem' suffix
|
|
|
|
char pPath[PLATFORM_MAX_PATH];
|
|
|
|
size_t len = strlen(pFile);
|
|
|
|
const char *ext = libsys->GetFileExtension(pFile);
|
|
|
|
if (!ext || stricmp(ext, "dem") != 0)
|
|
|
|
ext = ".dem";
|
|
|
|
else
|
|
|
|
ext = "";
|
|
|
|
smutils->Format(pPath, sizeof(pPath), "%s%s", pFile, ext);
|
2016-02-29 14:55:23 +01:00
|
|
|
|
|
|
|
#if SOURCE_ENGINE == SE_CSGO
|
2016-02-29 09:59:09 +01:00
|
|
|
if (hltvdirector->GetHLTVServerCount() > 1)
|
|
|
|
{
|
|
|
|
for (int i = 0; i < hltvdirector->GetHLTVServerCount(); i++)
|
|
|
|
{
|
|
|
|
IHLTVServer *otherserver = hltvdirector->GetHLTVServer(i);
|
|
|
|
if (!otherserver->IsRecording())
|
|
|
|
continue;
|
|
|
|
|
|
|
|
// Cannot record. another SourceTV is currently recording into that file.
|
|
|
|
if (!stricmp(pPath, otherserver->GetRecordingDemoFilename()))
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
2016-02-29 14:55:23 +01:00
|
|
|
#endif
|
2016-02-29 09:59:09 +01:00
|
|
|
|
2016-03-06 12:16:38 +01:00
|
|
|
hltvserver->GetDemoRecorder()->StartRecording(pPath, false);
|
2016-02-29 09:59:09 +01:00
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// native bool:SourceTV_StopRecording();
|
|
|
|
static cell_t Native_StopRecording(IPluginContext *pContext, const cell_t *params)
|
|
|
|
{
|
2016-04-28 15:45:20 +02:00
|
|
|
if (hltvserver == nullptr)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
// Failed to find the IDemoRecorder instance
|
|
|
|
if (!hltvserver->GetDemoRecorder())
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
// Not recording
|
2016-03-06 12:16:38 +01:00
|
|
|
if (!hltvserver->GetDemoRecorder()->IsRecording())
|
2016-02-29 09:59:09 +01:00
|
|
|
return 0;
|
|
|
|
|
2016-02-29 14:55:23 +01:00
|
|
|
#if SOURCE_ENGINE == SE_CSGO
|
2016-03-06 12:16:38 +01:00
|
|
|
hltvserver->GetDemoRecorder()->StopRecording(NULL);
|
2016-02-29 09:59:09 +01:00
|
|
|
// TODO: Stop recording on all other active hltvservers (tv_stoprecord in csgo does this)
|
2016-02-29 14:55:23 +01:00
|
|
|
#else
|
2016-03-06 12:16:38 +01:00
|
|
|
hltvserver->GetDemoRecorder()->StopRecording();
|
2016-02-29 14:55:23 +01:00
|
|
|
#endif
|
2016-02-29 09:59:09 +01:00
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// native bool:SourceTV_GetDemoFileName(String:sFilename[], maxlen);
|
|
|
|
static cell_t Native_GetDemoFileName(IPluginContext *pContext, const cell_t *params)
|
|
|
|
{
|
2016-04-28 15:45:20 +02:00
|
|
|
if (hltvserver == nullptr)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
// Failed to find the IDemoRecorder instance
|
|
|
|
if (!hltvserver->GetDemoRecorder())
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
// Not recording
|
2016-03-06 12:16:38 +01:00
|
|
|
if (!hltvserver->GetDemoRecorder()->IsRecording())
|
2016-02-29 09:59:09 +01:00
|
|
|
return 0;
|
|
|
|
|
2016-07-07 16:27:46 +02:00
|
|
|
char *pDemoFile = hltvserver->GetDemoFileName();
|
2016-02-29 09:59:09 +01:00
|
|
|
if (!pDemoFile)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
pContext->StringToLocalUTF8(params[1], params[2], pDemoFile, NULL);
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// native SourceTV_GetRecordingTick();
|
|
|
|
static cell_t Native_GetRecordingTick(IPluginContext *pContext, const cell_t *params)
|
|
|
|
{
|
2016-04-28 15:45:20 +02:00
|
|
|
if (hltvserver == nullptr)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
// Failed to find the IDemoRecorder instance
|
|
|
|
if (!hltvserver->GetDemoRecorder())
|
|
|
|
return -1;
|
|
|
|
|
2016-03-06 12:16:38 +01:00
|
|
|
if (!hltvserver->GetDemoRecorder()->IsRecording())
|
2016-02-29 09:59:09 +01:00
|
|
|
return -1;
|
|
|
|
|
2016-03-06 12:16:38 +01:00
|
|
|
return hltvserver->GetDemoRecorder()->GetRecordingTick();
|
2016-02-29 09:59:09 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// native bool:SourceTV_PrintToDemoConsole(const String:format[], any:...);
|
|
|
|
static cell_t Native_PrintToDemoConsole(IPluginContext *pContext, const cell_t *params)
|
|
|
|
{
|
|
|
|
if (hltvserver == nullptr)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (!iserver)
|
|
|
|
return 0;
|
2016-03-06 12:16:38 +01:00
|
|
|
IClient *pClient = iserver->GetClient(hltvserver->GetHLTVServer()->GetHLTVSlot());
|
2016-02-29 09:59:09 +01:00
|
|
|
if (!pClient)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
char buffer[1024];
|
|
|
|
size_t len;
|
|
|
|
{
|
|
|
|
DetectExceptions eh(pContext);
|
|
|
|
len = smutils->FormatString(buffer, sizeof(buffer), pContext, params, 1);
|
|
|
|
if (eh.HasException())
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-03-03 04:13:56 +01:00
|
|
|
pClient->ClientPrintf("%s\n", buffer);
|
2016-02-29 09:59:09 +01:00
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// native SourceTV_GetSpectatorCount();
|
|
|
|
static cell_t Native_GetSpectatorCount(IPluginContext *pContext, const cell_t *params)
|
|
|
|
{
|
|
|
|
if (hltvserver == nullptr)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
return hltvserver->GetBaseServer()->GetNumClients();
|
|
|
|
}
|
|
|
|
|
|
|
|
// native SourceTV_GetMaxClients();
|
|
|
|
static cell_t Native_GetMaxClients(IPluginContext *pContext, const cell_t *params)
|
|
|
|
{
|
|
|
|
if (hltvserver == nullptr)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
return hltvserver->GetBaseServer()->GetMaxClients();
|
|
|
|
}
|
|
|
|
|
|
|
|
// native SourceTV_GetClientCount();
|
|
|
|
static cell_t Native_GetClientCount(IPluginContext *pContext, const cell_t *params)
|
|
|
|
{
|
|
|
|
if (hltvserver == nullptr)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
return hltvserver->GetBaseServer()->GetClientCount();
|
|
|
|
}
|
|
|
|
|
|
|
|
// native bool:SourceTV_IsClientConnected(client);
|
|
|
|
static cell_t Native_IsClientConnected(IPluginContext *pContext, const cell_t *params)
|
|
|
|
{
|
|
|
|
if (hltvserver == nullptr)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
cell_t client = params[1];
|
|
|
|
if (client < 1 || client > hltvserver->GetBaseServer()->GetClientCount())
|
|
|
|
{
|
|
|
|
pContext->ReportError("Invalid spectator client index %d.", client);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-03-06 12:50:07 +01:00
|
|
|
HLTVClientWrapper *pClient = hltvserver->GetClient(client);
|
2016-02-29 09:59:09 +01:00
|
|
|
return pClient->IsConnected();
|
|
|
|
}
|
|
|
|
|
2016-03-06 14:09:10 +01:00
|
|
|
// native bool:SourceTV_IsClientProxy(client);
|
|
|
|
static cell_t Native_IsClientProxy(IPluginContext *pContext, const cell_t *params)
|
|
|
|
{
|
|
|
|
if (hltvserver == nullptr)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
cell_t client = params[1];
|
|
|
|
if (client < 1 || client > hltvserver->GetBaseServer()->GetClientCount())
|
|
|
|
{
|
|
|
|
pContext->ReportError("Invalid spectator client index %d.", client);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
HLTVClientWrapper *pClient = hltvserver->GetClient(client);
|
|
|
|
if (!pClient->IsConnected())
|
|
|
|
{
|
|
|
|
pContext->ReportError("Client %d is not connected.", client);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return pClient->BaseClient()->IsHLTV();
|
|
|
|
}
|
|
|
|
|
2016-03-06 14:11:33 +01:00
|
|
|
// native SourceTV_GetClientName(client, String:name[], maxlen);
|
|
|
|
static cell_t Native_GetClientName(IPluginContext *pContext, const cell_t *params)
|
2016-02-29 09:59:09 +01:00
|
|
|
{
|
|
|
|
if (hltvserver == nullptr)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
cell_t client = params[1];
|
|
|
|
if (client < 1 || client > hltvserver->GetBaseServer()->GetClientCount())
|
|
|
|
{
|
|
|
|
pContext->ReportError("Invalid spectator client index %d.", client);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-03-06 12:50:07 +01:00
|
|
|
HLTVClientWrapper *pClient = hltvserver->GetClient(client);
|
2016-03-03 02:47:06 +01:00
|
|
|
if (!pClient->IsConnected())
|
2016-02-29 09:59:09 +01:00
|
|
|
{
|
|
|
|
pContext->ReportError("Client %d is not connected.", client);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-03-03 02:47:06 +01:00
|
|
|
pContext->StringToLocalUTF8(params[2], static_cast<size_t>(params[3]), pClient->Name(), NULL);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-03-06 14:11:33 +01:00
|
|
|
// native SourceTV_GetClientIP(client, String:ip[], maxlen);
|
|
|
|
static cell_t Native_GetClientIP(IPluginContext *pContext, const cell_t *params)
|
2016-03-03 02:47:06 +01:00
|
|
|
{
|
|
|
|
if (hltvserver == nullptr)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
cell_t client = params[1];
|
|
|
|
if (client < 1 || client > hltvserver->GetBaseServer()->GetClientCount())
|
|
|
|
{
|
|
|
|
pContext->ReportError("Invalid spectator client index %d.", client);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-03-06 12:50:07 +01:00
|
|
|
HLTVClientWrapper *pClient = hltvserver->GetClient(client);
|
2016-03-03 02:47:06 +01:00
|
|
|
if (!pClient->IsConnected())
|
|
|
|
{
|
|
|
|
pContext->ReportError("Client %d is not connected.", client);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
pContext->StringToLocalUTF8(params[2], static_cast<size_t>(params[3]), pClient->Ip(), NULL);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-03-06 14:11:33 +01:00
|
|
|
// native SourceTV_GetClientPassword(client, String:password[], maxlen);
|
|
|
|
static cell_t Native_GetClientPassword(IPluginContext *pContext, const cell_t *params)
|
2016-03-03 02:47:06 +01:00
|
|
|
{
|
|
|
|
if (hltvserver == nullptr)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
cell_t client = params[1];
|
|
|
|
if (client < 1 || client > hltvserver->GetBaseServer()->GetClientCount())
|
|
|
|
{
|
|
|
|
pContext->ReportError("Invalid spectator client index %d.", client);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-03-06 12:50:07 +01:00
|
|
|
HLTVClientWrapper *pClient = hltvserver->GetClient(client);
|
2016-03-03 02:47:06 +01:00
|
|
|
if (!pClient->IsConnected())
|
|
|
|
{
|
|
|
|
pContext->ReportError("Client %d is not connected.", client);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
pContext->StringToLocalUTF8(params[2], static_cast<size_t>(params[3]), pClient->Password(), NULL);
|
2016-02-29 09:59:09 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// native SourceTV_KickClient(client, const String:sReason[]);
|
|
|
|
static cell_t Native_KickClient(IPluginContext *pContext, const cell_t *params)
|
|
|
|
{
|
|
|
|
if (hltvserver == nullptr)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
cell_t client = params[1];
|
|
|
|
if (client < 1 || client > hltvserver->GetBaseServer()->GetClientCount())
|
|
|
|
{
|
|
|
|
pContext->ReportError("Invalid spectator client index %d.", client);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-03-06 12:50:07 +01:00
|
|
|
HLTVClientWrapper *pClient = hltvserver->GetClient(client);
|
2016-03-03 02:47:06 +01:00
|
|
|
if (!pClient->IsConnected())
|
2016-02-29 09:59:09 +01:00
|
|
|
{
|
|
|
|
pContext->ReportError("Client %d is not connected.", client);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
char *pReason;
|
|
|
|
pContext->LocalToString(params[2], &pReason);
|
|
|
|
|
2016-03-03 02:47:06 +01:00
|
|
|
pClient->Kick(pReason);
|
2016-02-29 09:59:09 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-11-14 10:01:44 +01:00
|
|
|
// native SourceTV_PrintToChat(client, const String:format[], any:...);
|
|
|
|
static cell_t Native_PrintToChat(IPluginContext *pContext, const cell_t *params)
|
|
|
|
{
|
|
|
|
if (hltvserver == nullptr)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
cell_t client = params[1];
|
|
|
|
if (client < 1 || client > hltvserver->GetBaseServer()->GetClientCount())
|
|
|
|
{
|
|
|
|
pContext->ReportError("Invalid spectator client index %d.", client);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
HLTVClientWrapper *pClient = hltvserver->GetClient(client);
|
|
|
|
if (!pClient->IsConnected())
|
|
|
|
{
|
|
|
|
pContext->ReportError("Client %d is not connected.", client);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
char buffer[1024];
|
|
|
|
size_t len;
|
|
|
|
{
|
2018-11-18 08:09:10 +01:00
|
|
|
|
|
|
|
#if SOURCE_ENGINE != SE_CSGO
|
|
|
|
// There's no way to set the current translation without a client index, so we'll save / restore the language on the relay.
|
2018-11-22 07:15:12 +01:00
|
|
|
int iHLTVRelay = hltvserver->GetHLTVServer()->GetHLTVSlot() + 1;
|
|
|
|
IGamePlayer *pHLTVRelay = playerhelpers->GetGamePlayer(iHLTVRelay);
|
|
|
|
pHLTVRelay->SetLanguageId(pClient->GetLanguageId());
|
2018-11-18 08:09:10 +01:00
|
|
|
|
2018-11-22 07:15:12 +01:00
|
|
|
int restoreTarget = translator->SetGlobalTarget(iHLTVRelay);
|
2018-11-18 08:09:10 +01:00
|
|
|
#endif
|
|
|
|
|
2016-11-14 10:01:44 +01:00
|
|
|
DetectExceptions eh(pContext);
|
|
|
|
len = smutils->FormatString(buffer, sizeof(buffer), pContext, params, 2);
|
2018-11-18 08:09:10 +01:00
|
|
|
|
|
|
|
#if SOURCE_ENGINE != SE_CSGO
|
2018-11-22 07:15:12 +01:00
|
|
|
pHLTVRelay->SetLanguageId(translator->GetServerLanguage());
|
2018-11-18 08:09:10 +01:00
|
|
|
translator->SetGlobalTarget(restoreTarget);
|
|
|
|
#endif
|
|
|
|
|
2016-11-14 10:01:44 +01:00
|
|
|
if (eh.HasException())
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
IGameEvent *msg = gameevents->CreateEvent("hltv_chat", true);
|
|
|
|
if (!msg)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
#if SOURCE_ENGINE == SE_CSGO
|
|
|
|
wchar_t wBuffer[1024];
|
|
|
|
V_UTF8ToUnicode(buffer, wBuffer, sizeof(wBuffer));
|
|
|
|
msg->SetWString("text", wBuffer);
|
|
|
|
#else
|
|
|
|
msg->SetString("text", buffer);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (g_bHasClientFireGameEventOffset)
|
|
|
|
{
|
|
|
|
void *pGameClient = pClient->BaseClient();
|
|
|
|
// The IClient vtable is +4 from the CBaseClient vtable due to multiple inheritance.
|
|
|
|
pGameClient = (void *)((intptr_t)pGameClient - 4);
|
|
|
|
SH_MCALL(pGameClient, CBaseClient_FireGameEvent)(msg);
|
|
|
|
}
|
|
|
|
|
|
|
|
gameevents->FreeEvent(msg);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-11-14 10:09:03 +01:00
|
|
|
// native SourceTV_PrintToConsole(client, const String:format[], any:...);
|
|
|
|
static cell_t Native_PrintToConsole(IPluginContext *pContext, const cell_t *params)
|
|
|
|
{
|
|
|
|
if (hltvserver == nullptr)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
cell_t client = params[1];
|
|
|
|
if (client < 1 || client > hltvserver->GetBaseServer()->GetClientCount())
|
|
|
|
{
|
|
|
|
pContext->ReportError("Invalid spectator client index %d.", client);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
HLTVClientWrapper *pClient = hltvserver->GetClient(client);
|
|
|
|
if (!pClient->IsConnected())
|
|
|
|
{
|
|
|
|
pContext->ReportError("Client %d is not connected.", client);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
char buffer[1024];
|
|
|
|
size_t len;
|
|
|
|
{
|
2018-11-18 08:09:10 +01:00
|
|
|
|
|
|
|
#if SOURCE_ENGINE != SE_CSGO
|
|
|
|
// There's no way to set the current translation without a client index, so we'll save / restore the language on the relay.
|
2018-11-22 07:15:12 +01:00
|
|
|
int iHLTVRelay = hltvserver->GetHLTVServer()->GetHLTVSlot() + 1;
|
|
|
|
IGamePlayer *pHLTVRelay = playerhelpers->GetGamePlayer(iHLTVRelay);
|
|
|
|
pHLTVRelay->SetLanguageId(pClient->GetLanguageId());
|
2018-11-18 08:09:10 +01:00
|
|
|
|
2018-11-22 07:15:12 +01:00
|
|
|
int restoreTarget = translator->SetGlobalTarget(iHLTVRelay);
|
2018-11-18 08:09:10 +01:00
|
|
|
#endif
|
|
|
|
|
2016-11-14 10:09:03 +01:00
|
|
|
DetectExceptions eh(pContext);
|
|
|
|
len = smutils->FormatString(buffer, sizeof(buffer), pContext, params, 2);
|
2018-11-18 08:09:10 +01:00
|
|
|
|
|
|
|
#if SOURCE_ENGINE != SE_CSGO
|
2018-11-22 07:15:12 +01:00
|
|
|
pHLTVRelay->SetLanguageId(translator->GetServerLanguage());
|
2018-11-18 08:09:10 +01:00
|
|
|
translator->SetGlobalTarget(restoreTarget);
|
|
|
|
#endif
|
|
|
|
|
2016-11-14 10:09:03 +01:00
|
|
|
if (eh.HasException())
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
pClient->BaseClient()->ClientPrintf("%s\n", buffer);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-11-14 10:27:38 +01:00
|
|
|
|
|
|
|
// native SourceTV_SetClientTVTitle(client, const String:format[], any:...);
|
|
|
|
static cell_t Native_SetClientTVTitle(IPluginContext *pContext, const cell_t *params)
|
|
|
|
{
|
|
|
|
if (hltvserver == nullptr)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
cell_t client = params[1];
|
|
|
|
if (client < 1 || client > hltvserver->GetBaseServer()->GetClientCount())
|
|
|
|
{
|
|
|
|
pContext->ReportError("Invalid spectator client index %d.", client);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
HLTVClientWrapper *pClient = hltvserver->GetClient(client);
|
|
|
|
if (!pClient->IsConnected())
|
|
|
|
{
|
|
|
|
pContext->ReportError("Client %d is not connected.", client);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
char buffer[1024];
|
|
|
|
size_t len;
|
|
|
|
{
|
|
|
|
DetectExceptions eh(pContext);
|
|
|
|
len = smutils->FormatString(buffer, sizeof(buffer), pContext, params, 2);
|
|
|
|
if (eh.HasException())
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
IGameEvent *msg = gameevents->CreateEvent("hltv_title", true);
|
|
|
|
if (!msg)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
#if SOURCE_ENGINE == SE_CSGO
|
|
|
|
wchar_t wBuffer[1024];
|
|
|
|
V_UTF8ToUnicode(buffer, wBuffer, sizeof(wBuffer));
|
|
|
|
msg->SetWString("text", wBuffer);
|
|
|
|
#else
|
|
|
|
msg->SetString("text", buffer);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (g_bHasClientFireGameEventOffset)
|
|
|
|
{
|
|
|
|
void *pGameClient = pClient->BaseClient();
|
|
|
|
// The IClient vtable is +4 from the CBaseClient vtable due to multiple inheritance.
|
|
|
|
pGameClient = (void *)((intptr_t)pGameClient - 4);
|
|
|
|
SH_MCALL(pGameClient, CBaseClient_FireGameEvent)(msg);
|
|
|
|
}
|
|
|
|
|
|
|
|
gameevents->FreeEvent(msg);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-02-29 09:59:09 +01:00
|
|
|
const sp_nativeinfo_t sourcetv_natives[] =
|
|
|
|
{
|
2016-03-01 15:00:27 +01:00
|
|
|
{ "SourceTV_GetServerInstanceCount", Native_GetServerInstanceCount },
|
|
|
|
{ "SourceTV_SelectServerInstance", Native_SelectServerInstance },
|
|
|
|
{ "SourceTV_GetSelectedServerInstance", Native_GetSelectedServerInstance },
|
2016-03-06 12:16:38 +01:00
|
|
|
{ "SourceTV_IsActive", Native_IsActive },
|
2016-03-03 12:28:49 +01:00
|
|
|
{ "SourceTV_IsMasterProxy", Native_IsMasterProxy },
|
|
|
|
{ "SourceTV_GetServerIP", Native_GetServerIP },
|
|
|
|
{ "SourceTV_GetServerPort", Native_GetServerPort },
|
2016-02-29 09:59:09 +01:00
|
|
|
{ "SourceTV_GetBotIndex", Native_GetBotIndex },
|
|
|
|
{ "SourceTV_GetLocalStats", Native_GetLocalStats },
|
2016-03-01 15:00:27 +01:00
|
|
|
{ "SourceTV_GetGlobalStats", Native_GetGlobalStats },
|
2016-02-29 09:59:09 +01:00
|
|
|
{ "SourceTV_GetBroadcastTick", Native_GetBroadcastTick },
|
|
|
|
{ "SourceTV_GetDelay", Native_GetDelay },
|
2016-03-01 15:00:27 +01:00
|
|
|
{ "SourceTV_BroadcastScreenMessage", Native_BroadcastScreenMessage },
|
2016-02-29 09:59:09 +01:00
|
|
|
{ "SourceTV_BroadcastConsoleMessage", Native_BroadcastConsoleMessage },
|
2016-03-03 04:13:21 +01:00
|
|
|
{ "SourceTV_BroadcastChatMessage", Native_BroadcastChatMessage },
|
2016-02-29 09:59:09 +01:00
|
|
|
{ "SourceTV_GetViewEntity", Native_GetViewEntity },
|
2016-03-01 02:19:19 +01:00
|
|
|
{ "SourceTV_GetViewOrigin", Native_GetViewOrigin },
|
|
|
|
{ "SourceTV_ForceFixedCameraShot", Native_ForceFixedCameraShot },
|
|
|
|
{ "SourceTV_ForceChaseCameraShot", Native_ForceChaseCameraShot },
|
2016-02-29 09:59:09 +01:00
|
|
|
{ "SourceTV_StartRecording", Native_StartRecording },
|
|
|
|
{ "SourceTV_StopRecording", Native_StopRecording },
|
|
|
|
{ "SourceTV_IsRecording", Native_IsRecording },
|
|
|
|
{ "SourceTV_GetDemoFileName", Native_GetDemoFileName },
|
|
|
|
{ "SourceTV_GetRecordingTick", Native_GetRecordingTick },
|
|
|
|
{ "SourceTV_PrintToDemoConsole", Native_PrintToDemoConsole },
|
|
|
|
{ "SourceTV_GetSpectatorCount", Native_GetSpectatorCount },
|
|
|
|
{ "SourceTV_GetMaxClients", Native_GetMaxClients },
|
|
|
|
{ "SourceTV_GetClientCount", Native_GetClientCount },
|
|
|
|
{ "SourceTV_IsClientConnected", Native_IsClientConnected },
|
2016-03-06 14:09:10 +01:00
|
|
|
{ "SourceTV_IsClientProxy", Native_IsClientProxy },
|
2016-03-06 14:11:33 +01:00
|
|
|
{ "SourceTV_GetClientName", Native_GetClientName },
|
|
|
|
{ "SourceTV_GetClientIP", Native_GetClientIP },
|
|
|
|
{ "SourceTV_GetClientPassword", Native_GetClientPassword },
|
2016-02-29 09:59:09 +01:00
|
|
|
{ "SourceTV_KickClient", Native_KickClient },
|
2016-11-14 10:01:44 +01:00
|
|
|
{ "SourceTV_PrintToChat", Native_PrintToChat },
|
2016-11-14 10:09:03 +01:00
|
|
|
{ "SourceTV_PrintToConsole", Native_PrintToConsole },
|
2016-11-14 10:27:38 +01:00
|
|
|
{ "SourceTV_SetClientTVTitle", Native_SetClientTVTitle },
|
2016-02-29 09:59:09 +01:00
|
|
|
{ NULL, NULL },
|
|
|
|
};
|