diff options
| author | navewindre <nw@moneybot.cc> | 2023-12-04 18:06:10 +0100 |
|---|---|---|
| committer | navewindre <nw@moneybot.cc> | 2023-12-04 18:06:10 +0100 |
| commit | aef0d1c1268ab7d4bc18996c9c6b4da16a40aadc (patch) | |
| tree | 43e766b51704f4ab8b383583bdc1871eeeb9c698 /sourcemod/scripting/include | |
| parent | 38f1140c11724da05a23a10385061200b907cf6e (diff) | |
bbbbbbbbwaaaaaaaaaaa
Diffstat (limited to 'sourcemod/scripting/include')
62 files changed, 20631 insertions, 0 deletions
diff --git a/sourcemod/scripting/include/GlobalAPI.inc b/sourcemod/scripting/include/GlobalAPI.inc new file mode 100644 index 0000000..8813645 --- /dev/null +++ b/sourcemod/scripting/include/GlobalAPI.inc @@ -0,0 +1,822 @@ +// ================== DOUBLE INCLUDE ========================= // + +#if defined _GlobalAPI_included_ +#endinput +#endif +#define _GlobalAPI_included_ + +// ======================= DEFINITIONS ======================= // + +#define DEFAULT_DATA 0 +#define DEFAULT_INT -1 +#define DEFAULT_STRING "" +#define DEFAULT_FLOAT -1.0 +#define DEFAULT_BOOL view_as<bool>(-1) + +#define GlobalAPI_Plugin_Version "2.0.0" +#define GlobalAPI_Plugin_Desc "Plugin helper for GlobalAPI Production & Staging" +#define GlobalAPI_Plugin_Url "https://bitbucket.org/kztimerglobalteam/GlobalAPI-SMPlugin" +#define GlobalAPI_Plugin_NameVersion "GlobalAPI Plugin " ... GlobalAPI_Plugin_Version + +#define GlobalAPI_Backend_Version "v2.0" +#define GlobalAPI_Backend_Staging_Version "v2.0" +#define GlobalAPI_BaseUrl "https://kztimerglobal.com/api/" ... GlobalAPI_Backend_Version +#define GlobalAPI_Staging_BaseUrl "https://globalapi.ruto.sh/api/" ... GlobalAPI_Backend_Staging_Version + +#define GlobalAPI_Max_BaseUrl_Length 128 +#define GlobalAPI_Max_QueryParam_Num 20 +#define GlobalAPI_Max_QueryParam_Length 64 +#define GlobalAPI_Max_QueryParams_Length (GlobalAPI_Max_QueryParam_Num * GlobalAPI_Max_QueryParam_Length) +#define GlobalAPI_Max_QueryUrl_Length (GlobalAPI_Max_QueryParams_Length + GlobalAPI_Max_BaseUrl_Length) +#define GlobalAPI_Max_QueryParam_Array_Length 64 + +#define GlobalAPI_Max_APIKey_Length 128 +#define GlobalAPI_Max_PluginName_Length 64 +#define GlobalAPI_Max_PluginVersion_Length 32 +#define GlobalAPI_Data_File_Extension "GAPI" + +// ======================= INCLUDES ========================== // + +#include <GlobalAPI/requestdata> +#include <GlobalAPI/responses> +#include <GlobalAPI/stocks> + +// ======================= ENUMS ============================= // + +/** + * Defines what request method is used on requests + */ +enum +{ + GlobalAPIRequestType_GET = 0, /**< Request uses GET HTTP method */ + GlobalAPIRequestType_POST /**< Request uses POST HTTP method */ +}; + +/** + * Defines what accept type is used on requests + */ +enum +{ + GlobalAPIRequestAcceptType_JSON = 0, /**< Request uses application/json HTTP accept type */ + GlobalAPIRequestAcceptType_OctetStream /**< Request uses application/octet-stream HTTP accept type */ +}; + +/** + * Defines what content type is used on requests + */ +enum +{ + GlobalAPIRequestContentType_JSON = 0, /**< Request uses application/json HTTP content type */ + GlobalAPIRequestContentType_OctetStream /**< Request uses application/octet-stream HTTP content type */ +}; + +// ======================= TYPEDEFS ========================== // + +/* + Function types when API call finishes +*/ +typeset OnAPICallFinished +{ + /** + * Called when an API call has finished + * + * @param hResponse JSON_Object handle to the response + * @param hData GlobalAPIRequestData handle for the request + * @noreturn + */ + function void(JSON_Object hResponse, GlobalAPIRequestData hData); + + /** + * Called when an API call has finished + * + * @param hResponse JSON_Object handle to the response + * @param hData GlobalAPIRequestData handle for the request + * @param data Optional data that was passed + * @noreturn + */ + function void(JSON_Object hResponse, GlobalAPIRequestData hData, any data); +}; + +// ======================= FORWARDS ========================== // + +/** + * Called when GlobalAPI plugin is initialized, + * this means API Key is loaded and all the cvars are loaded + * + * @noreturn + */ +forward void GlobalAPI_OnInitialized(); + +/** + * Called when GlobalAPI plugin has failed a request + * + * @param request Handle to the request failed + * @param hData Handle to request's GlobalAPIRequestData + * @noreturn + */ +forward void GlobalAPI_OnRequestFailed(Handle request, GlobalAPIRequestData hData); + +/** + * Called when GlobalAPI plugin has started a request + * + * @param request Handle to the request started + * @param hData Handle to request's GlobalAPIRequestData + * @noreturn + */ +forward void GlobalAPI_OnRequestStarted(Handle request, GlobalAPIRequestData hData); + +/** + * Called when GlobalAPI plugin has finished a request + * + * @param request Handle to the request finished + * @param hData Handle to request's GlobalAPIRequestData + * @noreturn + */ +forward void GlobalAPI_OnRequestFinished(Handle request, GlobalAPIRequestData hData); + +// ======================= NATIVES =========================== // + +/** + * Gets a boolean of whether GlobalAPI plugin is initialized. + * + * @note See GlobalAPI_OnInitialized for the event version. + * @return Whether GlobalAPI plugin is initialized. + */ +native bool GlobalAPI_IsInit(); + +/** + * Gets the API Key used by GlobalAPI plugin + * + * @param buffer Buffer to store result in + * @param maxlength Max length of the buffer + * @noreturn + */ +native void GlobalAPI_GetAPIKey(char[] buffer, int maxlength); + +/** + * Gets whether GlobalAPI is using an API Key + * + * @note This does not mean the API Key is valid! + * @return Whether API Key is used by GlobalAPI plugin + */ +native bool GlobalAPI_HasAPIKey(); + +/** + * Gets whether GlobalAPI is using the staging endpoint + * + * @note It is not safe to call this before GlobalAPI_OnInitialized! + * @return Whether staging endpoint is used by GlobalAPI plugin + */ +native bool GlobalAPI_IsStaging(); + +/** + * Gets whether GlobalAPI is in debug mode + * + * @note It is not safe to call this before GlobalAPI_OnInitialized! + * @return Whether GlobalAPI plugin is in debug mode + */ +native bool GlobalAPI_IsDebugging(); + +/** + * Sends a request in GlobalAPI plugin format + * + * @param hData Handle to GlobalAPIRequestData + * @return Whether the request was sent successfully + */ +native bool GlobalAPI_SendRequest(GlobalAPIRequestData hData); + +/** + * Sends a debug message to GlobalAPI plugin logs if debugging is enabled + * + * @param message Formatting rules + * @param ... Variable number of format parameters + * @note This is not safe to use before convars have loaded + * @return Whether the message was logged + */ +native bool GlobalAPI_DebugMessage(const char[] message, any ...); + +/** + * Starts a GET HTTP Request to /api/{version}/auth/status + * + * @param callback Callback when request has finished + * @param data Optional data to pass + * @return Whether request was successfully sent + */ +native bool GlobalAPI_GetAuthStatus(OnAPICallFinished callback = INVALID_FUNCTION, any data = DEFAULT_DATA); + +/** + * Starts a GET HTTP Request to /api/{version}/bans + * + * @param callback Callback when request has finished + * @param data Optional data to pass + * @param banTypes Ban types to query + * @param banTypesList -Unsupported at the moment- + * @param isExpired Whether to query for isExpired or not + * @param ipAddress IP address to query + * @param steamId64 SteamID64 to query + * @param steamId SteamID2 to query + * @param notesContain Notes to query + * @param statsContain Stats to query + * @param serverId Server ID to query + * @param createdSince Created since date to query + * @param updatedSince Updated since date to query + * @param offset Offset of the dataset to query + * @param limit Amount of items returned for the query + * @return Whether request was successfully sent + */ +native bool GlobalAPI_GetBans(OnAPICallFinished callback = INVALID_FUNCTION, any data = DEFAULT_DATA, const char[] banTypes = DEFAULT_STRING, + const char[] banTypesList = DEFAULT_STRING, bool isExpired = DEFAULT_BOOL, const char[] ipAddress = DEFAULT_STRING, + const char[] steamId64 = DEFAULT_STRING, const char[] steamId = DEFAULT_STRING, const char[] notesContain = DEFAULT_STRING, + const char[] statsContain = DEFAULT_STRING, int serverId = DEFAULT_INT, const char[] createdSince = DEFAULT_STRING, + const char[] updatedSince = DEFAULT_STRING, int offset = DEFAULT_INT, int limit = DEFAULT_INT); + +/** + * Starts a POST HTTP Request to /api/{version}/bans + * + * @param callback Callback when request has finished + * @param data Optional data to pass + * @param steamId SteamID2 of the user + * @param banType Type of the ban + * @param stats Stats of the ban + * @param notes Notes of the ban + * @param ipAddress IP address of the user + * @return Whether request was successfully sent + */ +native bool GlobalAPI_CreateBan(OnAPICallFinished callback = INVALID_FUNCTION, any data = DEFAULT_DATA, + const char[] steamId, const char[] banType, const char[] stats, + const char[] notes, const char[] ipAddress); + +/** + * Starts a GET HTTP Request to /api/{version}/jumpstats + * + * @param callback Callback when request has finished + * @param data Optional data to pass + * @param id Id to query + * @param serverId Server id to query + * @param steamId64 SteamID64 to query + * @param steamId SteamID2 to query + * @param jumpType Jump type to query + * @param steamId64List -Unsupported at the moment- + * @param jumpTypeList -Unsupported at the moment- + * @param greaterThanDistance Greater than distance to query + * @param lessThanDistance Less than distance to query + * @param isMsl Whether to query for isMsl or not + * @param isCrouchBind Whether to query for isCrouchBind or not + * @param isForwardBind Whether to query for isForwardBind or not + * @param isCrouchBoost Whether to query for isCrouchBoost or not + * @param updatedById Updated by id to query + * @param createdSince Created since date to query + * @param updatedSince Updated since date to query + * @param offset Offset of the dataset to query + * @param limit Amount of items returned for the query + * @return Whether request was successfully sent + */ +native bool GlobalAPI_GetJumpstats(OnAPICallFinished callback = INVALID_FUNCTION, any data = DEFAULT_DATA, int id = DEFAULT_INT, + int serverId = DEFAULT_INT, const char[] steamId64 = DEFAULT_STRING, const char[] steamId = DEFAULT_STRING, + const char[] jumpType = DEFAULT_STRING, const char[] steamId64List = DEFAULT_STRING, + const char[] jumpTypeList = DEFAULT_STRING, float greaterThanDistance = DEFAULT_FLOAT, + float lessThanDistance = DEFAULT_FLOAT, bool isMsl = DEFAULT_BOOL, bool isCrouchBind = DEFAULT_BOOL, + bool isForwardBind = DEFAULT_BOOL, bool isCrouchBoost = DEFAULT_BOOL, int updatedById = DEFAULT_INT, + const char[] createdSince = DEFAULT_STRING, const char[] updatedSince = DEFAULT_STRING, + int offset = DEFAULT_INT, int limit = DEFAULT_INT); + +/** + * Starts a POST HTTP Request to /api/{version}/jumpstats + * + * @param callback Callback when request has finished + * @param data Optional data to pass + * @param steamId SteamID2 of the user + * @param jumpType Type of the jump + * @param distance Distance of the jump + * @param jumpJsonInfo Data of the jump + * @param tickRate Tickrate of the server + * @param mslCount Msl count of the jump + * @param isCrouchBind Whether crouch bind was used + * @param isForwardBind Whether forward bind was used + * @param isCrouchBoost Whether crouch boost was used + * @param strafeCount Strafe count of the jump + * @return Whether request was successfully sent + */ +native bool GlobalAPI_CreateJumpstat(OnAPICallFinished callback = INVALID_FUNCTION, any data = DEFAULT_DATA, const char[] steamId, + int jumpType, float distance, const char[] jumpJsonInfo, int tickRate, int mslCount, + bool isCrouchBind, bool isForwardBind, bool isCrouchBoost, int strafeCount); + +/** + * Starts a GET HTTP Request to /api/{version}/jumpstats/{jump_type}/top + * + * @param callback Callback when request has finished + * @param data Optional data to pass + * @param jumpType Jump type to query + * @param id Id to query + * @param serverId Server Id to query + * @param steamId64 SteamID64 to query + * @param steamId SteamID2 to query + * @param steamId64List -Unsupported at the moment- + * @param jumpTypeList -Unsupported at the moment- + * @param greaterThanDistance Greater than distance to query + * @param lessThanDistance Less than distance to query + * @param isMsl Whether to query for isMsl or not + * @param isCrouchBind Whether to query for isCrouchBind or not + * @param isForwardBind Whether to query for isForwardBind or not + * @param isCrouchBoost Whether to query for isCrouchBoost or not + * @param updatedById Updated by id to query + * @param createdSince Created since date to query + * @param updatedSince Updated since date to query + * @param offset Offset of the dataset to query + * @param limit Amount of items returned for the query + * @return Whether request was successfully sent + */ +native bool GlobalAPI_GetJumpstatTop(OnAPICallFinished callback = INVALID_FUNCTION, any data = DEFAULT_DATA, const char[] jumpType, + int id = DEFAULT_INT, int serverId = DEFAULT_INT, const char[] steamId64 = DEFAULT_STRING, + const char[] steamId = DEFAULT_STRING, const char[] steamId64List = DEFAULT_STRING, + const char[] jumpTypeList = DEFAULT_STRING, float greaterThanDistance = DEFAULT_FLOAT, + float lessThanDistance = DEFAULT_FLOAT, bool isMsl = DEFAULT_BOOL, bool isCrouchBind = DEFAULT_BOOL, + bool isForwardBind = DEFAULT_BOOL, bool isCrouchBoost = DEFAULT_BOOL, int updatedById = DEFAULT_INT, + const char[] createdSince = DEFAULT_STRING, const char[] updatedSince = DEFAULT_STRING, + int offset = DEFAULT_INT, int limit = DEFAULT_INT); + +/** + * Starts a GET HTTP Request to /api/{version}/jumpstats/{jump_type}/top30 + * + * @param callback Callback when request has finished + * @param data Optional data to pass + * @param jumpType Jump type to query + * @return Whether request was successfully sent + */ +native bool GlobalAPI_GetJumpstatTop30(OnAPICallFinished callback = INVALID_FUNCTION, any data = DEFAULT_DATA, const char[] jumpType); + +/** + * Starts a GET HTTP Request to /api/{version}/maps + * + * @param callback Callback when request has finished + * @param data Optional data to pass + * @param name Map name to query + * @param largerThanFilesize Larger than filesize to query + * @param smallerThanFilesize Smaller than filesize to query + * @param isValidated Whether to query for isValidated or not + * @param difficulty Map difficulty to query + * @param createdSince Created since date to query + * @param updatedSince Updated since date to query + * @param offset Offset of the dataset to query + * @param limit Amount of items returned for the query + * @return Whether request was successfully sent + */ +native bool GlobalAPI_GetMaps(OnAPICallFinished callback = INVALID_FUNCTION, any data = DEFAULT_DATA, const char[] name = DEFAULT_STRING, + int largerThanFilesize = DEFAULT_INT, int smallerThanFilesize = DEFAULT_INT, bool isValidated = DEFAULT_BOOL, + int difficulty = DEFAULT_INT, const char[] createdSince = DEFAULT_STRING, const char[] updatedSince = DEFAULT_STRING, + int offset = DEFAULT_INT, int limit = DEFAULT_INT); + +/** + * Starts a GET HTTP Request to /api/{version}/maps/{id} + * + * @param callback Callback when request has finished + * @param data Optional data to pass + * @param id Map id to query + * @return Whether request was successfully sent + */ +native bool GlobalAPI_GetMapById(OnAPICallFinished callback = INVALID_FUNCTION, any data = DEFAULT_DATA, int id); + +/** + * Starts a GET HTTP Request to /api/{version}/maps/name/{map_name} + * + * @param callback Callback when request has finished + * @param data Optional data to pass + * @param name Map name to query + * @return Whether request was successfully sent + */ +native bool GlobalAPI_GetMapByName(OnAPICallFinished callback = INVALID_FUNCTION, any data = DEFAULT_DATA, const char[] name); + +/** + * Starts a GET HTTP Request to /api/{version}/modes + * + * @param callback Callback when request has finished + * @param data Optional data to pass + * @return Whether request was successfully sent + */ +native bool GlobalAPI_GetModes(OnAPICallFinished callback = INVALID_FUNCTION, any data = DEFAULT_DATA); + +/** + * Starts a GET HTTP Request to /api/{version}/modes/id/{id} + * + * @param callback Callback when request has finished + * @param data Optional data to pass + * @param id Mode id to query + * @return Whether request was successfully sent + */ +native bool GlobalAPI_GetModeById(OnAPICallFinished callback = INVALID_FUNCTION, any data = DEFAULT_DATA, int id); + +/** + * Starts a GET HTTP Request to /api/{version}/modes/name/{mode_name} + * + * @param callback Callback when request has finished + * @param data Optional data to pass + * @param name Mode name to query + * @return Whether request was successfully sent + */ +native bool GlobalAPI_GetModeByName(OnAPICallFinished callback = INVALID_FUNCTION, any data = DEFAULT_DATA, const char[] name); + +/** + * Starts a GET HTTP Request to /api/{version}/players + * + * @param callback Callback when request has finished + * @param data Optional data to pass + * @param steamId SteamID2 to query + * @param isBanned Whether to query for isBanned or not + * @param totalRecords Total records to query + * @param ipAddress IP address to query + * @param steamId64List -Unsupported at the moment- + * @return Whether request was successfully sent + */ +native bool GlobalAPI_GetPlayers(OnAPICallFinished callback = INVALID_FUNCTION, any data = DEFAULT_DATA, const char[] steamId = DEFAULT_STRING, + bool isBanned = DEFAULT_BOOL, int totalRecords = DEFAULT_INT, const char[] ipAddress = DEFAULT_STRING, + const char[] steamId64List = DEFAULT_STRING); + +/** + * Starts a GET HTTP Request to /api/{version}/players/steamid/{steamid} + * + * @param callback Callback when request has finished + * @param data Optional data to pass + * @param steamId SteamID2 to query + * @return Whether request was successfully sent + */ +native bool GlobalAPI_GetPlayerBySteamId(OnAPICallFinished callback = INVALID_FUNCTION, any data = DEFAULT_DATA, const char[] steamId); + +/** + * Starts a GET HTTP Request to /api/{version}/players/steamid/{steamid}/ip/{ip} + * + * @param callback Callback when request has finished + * @param data Optional data to pass + * @param steamId SteamID2 to query + * @param ipAddress IP address to query + * @return Whether request was successfully sent + */ +native bool GlobalAPI_GetPlayerBySteamIdAndIp(OnAPICallFinished callback = INVALID_FUNCTION, any data = DEFAULT_DATA, + const char[] steamId, const char[] ipAddress); + +/** + * Starts a POST HTTP Request to /api/{version}/records + * + * @param callback Callback when request has finished + * @param data Optional data to pass + * @param steamId SteamID2 of the user + * @param mapId Map id of the record + * @param mode Mode of the record + * @param stage Stage of the record + * @param tickRate Tickrate of the server + * @param teleports Teleport count of the record + * @param time Elapsed time of the record + * @return Whether request was successfully sent + */ +native bool GlobalAPI_CreateRecord(OnAPICallFinished callback = INVALID_FUNCTION, any data = DEFAULT_DATA, const char[] steamId, + int mapId, const char[] mode, int stage, int tickRate, int teleports, float time); + +/** + * Starts a GET HTTP Request to /api/{version}/records/place/{id} + * + * @note This is deprecated! + * @param callback Callback when request has finished + * @param data Optional data to pass + * @param id Id to query + * @return Whether request was successfully sent + */ +native bool GlobalAPI_GetRecordPlaceById(OnAPICallFinished callback = INVALID_FUNCTION, any data = DEFAULT_DATA, int id); + +/** + * Starts a GET HTTP Request to /api/{version}/records/top + * + * @param callback Callback when request has finished + * @param data Optional data to pass + * @param steamId SteamID2 to query + * @param steamId64 SteamID64 to query + * @param mapId Map id to query + * @param mapName Map name to query + * @param tickRate Tickrate to query + * @param stage Stage to query + * @param modes Mode(s) to query + * @param hasTeleports Whether to query for hasTeleports or not + * @param playerName Player name to query + * @param offset Offset of the dataset to query + * @param limit Amount of items returned for the query + * @return Whether request was successfully sent + */ +native bool GlobalAPI_GetRecordsTop(OnAPICallFinished callback = INVALID_FUNCTION, any data = DEFAULT_DATA, + const char[] steamId = DEFAULT_STRING, const char[] steamId64 = DEFAULT_STRING, int mapId = DEFAULT_INT, + const char[] mapName = DEFAULT_STRING, int tickRate = DEFAULT_INT, int stage = DEFAULT_INT, + const char[] modes = DEFAULT_STRING, bool hasTeleports = DEFAULT_BOOL, + const char[] playerName = DEFAULT_STRING, int offset = DEFAULT_INT, int limit = DEFAULT_INT); + +/** + * Starts a GET HTTP Request to /api/{version}/records/top/recent + * + * @param callback Callback when request has finished + * @param data Optional data to pass + * @param steamId SteamID2 to query + * @param steamId64 SteamID64 to query + * @param mapId Map id to query + * @param mapName Map name to query + * @param tickRate Tickrate to query + * @param stage Stage to query + * @param modes Mode(s) to query + * @param topAtLeast Place top at least to query + * @param topOverallAtLeast Place top overall at least to query + * @param hasTeleports Whether to query for hasTeleports or not + * @param createdSince Created since date to query + * @param playerName Player name to query + * @param offset Offset of the dataset to query + * @param limit Amount of items returned for the query + * @return Whether request was successfully sent + */ +native bool GlobalAPI_GetRecordsTopRecent(OnAPICallFinished callback = INVALID_FUNCTION, any data = DEFAULT_DATA, + const char[] steamId = DEFAULT_STRING, const char[] steamId64 = DEFAULT_STRING, + int mapId = DEFAULT_INT, const char[] mapName = DEFAULT_STRING, + int tickRate = DEFAULT_INT, int stage = DEFAULT_INT, + const char[] modes = DEFAULT_STRING, int topAtLeast = DEFAULT_INT, + int topOverallAtLeast = DEFAULT_INT, bool hasTeleports = DEFAULT_BOOL, + const char[] createdSince = DEFAULT_STRING, const char[] playerName = DEFAULT_STRING, + int offset = DEFAULT_INT, int limit = DEFAULT_INT); + +/** + * Starts a GET HTTP Request to /api/{version}/records/top/world_records + * + * @param callback Callback when request has finished + * @param data Optional data to pass + * @param ids Array of ids to query + * @param idsLength Length of the ids array + * @param mapIds Array of map ids to query + * @param mapIdsLength Length of the map ids array + * @param stages Array of stages to query + * @param stagesLength Length of the stages array + * @param modeIds Array of mode ids to query + * @param modeIdsLength Length of the mode ids array + * @param tickRates Array of tickrates to query + * @param tickRatesLength Length of the tickrates array + * @param hasTeleports Whether to query for hasTeleports or not + * @param mapTag Map tags to query + * @param offset Offset of the dataset to query + * @param limit Amount of items returned for the query + * @return Whether request was successfully sent + */ +native bool GlobalAPI_GetRecordsTopWorldRecords(OnAPICallFinished callback = INVALID_FUNCTION, any data = DEFAULT_DATA, + int[] ids = {}, int idsLength = DEFAULT_INT, + int[] mapIds = {}, int mapIdsLength = DEFAULT_INT, + int[] stages = {}, int stagesLength = DEFAULT_INT, + int[] modeIds = {}, int modeIdsLength = DEFAULT_INT, + int[] tickRates = {}, int tickRatesLength = DEFAULT_INT, + bool hasTeleports = DEFAULT_BOOL, char[] mapTag = DEFAULT_STRING, + int offset = DEFAULT_INT, int limit = DEFAULT_INT); + +/** + * Starts a GET HTTP Request to /api/{version}/servers + * + * @param callback Callback when request has finished + * @param data Optional data to pass + * @param id Id to query + * @param port Port to query + * @param ip IP address to query + * @param name Server name to query + * @param ownerSteamId64 Owner's steamid64 to query + * @param approvalStatus Approval status to query + * @param offset Offset of the dataset to query + * @param limit Amount of items returned for the query + * @return Whether request was successfully sent + */ +native bool GlobalAPI_GetServers(OnAPICallFinished callback = INVALID_FUNCTION, any data = DEFAULT_DATA, + int id = DEFAULT_INT, int port = DEFAULT_INT, const char[] ip = DEFAULT_STRING, + const char[] name = DEFAULT_STRING, const char[] ownerSteamId64 = DEFAULT_STRING, + int approvalStatus = DEFAULT_INT, int offset = DEFAULT_INT, int limit = DEFAULT_INT); + +/** + * Starts a GET HTTP Request to /api/{version}/servers/{id} + * + * @param callback Callback when request has finished + * @param data Optional data to pass + * @param id Id to query + * @return Whether request was successfully sent + */ +native bool GlobalAPI_GetServerById(OnAPICallFinished callback = INVALID_FUNCTION, any data = DEFAULT_DATA, int id); + +/** + * Starts a GET HTTP Request to /api/{version}/servers/name/{server_name} + * + * @param callback Callback when request has finished + * @param data Optional data to pass + * @param serverName Server name to query + * @return Whether request was successfully sent + */ +native bool GlobalAPI_GetServersByName(OnAPICallFinished callback = INVALID_FUNCTION, any data = DEFAULT_DATA, const char[] serverName); + +/** + * Starts a GET HTTP Request to /api/{version}/player_ranks + * + * @param callback Callback when request has finished + * @param data Optional data to pass + * @param pointsGreaterThan Points greater than to query + * @param averageGreaterThan Average greater than to query + * @param ratingGreaterThan Rating greater than to query + * @param finishesGreaterThan Finishes greater than to query + * @param steamId64List Comma-separated stirng of steamid64s to query + * @param recordFilterIds Array of record filter ids to query + * @param recordFilterIdsLength Length of the record filter ids array + * @param mapIds Array of map ids to query + * @param mapIdsLength Length of the map ids array + * @param stages Array of stages to query + * @param stagesLength Length of the stages array + * @param modeIds Array of mode ids to query + * @param modeIdsLength Length of the mode ids array + * @param tickRates Array of tickrates to query + * @param tickRatesLength Length of the tickrates array + * @param hasTeleports Whether to query for hasTeleports or not + * @param offset Offset of the dataset to query + * @param limit Amount of items returned for the query + * @return Whether request was successfully sent + */ +native bool GlobalAPI_GetPlayerRanks(OnAPICallFinished callback = INVALID_FUNCTION, any data = DEFAULT_DATA, + int pointsGreaterThan = DEFAULT_INT, float averageGreaterThan = DEFAULT_FLOAT, + float ratingGreaterThan = DEFAULT_FLOAT, int finishesGreaterThan = DEFAULT_INT, + const char[] steamId64List = DEFAULT_STRING, + int[] recordFilterIds = {}, int recordFilterIdsLength = DEFAULT_INT, + int[] mapIds = {}, int mapIdsLength = DEFAULT_INT, + int[] stages = {}, int stagesLength = DEFAULT_INT, + int[] modeIds = {}, int modeIdsLength = DEFAULT_INT, + int[] tickRates = {}, int tickRatesLength = DEFAULT_INT, + bool hasTeleports = DEFAULT_BOOL, int offset = DEFAULT_INT, int limit = DEFAULT_INT); + +/** + * Starts a GET HTTP Request to /api/{version}/record_filters + * + * @param callback Callback when request has finished + * @param data Optional data to pass + * @param ids Array of ids to query + * @param idsLength Length of the ids array + * @param mapIds Array of map ids to query + * @param mapIdsLength Length of the map ids array + * @param stages Array of stages to query + * @param stagesLength Length of the stages array + * @param modeIds Array of mode ids to query + * @param modeIdsLength Length of the mode ids array + * @param tickRates Array of tickrates to query + * @param tickRatesLength Length of the tickrates array + * @param hasTeleports Whether to query for hasTeleports or not + * @param offset Offset of the dataset to query + * @param limit Amount of items returned for the query + * @return Whether request was successfully sent + */ +native bool GlobalAPI_GetRecordFilters(OnAPICallFinished callback = INVALID_FUNCTION, any data = DEFAULT_DATA, + int[] ids = {}, int idsLength = DEFAULT_INT, + int[] mapIds = {}, int mapIdsLength = DEFAULT_INT, + int[] stages = {}, int stagesLength = DEFAULT_INT, + int[] modeIds = {}, int modeIdsLength = DEFAULT_INT, + int[] tickRates = {}, int tickRatesLength = DEFAULT_INT, + bool hasTeleports = DEFAULT_BOOL, bool isOverall = DEFAULT_BOOL, + int offset = DEFAULT_INT, int limit = DEFAULT_INT); + +/** + * Starts a GET HTTP Request to /api/{version}/record_filters/distributions + * + * @param callback Callback when request has finished + * @param data Optional data to pass + * @param ids Array of ids to query + * @param idsLength Length of the ids array + * @param mapIds Array of map ids to query + * @param mapIdsLength Length of the map ids array + * @param stages Array of stages to query + * @param stagesLength Length of the stages array + * @param modeIds Array of mode ids to query + * @param modeIdsLength Length of the mode ids array + * @param tickRates Array of tickrates to query + * @param tickRatesLength Length of the tickrates array + * @param hasTeleports Whether to query for hasTeleports or not + * @param offset Offset of the dataset to query + * @param limit Amount of items returned for the query + * @return Whether request was successfully sent + */ +native bool GlobalAPI_GetRecordFilterDistributions(OnAPICallFinished callback = INVALID_FUNCTION, any data = DEFAULT_DATA, + int[] ids = {}, int idsLength = DEFAULT_INT, + int[] mapIds = {}, int mapIdsLength = DEFAULT_INT, + int[] stages = {}, int stagesLength = DEFAULT_INT, + int[] modeIds = {}, int modeIdsLength = DEFAULT_INT, + int[] tickRates = {}, int tickRatesLength = DEFAULT_INT, + bool hasTeleports = DEFAULT_BOOL, bool isOverall = DEFAULT_BOOL, + int offset = DEFAULT_INT, int limit = DEFAULT_INT); + +/** + * Starts a GET HTTP Request to /api/{version}/records/replay/list + * + * @param callback Callback when request has finished + * @param data Optional data to pass + * @param offset Offset of the dataset to query + * @param limit Amount of items returned for the query + * @return Whether request was successfully sent + */ +native bool GlobalAPI_GetReplayList(OnAPICallFinished callback = INVALID_FUNCTION, any data = DEFAULT_DATA, + int offset = DEFAULT_INT, int limit = DEFAULT_INT); + +/** + * Starts a GET HTTP Request to /api/{version}/records/{recordId}/replay + * + * @param callback Callback when request has finished + * @param data Optional data to pass + * @param recordId Record id to query + * @return Whether request was successfully sent + */ +native bool GlobalAPI_GetReplayByRecordId(OnAPICallFinished callback = INVALID_FUNCTION, any data = DEFAULT_DATA, int recordId); +/** + * Starts a GET HTTP Request to /api/{version}/records/replay/{replayId} + * + * @param callback Callback when request has finished + * @param data Optional data to pass + * @param replayId Replay id to query + * @return Whether request was successfully sent + */ +native bool GlobalAPI_GetReplayByReplayId(OnAPICallFinished callback = INVALID_FUNCTION, any data = DEFAULT_DATA, int replayId); + +/** + * Starts a POST HTTP Request to /api/{version}/records/{recordId}/replay + * + * @param callback Callback when request has finished + * @param data Optional data to pass + * @param recordId Id of the record + * @param replayFile Path to the replay file + * @return Whether request was successfully sent + */ +native bool GlobalAPI_CreateReplayForRecordId(OnAPICallFinished callback = INVALID_FUNCTION, any data = DEFAULT_DATA, + int recordId, const char[] replayFile); + +// ======================= PLUGIN INFO ======================= // + +public SharedPlugin __pl_GlobalAPI = +{ + name = "GlobalAPI", + file = "GlobalAPI.smx", + #if defined REQUIRE_PLUGIN + required = 1, + #else + required = 0, + #endif +}; + +#if !defined REQUIRE_PLUGIN +public void __pl_GlobalAPI_SetNTVOptional() +{ + // Plugin + MarkNativeAsOptional("GlobalAPI_IsInit"); + MarkNativeAsOptional("GlobalAPI_GetAPIKey"); + MarkNativeAsOptional("GlobalAPI_HasAPIKey"); + MarkNativeAsOptional("GlobalAPI_IsStaging"); + MarkNativeAsOptional("GlobalAPI_IsDebugging"); + MarkNativeAsOptional("GlobalAPI_SendRequest"); + MarkNativeAsOptional("GlobalAPI_DebugMessage"); + + // Auth + MarkNativeAsOptional("GlobalAPI_GetAuthStatus"); + + // Bans + MarkNativeAsOptional("GlobalAPI_GetBans"); + MarkNativeAsOptional("GlobalAPI_CreateBan"); + + // Jumpstats + MarkNativeAsOptional("GlobalAPI_GetJumpstats"); + MarkNativeAsOptional("GlobalAPI_GetJumpstatTop"); + MarkNativeAsOptional("GlobalAPI_GetJumpstatTop30"); + + // Maps + MarkNativeAsOptional("GlobalAPI_GetMaps"); + MarkNativeAsOptional("GlobalAPI_GetMapById"); + MarkNativeAsOptional("GlobalAPI_GetMapByName"); + + // Modes + MarkNativeAsOptional("GlobalAPI_GetModes"); + MarkNativeAsOptional("GlobalAPI_GetModeById"); + MarkNativeAsOptional("GlobalAPI_GetModeByName"); + + // Players + MarkNativeAsOptional("GlobalAPI_GetPlayers"); + MarkNativeAsOptional("GlobalAPI_GetPlayerBySteamId"); + MarkNativeAsOptional("GlobalAPI_GetPlayerBySteamIdAndIp"); + + // Records + MarkNativeAsOptional("GlobalAPI_CreateRecord"); + MarkNativeAsOptional("GlobalAPI_GetRecordPlaceById"); + MarkNativeAsOptional("GlobalAPI_GetRecordsTop"); + MarkNativeAsOptional("GlobalAPI_GetRecordsTopRecent"); + MarkNativeAsOptional("GlobalAPI_GetRecordsTopWorldRecords"); + + // Servers + MarkNativeAsOptional("GlobalAPI_GetServers"); + MarkNativeAsOptional("GlobalAPI_GetServerById"); + MarkNativeAsOptional("GlobalAPI_GetServersByName"); + + // Ranks + MarkNativeAsOptional("GlobalAPI_GetPlayerRanks"); + + // Record Filters + MarkNativeAsOptional("GlobalAPI_GetRecordFilters"); + MarkNativeAsOptional("GlobalAPI_GetRecordFilterDistributions"); + + // Replays + MarkNativeAsOptional("GlobalAPI_GetReplayList"); + MarkNativeAsOptional("GlobalAPI_GetReplayByRecordId"); + MarkNativeAsOptional("GlobalAPI_GetReplayByReplayId"); + MarkNativeAsOptional("GlobalAPI_CreateReplayForRecordId"); +} +#endif diff --git a/sourcemod/scripting/include/GlobalAPI/iterable.inc b/sourcemod/scripting/include/GlobalAPI/iterable.inc new file mode 100644 index 0000000..585873b --- /dev/null +++ b/sourcemod/scripting/include/GlobalAPI/iterable.inc @@ -0,0 +1,55 @@ +// ================== DOUBLE INCLUDE ========================= // + +#if defined _GlobalAPI_Iterable_included_ +#endinput +#endif +#define _GlobalAPI_Iterable_included_ + +// =========================================================== // + +#include <json> + +// =========================================================== // + +/* + Helper methodmap for JSON_Object arrays +*/ +methodmap APIIterable < JSON_Object +{ + /** + * Creates a new APIIterable + * + * @param hItems JSON_Object array handle + * @return A new APIIterable handle + */ + public APIIterable(JSON_Object hItems) + { + if (hItems.HasKey("result")) + { + return view_as<APIIterable>(hItems.GetObject("result")); + } + return view_as<APIIterable>(hItems); + } + + /* + Gets count of the items in the array + */ + property int Count + { + public get() { return this.Length; } + } + + /** + * Gets an object from the array by index + * + * @note This is an alias to GetObjectIndexed + * @param index Index of the object we want to retrieve + * @return JSON_Object handle to the object retrieved + */ + public JSON_Object GetById(int index) + { + return this.GetObjectIndexed(index); + } +} + +// =========================================================== //
\ No newline at end of file diff --git a/sourcemod/scripting/include/GlobalAPI/request.inc b/sourcemod/scripting/include/GlobalAPI/request.inc new file mode 100644 index 0000000..e683125 --- /dev/null +++ b/sourcemod/scripting/include/GlobalAPI/request.inc @@ -0,0 +1,185 @@ +// ================== DOUBLE INCLUDE ========================= // + +#if defined _GlobalAPI_Request_included_ +#endinput +#endif +#define _GlobalAPI_Request_included_ + +// =========================================================== // + +static char gC_acceptTypePhrases[][] = +{ + "application/json", + "application/octet-stream" +}; + +static char gC_contentTypePhrases[][] = +{ + "application/json", + "application/octet-stream" +}; + +// =========================================================== // + +methodmap GlobalAPIRequest < Handle +{ + /** + * Creates a new GlobalAPIRequest + * + * @param url URL of the request + * @param method SteamWorks k_ETTPMethod of the request + * @return A new GlobalAPIRequest handle + */ + public GlobalAPIRequest(char[] url, EHTTPMethod method) + { + Handle request = SteamWorks_CreateHTTPRequest(method, url); + return view_as<GlobalAPIRequest>(request); + } + + /** + * Sets request timeout + * + * @param seconds Timeout in seconds + * @return Whether the operation was successful + */ + public bool SetTimeout(int seconds) + { + return SteamWorks_SetHTTPRequestAbsoluteTimeoutMS(this, seconds * 1000); + } + + /** + * Sets request body + * + * @param hData GlobalAPIRequestData containing contentType + * @param body Request body to set + * @param maxlength Maxlength of the body + * @return Whether the operation was successful + */ + public bool SetBody(GlobalAPIRequestData hData, char[] body, int maxlength) + { + return SteamWorks_SetHTTPRequestRawPostBody(this, gC_contentTypePhrases[hData.ContentType], body, maxlength); + } + + /** + * Sets request body from a file + * + * @param hData GlobalAPIRequestData containing contentType + * @return Whether the operation was successful + */ + public bool SetBodyFromFile(GlobalAPIRequestData hData, char[] file) + { + return SteamWorks_SetHTTPRequestRawPostBodyFromFile(this, gC_contentTypePhrases[hData.ContentType], file); + } + + /** + * Sets a request context value + * + * @param data Any data to pass + * @return Whether the operation was successful + */ + public bool SetData(any data1, any data2 = 0) + { + return SteamWorks_SetHTTPRequestContextValue(this, data1, data2); + } + + /** + * Sets predefined HTTP callbacks + * + * @note Predefined values respectively: + * @note Global_HTTP_Completed, Global_HTTP_Headers and Global_HTTP_DataReceived + * @noreturn + */ + public void SetCallbacks() + { + SteamWorks_SetHTTPCallbacks(this, Global_HTTP_Completed, Global_HTTP_Headers, Global_HTTP_DataReceived); + } + + /** + * Sets "Accept" header + * + * @param hData GlobalAPIRequestData containing acceptType + * @return Whether the operation was successful + */ + public bool SetAcceptHeaders(GlobalAPIRequestData hData) + { + return SteamWorks_SetHTTPRequestHeaderValue(this, "Accept", gC_acceptTypePhrases[hData.AcceptType]); + } + + /** + * Sets "powered by" header + * + * @return Whether the operation was successful + */ + public bool SetPoweredByHeader() + { + return SteamWorks_SetHTTPRequestHeaderValue(this, "X-Powered-By", GlobalAPI_Plugin_NameVersion); + } + + /** + * Sets authentication header + * + * @return Whether the operation was successful + */ + public bool SetAuthenticationHeader(char[] apiKey) + { + return SteamWorks_SetHTTPRequestHeaderValue(this, "X-ApiKey", apiKey); + } + + /** + * Sets envinroment headers (MetaMod & SourceMod) + * + * @param mmVersion MetaMod version string + * @param smVersion SourceMod version string + * @return Whether the operation was successful + */ + public bool SetEnvironmentHeaders(char[] mmVersion, char[] smVersion) + { + return SteamWorks_SetHTTPRequestHeaderValue(this, "X-MetaMod-Version", mmVersion) + && SteamWorks_SetHTTPRequestHeaderValue(this, "X-SourceMod-Version", smVersion); + } + + /** + * Sets content type header + * + * @param hData GlobalAPIRequestData containing contentType + * @return Whether the operation was successful + */ + public bool SetContentTypeHeader(GlobalAPIRequestData hData) + { + return SteamWorks_SetHTTPRequestHeaderValue(this, "Content-Type", gC_contentTypePhrases[hData.ContentType]); + } + + /** + * Sets request origin header + * + * @param hData GlobalAPIRequestData containing pluginName + * @return Whether the operation was successful + */ + public bool SetRequestOriginHeader(GlobalAPIRequestData hData) + { + char pluginName[GlobalAPI_Max_PluginName_Length]; + hData.GetString("pluginName", pluginName, sizeof(pluginName)); + + char pluginVersion[GlobalAPI_Max_PluginVersion_Length + 2]; + hData.GetString("pluginVersion", pluginVersion, sizeof(pluginVersion)); + + char fullPluginDisplay[sizeof(pluginName) + sizeof(pluginVersion) + 6]; + Format(fullPluginDisplay, sizeof(fullPluginDisplay), "%s (V.%s)", pluginName, pluginVersion); + + return SteamWorks_SetHTTPRequestHeaderValue(this, "X-Request-Origin", fullPluginDisplay); + } + + /** + * Sends our request with all available data + * + * @param hData GlobalAPIRequestData handle with all required keys + * @return Whether the operation was successful + */ + public bool Send(GlobalAPIRequestData hData) + { + Call_Private_OnHTTPStart(this, hData); + return SteamWorks_SendHTTPRequest(this); + } +} + +// =========================================================== //
\ No newline at end of file diff --git a/sourcemod/scripting/include/GlobalAPI/requestdata.inc b/sourcemod/scripting/include/GlobalAPI/requestdata.inc new file mode 100644 index 0000000..f055ee8 --- /dev/null +++ b/sourcemod/scripting/include/GlobalAPI/requestdata.inc @@ -0,0 +1,534 @@ +// ================== DOUBLE INCLUDE ========================= // + +#if defined _GlobalAPI_RequestData_included_ +#endinput +#endif +#define _GlobalAPI_RequestData_included_ + +// =========================================================== // + +#include <json> + +// =========================================================== // + +/* + Helper methodmap for wrapping data related to requests +*/ +methodmap GlobalAPIRequestData < JSON_Object +{ + /** + * Creates a new GlobalAPIRequestData + * + * @note You can pass a plugin handle or name and/or version + * @note Plugin handle is always preferred + * @param plugin Handle to calling plugin + * @param pluginName Name of the calling plugin + * @param pluginVersion Version of the calling plugin + * @return A new GlobalAPIRequestData handle + */ + public GlobalAPIRequestData(Handle plugin = null, char[] pluginName = "Unknown", char[] pluginVersion = "Unknown") + { + JSON_Object requestData = new JSON_Object(); + + if (plugin == null) + { + requestData.SetString("pluginName", pluginName); + requestData.SetString("pluginVersion", pluginVersion); + } + else + { + requestData.SetString("pluginName", GetPluginDisplayName(plugin)); + requestData.SetString("pluginVersion", GetPluginVersion(plugin)); + } + + requestData.SetKeyHidden("pluginName", true); + requestData.SetKeyHidden("pluginVersion", true); + + requestData.SetInt("acceptType", 0); + requestData.SetKeyHidden("acceptType", true); + + requestData.SetInt("contentType", 0); + requestData.SetKeyHidden("contentType", true); + + return view_as<GlobalAPIRequestData>(requestData); + } + + /** + * Sets a key as default + * + * @note This sets them as "Handle" type + * @note - See GlobalAPI.inc for default values + * @param key Key to set as default + * @noreturn + */ + public void SetDefault(char[] key) + { + this.SetHandle(key); + this.SetKeyHidden(key, true); + } + + /** + * Sets url to the request data + * + * @param url Url to set + * @noreturn + */ + public void AddUrl(char[] url) + { + this.SetString("url", url); + this.SetKeyHidden("url", true); + } + + /** + * Sets endpoint to the request data + * + * @param endpoint Endpoint to set + * @noreturn + */ + public void AddEndpoint(char[] endpoint) + { + this.SetString("endpoint", endpoint); + this.SetKeyHidden("endpoint", true); + } + + /** + * Sets body file path to the request data + * + * @note Path to file with data to be posted + * @param path Body file (path) to set + * @noreturn + */ + public void AddBodyFile(char[] path) + { + this.SetString("bodyFile", path); + this.SetKeyHidden("bodyFile", true); + } + + /** + * Sets data file path to the request data + * + * @note Path for downloaded files + * @param path Data path to set + * @noreturn + */ + public void AddDataPath(char[] path) + { + this.SetString("dataFilePath", path); + this.SetKeyHidden("dataFilePath", true); + } + + /* + Get or set the request's "acceptType" + */ + property int AcceptType + { + public get() + { + return this.GetInt("acceptType"); + } + public set(int type) + { + this.SetInt("acceptType", type); + } + } + + /* + Get or set the request's "contentType" + */ + property int ContentType + { + public get() + { + return this.GetInt("contentType"); + } + public set(int type) + { + this.SetInt("contentType", type); + } + } + + /* + Get or set the request's "keyRequired" + */ + property bool KeyRequired + { + public get() + { + return this.GetBool("keyRequired"); + } + public set(bool required) + { + this.SetBool("keyRequired", required); + this.SetKeyHidden("keyRequired", true); + } + } + + /* + Get or set the request's "isRetried" + */ + property bool IsRetried + { + public get() + { + return this.GetBool("isRetried"); + } + public set(bool retried) + { + this.SetBool("isRetried", retried); + this.SetKeyHidden("isRetried", true); + } + } + + /* + Get or set the request's "bodyLength" + */ + property int BodyLength + { + public get() + { + return this.GetInt("bodyLength"); + } + public set(int length) + { + this.SetInt("bodyLength", length); + this.SetKeyHidden("bodyLength", true); + } + } + + /* + Get or set the request's "status" + */ + property int Status + { + public get() + { + return this.GetInt("status"); + } + public set(int status) + { + this.SetInt("status", status); + this.SetKeyHidden("status", true); + } + } + + /* + Get or set the request's "responseTime" + */ + property int ResponseTime + { + public get() + { + return this.GetInt("responseTime"); + } + public set(int responseTime) + { + this.SetInt("responseTime", responseTime); + this.SetKeyHidden("responseTime", true); + } + } + + /* + Get or set the request's "requestType" + */ + property int RequestType + { + public get() + { + return this.GetInt("requestType"); + } + public set(int type) + { + this.SetInt("requestType", type); + this.SetKeyHidden("requestType", true); + } + } + + /* + Get or set the request's "failure" + */ + property bool Failure + { + public get() + { + return this.GetBool("failure"); + } + public set(bool failure) + { + this.SetBool("failure", failure); + this.SetKeyHidden("failure", true); + } + } + + /* + Get or set the request's "callback" + */ + property Handle Callback + { + public get() + { + return view_as<Handle>(this.GetInt("callback")); + } + public set(Handle hFwd) + { + this.SetHandle("callback", hFwd); + this.SetKeyType("callback", Type_Int); + this.SetKeyHidden("callback", true); + } + } + + /* + Get or set the request's "data" + */ + property any Data + { + public get() + { + return this.GetInt("data"); + } + public set(any data) + { + this.SetInt("data", data); + this.SetKeyHidden("data", true); + } + } + + /** + * Adds a number to the request data + * + * @note Default values are added as "defaults" + * @note See GlobalAPI.inc for the default values + * @param key Key name to set + * @param value Value of the key + * @noreturn + */ + public void AddNum(char[] key, int value) + { + if (value == -1) + { + this.SetDefault(key); + } + else + { + this.SetInt(key, value); + } + } + + /** + * Adds a float to the request data + * + * @note Default values are added as "defaults" + * @note See GlobalAPI.inc for the default values + * @param key Key name to set + * @param value Value of the key + * @noreturn + */ + public void AddFloat(char[] key, float value) + { + if (value == -1.000000) + { + this.SetDefault(key); + } + else + { + this.SetFloat(key, value); + } + } + + /** + * Adds a string to the request data + * + * @note Default values are added as "defaults" + * @note See GlobalAPI.inc for the default values + * @param key Key name to set + * @param value Value of the key + * @noreturn + */ + public void AddString(char[] key, char[] value) + { + if (StrEqual(value, "")) + { + this.SetDefault(key); + } + else + { + this.SetString(key, value); + } + } + + /** + * Adds a boolean to the request data + * + * @note Default values are added as "defaults" + * @note See GlobalAPI.inc for the default values + * @param key Key name to set + * @param value Value of the key + * @noreturn + */ + public void AddBool(char[] key, bool value) + { + if (value != true && value != false) + { + this.SetDefault(key); + } + else + { + this.SetBool(key, value); + } + } + + /** + * Adds integer array to the request data + * + * @note Max length <= 0 are added as defaults + * @param key Key name to set + * @param value Values (array) of the key + * @param maxlength Max length of the values array + * @noreturn + */ + public void AddIntArray(char[] key, int[] value, int maxlength) + { + if (maxlength <= 0) + { + this.SetDefault(key); + } + else + { + JSON_Object hArray = new JSON_Object(true); + + for (int i = 0; i < maxlength; i++) + { + hArray.PushInt(value[i]); + } + + this.SetObject(key, hArray); + } + } + + /** + * Adds string array to the request data + * + * @note Item count <= 0 are added as defaults + * @param key Key name to set + * @param itemCount Amount of strings in the array + * @noreturn + */ + public void AddStringArray(char[] key, char[][] value, int itemCount) + { + if (itemCount <= 0) + { + this.SetDefault(key); + } + else + { + JSON_Object hArray = new JSON_Object(true); + + for (int i = 0; i < itemCount; i++) + { + hArray.PushString(value[i]); + } + + this.SetObject(key, hArray); + } + } + + /** + * Converts all of the request data into a query string representation + * + * @note This ignores "hidden" keys + * @param queryString Buffer to store the result in + * @param maxlength Max length of the buffer + * @noreturn + */ + public void ToString(char[] queryString, int maxlength) + { + StringMapSnapshot paramsMap = this.Snapshot(); + + char key[64]; + char value[1024]; + + int paramCount = 0; + + for (int i = 0; i < paramsMap.Length; i++) + { + paramsMap.GetKey(i, key, sizeof(key)); + if (this.GetKeyHidden(key) || json_is_meta_key(key)) + { + continue; + } + + switch(this.GetKeyType(key)) + { + case Type_String: + { + this.GetString(key, value, sizeof(value)); + AppendToQueryString(paramCount, queryString, maxlength, key, value); + } + case Type_Float: + { + float temp = this.GetFloat(key); + FloatToString(temp, value, sizeof(value)); + AppendToQueryString(paramCount, queryString, maxlength, key, value); + } + case Type_Int: + { + int temp = this.GetInt(key); + IntToString(temp, value, sizeof(value)); + AppendToQueryString(paramCount, queryString, maxlength, key, value); + } + case Type_Bool: + { + bool temp = this.GetBool(key); + BoolToString(temp, value, sizeof(value)); + AppendToQueryString(paramCount, queryString, maxlength, key, value); + } + case Type_Object: + { + JSON_Object hObject = this.GetObject(key); + + if (!hObject.IsArray) continue; + + for (int x = 0; x < hObject.Length; x++) + { + switch (hObject.GetKeyTypeIndexed(x)) + { + case Type_Int: + { + int temp = hObject.GetIntIndexed(x); + IntToString(temp, value, sizeof(value)); + AppendToQueryString(paramCount, queryString, maxlength, key, value); + } + case Type_String: + { + hObject.GetStringIndexed(x, value, sizeof(value)); + AppendToQueryString(paramCount, queryString, maxlength, key, value); + } + } + } + } + } + } + + delete paramsMap; + } +} + +// =====[ PRIVATE ]===== + +static void BoolToString(bool value, char[] buffer, int maxlength) +{ + FormatEx(buffer, maxlength, "%s", value ? "true" : "false"); +} + +static void AppendToQueryString(int &index, char[] buffer, int maxlength, char[] key, char[] value) +{ + if (index == 0) + { + index++; + Format(buffer, maxlength, "?%s=%s", key, value); + } + else + { + index++; + Format(buffer, maxlength, "%s&%s=%s", buffer, key, value); + } +} diff --git a/sourcemod/scripting/include/GlobalAPI/responses.inc b/sourcemod/scripting/include/GlobalAPI/responses.inc new file mode 100644 index 0000000..62ebfd2 --- /dev/null +++ b/sourcemod/scripting/include/GlobalAPI/responses.inc @@ -0,0 +1,575 @@ +// ================== DOUBLE INCLUDE ========================= // + +#if defined _GlobalAPI_Responses_included_ +#endinput +#endif +#define _GlobalAPI_Responses_included_ + +// =========================================================== // + +#include <json> +#include <GlobalAPI/iterable> + +// =========================================================== // + +methodmap APIAuth < JSON_Object +{ + public APIAuth(JSON_Object hAuth) + { + return view_as<APIAuth>(hAuth); + } + + public bool GetType(char[] buffer, int maxlength) + { + return this.GetString("Type", buffer, maxlength); + } + + property bool IsValid + { + public get() { return this.GetBool("IsValid"); } + } + + property int Identity + { + public get() { return this.GetInt("Identity"); } + } +} + +// =========================================================== // + +methodmap APIBan < JSON_Object +{ + public APIBan(JSON_Object hBan) + { + return view_as<APIBan>(hBan); + } + + property int Id + { + public get() { return this.GetInt("id"); } + } + + property int UpdatedById + { + public get() { return this.GetInt("updated_by_id"); } + } + + public void GetStats(char[] buffer, int maxlength) + { + this.GetString("stats", buffer, maxlength); + } + + public void GetBanType(char[] buffer, int maxlength) + { + this.GetString("ban_type", buffer, maxlength); + } + + public void GetExpiresOn(char[] buffer, int maxlength) + { + this.GetString("expires_on", buffer, maxlength); + } + + public void GetSteamId64(char[] buffer, int maxlength) + { + this.GetString("steamid64", buffer, maxlength); + } + + public void GetPlayerName(char[] buffer, int maxlength) + { + this.GetString("player_name", buffer, maxlength); + } + + public void GetNotes(char[] buffer, int maxlength) + { + this.GetString("notes", buffer, maxlength); + } + + public void GetSteamId(char[] buffer, int maxlength) + { + this.GetString("steam_id", buffer, maxlength); + } + + public void GetUpdatedOn(char[] buffer, int maxlength) + { + this.GetString("updated_on", buffer, maxlength); + } + + property int ServerId + { + public get() { return this.GetInt("server_id"); } + } + + public void GetCreatedOn(char[] buffer, int maxlength) + { + this.GetString("created_on", buffer, maxlength); + } +} + +// =========================================================== // + +methodmap APIJumpstat < JSON_Object +{ + public APIJumpstat(JSON_Object hJump) + { + return view_as<APIJumpstat>(hJump); + } + + property int Id + { + public get() { return this.GetInt("id"); } + } + + property int ServerId + { + public get() { return this.GetInt("server_id"); } + } + + public void GetSteamId64(char[] buffer, int maxlength) + { + this.GetString("steamid64", buffer, maxlength); + } + + public void GetName(char[] buffer, int maxlength) + { + this.GetString("player_name", buffer, maxlength); + } + + public void GetSteamId(char[] buffer, int maxlength) + { + this.GetString("steam_id", buffer, maxlength); + } + + property int JumpType + { + public get() { return this.GetInt("jump_type"); } + } + + property float Distance + { + public get() { return this.GetFloat("distance"); } + } + + property int TickRate + { + public get() { return this.GetInt("tickrate"); } + } + + property int MslCount + { + public get() { return this.GetInt("msl_count"); } + } + + property int StrafeCount + { + public get() { return this.GetInt("strafe_count"); } + } + + property bool IsCrouchBind + { + public get() { return this.GetBool("is_crouch_bind"); } + } + + property bool IsForwardBind + { + public get() { return this.GetBool("is_forward_bind"); } + } + + property bool IsCrouchBoost + { + public get() { return this.GetBool("is_crouch_boost"); } + } + + property int UpdatedById + { + public get() { return this.GetInt("updated_by_id"); } + } + + public void GetCreatedOn(char[] buffer, int maxlength) + { + this.GetString("created_on", buffer, maxlength); + } + + public void GetUpdatedOn(char[] buffer, int maxlength) + { + this.GetString("updated_on", buffer, maxlength); + } +} + +// =========================================================== // + +methodmap APIMap < JSON_Object +{ + public APIMap(JSON_Object hMap) + { + return view_as<APIMap>(hMap); + } + + property int Id + { + public get() { return this.GetInt("id"); } + } + + public void GetName(char[] buffer, int maxlength) + { + this.GetString("name", buffer, maxlength); + } + + property int Filesize + { + public get() { return this.GetInt("filesize"); } + } + + property bool IsValidated + { + public get() { return this.GetBool("validated"); } + } + + property int Difficulty + { + public get() { return this.GetInt("difficulty"); } + } + + public void GetCreatedOn(char[] buffer, int maxlength) + { + this.GetString("created_on", buffer, maxlength); + } + + public void GetUpdatedOn(char[] buffer, int maxlength) + { + this.GetString("updated_on", buffer, maxlength); + } + + public void GetApprovedBySteamId64(char[] buffer, int maxlength) + { + this.GetString("approved_by_steamid64", buffer, maxlength); + } +} + +// =========================================================== // + +methodmap APIMode < JSON_Object +{ + public APIMode(JSON_Object hMode) + { + return view_as<APIMode>(hMode); + } + + property int Id + { + public get() { return this.GetInt("id"); } + } + + public void GetName(char[] buffer, int maxlength) + { + this.GetString("name", buffer, maxlength); + } + + public void GetDescription(char[] buffer, int maxlength) + { + this.GetString("description", buffer, maxlength); + } + + property int LatestVersion + { + public get() { return this.GetInt("latest_version"); } + } + + public void GetLatestVersionDesc(char[] buffer, int maxlength) + { + this.GetString("latest_version_description", buffer, maxlength); + } + + public void GetWebsite(char[] buffer, int maxlength) + { + this.GetString("website", buffer, maxlength); + } + + public void GetRepository(char[] buffer, int maxlength) + { + this.GetString("repo", buffer, maxlength); + } + + public void GetContactSteamId64(char[] buffer, int maxlength) + { + this.GetString("contact_steamid64", buffer, maxlength); + } + + // TODO: Add supported_tickrates + + public void GetCreatedOn(char[] buffer, int maxlength) + { + this.GetString("created_on", buffer, maxlength); + } + + public void GetUpdatedOn(char[] buffer, int maxlength) + { + this.GetString("updated_on", buffer, maxlength); + } + + public void GetUpdatedById(char[] buffer, int maxlength) + { + this.GetString("updated_by_id", buffer, maxlength); + } +} + +// =========================================================== // + +methodmap APIPlayerRank < JSON_Object +{ + public APIPlayerRank(JSON_Object hPlayerRank) + { + return view_as<APIPlayerRank>(hPlayerRank); + } + + property int Points + { + public get() { return this.GetInt("points"); } + } + + property int PointsOverall + { + public get() { return this.GetInt("points_overall"); } + } + + property float Average + { + public get() { return this.GetFloat("average"); } + } + + property float Rating + { + public get() { return this.GetFloat("rating"); } + } + + property int Finishes + { + public get() { return this.GetInt("finishes"); } + } + + public void GetSteamId64(char[] buffer, int maxlength) + { + this.GetString("steamid64", buffer, maxlength); + } + + public void GetSteamId(char[] buffer, int maxlength) + { + this.GetString("steamid", buffer, maxlength); + } + + public void GetPlayerName(char[] buffer, int maxlength) + { + this.GetString("player_name", buffer, maxlength); + } +} + +// =========================================================== // + +methodmap APIPlayer < JSON_Object +{ + public APIPlayer(JSON_Object hPlayer) + { + return view_as<APIPlayer>(hPlayer); + } + + public void GetSteamId64(char[] buffer, int maxlength) + { + this.GetString("steamid64", buffer, maxlength); + } + + public void GetSteamId(char[] buffer, int maxlength) + { + this.GetString("steam_id", buffer, maxlength); + } + + property bool IsBanned + { + public get() { return this.GetBool("is_banned"); } + } + + property int TotalRecords + { + public get() { return this.GetInt("total_records"); } + } + + public void GetName(char[] buffer, int maxlength) + { + this.GetString("name", buffer, maxlength); + } +} + +// =========================================================== // + +methodmap APIRecord < JSON_Object +{ + public APIRecord(JSON_Object hRecord) + { + return view_as<APIRecord>(hRecord); + } + + property int Id + { + public get() { return this.GetInt("id"); } + } + + public void GetSteamId64(char[] buffer, int maxlength) + { + this.GetString("steamid64", buffer, maxlength); + } + + public void GetPlayerName(char[] buffer, int maxlength) + { + this.GetString("player_name", buffer, maxlength); + } + + public void GetSteamId(char[] buffer, int maxlength) + { + this.GetString("steam_id", buffer, maxlength); + } + + property int ServerId + { + public get() { return this.GetInt("server_id"); } + } + + property int MapId + { + public get() { return this.GetInt("map_id"); } + } + + property int Stage + { + public get() { return this.GetInt("stage"); } + } + + public void GetMode(char[] buffer, int maxlength) + { + this.GetString("mode", buffer, maxlength); + } + + property int TickRate + { + public get() { return this.GetInt("tickrate"); } + } + + property float Time + { + public get() { return this.GetFloat("time"); } + } + + property int Teleports + { + public get() { return this.GetInt("teleports"); } + } + + public void GetCreatedOn(char[] buffer, int maxlength) + { + this.GetString("created_on", buffer, maxlength); + } + + public void GetUpdatedOn(char[] buffer, int maxlength) + { + this.GetString("updated_on", buffer, maxlength); + } + + property int UpdatedBy + { + public get() { return this.GetInt("updated_by"); } + } + + public void GetServerName(char[] buffer, int maxlength) + { + this.GetString("server_name", buffer, maxlength); + } + + public void GetMapName(char[] buffer, int maxlength) + { + this.GetString("map_name", buffer, maxlength); + } +} + +// =========================================================== // + +methodmap APIServer < JSON_Object +{ + public APIServer(JSON_Object hServer) + { + return view_as<APIServer>(hServer); + } + + property int Port + { + public get() { return this.GetInt("port"); } + } + + public void GetIPAddress(char[] buffer, int maxlength) + { + this.GetString("ip", buffer, maxlength); + } + + public void GetName(char[] buffer, int maxlength) + { + this.GetString("name", buffer, maxlength); + } + + public void GetOwnerSteamId64(char[] buffer, int maxlength) + { + this.GetString("owner_steamid64", buffer, maxlength); + } +} + +// =========================================================== // + +methodmap APIRecordFilter < JSON_Object +{ + public APIRecordFilter(JSON_Object hRecordFilter) + { + return view_as<APIRecordFilter>(hRecordFilter); + } + + property int Id + { + public get() { return this.GetInt("id"); } + } + + property int MapId + { + public get() { return this.GetInt("map_id"); } + } + + property int Stage + { + public get() { return this.GetInt("stage"); } + } + + property int ModeId + { + public get() { return this.GetInt("mode_id"); } + } + + property int TickRate + { + public get() { return this.GetInt("tickrate"); } + } + + property bool HasTeleports + { + public get() { return this.GetBool("has_teleports"); } + } + + public void GetCreatedOn(char[] buffer, int maxlength) + { + this.GetString("created_on", buffer, maxlength); + } + + public void GetUpdatedOn(char[] buffer, int maxlength) + { + this.GetString("updated_on", buffer, maxlength); + } + + public void GetUpdatedById(char[] buffer, int maxlength) + { + this.GetString("updated_by_id", buffer, maxlength); + } +} + +// =========================================================== // diff --git a/sourcemod/scripting/include/GlobalAPI/stocks.inc b/sourcemod/scripting/include/GlobalAPI/stocks.inc new file mode 100644 index 0000000..52596c4 --- /dev/null +++ b/sourcemod/scripting/include/GlobalAPI/stocks.inc @@ -0,0 +1,67 @@ +// ================== DOUBLE INCLUDE ========================= // + +#if defined _GlobalAPI_Stocks_included_ +#endinput +#endif +#define _GlobalAPI_Stocks_included_ + +// =========================================================== // + +/** + * Gets plugin's display name from its handle + * + * @param plugin Plugin handle to retrieve name from + * @return String representation of the plugin name + */ +stock char[] GetPluginDisplayName(Handle plugin) +{ + char pluginName[GlobalAPI_Max_PluginName_Length] = "Unknown"; + GetPluginInfo(plugin, PlInfo_Name, pluginName, sizeof(pluginName)); + + return pluginName; +} + +/** + * Gets plugin's version from its handle + * + * @param plugin Plugin handle to retrieve version from + * @return String representation of the plugin version + */ +stock char[] GetPluginVersion(Handle plugin) +{ + char pluginVersion[GlobalAPI_Max_PluginVersion_Length] = "Unknown"; + GetPluginInfo(plugin, PlInfo_Version, pluginVersion, sizeof(pluginVersion)); + + return pluginVersion; +} + +/** + * Gets current map's "display name" + * + * @param buffer Buffer to store the result in + * @param maxlength Max length of the buffer + * @noreturn + */ +stock void GetMapDisplay(char[] buffer, int maxlength) +{ + char map[PLATFORM_MAX_PATH]; + GetCurrentMap(map, sizeof(map)); + GetMapDisplayName(map, map, sizeof(map)); + + FormatEx(buffer, maxlength, map); +} + +/** + * Gets current map's full (game dir) path + * + * @param buffer Buffer to store result in + * @param maxlength Max length of the buffer + * @noreturn + */ +stock void GetMapFullPath(char[] buffer, int maxlength) +{ + char mapPath[PLATFORM_MAX_PATH]; + GetCurrentMap(mapPath, sizeof(mapPath)); + + Format(buffer, maxlength, "maps/%s.bsp", mapPath); +} diff --git a/sourcemod/scripting/include/SteamWorks.inc b/sourcemod/scripting/include/SteamWorks.inc new file mode 100644 index 0000000..0e4aec3 --- /dev/null +++ b/sourcemod/scripting/include/SteamWorks.inc @@ -0,0 +1,413 @@ +#if defined _SteamWorks_Included + #endinput +#endif +#define _SteamWorks_Included + +/* results from UserHasLicenseForApp */ +enum EUserHasLicenseForAppResult +{ + k_EUserHasLicenseResultHasLicense = 0, // User has a license for specified app + k_EUserHasLicenseResultDoesNotHaveLicense = 1, // User does not have a license for the specified app + k_EUserHasLicenseResultNoAuth = 2, // User has not been authenticated +}; + +/* General result codes */ +enum EResult +{ + k_EResultOK = 1, // success + k_EResultFail = 2, // generic failure + k_EResultNoConnection = 3, // no/failed network connection +// k_EResultNoConnectionRetry = 4, // OBSOLETE - removed + k_EResultInvalidPassword = 5, // password/ticket is invalid + k_EResultLoggedInElsewhere = 6, // same user logged in elsewhere + k_EResultInvalidProtocolVer = 7, // protocol version is incorrect + k_EResultInvalidParam = 8, // a parameter is incorrect + k_EResultFileNotFound = 9, // file was not found + k_EResultBusy = 10, // called method busy - action not taken + k_EResultInvalidState = 11, // called object was in an invalid state + k_EResultInvalidName = 12, // name is invalid + k_EResultInvalidEmail = 13, // email is invalid + k_EResultDuplicateName = 14, // name is not unique + k_EResultAccessDenied = 15, // access is denied + k_EResultTimeout = 16, // operation timed out + k_EResultBanned = 17, // VAC2 banned + k_EResultAccountNotFound = 18, // account not found + k_EResultInvalidSteamID = 19, // steamID is invalid + k_EResultServiceUnavailable = 20, // The requested service is currently unavailable + k_EResultNotLoggedOn = 21, // The user is not logged on + k_EResultPending = 22, // Request is pending (may be in process, or waiting on third party) + k_EResultEncryptionFailure = 23, // Encryption or Decryption failed + k_EResultInsufficientPrivilege = 24, // Insufficient privilege + k_EResultLimitExceeded = 25, // Too much of a good thing + k_EResultRevoked = 26, // Access has been revoked (used for revoked guest passes) + k_EResultExpired = 27, // License/Guest pass the user is trying to access is expired + k_EResultAlreadyRedeemed = 28, // Guest pass has already been redeemed by account, cannot be acked again + k_EResultDuplicateRequest = 29, // The request is a duplicate and the action has already occurred in the past, ignored this time + k_EResultAlreadyOwned = 30, // All the games in this guest pass redemption request are already owned by the user + k_EResultIPNotFound = 31, // IP address not found + k_EResultPersistFailed = 32, // failed to write change to the data store + k_EResultLockingFailed = 33, // failed to acquire access lock for this operation + k_EResultLogonSessionReplaced = 34, + k_EResultConnectFailed = 35, + k_EResultHandshakeFailed = 36, + k_EResultIOFailure = 37, + k_EResultRemoteDisconnect = 38, + k_EResultShoppingCartNotFound = 39, // failed to find the shopping cart requested + k_EResultBlocked = 40, // a user didn't allow it + k_EResultIgnored = 41, // target is ignoring sender + k_EResultNoMatch = 42, // nothing matching the request found + k_EResultAccountDisabled = 43, + k_EResultServiceReadOnly = 44, // this service is not accepting content changes right now + k_EResultAccountNotFeatured = 45, // account doesn't have value, so this feature isn't available + k_EResultAdministratorOK = 46, // allowed to take this action, but only because requester is admin + k_EResultContentVersion = 47, // A Version mismatch in content transmitted within the Steam protocol. + k_EResultTryAnotherCM = 48, // The current CM can't service the user making a request, user should try another. + k_EResultPasswordRequiredToKickSession = 49,// You are already logged in elsewhere, this cached credential login has failed. + k_EResultAlreadyLoggedInElsewhere = 50, // You are already logged in elsewhere, you must wait + k_EResultSuspended = 51, // Long running operation (content download) suspended/paused + k_EResultCancelled = 52, // Operation canceled (typically by user: content download) + k_EResultDataCorruption = 53, // Operation canceled because data is ill formed or unrecoverable + k_EResultDiskFull = 54, // Operation canceled - not enough disk space. + k_EResultRemoteCallFailed = 55, // an remote call or IPC call failed + k_EResultPasswordUnset = 56, // Password could not be verified as it's unset server side + k_EResultExternalAccountUnlinked = 57, // External account (PSN, Facebook...) is not linked to a Steam account + k_EResultPSNTicketInvalid = 58, // PSN ticket was invalid + k_EResultExternalAccountAlreadyLinked = 59, // External account (PSN, Facebook...) is already linked to some other account, must explicitly request to replace/delete the link first + k_EResultRemoteFileConflict = 60, // The sync cannot resume due to a conflict between the local and remote files + k_EResultIllegalPassword = 61, // The requested new password is not legal + k_EResultSameAsPreviousValue = 62, // new value is the same as the old one ( secret question and answer ) + k_EResultAccountLogonDenied = 63, // account login denied due to 2nd factor authentication failure + k_EResultCannotUseOldPassword = 64, // The requested new password is not legal + k_EResultInvalidLoginAuthCode = 65, // account login denied due to auth code invalid + k_EResultAccountLogonDeniedNoMail = 66, // account login denied due to 2nd factor auth failure - and no mail has been sent + k_EResultHardwareNotCapableOfIPT = 67, // + k_EResultIPTInitError = 68, // + k_EResultParentalControlRestricted = 69, // operation failed due to parental control restrictions for current user + k_EResultFacebookQueryError = 70, // Facebook query returned an error + k_EResultExpiredLoginAuthCode = 71, // account login denied due to auth code expired + k_EResultIPLoginRestrictionFailed = 72, + k_EResultAccountLockedDown = 73, + k_EResultAccountLogonDeniedVerifiedEmailRequired = 74, + k_EResultNoMatchingURL = 75, + k_EResultBadResponse = 76, // parse failure, missing field, etc. + k_EResultRequirePasswordReEntry = 77, // The user cannot complete the action until they re-enter their password + k_EResultValueOutOfRange = 78, // the value entered is outside the acceptable range + k_EResultUnexpectedError = 79, // something happened that we didn't expect to ever happen + k_EResultDisabled = 80, // The requested service has been configured to be unavailable + k_EResultInvalidCEGSubmission = 81, // The set of files submitted to the CEG server are not valid ! + k_EResultRestrictedDevice = 82, // The device being used is not allowed to perform this action + k_EResultRegionLocked = 83, // The action could not be complete because it is region restricted + k_EResultRateLimitExceeded = 84, // Temporary rate limit exceeded, try again later, different from k_EResultLimitExceeded which may be permanent + k_EResultAccountLoginDeniedNeedTwoFactor = 85, // Need two-factor code to login + k_EResultItemDeleted = 86, // The thing we're trying to access has been deleted + k_EResultAccountLoginDeniedThrottle = 87, // login attempt failed, try to throttle response to possible attacker + k_EResultTwoFactorCodeMismatch = 88, // two factor code mismatch + k_EResultTwoFactorActivationCodeMismatch = 89, // activation code for two-factor didn't match + k_EResultAccountAssociatedToMultiplePartners = 90, // account has been associated with multiple partners + k_EResultNotModified = 91, // data not modified + k_EResultNoMobileDevice = 92, // the account does not have a mobile device associated with it + k_EResultTimeNotSynced = 93, // the time presented is out of range or tolerance + k_EResultSmsCodeFailed = 94, // SMS code failure (no match, none pending, etc.) + k_EResultAccountLimitExceeded = 95, // Too many accounts access this resource + k_EResultAccountActivityLimitExceeded = 96, // Too many changes to this account + k_EResultPhoneActivityLimitExceeded = 97, // Too many changes to this phone + k_EResultRefundToWallet = 98, // Cannot refund to payment method, must use wallet + k_EResultEmailSendFailure = 99, // Cannot send an email + k_EResultNotSettled = 100, // Can't perform operation till payment has settled + k_EResultNeedCaptcha = 101, // Needs to provide a valid captcha + k_EResultGSLTDenied = 102, // a game server login token owned by this token's owner has been banned + k_EResultGSOwnerDenied = 103, // game server owner is denied for other reason (account lock, community ban, vac ban, missing phone) + k_EResultInvalidItemType = 104 // the type of thing we were requested to act on is invalid +}; + +/* This enum is used in client API methods, do not re-number existing values. */ +enum EHTTPMethod +{ + k_EHTTPMethodInvalid = 0, + k_EHTTPMethodGET, + k_EHTTPMethodHEAD, + k_EHTTPMethodPOST, + k_EHTTPMethodPUT, + k_EHTTPMethodDELETE, + k_EHTTPMethodOPTIONS, + k_EHTTPMethodPATCH, + + // The remaining HTTP methods are not yet supported, per rfc2616 section 5.1.1 only GET and HEAD are required for + // a compliant general purpose server. We'll likely add more as we find uses for them. + + // k_EHTTPMethodTRACE, + // k_EHTTPMethodCONNECT +}; + + +/* HTTP Status codes that the server can send in response to a request, see rfc2616 section 10.3 for descriptions + of each of these. */ +enum EHTTPStatusCode +{ + // Invalid status code (this isn't defined in HTTP, used to indicate unset in our code) + k_EHTTPStatusCodeInvalid = 0, + + // Informational codes + k_EHTTPStatusCode100Continue = 100, + k_EHTTPStatusCode101SwitchingProtocols = 101, + + // Success codes + k_EHTTPStatusCode200OK = 200, + k_EHTTPStatusCode201Created = 201, + k_EHTTPStatusCode202Accepted = 202, + k_EHTTPStatusCode203NonAuthoritative = 203, + k_EHTTPStatusCode204NoContent = 204, + k_EHTTPStatusCode205ResetContent = 205, + k_EHTTPStatusCode206PartialContent = 206, + + // Redirection codes + k_EHTTPStatusCode300MultipleChoices = 300, + k_EHTTPStatusCode301MovedPermanently = 301, + k_EHTTPStatusCode302Found = 302, + k_EHTTPStatusCode303SeeOther = 303, + k_EHTTPStatusCode304NotModified = 304, + k_EHTTPStatusCode305UseProxy = 305, + //k_EHTTPStatusCode306Unused = 306, (used in old HTTP spec, now unused in 1.1) + k_EHTTPStatusCode307TemporaryRedirect = 307, + + // Error codes + k_EHTTPStatusCode400BadRequest = 400, + k_EHTTPStatusCode401Unauthorized = 401, // You probably want 403 or something else. 401 implies you're sending a WWW-Authenticate header and the client can sent an Authorization header in response. + k_EHTTPStatusCode402PaymentRequired = 402, // This is reserved for future HTTP specs, not really supported by clients + k_EHTTPStatusCode403Forbidden = 403, + k_EHTTPStatusCode404NotFound = 404, + k_EHTTPStatusCode405MethodNotAllowed = 405, + k_EHTTPStatusCode406NotAcceptable = 406, + k_EHTTPStatusCode407ProxyAuthRequired = 407, + k_EHTTPStatusCode408RequestTimeout = 408, + k_EHTTPStatusCode409Conflict = 409, + k_EHTTPStatusCode410Gone = 410, + k_EHTTPStatusCode411LengthRequired = 411, + k_EHTTPStatusCode412PreconditionFailed = 412, + k_EHTTPStatusCode413RequestEntityTooLarge = 413, + k_EHTTPStatusCode414RequestURITooLong = 414, + k_EHTTPStatusCode415UnsupportedMediaType = 415, + k_EHTTPStatusCode416RequestedRangeNotSatisfiable = 416, + k_EHTTPStatusCode417ExpectationFailed = 417, + k_EHTTPStatusCode4xxUnknown = 418, // 418 is reserved, so we'll use it to mean unknown + k_EHTTPStatusCode429TooManyRequests = 429, + + // Server error codes + k_EHTTPStatusCode500InternalServerError = 500, + k_EHTTPStatusCode501NotImplemented = 501, + k_EHTTPStatusCode502BadGateway = 502, + k_EHTTPStatusCode503ServiceUnavailable = 503, + k_EHTTPStatusCode504GatewayTimeout = 504, + k_EHTTPStatusCode505HTTPVersionNotSupported = 505, + k_EHTTPStatusCode5xxUnknown = 599, +}; + +/* list of possible return values from the ISteamGameCoordinator API */ +enum EGCResults +{ + k_EGCResultOK = 0, + k_EGCResultNoMessage = 1, // There is no message in the queue + k_EGCResultBufferTooSmall = 2, // The buffer is too small for the requested message + k_EGCResultNotLoggedOn = 3, // The client is not logged onto Steam + k_EGCResultInvalidMessage = 4, // Something was wrong with the message being sent with SendMessage +}; + +native bool:SteamWorks_IsVACEnabled(); +native bool:SteamWorks_GetPublicIP(ipaddr[4]); +native SteamWorks_GetPublicIPCell(); +native bool:SteamWorks_IsLoaded(); +native bool:SteamWorks_SetGameData(const String:sData[]); +native bool:SteamWorks_SetGameDescription(const String:sDesc[]); +native bool:SteamWorks_SetMapName(const String:sMapName[]); +native bool:SteamWorks_IsConnected(); +native bool:SteamWorks_SetRule(const String:sKey[], const String:sValue[]); +native bool:SteamWorks_ClearRules(); +native bool:SteamWorks_ForceHeartbeat(); +native bool:SteamWorks_GetUserGroupStatus(client, groupid); +native bool:SteamWorks_GetUserGroupStatusAuthID(authid, groupid); + +native EUserHasLicenseForAppResult:SteamWorks_HasLicenseForApp(client, app); +native EUserHasLicenseForAppResult:SteamWorks_HasLicenseForAppId(authid, app); +native SteamWorks_GetClientSteamID(client, String:sSteamID[], length); + +native bool:SteamWorks_RequestStatsAuthID(authid, appid); +native bool:SteamWorks_RequestStats(client, appid); +native bool:SteamWorks_GetStatCell(client, const String:sKey[], &value); +native bool:SteamWorks_GetStatAuthIDCell(authid, const String:sKey[], &value); +native bool:SteamWorks_GetStatFloat(client, const String:sKey[], &Float:value); +native bool:SteamWorks_GetStatAuthIDFloat(authid, const String:sKey[], &Float:value); + +native Handle:SteamWorks_CreateHTTPRequest(EHTTPMethod:method, const String:sURL[]); +native bool:SteamWorks_SetHTTPRequestContextValue(Handle:hHandle, any:data1, any:data2=0); +native bool:SteamWorks_SetHTTPRequestNetworkActivityTimeout(Handle:hHandle, timeout); +native bool:SteamWorks_SetHTTPRequestHeaderValue(Handle:hHandle, const String:sName[], const String:sValue[]); +native bool:SteamWorks_SetHTTPRequestGetOrPostParameter(Handle:hHandle, const String:sName[], const String:sValue[]); +native bool:SteamWorks_SetHTTPRequestUserAgentInfo(Handle:hHandle, const String:sUserAgentInfo[]); +native bool:SteamWorks_SetHTTPRequestRequiresVerifiedCertificate(Handle:hHandle, bool:bRequireVerifiedCertificate); +native bool:SteamWorks_SetHTTPRequestAbsoluteTimeoutMS(Handle:hHandle, unMilliseconds); + +#if SOURCEMOD_V_MAJOR >= 1 && SOURCEMOD_V_MINOR >= 9 +typeset SteamWorksHTTPRequestCompleted +{ + function void (Handle hRequest, bool bFailure, bool bRequestSuccessful, EHTTPStatusCode eStatusCode); + function void (Handle hRequest, bool bFailure, bool bRequestSuccessful, EHTTPStatusCode eStatusCode, any data1); + function void (Handle hRequest, bool bFailure, bool bRequestSuccessful, EHTTPStatusCode eStatusCode, any data1, any data2); +}; + +typeset SteamWorksHTTPHeadersReceived +{ + function void (Handle hRequest, bool bFailure); + function void (Handle hRequest, bool bFailure, any data1); + function void (Handle hRequest, bool bFailure, any data1, any data2); +}; + +typeset SteamWorksHTTPDataReceived +{ + function void (Handle hRequest, bool bFailure, int offset, int bytesreceived); + function void (Handle hRequest, bool bFailure, int offset, int bytesreceived, any data1); + function void (Handle hRequest, bool bFailure, int offset, int bytesreceived, any data1, any data2); +}; + +typeset SteamWorksHTTPBodyCallback +{ + function void (const char[] sData); + function void (const char[] sData, any value); + function void (const int[] data, any value, int datalen); +}; + +#else + +funcenum SteamWorksHTTPRequestCompleted +{ + public(Handle:hRequest, bool:bFailure, bool:bRequestSuccessful, EHTTPStatusCode:eStatusCode), + public(Handle:hRequest, bool:bFailure, bool:bRequestSuccessful, EHTTPStatusCode:eStatusCode, any:data1), + public(Handle:hRequest, bool:bFailure, bool:bRequestSuccessful, EHTTPStatusCode:eStatusCode, any:data1, any:data2) +}; + +funcenum SteamWorksHTTPHeadersReceived +{ + public(Handle:hRequest, bool:bFailure), + public(Handle:hRequest, bool:bFailure, any:data1), + public(Handle:hRequest, bool:bFailure, any:data1, any:data2) +}; + +funcenum SteamWorksHTTPDataReceived +{ + public(Handle:hRequest, bool:bFailure, offset, bytesreceived), + public(Handle:hRequest, bool:bFailure, offset, bytesreceived, any:data1), + public(Handle:hRequest, bool:bFailure, offset, bytesreceived, any:data1, any:data2) +}; + +funcenum SteamWorksHTTPBodyCallback +{ + public(const String:sData[]), + public(const String:sData[], any:value), + public(const data[], any:value, datalen) +}; + +#endif + +native bool:SteamWorks_SetHTTPCallbacks(Handle:hHandle, SteamWorksHTTPRequestCompleted:fCompleted = INVALID_FUNCTION, SteamWorksHTTPHeadersReceived:fHeaders = INVALID_FUNCTION, SteamWorksHTTPDataReceived:fData = INVALID_FUNCTION, Handle:hCalling = INVALID_HANDLE); +native bool:SteamWorks_SendHTTPRequest(Handle:hRequest); +native bool:SteamWorks_SendHTTPRequestAndStreamResponse(Handle:hRequest); +native bool:SteamWorks_DeferHTTPRequest(Handle:hRequest); +native bool:SteamWorks_PrioritizeHTTPRequest(Handle:hRequest); +native bool:SteamWorks_GetHTTPResponseHeaderSize(Handle:hRequest, const String:sHeader[], &size); +native bool:SteamWorks_GetHTTPResponseHeaderValue(Handle:hRequest, const String:sHeader[], String:sValue[], size); +native bool:SteamWorks_GetHTTPResponseBodySize(Handle:hRequest, &size); +native bool:SteamWorks_GetHTTPResponseBodyData(Handle:hRequest, String:sBody[], length); +native bool:SteamWorks_GetHTTPStreamingResponseBodyData(Handle:hRequest, cOffset, String:sBody[], length); +native bool:SteamWorks_GetHTTPDownloadProgressPct(Handle:hRequest, &Float:percent); +native bool:SteamWorks_GetHTTPRequestWasTimedOut(Handle:hRequest, &bool:bWasTimedOut); +native bool:SteamWorks_SetHTTPRequestRawPostBody(Handle:hRequest, const String:sContentType[], const String:sBody[], bodylen); +native bool:SteamWorks_SetHTTPRequestRawPostBodyFromFile(Handle:hRequest, const String:sContentType[], const String:sFileName[]); + +native bool:SteamWorks_GetHTTPResponseBodyCallback(Handle:hRequest, SteamWorksHTTPBodyCallback:fCallback, any:data = 0, Handle:hPlugin = INVALID_HANDLE); /* Look up, moved definition for 1.7+ compat. */ +native bool:SteamWorks_WriteHTTPResponseBodyToFile(Handle:hRequest, const String:sFileName[]); + +forward SW_OnValidateClient(ownerauthid, authid); +forward SteamWorks_OnValidateClient(ownerauthid, authid); +forward SteamWorks_SteamServersConnected(); +forward SteamWorks_SteamServersConnectFailure(EResult:result); +forward SteamWorks_SteamServersDisconnected(EResult:result); + +forward Action:SteamWorks_RestartRequested(); +forward SteamWorks_TokenRequested(String:sToken[], maxlen); + +forward SteamWorks_OnClientGroupStatus(authid, groupid, bool:isMember, bool:isOfficer); + +forward EGCResults:SteamWorks_GCSendMessage(unMsgType, const String:pubData[], cubData); +forward SteamWorks_GCMsgAvailable(cubData); +forward EGCResults:SteamWorks_GCRetrieveMessage(punMsgType, const String:pubDest[], cubDest, pcubMsgSize); + +native EGCResults:SteamWorks_SendMessageToGC(unMsgType, const String:pubData[], cubData); + +public Extension:__ext_SteamWorks = +{ + name = "SteamWorks", + file = "SteamWorks.ext", +#if defined AUTOLOAD_EXTENSIONS + autoload = 1, +#else + autoload = 0, +#endif +#if defined REQUIRE_EXTENSIONS + required = 1, +#else + required = 0, +#endif +}; + +#if !defined REQUIRE_EXTENSIONS +public __ext_SteamWorks_SetNTVOptional() +{ + MarkNativeAsOptional("SteamWorks_IsVACEnabled"); + MarkNativeAsOptional("SteamWorks_GetPublicIP"); + MarkNativeAsOptional("SteamWorks_GetPublicIPCell"); + MarkNativeAsOptional("SteamWorks_IsLoaded"); + MarkNativeAsOptional("SteamWorks_SetGameData"); + MarkNativeAsOptional("SteamWorks_SetGameDescription"); + MarkNativeAsOptional("SteamWorks_IsConnected"); + MarkNativeAsOptional("SteamWorks_SetRule"); + MarkNativeAsOptional("SteamWorks_ClearRules"); + MarkNativeAsOptional("SteamWorks_ForceHeartbeat"); + MarkNativeAsOptional("SteamWorks_GetUserGroupStatus"); + MarkNativeAsOptional("SteamWorks_GetUserGroupStatusAuthID"); + + MarkNativeAsOptional("SteamWorks_HasLicenseForApp"); + MarkNativeAsOptional("SteamWorks_HasLicenseForAppId"); + MarkNativeAsOptional("SteamWorks_GetClientSteamID"); + + MarkNativeAsOptional("SteamWorks_RequestStatsAuthID"); + MarkNativeAsOptional("SteamWorks_RequestStats"); + MarkNativeAsOptional("SteamWorks_GetStatCell"); + MarkNativeAsOptional("SteamWorks_GetStatAuthIDCell"); + MarkNativeAsOptional("SteamWorks_GetStatFloat"); + MarkNativeAsOptional("SteamWorks_GetStatAuthIDFloat"); + + MarkNativeAsOptional("SteamWorks_SendMessageToGC"); + + MarkNativeAsOptional("SteamWorks_CreateHTTPRequest"); + MarkNativeAsOptional("SteamWorks_SetHTTPRequestContextValue"); + MarkNativeAsOptional("SteamWorks_SetHTTPRequestNetworkActivityTimeout"); + MarkNativeAsOptional("SteamWorks_SetHTTPRequestHeaderValue"); + MarkNativeAsOptional("SteamWorks_SetHTTPRequestGetOrPostParameter"); + + MarkNativeAsOptional("SteamWorks_SetHTTPCallbacks"); + MarkNativeAsOptional("SteamWorks_SendHTTPRequest"); + MarkNativeAsOptional("SteamWorks_SendHTTPRequestAndStreamResponse"); + MarkNativeAsOptional("SteamWorks_DeferHTTPRequest"); + MarkNativeAsOptional("SteamWorks_PrioritizeHTTPRequest"); + MarkNativeAsOptional("SteamWorks_GetHTTPResponseHeaderSize"); + MarkNativeAsOptional("SteamWorks_GetHTTPResponseHeaderValue"); + MarkNativeAsOptional("SteamWorks_GetHTTPResponseBodySize"); + MarkNativeAsOptional("SteamWorks_GetHTTPResponseBodyData"); + MarkNativeAsOptional("SteamWorks_GetHTTPStreamingResponseBodyData"); + MarkNativeAsOptional("SteamWorks_GetHTTPDownloadProgressPct"); + MarkNativeAsOptional("SteamWorks_SetHTTPRequestRawPostBody"); + MarkNativeAsOptional("SteamWorks_SetHTTPRequestRawPostBodyFromFile"); + + MarkNativeAsOptional("SteamWorks_GetHTTPResponseBodyCallback"); + MarkNativeAsOptional("SteamWorks_WriteHTTPResponseBodyToFile"); +} +#endif
\ No newline at end of file diff --git a/sourcemod/scripting/include/autoexecconfig.inc b/sourcemod/scripting/include/autoexecconfig.inc new file mode 100644 index 0000000..e057b1b --- /dev/null +++ b/sourcemod/scripting/include/autoexecconfig.inc @@ -0,0 +1,765 @@ +/** + * AutoExecConfig + * + * Copyright (C) 2013-2017 Impact + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * 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/> + */ + +#if defined _autoexecconfig_included + #endinput +#endif +#define _autoexecconfig_included + + +#include <sourcemod> + +#define AUTOEXECCONFIG_VERSION "0.1.5" +#define AUTOEXECCONFIG_URL "https://forums.alliedmods.net/showthread.php?t=204254" + +// Append +#define AUTOEXEC_APPEND_BAD_FILENAME 0 +#define AUTOEXEC_APPEND_FILE_NOT_FOUND 1 +#define AUTOEXEC_APPEND_BAD_HANDLE 2 +#define AUTOEXEC_APPEND_SUCCESS 3 + + + +// Find +#define AUTOEXEC_FIND_BAD_FILENAME 10 +#define AUTOEXEC_FIND_FILE_NOT_FOUND 11 +#define AUTOEXEC_FIND_BAD_HANDLE 12 +#define AUTOEXEC_FIND_NOT_FOUND 13 +#define AUTOEXEC_FIND_SUCCESS 14 + + + +// Clean +#define AUTOEXEC_CLEAN_FILE_NOT_FOUND 20 +#define AUTOEXEC_CLEAN_BAD_HANDLE 21 +#define AUTOEXEC_CLEAN_SUCCESS 22 + + + +// General +#define AUTOEXEC_NO_CONFIG 30 + + + +// Formatter +#define AUTOEXEC_FORMAT_BAD_FILENAME 40 +#define AUTOEXEC_FORMAT_SUCCESS 41 + + + +// Global variables +static char g_sConfigFile[PLATFORM_MAX_PATH]; +static char g_sRawFileName[PLATFORM_MAX_PATH]; +static char g_sFolderPath[PLATFORM_MAX_PATH]; + +static bool g_bCreateFile = false; +static Handle g_hPluginHandle = null; + +static bool g_bCreateDirectory = false; +static int g_bCreateDirectoryMode = FPERM_U_READ|FPERM_U_WRITE|FPERM_U_EXEC|FPERM_G_READ|FPERM_G_EXEC|FPERM_O_READ|FPERM_O_EXEC; + + +// Workaround for now +static int g_iLastFindResult; +static int g_iLastAppendResult; + + + + +/** + * Returns the last result from the parser. + * + * @return Returns one of the AUTOEXEC_FIND values or -1 if not set. +*/ +stock int AutoExecConfig_GetFindResult() +{ + return g_iLastFindResult; +} + + + + + +/** + * Returns the last result from the appender. + * + * @return Returns one of the AUTOEXEC_APPEND values or -1 if not set. +*/ +stock int AutoExecConfig_GetAppendResult() +{ + return g_iLastAppendResult; +} + + +/** + * Set if the config file should be created by the autoexecconfig include itself if it doesn't exist. + * + * @param create True if config file should be created, false otherwise. + * @noreturn + */ +stock void AutoExecConfig_SetCreateFile(bool create) +{ + g_bCreateFile = create; +} + + +/** + * Set if the config file's folder should be created by the autoexecconfig include itself if it doesn't exist. + * Note: Must be used before AutoExecConfig_SetFile as the potential creation of it happens there + * + * @param create True if config file should be created, false otherwise. + * @param mode Folder permission mode, default is u=rwx,g=rx,o=rx. + * @noreturn + */ +stock void AutoExecConfig_SetCreateDirectory(bool create, int mode=FPERM_U_READ|FPERM_U_WRITE|FPERM_U_EXEC|FPERM_G_READ|FPERM_G_EXEC|FPERM_O_READ|FPERM_O_EXEC) +{ + g_bCreateDirectory = create; + g_bCreateDirectoryMode = mode; +} + + +/** + * Returns if the config file should be created if it doesn't exist. + * + * @return Returns true, if the config file should be created or false if it should not. + */ +stock bool AutoExecConfig_GetCreateFile() +{ + return g_bCreateFile; +} + + +/** + * Set the plugin for which the config file should be created. + * Set to null to use the calling plugin. + * Used to print the correct filename in the top comment when creating the file. + * + * @param plugin The plugin to create convars for or null to use the calling plugin. + * @noreturn + */ +stock void AutoExecConfig_SetPlugin(Handle plugin) +{ + g_hPluginHandle = plugin; +} + + +/** + * Returns the plugin's handle for which the config file is created. + * + * @return The plugin handle + */ +stock Handle AutoExecConfig_GetPlugin() +{ + return g_hPluginHandle; +} + + +/** + * Set the global autoconfigfile used by functions of this file. + * Note: does not support subfolders like folder1/folder2 + * + * @param file Name of the config file, path and .cfg extension is being added if not given. + * @param folder Folder under cfg/ to use. By default this is "sourcemod." + * @return True if formatter returned success, false otherwise. +*/ +stock bool AutoExecConfig_SetFile(char[] file, char[] folder="sourcemod") +{ + Format(g_sConfigFile, sizeof(g_sConfigFile), "%s", file); + + // Global buffers for cfg execution + strcopy(g_sRawFileName, sizeof(g_sRawFileName), file); + strcopy(g_sFolderPath, sizeof(g_sFolderPath), folder); + + + // Format the filename + return AutoExecConfig_FormatFileName(g_sConfigFile, sizeof(g_sConfigFile), folder) == AUTOEXEC_FORMAT_SUCCESS; +} + + + + + + +/** + * Get the formatted autoconfigfile used by functions of this file. + * + * @param buffer String to format. + * @param size Maximum size of buffer + * @return True if filename was set, false otherwise. +*/ +stock bool AutoExecConfig_GetFile(char[] buffer,int size) +{ + if (strlen(g_sConfigFile) > 0) + { + strcopy(buffer, size, g_sConfigFile); + + return true; + } + + // Security for decl users + buffer[0] = '\0'; + + return false; +} + + + + + + +/** + * Creates a convar and appends it to the autoconfigfile if not found. + * FCVAR_DONTRECORD will be skipped. + * + * @param name Name of new convar. + * @param defaultValue String containing the default value of new convar. + * @param description Optional description of the convar. + * @param flags Optional bitstring of flags determining how the convar should be handled. See FCVAR_* constants for more details. + * @param hasMin Optional boolean that determines if the convar has a minimum value. + * @param min Minimum floating point value that the convar can have if hasMin is true. + * @param hasMax Optional boolean that determines if the convar has a maximum value. + * @param max Maximum floating point value that the convar can have if hasMax is true. + * @return A handle to the newly created convar. If the convar already exists, a handle to it will still be returned. + * @error Convar name is blank or is the same as an existing console command. +*/ +stock ConVar AutoExecConfig_CreateConVar(const char[] name, const char[] defaultValue, const char[] description="", int flags=0, bool hasMin=false, float min=0.0, bool hasMax=false, float max=0.0) +{ + // If configfile was set and convar has no dontrecord flag + if (!(flags & FCVAR_DONTRECORD) && strlen(g_sConfigFile) > 0) + { + // Reset the results + g_iLastFindResult = -1; + g_iLastAppendResult = -1; + + + // Add it if not found + char buffer[64]; + + g_iLastFindResult = AutoExecConfig_FindValue(name, buffer, sizeof(buffer), true); + + // We only add this convar if it doesn't exist, or the file doesn't exist and it should be auto-generated + if (g_iLastFindResult == AUTOEXEC_FIND_NOT_FOUND || (g_iLastFindResult == AUTOEXEC_FIND_FILE_NOT_FOUND && g_bCreateFile)) + { + g_iLastAppendResult = AutoExecConfig_AppendValue(name, defaultValue, description, flags, hasMin, min, hasMax, max); + } + } + + + // Create the convar + return CreateConVar(name, defaultValue, description, flags, hasMin, min, hasMax, max); +} + + + + +/** + * Executes the autoconfigfile and adds it to the OnConfigsExecuted forward. + * If we didn't create it ourselves we let SourceMod create it. + * + * @noreturn +*/ +stock void AutoExecConfig_ExecuteFile() +{ + // Only let sourcemod create the file, if we didn't do that already. + AutoExecConfig(!g_bCreateFile, g_sRawFileName, g_sFolderPath); +} + + + + + +/** + * Formats a autoconfigfile, prefixes path and adds .cfg extension if missing. + * + * @param buffer String to format. + * @param size Maximum size of buffer. + * @return Returns one of the AUTOEXEC_FORMAT values.. +*/ +stock static int AutoExecConfig_FormatFileName(char[] buffer, int size, char[] folder="sourcemod") +{ + // No config set + if (strlen(g_sConfigFile) < 1) + { + return AUTOEXEC_NO_CONFIG; + } + + + // Can't be an cfgfile + if (StrContains(g_sConfigFile, ".cfg") != -1 && strlen(g_sConfigFile) < 4) + { + return AUTOEXEC_FORMAT_BAD_FILENAME; + } + + + // Pathprefix + char pathprefixbuffer[PLATFORM_MAX_PATH]; + if (strlen(folder) > 0) + { + Format(pathprefixbuffer, sizeof(pathprefixbuffer), "cfg/%s/", folder); + + if (g_bCreateDirectory && !DirExists(pathprefixbuffer)) + { + CreateDirectory(pathprefixbuffer, g_bCreateDirectoryMode); + } + } + else + { + Format(pathprefixbuffer, sizeof(pathprefixbuffer), "cfg/"); + } + + + char filebuffer[PLATFORM_MAX_PATH]; + filebuffer[0] = '\0'; + + // Add path if file doesn't begin with it + if (StrContains(buffer, pathprefixbuffer) != 0) + { + StrCat(filebuffer, sizeof(filebuffer), pathprefixbuffer); + } + + StrCat(filebuffer, sizeof(filebuffer), g_sConfigFile); + + + // Add .cfg extension if file doesn't end with it + if (StrContains(filebuffer[strlen(filebuffer) - 4], ".cfg") != 0) + { + StrCat(filebuffer, sizeof(filebuffer), ".cfg"); + } + + strcopy(buffer, size, filebuffer); + + return AUTOEXEC_FORMAT_SUCCESS; +} + + + + + + +/** + * Appends a convar to the global autoconfigfile + * + * @param name Name of new convar. + * @param defaultValue String containing the default value of new convar. + * @param description Optional description of the convar. + * @param flags Optional bitstring of flags determining how the convar should be handled. See FCVAR_* constants for more details. + * @param hasMin Optional boolean that determines if the convar has a minimum value. + * @param min Minimum floating point value that the convar can have if hasMin is true. + * @param hasMax Optional boolean that determines if the convar has a maximum value. + * @param max Maximum floating point value that the convar can have if hasMax is true. + * @return Returns one of the AUTOEXEC_APPEND values +*/ +stock int AutoExecConfig_AppendValue(const char[] name, const char[] defaultValue, const char[] description, int flags, bool hasMin, float min, bool hasMax, float max) +{ + // No config set + if (strlen(g_sConfigFile) < 1) + { + return AUTOEXEC_NO_CONFIG; + } + + + char filebuffer[PLATFORM_MAX_PATH]; + strcopy(filebuffer, sizeof(filebuffer), g_sConfigFile); + + + //PrintToServer("pathbuffer: %s", filebuffer); + + bool bFileExists = FileExists(filebuffer); + + if (g_bCreateFile || bFileExists) + { + // If the file already exists we open it in append mode, otherwise we use a write mode which creates the file + File fFile = OpenFile(filebuffer, (bFileExists ? "a" : "w")); + char writebuffer[2048]; + + + if (fFile == null) + { + return AUTOEXEC_APPEND_BAD_HANDLE; + } + + // We just created the file, so add some header about version and stuff + if (g_bCreateFile && !bFileExists) + { + fFile.WriteLine( "// This file was auto-generated by AutoExecConfig v%s (%s)", AUTOEXECCONFIG_VERSION, AUTOEXECCONFIG_URL); + + GetPluginFilename(g_hPluginHandle, writebuffer, sizeof(writebuffer)); + Format(writebuffer, sizeof(writebuffer), "// ConVars for plugin \"%s\"", writebuffer); + fFile.WriteLine(writebuffer); + } + + // Spacer + fFile.WriteLine("\n"); + + + // This is used for multiline comments + int newlines = GetCharCountInStr('\n', description); + if (newlines == 0) + { + // We have no newlines, we can write the description to the file as is + Format(writebuffer, sizeof(writebuffer), "// %s", description); + fFile.WriteLine(writebuffer); + } + else + { + char[][] newlineBuf = new char[newlines +1][2048]; + ExplodeString(description, "\n", newlineBuf, newlines +1, 2048, false); + + // Each newline gets a commented newline + for (int i; i <= newlines; i++) + { + if (strlen(newlineBuf[i]) > 0) + { + fFile.WriteLine("// %s", newlineBuf[i]); + } + } + } + + + // Descspacer + fFile.WriteLine("// -"); + + + // Default + Format(writebuffer, sizeof(writebuffer), "// Default: \"%s\"", defaultValue); + fFile.WriteLine(writebuffer); + + + // Minimum + if (hasMin) + { + Format(writebuffer, sizeof(writebuffer), "// Minimum: \"%f\"", min); + fFile.WriteLine(writebuffer); + } + + + // Maximum + if (hasMax) + { + Format(writebuffer, sizeof(writebuffer), "// Maximum: \"%f\"", max); + fFile.WriteLine(writebuffer); + } + + + // Write end and defaultvalue + Format(writebuffer, sizeof(writebuffer), "%s \"%s\"", name, defaultValue); + fFile.WriteLine(writebuffer); + + + fFile.Close(); + + return AUTOEXEC_APPEND_SUCCESS; + } + + return AUTOEXEC_APPEND_FILE_NOT_FOUND; +} + + + + + + +/** + * Returns a convar's value from the global autoconfigfile + * + * @param cvar Cvar to search for. + * @param value Buffer to store result into. + * @param size Maximum size of buffer. + * @param caseSensitive Whether or not the search should be case sensitive. + * @return Returns one of the AUTOEXEC_FIND values +*/ +stock int AutoExecConfig_FindValue(const char[] cvar, char[] value, int size, bool caseSensitive=false) +{ + // Security for decl users + value[0] = '\0'; + + + // No config set + if (strlen(g_sConfigFile) < 1) + { + return AUTOEXEC_NO_CONFIG; + } + + + char filebuffer[PLATFORM_MAX_PATH]; + strcopy(filebuffer, sizeof(filebuffer), g_sConfigFile); + + + + //PrintToServer("pathbuffer: %s", filebuffer); + + bool bFileExists = FileExists(filebuffer); + + // We want to create the config file and it doesn't exist yet. + if (g_bCreateFile && !bFileExists) + { + return AUTOEXEC_FIND_FILE_NOT_FOUND; + } + + + if (bFileExists) + { + File fFile = OpenFile(filebuffer, "r"); + int valuestart; + int valueend; + int cvarend; + + // Just an reminder to self, leave the values that high + char sConvar[64]; + char sValue[64]; + char readbuffer[2048]; + char copybuffer[2048]; + + if (fFile == null) + { + return AUTOEXEC_FIND_BAD_HANDLE; + } + + + while (!fFile.EndOfFile() && fFile.ReadLine(readbuffer, sizeof(readbuffer))) + { + // Is a comment or not valid + if (IsCharSpace(readbuffer[0]) || readbuffer[0] == '/' || (!IsCharNumeric(readbuffer[0]) && !IsCharAlpha(readbuffer[0])) ) + { + continue; + } + + + // Has not enough spaces, must have at least 1 + if (GetCharCountInStr(' ', readbuffer) < 1) + { + continue; + } + + + // Ignore cvars which aren't quoted + if (GetCharCountInStr('"', readbuffer) != 2) + { + continue; + } + + + + // Get the start of the value + if ( (valuestart = StrContains(readbuffer, "\"")) == -1 ) + { + continue; + } + + + // Get the end of the value + if ( (valueend = StrContains(readbuffer[valuestart+1], "\"")) == -1 ) + { + continue; + } + + + // Get the start of the cvar, + if ( (cvarend = StrContains(readbuffer, " ")) == -1 || cvarend >= valuestart) + { + continue; + } + + + // Skip if cvarendindex is before valuestartindex + if (cvarend >= valuestart) + { + continue; + } + + + // Convar + // Tempcopy for security + strcopy(copybuffer, sizeof(copybuffer), readbuffer); + copybuffer[cvarend] = '\0'; + + strcopy(sConvar, sizeof(sConvar), copybuffer); + + + // Value + // Tempcopy for security + strcopy(copybuffer, sizeof(copybuffer), readbuffer[valuestart+1]); + copybuffer[valueend] = '\0'; + + strcopy(sValue, sizeof(sValue), copybuffer); + + + //PrintToServer("Cvar %s has a value of %s", sConvar, sValue); + + if (StrEqual(sConvar, cvar, caseSensitive)) + { + Format(value, size, "%s", sConvar); + + fFile.Close(); + return AUTOEXEC_FIND_SUCCESS; + } + } + + fFile.Close(); + return AUTOEXEC_FIND_NOT_FOUND; + } + + + return AUTOEXEC_FIND_FILE_NOT_FOUND; +} + + + + + + +/** + * Cleans the global autoconfigfile from too much spaces + * + * @return One of the AUTOEXEC_CLEAN values. +*/ +stock int AutoExecConfig_CleanFile() +{ + // No config set + if (strlen(g_sConfigFile) < 1) + { + return AUTOEXEC_NO_CONFIG; + } + + + char sfile[PLATFORM_MAX_PATH]; + strcopy(sfile, sizeof(sfile), g_sConfigFile); + + + // Security + if (!FileExists(sfile)) + { + return AUTOEXEC_CLEAN_FILE_NOT_FOUND; + } + + + + char sfile2[PLATFORM_MAX_PATH]; + Format(sfile2, sizeof(sfile2), "%s_tempcopy", sfile); + + + char readbuffer[2048]; + int count; + bool firstreached; + + + // Open files + File fFile1 = OpenFile(sfile, "r"); + File fFile2 = OpenFile(sfile2, "w"); + + + + // Check filehandles + if (fFile1 == null || fFile2 == null) + { + if (fFile1 != null) + { + //PrintToServer("Handle1 invalid"); + fFile1.Close(); + } + + if (fFile2 != null) + { + //PrintToServer("Handle2 invalid"); + fFile2.Close(); + } + + return AUTOEXEC_CLEAN_BAD_HANDLE; + } + + + + while (!fFile1.EndOfFile() && fFile1.ReadLine(readbuffer, sizeof(readbuffer))) + { + // Is space + if (IsCharSpace(readbuffer[0])) + { + count++; + } + // No space, count from start + else + { + count = 0; + } + + + // Don't write more than 1 space if seperation after informations have been reached + if (count < 2 || !firstreached) + { + ReplaceString(readbuffer, sizeof(readbuffer), "\n", ""); + fFile2.WriteLine(readbuffer); + } + + + // First bigger seperation after informations has been reached + if (count == 2) + { + firstreached = true; + } + } + + + fFile1.Close(); + fFile2.Close(); + + + // This might be a risk, for now it works + DeleteFile(sfile); + RenameFile(sfile, sfile2); + + return AUTOEXEC_CLEAN_SUCCESS; +} + + + + + + +/** + * Returns how many times the given char occures in the given string. + * + * @param str String to search for in. + * @return Occurences of the given char found in string. +*/ +stock static int GetCharCountInStr(int character, const char[] str) +{ + int len = strlen(str); + int count; + + for (int i; i < len; i++) + { + if (str[i] == character) + { + count++; + } + } + + return count; +} + + + + + + +#pragma deprecated +stock bool AutoExecConfig_CacheConvars() +{ + return false; +} diff --git a/sourcemod/scripting/include/colors.inc b/sourcemod/scripting/include/colors.inc new file mode 100644 index 0000000..3ce8b6a --- /dev/null +++ b/sourcemod/scripting/include/colors.inc @@ -0,0 +1,945 @@ +/************************************************************************** + * * + * Colored Chat Functions * + * Author: exvel, Editor: Popoklopsi, Powerlord, Bara * + * Version: 1.2.3 * + * by modified by 1NutWunDeR * + **************************************************************************/ + +/*Info: purple works only with CPrintToChat and CPrintToChatAll and without {blue} in the same string + (volvo gave them the same color code and saytext2 overrides purple with your current teamcolor.)*/ + +#if defined _colors_included + #endinput +#endif +#define _colors_included + +#define MAX_MESSAGE_LENGTH 320 +#define MAX_COLORS 17 + +#define SERVER_INDEX 0 +#define NO_INDEX -1 +#define NO_PLAYER -2 + +enum Colors +{ + Color_Default = 0, + Color_Darkred, + Color_Green, + Color_Lightgreen, + Color_Red, + Color_Blue, + Color_Olive, + Color_Lime, + Color_Orange, + Color_Purple, + Color_Grey, + Color_Yellow, + Color_Lightblue, + Color_Steelblue, + Color_Darkblue, + Color_Pink, + Color_Lightred, +} + +/* Colors' properties */ +// {"{default}", "{darkred}", "{green}", "{lightgreen}", "{orange}", "{blue}", "{olive}", "{lime}", "{red}", "{purple}", "{grey}", "{yellow}", "{lightblue}", "{steelblue}", "{darkblue}", "{pink}", "{lightred}"}; +new String:CTag[][] = {"{d}", "{dr}", "{gr}", "{lg}", "{o}", "{b}", "{ol}", "{l}", "{r}", "{p}", "{g}", "{y}", "{lb}", "{sb}", "{db}", "{pi}", "{lr}"}; +new String:CTagCode[][] = {"\x01", "\x02", "\x04", "\x03", "\x03", "\x03", "\x05", "\x06", "\x07", "\x03", "\x08", "\x09","\x0A","\x0B","\x0C","\x0E","\x0F"}; +new bool:CTagReqSayText2[] = {false, false, false, true, true, true, false, false, false, false, false, false, false, false, false, false, false}; +new bool:CEventIsHooked = false; +new bool:CSkipList[MAXPLAYERS+1] = {false,...}; + +/* Game default profile */ +new bool:CProfile_Colors[] = {true, false, true, false, false, false, false, false, false, false, false, false, false, false, false, false, false}; +new CProfile_TeamIndex[] = {NO_INDEX, NO_INDEX, NO_INDEX, NO_INDEX, NO_INDEX, NO_INDEX, NO_INDEX, NO_INDEX, NO_INDEX, NO_INDEX, NO_INDEX, NO_INDEX, NO_INDEX, NO_INDEX, NO_INDEX}; +new bool:CProfile_SayText2 = false; + + +static Handle:sm_show_activity = INVALID_HANDLE; + +/** + * Prints a message to a specific client in the chat area. + * Supports color tags. + * + * @param client Client index. + * @param szMessage Message (formatting rules). + * @return No return + * + * On error/Errors: If the client is not connected an error will be thrown. + */ +stock CPrintToChat(client, const String:szMessage[], any:...) +{ + if (client <= 0 || client > MaxClients) + ThrowError("Invalid client index %d", client); + + if (!IsClientInGame(client)) + ThrowError("Client %d is not in game", client); + + decl String:szBuffer[MAX_MESSAGE_LENGTH]; + decl String:szCMessage[MAX_MESSAGE_LENGTH]; + + SetGlobalTransTarget(client); + + Format(szBuffer, sizeof(szBuffer), "\x01%s", szMessage); + VFormat(szCMessage, sizeof(szCMessage), szBuffer, 3); + + new index = CFormat(szCMessage, sizeof(szCMessage)); + + if (index == NO_INDEX) + PrintToChat(client, "%s", szCMessage); + else + CSayText2(client, index, szCMessage); +} + +/** + * Reples to a message in a command. A client index of 0 will use PrintToServer(). + * If the command was from the console, PrintToConsole() is used. If the command was from chat, CPrintToChat() is used. + * Supports color tags. + * + * @param client Client index, or 0 for server. + * @param szMessage Formatting rules. + * @param ... Variable number of format parameters. + * @return No return + * + * On error/Errors: If the client is not connected or invalid. + */ +stock CReplyToCommand(client, const String:szMessage[], any:...) +{ + decl String:szCMessage[MAX_MESSAGE_LENGTH]; + SetGlobalTransTarget(client); + VFormat(szCMessage, sizeof(szCMessage), szMessage, 3); + + if (client == 0) + { + CRemoveTags(szCMessage, sizeof(szCMessage)); + PrintToServer("%s", szCMessage); + } + else if (GetCmdReplySource() == SM_REPLY_TO_CONSOLE) + { + CRemoveTags(szCMessage, sizeof(szCMessage)); + PrintToConsole(client, "%s", szCMessage); + } + else + { + CPrintToChat(client, "%s", szCMessage); + } +} + +/** + * Reples to a message in a command. A client index of 0 will use PrintToServer(). + * If the command was from the console, PrintToConsole() is used. If the command was from chat, CPrintToChat() is used. + * Supports color tags. + * + * @param client Client index, or 0 for server. + * @param author Author index whose color will be used for teamcolor tag. + * @param szMessage Formatting rules. + * @param ... Variable number of format parameters. + * @return No return + * + * On error/Errors: If the client is not connected or invalid. + */ +stock CReplyToCommandEx(client, author, const String:szMessage[], any:...) +{ + decl String:szCMessage[MAX_MESSAGE_LENGTH]; + SetGlobalTransTarget(client); + VFormat(szCMessage, sizeof(szCMessage), szMessage, 4); + + if (client == 0) + { + CRemoveTags(szCMessage, sizeof(szCMessage)); + PrintToServer("%s", szCMessage); + } + else if (GetCmdReplySource() == SM_REPLY_TO_CONSOLE) + { + CRemoveTags(szCMessage, sizeof(szCMessage)); + PrintToConsole(client, "%s", szCMessage); + } + else + { + CPrintToChatEx(client, author, "%s", szCMessage); + } +} + +/** + * Prints a message to all clients in the chat area. + * Supports color tags. + * + * @param client Client index. + * @param szMessage Message (formatting rules) + * @return No return + */ +stock CPrintToChatAll(const String:szMessage[], any:...) +{ + decl String:szBuffer[MAX_MESSAGE_LENGTH]; + + for (new i = 1; i <= MaxClients; i++) + { + if (IsClientInGame(i) && !IsFakeClient(i) && !CSkipList[i]) + { + SetGlobalTransTarget(i); + VFormat(szBuffer, sizeof(szBuffer), szMessage, 2); + + CPrintToChat(i, "%s", szBuffer); + } + + CSkipList[i] = false; + } +} + +/** + * Prints a message to a specific client in the chat area. + * Supports color tags and teamcolor tag. + * + * @param client Client index. + * @param author Author index whose color will be used for teamcolor tag. + * @param szMessage Message (formatting rules). + * @return No return + * + * On error/Errors: If the client or author are not connected an error will be thrown. + */ +stock CPrintToChatEx(client, author, const String:szMessage[], any:...) +{ + if (client <= 0 || client > MaxClients) + ThrowError("Invalid client index %d", client); + + if (!IsClientInGame(client)) + ThrowError("Client %d is not in game", client); + + if (author < 0 || author > MaxClients) + ThrowError("Invalid client index %d", author); + + decl String:szBuffer[MAX_MESSAGE_LENGTH]; + decl String:szCMessage[MAX_MESSAGE_LENGTH]; + + SetGlobalTransTarget(client); + + Format(szBuffer, sizeof(szBuffer), "\x01%s", szMessage); + VFormat(szCMessage, sizeof(szCMessage), szBuffer, 4); + + new index = CFormat(szCMessage, sizeof(szCMessage), author); + + if (index == NO_INDEX) + PrintToChat(client, "%s", szCMessage); + else + CSayText2(client, author, szCMessage); +} + +/** + * Prints a message to all clients in the chat area. + * Supports color tags and teamcolor tag. + * + * @param author Author index whos color will be used for teamcolor tag. + * @param szMessage Message (formatting rules). + * @return No return + * + * On error/Errors: If the author is not connected an error will be thrown. + */ +stock CPrintToChatAllEx(author, const String:szMessage[], any:...) +{ + if (author < 0 || author > MaxClients) + ThrowError("Invalid client index %d", author); + + if (!IsClientInGame(author)) + ThrowError("Client %d is not in game", author); + + decl String:szBuffer[MAX_MESSAGE_LENGTH]; + + for (new i = 1; i <= MaxClients; i++) + { + if (IsClientInGame(i) && !IsFakeClient(i) && !CSkipList[i]) + { + SetGlobalTransTarget(i); + VFormat(szBuffer, sizeof(szBuffer), szMessage, 3); + + CPrintToChatEx(i, author, "%s", szBuffer); + } + + CSkipList[i] = false; + } +} + +/** + * Removes color tags from the string. + * + * @param szMessage String. + * @return No return + */ +stock CRemoveTags(String:szMessage[], maxlength) +{ + for (new i = 0; i < MAX_COLORS; i++) + ReplaceString(szMessage, maxlength, CTag[i], "", false); + + ReplaceString(szMessage, maxlength, "{teamcolor}", "", false); +} + +/** + * Checks whether a color is allowed or not + * + * @param tag Color Tag. + * @return True when color is supported, otherwise false + */ +stock CColorAllowed(Colors:color) +{ + if (!CEventIsHooked) + { + CSetupProfile(); + + CEventIsHooked = true; + } + + return CProfile_Colors[color]; +} + +/** + * Replace the color with another color + * Handle with care! + * + * @param color color to replace. + * @param newColor color to replace with. + * @noreturn + */ +stock CReplaceColor(Colors:color, Colors:newColor) +{ + if (!CEventIsHooked) + { + CSetupProfile(); + + CEventIsHooked = true; + } + + CProfile_Colors[color] = CProfile_Colors[newColor]; + CProfile_TeamIndex[color] = CProfile_TeamIndex[newColor]; + + CTagReqSayText2[color] = CTagReqSayText2[newColor]; + Format(CTagCode[color], sizeof(CTagCode[]), CTagCode[newColor]) +} + +/** + * This function should only be used right in front of + * CPrintToChatAll or CPrintToChatAllEx and it tells + * to those funcions to skip specified client when printing + * message to all clients. After message is printed client will + * no more be skipped. + * + * @param client Client index + * @return No return + */ +stock CSkipNextClient(client) +{ + if (client <= 0 || client > MaxClients) + ThrowError("Invalid client index %d", client); + + CSkipList[client] = true; +} + +/** + * Replaces color tags in a string with color codes + * + * @param szMessage String. + * @param maxlength Maximum length of the string buffer. + * @return Client index that can be used for SayText2 author index + * + * On error/Errors: If there is more then one team color is used an error will be thrown. + */ +stock CFormat(String:szMessage[], maxlength, author=NO_INDEX) +{ + decl String:szGameName[30]; + + GetGameFolderName(szGameName, sizeof(szGameName)); + + /* Hook event for auto profile setup on map start */ + if (!CEventIsHooked) + { + CSetupProfile(); + HookEvent("server_spawn", CEvent_MapStart, EventHookMode_PostNoCopy); + + CEventIsHooked = true; + } + + new iRandomPlayer = NO_INDEX; + + // On CS:GO set invisible precolor + if (StrEqual(szGameName, "csgo", false)) + Format(szMessage, maxlength, " \x01\x0B\x01%s", szMessage); + + /* If author was specified replace {teamcolor} tag */ + if (author != NO_INDEX) + { + if (CProfile_SayText2) + { + ReplaceString(szMessage, maxlength, "{teamcolor}", "\x03", false); + + iRandomPlayer = author; + } + /* If saytext2 is not supported by game replace {teamcolor} with green tag */ + else + ReplaceString(szMessage, maxlength, "{teamcolor}", CTagCode[Color_Green], false); + } + else + ReplaceString(szMessage, maxlength, "{teamcolor}", "", false); + + /* For other color tags we need a loop */ + for (new i = 0; i < MAX_COLORS; i++) + { + /* If tag not found - skip */ + if (StrContains(szMessage, CTag[i], false) == -1) + continue; + + /* If tag is not supported by game replace it with green tag */ + else if (!CProfile_Colors[i]) + ReplaceString(szMessage, maxlength, CTag[i], CTagCode[Color_Green], false); + + /* If tag doesn't need saytext2 simply replace */ + else if (!CTagReqSayText2[i]) + ReplaceString(szMessage, maxlength, CTag[i], CTagCode[i], false); + + /* Tag needs saytext2 */ + else + { + /* If saytext2 is not supported by game replace tag with green tag */ + if (!CProfile_SayText2) + ReplaceString(szMessage, maxlength, CTag[i], CTagCode[Color_Green], false); + + /* Game supports saytext2 */ + else + { + /* If random player for tag wasn't specified replace tag and find player */ + if (iRandomPlayer == NO_INDEX) + { + /* Searching for valid client for tag */ + iRandomPlayer = CFindRandomPlayerByTeam(CProfile_TeamIndex[i]); + + /* If player not found replace tag with green color tag */ + if (iRandomPlayer == NO_PLAYER) + ReplaceString(szMessage, maxlength, CTag[i], CTagCode[Color_Green], false); + + /* If player was found simply replace */ + else + ReplaceString(szMessage, maxlength, CTag[i], CTagCode[i], false); + + } + /* If found another team color tag throw error */ + else + { + //ReplaceString(szMessage, maxlength, CTag[i], ""); + ThrowError("Using two team colors in one message is not allowed"); + } + } + + } + } + + return iRandomPlayer; +} + +/** + * Founds a random player with specified team + * + * @param color_team Client team. + * @return Client index or NO_PLAYER if no player found + */ +stock CFindRandomPlayerByTeam(color_team) +{ + if (color_team == SERVER_INDEX) + return 0; + else + { + for (new i = 1; i <= MaxClients; i++) + { + if (IsClientInGame(i) && GetClientTeam(i) == color_team) + return i; + } + } + + return NO_PLAYER; +} + +/** + * Sends a SayText2 usermessage to a client + * + * @param szMessage Client index + * @param maxlength Author index + * @param szMessage Message + * @return No return. + */ +stock CSayText2(client, author, const String:szMessage[]) +{ + new Handle:hBuffer = StartMessageOne("SayText2", client, USERMSG_RELIABLE|USERMSG_BLOCKHOOKS); + + if(GetFeatureStatus(FeatureType_Native, "GetUserMessageType") == FeatureStatus_Available && GetUserMessageType() == UM_Protobuf) + { + PbSetInt(hBuffer, "ent_idx", author); + PbSetBool(hBuffer, "chat", true); + PbSetString(hBuffer, "msg_name", szMessage); + PbAddString(hBuffer, "params", ""); + PbAddString(hBuffer, "params", ""); + PbAddString(hBuffer, "params", ""); + PbAddString(hBuffer, "params", ""); + } + else + { + BfWriteByte(hBuffer, author); + BfWriteByte(hBuffer, true); + BfWriteString(hBuffer, szMessage); + } + + EndMessage(); +} + +/** + * Creates game color profile + * This function must be edited if you want to add more games support + * + * @return No return. + */ +stock CSetupProfile() +{ + decl String:szGameName[30]; + GetGameFolderName(szGameName, sizeof(szGameName)); + + if (StrEqual(szGameName, "cstrike", false)) + { + CProfile_Colors[Color_Lightgreen] = true; + CProfile_Colors[Color_Orange] = true; + CProfile_Colors[Color_Blue] = true; + CProfile_Colors[Color_Olive] = true; + CProfile_TeamIndex[Color_Lightgreen] = SERVER_INDEX; + CProfile_TeamIndex[Color_Orange] = 2; + CProfile_TeamIndex[Color_Blue] = 3; + CProfile_SayText2 = true; + } + else if (StrEqual(szGameName, "csgo", false)) + { + CProfile_Colors[Color_Red] = true; + CProfile_Colors[Color_Blue] = true; + CProfile_Colors[Color_Olive] = true; + CProfile_Colors[Color_Darkred] = true; + CProfile_Colors[Color_Lime] = true; + CProfile_Colors[Color_Purple] = true; + CProfile_Colors[Color_Grey] = true; + CProfile_Colors[Color_Yellow] = true; + CProfile_Colors[Color_Lightblue] = true; + CProfile_Colors[Color_Steelblue] = true; + CProfile_Colors[Color_Darkblue] = true; + CProfile_Colors[Color_Pink] = true; + CProfile_Colors[Color_Lightred] = true; + CProfile_TeamIndex[Color_Orange] = 2; + CProfile_TeamIndex[Color_Blue] = 3; + CProfile_SayText2 = true; + } + else if (StrEqual(szGameName, "tf", false)) + { + CProfile_Colors[Color_Lightgreen] = true; + CProfile_Colors[Color_Orange] = true; + CProfile_Colors[Color_Blue] = true; + CProfile_Colors[Color_Olive] = true; + CProfile_TeamIndex[Color_Lightgreen] = SERVER_INDEX; + CProfile_TeamIndex[Color_Orange] = 2; + CProfile_TeamIndex[Color_Blue] = 3; + CProfile_SayText2 = true; + } + else if (StrEqual(szGameName, "left4dead", false) || StrEqual(szGameName, "left4dead2", false)) + { + CProfile_Colors[Color_Lightgreen] = true; + CProfile_Colors[Color_Orange] = true; + CProfile_Colors[Color_Blue] = true; + CProfile_Colors[Color_Olive] = true; + CProfile_TeamIndex[Color_Lightgreen] = SERVER_INDEX; + CProfile_TeamIndex[Color_Orange] = 3; + CProfile_TeamIndex[Color_Blue] = 2; + CProfile_SayText2 = true; + } + else if (StrEqual(szGameName, "hl2mp", false)) + { + /* hl2mp profile is based on mp_teamplay convar */ + if (GetConVarBool(FindConVar("mp_teamplay"))) + { + CProfile_Colors[Color_Orange] = true; + CProfile_Colors[Color_Blue] = true; + CProfile_Colors[Color_Olive] = true; + CProfile_TeamIndex[Color_Orange] = 3; + CProfile_TeamIndex[Color_Blue] = 2; + CProfile_SayText2 = true; + } + else + { + CProfile_SayText2 = false; + CProfile_Colors[Color_Olive] = true; + } + } + else if (StrEqual(szGameName, "dod", false)) + { + CProfile_Colors[Color_Olive] = true; + CProfile_SayText2 = false; + } + /* Profile for other games */ + else + { + if (GetUserMessageId("SayText2") == INVALID_MESSAGE_ID) + { + CProfile_SayText2 = false; + } + else + { + CProfile_Colors[Color_Orange] = true; + CProfile_Colors[Color_Blue] = true; + CProfile_TeamIndex[Color_Orange] = 2; + CProfile_TeamIndex[Color_Blue] = 3; + CProfile_SayText2 = true; + } + } +} + +public Action:CEvent_MapStart(Handle:event, const String:name[], bool:dontBroadcast) +{ + CSetupProfile(); + + for (new i = 1; i <= MaxClients; i++) + CSkipList[i] = false; +} + +/** + * Displays usage of an admin command to users depending on the + * setting of the sm_show_activity cvar. + * + * This version does not display a message to the originating client + * if used from chat triggers or menus. If manual replies are used + * for these cases, then this function will suffice. Otherwise, + * CShowActivity2() is slightly more useful. + * Supports color tags. + * + * @param client Client index doing the action, or 0 for server. + * @param format Formatting rules. + * @param ... Variable number of format parameters. + * @noreturn + * @error + */ +stock CShowActivity(client, const String:format[], any:...) +{ + if (sm_show_activity == INVALID_HANDLE) + sm_show_activity = FindConVar("sm_show_activity"); + + new String:tag[] = "[SM] "; + + decl String:szBuffer[MAX_MESSAGE_LENGTH]; + //decl String:szCMessage[MAX_MESSAGE_LENGTH]; + new value = GetConVarInt(sm_show_activity); + new ReplySource:replyto = GetCmdReplySource(); + + new String:name[MAX_NAME_LENGTH] = "Console"; + new String:sign[MAX_NAME_LENGTH] = "ADMIN"; + new bool:display_in_chat = false; + if (client != 0) + { + if (client < 0 || client > MaxClients || !IsClientConnected(client)) + ThrowError("Client index %d is invalid", client); + + GetClientName(client, name, sizeof(name)); + new AdminId:id = GetUserAdmin(client); + if (id == INVALID_ADMIN_ID + || !GetAdminFlag(id, Admin_Generic, Access_Effective)) + { + sign = "PLAYER"; + } + + /* Display the message to the client? */ + if (replyto == SM_REPLY_TO_CONSOLE) + { + SetGlobalTransTarget(client); + VFormat(szBuffer, sizeof(szBuffer), format, 3); + + CRemoveTags(szBuffer, sizeof(szBuffer)); + PrintToConsole(client, "%s%s\n", tag, szBuffer); + display_in_chat = true; + } + } + else + { + SetGlobalTransTarget(LANG_SERVER); + VFormat(szBuffer, sizeof(szBuffer), format, 3); + + CRemoveTags(szBuffer, sizeof(szBuffer)); + PrintToServer("%s%s\n", tag, szBuffer); + } + + if (!value) + { + return 1; + } + + for (new i = 1; i <= MaxClients; i++) + { + if (!IsClientInGame(i) + || IsFakeClient(i) + || (display_in_chat && i == client)) + { + continue; + } + new AdminId:id = GetUserAdmin(i); + SetGlobalTransTarget(i); + if (id == INVALID_ADMIN_ID + || !GetAdminFlag(id, Admin_Generic, Access_Effective)) + { + /* Treat this as a normal user. */ + if ((value & 1) | (value & 2)) + { + new String:newsign[MAX_NAME_LENGTH]; + newsign = sign; + if ((value & 2) || (i == client)) + { + newsign = name; + } + VFormat(szBuffer, sizeof(szBuffer), format, 3); + + CPrintToChatEx(i, client, "%s%s: %s", tag, newsign, szBuffer); + } + } + else + { + /* Treat this as an admin user */ + new bool:is_root = GetAdminFlag(id, Admin_Root, Access_Effective); + if ((value & 4) + || (value & 8) + || ((value & 16) && is_root)) + { + new String:newsign[MAX_NAME_LENGTH] + newsign = sign; + if ((value & 8) || ((value & 16) && is_root) || (i == client)) + { + newsign = name; + } + VFormat(szBuffer, sizeof(szBuffer), format, 3); + + CPrintToChatEx(i, client, "%s%s: %s", tag, newsign, szBuffer); + } + } + } + + return 1; +} + +/** + * Same as CShowActivity(), except the tag parameter is used instead of "[SM] " (note that you must supply any spacing). + * Supports color tags. + * + * @param client Client index doing the action, or 0 for server. + * @param tags Tag to display with. + * @param format Formatting rules. + * @param ... Variable number of format parameters. + * @noreturn + * @error + */ +stock CShowActivityEx(client, const String:tag[], const String:format[], any:...) +{ + if (sm_show_activity == INVALID_HANDLE) + sm_show_activity = FindConVar("sm_show_activity"); + + decl String:szBuffer[MAX_MESSAGE_LENGTH]; + //decl String:szCMessage[MAX_MESSAGE_LENGTH]; + new value = GetConVarInt(sm_show_activity); + new ReplySource:replyto = GetCmdReplySource(); + + new String:name[MAX_NAME_LENGTH] = "Console"; + new String:sign[MAX_NAME_LENGTH] = "ADMIN"; + new bool:display_in_chat = false; + if (client != 0) + { + if (client < 0 || client > MaxClients || !IsClientConnected(client)) + ThrowError("Client index %d is invalid", client); + + GetClientName(client, name, sizeof(name)); + new AdminId:id = GetUserAdmin(client); + if (id == INVALID_ADMIN_ID + || !GetAdminFlag(id, Admin_Generic, Access_Effective)) + { + sign = "PLAYER"; + } + + /* Display the message to the client? */ + if (replyto == SM_REPLY_TO_CONSOLE) + { + SetGlobalTransTarget(client); + VFormat(szBuffer, sizeof(szBuffer), format, 4); + + CRemoveTags(szBuffer, sizeof(szBuffer)); + PrintToConsole(client, "%s%s\n", tag, szBuffer); + display_in_chat = true; + } + } + else + { + SetGlobalTransTarget(LANG_SERVER); + VFormat(szBuffer, sizeof(szBuffer), format, 4); + + CRemoveTags(szBuffer, sizeof(szBuffer)); + PrintToServer("%s%s\n", tag, szBuffer); + } + + if (!value) + { + return 1; + } + + for (new i = 1; i <= MaxClients; i++) + { + if (!IsClientInGame(i) + || IsFakeClient(i) + || (display_in_chat && i == client)) + { + continue; + } + new AdminId:id = GetUserAdmin(i); + SetGlobalTransTarget(i); + if (id == INVALID_ADMIN_ID + || !GetAdminFlag(id, Admin_Generic, Access_Effective)) + { + /* Treat this as a normal user. */ + if ((value & 1) | (value & 2)) + { + new String:newsign[MAX_NAME_LENGTH]; + newsign = sign; + if ((value & 2) || (i == client)) + { + newsign = name; + } + VFormat(szBuffer, sizeof(szBuffer), format, 4); + + CPrintToChatEx(i, client, "%s%s: %s", tag, newsign, szBuffer); + } + } + else + { + /* Treat this as an admin user */ + new bool:is_root = GetAdminFlag(id, Admin_Root, Access_Effective); + if ((value & 4) + || (value & 8) + || ((value & 16) && is_root)) + { + new String:newsign[MAX_NAME_LENGTH]; + newsign = sign; + if ((value & 8) || ((value & 16) && is_root) || (i == client)) + { + newsign = name; + } + VFormat(szBuffer, sizeof(szBuffer), format, 4); + + CPrintToChatEx(i, client, "%s%s: %s", tag, newsign, szBuffer); + } + } + } + + return 1; +} + +/** + * Displays usage of an admin command to users depending on the setting of the sm_show_activity cvar. + * All users receive a message in their chat text, except for the originating client, + * who receives the message based on the current ReplySource. + * Supports color tags. + * + * @param client Client index doing the action, or 0 for server. + * @param tags Tag to prepend to the message. + * @param format Formatting rules. + * @param ... Variable number of format parameters. + * @noreturn + * @error + */ +stock CShowActivity2(client, const String:tag[], const String:format[], any:...) +{ + if (sm_show_activity == INVALID_HANDLE) + sm_show_activity = FindConVar("sm_show_activity"); + + decl String:szBuffer[MAX_MESSAGE_LENGTH]; + //decl String:szCMessage[MAX_MESSAGE_LENGTH]; + new value = GetConVarInt(sm_show_activity); + GetCmdReplySource(); + + new String:name[MAX_NAME_LENGTH] = "Console"; + new String:sign[MAX_NAME_LENGTH] = "ADMIN"; + if (client != 0) + { + if (client < 0 || client > MaxClients || !IsClientConnected(client)) + ThrowError("Client index %d is invalid", client); + + GetClientName(client, name, sizeof(name)); + new AdminId:id = GetUserAdmin(client); + if (id == INVALID_ADMIN_ID + || !GetAdminFlag(id, Admin_Generic, Access_Effective)) + { + sign = "PLAYER"; + } + + SetGlobalTransTarget(client); + VFormat(szBuffer, sizeof(szBuffer), format, 4); + + /* We don't display directly to the console because the chat text + * simply gets added to the console, so we don't want it to print + * twice. + */ + CPrintToChatEx(client, client, "%s%s", tag, szBuffer); + } + else + { + SetGlobalTransTarget(LANG_SERVER); + VFormat(szBuffer, sizeof(szBuffer), format, 4); + + CRemoveTags(szBuffer, sizeof(szBuffer)); + PrintToServer("%s%s\n", tag, szBuffer); + } + + if (!value) + { + return 1; + } + + for (new i = 1; i <= MaxClients; i++) + { + if (!IsClientInGame(i) + || IsFakeClient(i) + || i == client) + { + continue; + } + new AdminId:id = GetUserAdmin(i); + SetGlobalTransTarget(i); + if (id == INVALID_ADMIN_ID + || !GetAdminFlag(id, Admin_Generic, Access_Effective)) + { + /* Treat this as a normal user. */ + if ((value & 1) | (value & 2)) + { + new String:newsign[MAX_NAME_LENGTH]; + newsign = sign; + if ((value & 2)) + { + newsign = name; + } + VFormat(szBuffer, sizeof(szBuffer), format, 4); + + CPrintToChatEx(i, client, "%s%s: %s", tag, newsign, szBuffer); + } + } + else + { + /* Treat this as an admin user */ + new bool:is_root = GetAdminFlag(id, Admin_Root, Access_Effective); + if ((value & 4) + || (value & 8) + || ((value & 16) && is_root)) + { + new String:newsign[MAX_NAME_LENGTH]; + newsign = sign; + if ((value & 8) || ((value & 16) && is_root)) + { + newsign = name; + } + VFormat(szBuffer, sizeof(szBuffer), format, 4); + + CPrintToChatEx(i, client, "%s%s: %s", tag, newsign, szBuffer); + } + } + } + + return 1; +}
\ No newline at end of file diff --git a/sourcemod/scripting/include/distbugfix.inc b/sourcemod/scripting/include/distbugfix.inc new file mode 100644 index 0000000..6a7ed18 --- /dev/null +++ b/sourcemod/scripting/include/distbugfix.inc @@ -0,0 +1,261 @@ + +#if defined _distbug_included + #endinput +#endif +#define _distbug_included + +#define DISTBUG_CONFIG_NAME "distbugfix" + +#define CHAT_PREFIX "{d}[{l}GC{d}]" +#define CONSOLE_PREFIX "[GC]" +#define CHAT_SPACER " {d}|{g} " +#define DISTBUG_VERSION "2.0.0" + +#define MAX_STRAFES 32 +#define MAX_EDGE 32.0 +#define MAX_JUMP_FRAMES 150 // for frame based arrays +#define MAX_BHOP_FRAMES 8 + +#define MAX_COOKIE_SIZE 32 + +enum +{ + SETTINGS_DISTBUG_ENABLED = (1 << 0), + SETTINGS_SHOW_VEER_BEAM = (1 << 1), + SETTINGS_SHOW_JUMP_BEAM = (1 << 2), + SETTINGS_SHOW_HUD_GRAPH = (1 << 3), + SETTINGS_DISABLE_STRAFE_STATS = (1 << 4), + SETTINGS_DISABLE_STRAFE_GRAPH = (1 << 5), + SETTINGS_ADV_CHAT_STATS = (1 << 6), +} + +enum JumpType +{ + // unprintable jumptypes. only for tracking + JUMPTYPE_NONE, + + JUMPTYPE_LJ, // longjump + JUMPTYPE_WJ, // weirdjump + JUMPTYPE_LAJ, // ladderjump + JUMPTYPE_BH, // bunnyhop + JUMPTYPE_CBH, // crouched bunnyhop +}; + +enum JumpDir +{ + JUMPDIR_FORWARDS, + JUMPDIR_BACKWARDS, + JUMPDIR_LEFT, + JUMPDIR_RIGHT, +}; + +enum StrafeType +{ + STRAFETYPE_OVERLAP, // IN_MOVELEFT and IN_MOVERIGHT are overlapping and sidespeed is 0 + STRAFETYPE_NONE, // IN_MOVELEFT and IN_MOVERIGHT are both not pressed and sidespeed is 0 + + STRAFETYPE_LEFT, // only IN_MOVELEFT is down and sidespeed isn't 0. + STRAFETYPE_OVERLAP_LEFT, // IN_MOVELEFT and IN_MOVERIGHT are overlapping, but sidespeed is smaller than 0 (not 0) + STRAFETYPE_NONE_LEFT, // IN_MOVELEFT and IN_MOVERIGHT are both not pressed and sidespeed is smaller than 0 (not 0) + + STRAFETYPE_RIGHT, // only IN_MOVERIGHT is down and sidespeed isn't 0. + STRAFETYPE_OVERLAP_RIGHT, // IN_MOVELEFT and IN_MOVERIGHT are overlapping, but sidespeed is bigger than 0 (not 0) + STRAFETYPE_NONE_RIGHT, // IN_MOVELEFT and IN_MOVERIGHT are both not pressed and sidespeed is bigger than 0 (not 0) +}; + +enum JumpBeamColour +{ + JUMPBEAM_NEUTRAL, // speed stays the same + JUMPBEAM_LOSS, // speed loss + JUMPBEAM_GAIN, // speed gain + JUMPBEAM_DUCK, // duck key down +}; + +enum struct PlayerData +{ + int tickCount; + int buttons; + int lastButtons; + int flags; + int lastFlags; + int framesOnGround; + int framesInAir; + MoveType movetype; + MoveType lastMovetype; + float stamina; + float lastStamina; + float forwardmove; + float lastForwardmove; + float sidemove; + float lastSidemove; + float gravity; + float angles[3]; + float lastAngles[3]; + float position[3]; + float lastPosition[3]; + float velocity[3]; + float lastVelocity[3]; + float lastGroundPos[3]; // last position where the player left the ground. + float ladderNormal[3]; + bool lastGroundPosWalkedOff; + bool landedDucked; + + int prespeedFog; + float prespeedStamina; + + float jumpGroundZ; + float jumpPos[3]; + float jumpAngles[3]; + float landGroundZ; + float landPos[3]; + + int fwdReleaseFrame; + int jumpFrame; + bool trackingJump; + bool failedJump; + bool jumpGotFailstats; + + // jump data + JumpType jumpType; + JumpType lastJumpType; + JumpDir jumpDir; + float jumpDistance; + float jumpPrespeed; + float jumpMaxspeed; + float jumpVeer; + float jumpAirpath; + float jumpSync; + float jumpEdge; + float jumpLandEdge; + float jumpBlockDist; + float jumpHeight; + float jumpJumpoffAngle; + int jumpAirtime; + int jumpFwdRelease; + int jumpOverlap; + int jumpDeadair; + + // strafes! + int strafeCount; + float strafeSync[MAX_STRAFES]; + float strafeGain[MAX_STRAFES]; + float strafeLoss[MAX_STRAFES]; + float strafeMax[MAX_STRAFES]; + int strafeAirtime[MAX_STRAFES]; + int strafeOverlap[MAX_STRAFES]; + int strafeDeadair[MAX_STRAFES]; + float strafeAvgGain[MAX_STRAFES]; + + float strafeAvgEfficiency[MAX_STRAFES]; + int strafeAvgEfficiencyCount[MAX_STRAFES]; // how many samples are in strafeAvgEfficiency + float strafeMaxEfficiency[MAX_STRAFES]; + + StrafeType strafeGraph[MAX_JUMP_FRAMES]; + float mouseGraph[MAX_JUMP_FRAMES]; + float jumpBeamX[MAX_JUMP_FRAMES]; + float jumpBeamY[MAX_JUMP_FRAMES]; + JumpBeamColour jumpBeamColour[MAX_JUMP_FRAMES]; +} + +/** + * Check if player is overlapping their MOVERIGHT and MOVELEFT buttons. + * + * @param x Buttons; + * @return True if overlapping, false otherwise. + */ +stock bool IsOverlapping(int buttons, JumpDir jumpDir) +{ + if (jumpDir == JUMPDIR_FORWARDS || jumpDir == JUMPDIR_BACKWARDS) + { + return (buttons & IN_MOVERIGHT) && (buttons & IN_MOVELEFT); + } + // else if (jumpDir == JUMPDIR_LEFT || jumpDir == JUMPDIR_RIGHT) + return (buttons & IN_FORWARD) && (buttons & IN_BACK); +} + +/** + * Checks if the player is not holding down their MOVERIGHT and MOVELEFT buttons. + * + * @param x Buttons. + * @return True if they're not holding either, false otherwise. + */ +stock bool IsDeadAirtime(int buttons, JumpDir jumpDir) +{ + if (jumpDir == JUMPDIR_FORWARDS || jumpDir == JUMPDIR_BACKWARDS) + { + return (!(buttons & IN_MOVERIGHT) && !(buttons & IN_MOVELEFT)); + } + // else if (jumpDir == JUMPDIR_LEFT || jumpDir == JUMPDIR_RIGHT) + return (!(buttons & IN_FORWARD) && !(buttons & IN_BACK)); +} + +stock void ToggleCVar(ConVar cvar) +{ + cvar.BoolValue = !cvar.BoolValue; +} + +stock void GetRealLandingOrigin(float landGroundZ, const float origin[3], const float velocity[3], float result[3]) +{ + if ((origin[2] - landGroundZ) == 0.0) + { + result = origin; + return; + } + + // this is like this because it works + float frametime = GetTickInterval(); + float verticalDistance = origin[2] - (origin[2] + velocity[2] * frametime); + float fraction = (origin[2] - landGroundZ) / verticalDistance; + + float addDistance[3]; + addDistance = velocity; + ScaleVector(addDistance, frametime * fraction); + + AddVectors(origin, addDistance, result); +} + +stock int FloatSign(float value) +{ + if (value > 0.0) + { + return 1; + } + else if (value < 0.0) + { + return -1; + } + return 0; +} + +stock int FloatSign2(float value) +{ + if (value >= 0.0) + { + return 1; + } + // else if (value < 0.0) + return -1; +} + +stock void ShowPanel(int client, int duration, const char[] message) +{ + Event show_survival_respawn_status = CreateEvent("show_survival_respawn_status"); + if (show_survival_respawn_status == INVALID_HANDLE) + { + return; + } + + show_survival_respawn_status.SetString("loc_token", message); + show_survival_respawn_status.SetInt("duration", duration); + show_survival_respawn_status.SetInt("userid", -1); + + if (client == -1) + { + show_survival_respawn_status.Fire(); + } + else + { + show_survival_respawn_status.FireToClient(client); + show_survival_respawn_status.Cancel(); + } +} diff --git a/sourcemod/scripting/include/gamechaos.inc b/sourcemod/scripting/include/gamechaos.inc new file mode 100644 index 0000000..eeaf040 --- /dev/null +++ b/sourcemod/scripting/include/gamechaos.inc @@ -0,0 +1,20 @@ + +// GameChaos's includes for various specific stuff + +#if defined _gamechaos_stocks_included + #endinput +#endif +#define _gamechaos_stocks_included + +#define GC_INCLUDES_VERSION 0x01_00_00 +#define GC_INCLUDES_VERSION_STRING "1.0.0" + +#include <gamechaos/arrays> +#include <gamechaos/client> +#include <gamechaos/debug> +#include <gamechaos/maths> +#include <gamechaos/misc> +#include <gamechaos/strings> +#include <gamechaos/tempents> +#include <gamechaos/tracing> +#include <gamechaos/vectors>
\ No newline at end of file diff --git a/sourcemod/scripting/include/gamechaos/arrays.inc b/sourcemod/scripting/include/gamechaos/arrays.inc new file mode 100644 index 0000000..eba62bb --- /dev/null +++ b/sourcemod/scripting/include/gamechaos/arrays.inc @@ -0,0 +1,52 @@ + +#if defined _gamechaos_stocks_arrays_included + #endinput +#endif +#define _gamechaos_stocks_arrays_included + +#define GC_ARRAYS_VERSION 0x01_00_00 +#define GC_ARRAYS_VERSION_STRING "1.0.0" + +/** + * Copies an array into an arraylist. + * + * @param array Arraylist Handle. + * @param index Index in the arraylist. + * @param values Array to copy. + * @param size Size of the array to copy. + * @param offset Arraylist offset to set. + * @return Number of cells copied. + * @error Invalid Handle or invalid index. + */ +stock int GCSetArrayArrayIndexOffset(ArrayList array, int index, const any[] values, int size, int offset) +{ + int cells; + for (int i; i < size; i++) + { + array.Set(index, values[i], offset + i); + cells++; + } + return cells; +} + +/** + * Copies an arraylist's specified cells to an array. + * + * @param array Arraylist Handle. + * @param index Index in the arraylist. + * @param result Array to copy to. + * @param size Size of the array to copy to. + * @param offset Arraylist offset. + * @return Number of cells copied. + * @error Invalid Handle or invalid index. + */ +stock int GCCopyArrayArrayIndex(const ArrayList array, int index, any[] result, int size, int offset) +{ + int cells; + for (int i = offset; i < (size + offset); i++) + { + result[i] = array.Get(index, i); + cells++; + } + return cells; +}
\ No newline at end of file diff --git a/sourcemod/scripting/include/gamechaos/client.inc b/sourcemod/scripting/include/gamechaos/client.inc new file mode 100644 index 0000000..cb2114a --- /dev/null +++ b/sourcemod/scripting/include/gamechaos/client.inc @@ -0,0 +1,300 @@ + +#if defined _gamechaos_stocks_client_included + #endinput +#endif +#define _gamechaos_stocks_client_included + +#define GC_CLIENT_VERSION 0x01_00_00 +#define GC_CLIENT_VERSION_STRING "1.0.0" + +/** + * Credit: Don't remember. + * Removes a player's weapon from the specified slot. + * + * @param client Client index. + * @param slot Weapon slot. + * @return True if removed, false otherwise. + */ +stock bool GCRemoveWeaponBySlot(int client, int slot) +{ + int entity = GetPlayerWeaponSlot(client, slot); + if (IsValidEdict(entity)) + { + RemovePlayerItem(client, entity); + AcceptEntityInput(entity, "kill"); + return true; + } + return false; +} + +/** + * Checks if a client is valid and not the server and optionally, whether he's alive. + * + * @param client Client index. + * @param alive Whether to check alive. + * @return True if valid, false otherwise. + */ +stock bool GCIsValidClient(int client, bool alive = false) +{ + return (client >= 1 && client <= MaxClients && IsClientConnected(client) && IsClientInGame(client) && !IsClientSourceTV(client) && (!alive || IsPlayerAlive(client))); +} + + + +/** + * Gets the value of m_flForwardMove. + * + * @param client Client index. + * @return Value of m_flForwardMove. + */ +stock float GCGetClientForwardMove(int client) +{ + return GetEntPropFloat(client, Prop_Data, "m_flForwardMove"); +} + +/** + * Gets the value of m_flSideMove. + * + * @param client Client index. + * @return Value of m_flSideMove. + */ +stock float GCGetClientSideMove(int client) +{ + return GetEntPropFloat(client, Prop_Data, "m_flSideMove"); +} + +/** + * Gets the client's abs origin. + * + * @param client Client index. + * @return result Player's origin. + */ +stock float[] GCGetClientAbsOriginRet(int client) +{ + float result[3] + GetClientAbsOrigin(client, result); + return result; +} + +/** + * Copies the client's velocity to a vector. + * + * @param client Client index. + * @param result Resultant vector. + */ +stock void GCGetClientVelocity(int client, float result[3]) +{ + GetEntPropVector(client, Prop_Data, "m_vecVelocity", result); +} + +/** + * Gets the client's velocity (m_vecVelocity). + * + * @param client Client index. + * @return result m_vecVelocity. + */ +stock float[] GCGetClientVelocityRet(int client) +{ + float result[3] + GetEntPropVector(client, Prop_Data, "m_vecVelocity", result); + return result +} + +/** + * Copies the client's basevelocity to a vector. + * + * @param client Client index. + * @param result Resultant vector. + */ +stock void GCGetClientBaseVelocity(int client, float result[3]) +{ + GetEntPropVector(client, Prop_Data, "m_vecBaseVelocity", result); +} + +/** + * Gets the client's basevelocity (m_vecBaseVelocity). + * + * @param client Client index. + * @return result m_vecBaseVelocity. + */ +stock float[] GCGetClientBaseVelocityRet(int client) +{ + float result[3]; + GetEntPropVector(client, Prop_Data, "m_vecBaseVelocity", result); + return result; +} + + +/** + * Gets the client's "m_flDuckSpeed" value. + * + * @param client Client index. + * @return "m_flDuckSpeed". + */ +stock float GCGetClientDuckSpeed(int client) +{ + return GetEntPropFloat(client, Prop_Send, "m_flDuckSpeed"); +} + +/** + * Gets the client's "m_flDuckAmount" value. + * + * @param client Client index. + * @return "m_flDuckAmount". + */ +stock float GCGetClientDuckAmount(int client) +{ + return GetEntPropFloat(client, Prop_Send, "m_flDuckAmount"); +} + +/** + * Gets the client's "m_bDucking" value. + * + * @param client Client index. + * @return "m_bDucking". + */ +stock int GCGetClientDucking(int client) +{ + return GetEntProp(client, Prop_Data, "m_bDucking"); +} + +/** + * Gets the client's "m_flMaxspeed" value. + * + * @param client Client index. + * @return "m_flMaxspeed". + */ +stock float GCGetClientMaxspeed(int client) +{ + return GetEntPropFloat(client, Prop_Send, "m_flMaxspeed"); +} + +/** + * Gets the client's "m_afButtonPressed" value. + * + * @param client Client index. + * @return "m_afButtonPressed". + */ +stock int GCGetClientButtonPressed(int client) +{ + return GetEntProp(client, Prop_Data, "m_afButtonPressed"); +} + +/** + * Gets the client's "m_afButtonReleased" value. + * + * @param client Client index. + * @return "m_afButtonReleased". + */ +stock int GCGetClientButtonReleased(int client) +{ + return GetEntProp(client, Prop_Data, "m_afButtonReleased"); +} + +/** + * Gets the client's "m_afButtonLast" value. + * + * @param client Client index. + * @return "m_afButtonLast". + */ +stock int GCGetClientButtonLast(int client) +{ + return GetEntProp(client, Prop_Data, "m_afButtonLast"); +} + +/** + * Gets the client's "m_afButtonForced" value. + * + * @param client Client index. + * @return "m_afButtonForced". + */ +stock int GCGetClientForcedButtons(int client) +{ + return GetEntProp(client, Prop_Data, "m_afButtonForced"); +} + +/** + * Gets the client's "m_flStamina" value. + * + * @param client Client index. + * @return "m_flStamina". + */ +stock float GCGetClientStamina(int client) +{ + return GetEntPropFloat(client, Prop_Send, "m_flStamina"); +} + + + +/** + * Sets the client's origin. + * + * @param client Client index. + * @param origin New origin. + */ +stock void GCSetClientAbsOrigin(int client, const float origin[3]) +{ + SetEntPropVector(client, Prop_Data, "m_vecAbsOrigin", origin); +} + +/** + * Sets the client's velocity. + * + * @param client Client index. + * @param velocity New velocity. + */ +stock void GCSetClientVelocity(int client, const float velocity[3]) +{ + SetEntPropVector(client, Prop_Data, "m_vecVelocity", velocity); +} + +/** + * Sets the client's "m_vecAbsVelocity". + * + * @param client Client index. + * @param velocity New "m_vecAbsVelocity". + */ +stock void GCSetClientAbsVelocity(int client, const float velocity[3]) +{ + SetEntPropVector(client, Prop_Data, "m_vecAbsVelocity", velocity); +} + +/** + * Sets the client's eye angles. + * Ang has to be a 2 member array or more + * + * @param client Client index. + * @param ang New eyeangles. + */ +stock void GCSetClientEyeAngles(int client, const float[] ang) +{ + SetEntPropFloat(client, Prop_Send, "m_angEyeAngles[0]", ang[0]); + SetEntPropFloat(client, Prop_Send, "m_angEyeAngles[1]", ang[1]); +} + + +/** + * Sets the client's "m_flDuckSpeed". + * + * @param client Client index. + * @param value New "m_flDuckSpeed". + */ +stock void GCSetClientDuckSpeed(int client, float value) +{ + SetEntPropFloat(client, Prop_Send, "m_flDuckSpeed", value); +} + +stock void GCSetClientDuckAmount(int client, float value) +{ + SetEntPropFloat(client, Prop_Send, "m_flDuckAmount", value); +} + +stock void GCSetClientForcedButtons(int client, int buttons) +{ + SetEntProp(client, Prop_Data, "m_afButtonForced", buttons); +} + +stock void GCSetClientStamina(int client, float stamina) +{ + SetEntPropFloat(client, Prop_Send, "m_flStamina", stamina) +}
\ No newline at end of file diff --git a/sourcemod/scripting/include/gamechaos/debug.inc b/sourcemod/scripting/include/gamechaos/debug.inc new file mode 100644 index 0000000..4e5b7e7 --- /dev/null +++ b/sourcemod/scripting/include/gamechaos/debug.inc @@ -0,0 +1,19 @@ + +// gamechaos's debug stocks +// useful stocks for debugging + +#if defined _gamechaos_debug_included + #endinput +#endif +#define _gamechaos_debug_included + +#define GC_DEBUG_VERSION 0x1_00_00 +#define GC_DEBUG_VERSION_STRING "1.0.0" + +#if defined GC_DEBUG + #define GC_ASSERT(%1) if (!(%1))SetFailState("Assertion failed: \""...#%1..."\"") + #define GC_DEBUGPRINT(%1) PrintToChatAll(%1) +#else + #define GC_ASSERT(%1)%2; + #define GC_DEBUGPRINT(%1)%2; +#endif diff --git a/sourcemod/scripting/include/gamechaos/isvalidclient.inc b/sourcemod/scripting/include/gamechaos/isvalidclient.inc new file mode 100644 index 0000000..bf80246 --- /dev/null +++ b/sourcemod/scripting/include/gamechaos/isvalidclient.inc @@ -0,0 +1,16 @@ + +#if defined _gamechaos_isvalidclient_client_included + #endinput +#endif +#define _gamechaos_isvalidclient_client_included + +/** + * Checks if a client is valid. + * + * @param client Client index. + * @return True if valid, false otherwise. + */ +stock bool IsValidClient(int client) +{ + return (client >= 0 && client <= MaxClients && IsValidEntity(client) && IsClientConnected(client) && IsClientInGame(client)); +}
\ No newline at end of file diff --git a/sourcemod/scripting/include/gamechaos/kreedzclimbing.inc b/sourcemod/scripting/include/gamechaos/kreedzclimbing.inc new file mode 100644 index 0000000..0cbb828 --- /dev/null +++ b/sourcemod/scripting/include/gamechaos/kreedzclimbing.inc @@ -0,0 +1,226 @@ +// +// Useful things for making plugins for Kreedz Climbing +// + +#if defined _gamechaos_kreedzclimbing_included + #endinput +#endif +#define _gamechaos_kreedzclimbing_included + +#define GC_KREEDZCLIMBING_VERSION 0x01_00_00 +#define GC_KREEDZCLIMBING_VERSION_STRING "1.0.0" + + +#define MAX_COURSE_SIZE 128 // Reasonable maximum characters a course name can have +#define COURSE_CVAR_COUNT 20 // the amount of Course<int> cvars + +// Kreedz Climbing Client Commands: +// These may be executed by a player via the console, with / in chat, or via binds. + +// specmode - Cycles spectator mode (F3 by default). +// kz_pause - Pauses the timer. +// flare - Fires a flare. +// gototimer | start - Returns to the last pressed start timer. +// spectate | spec - Enters spectator mode. +// forcespectator - Becomes a spectator no matter what (force respawns a dead player as well). +// stoptimer - Instantly stops the player's timer. +// climb | ct - Respawns at the map spawnpoint. +// InvalidateTimer - Invalidates the player's timer. An invalid timer can't earn rewards for completing the course. InvalidateTimer 1 displays the message, without the 1 it does not. + +// Kreedz Climbing Constants + +// Timer state (player->m_Local->Timer_Active) +#define TIMER_STATE_INVISIBLE 0 +#define TIMER_STATE_ACTIVE 1 +#define TIMER_STATE_INACTIVE 2 +#define TIMER_STATE_PAUSED 3 + +// Timer flags (player_manager->m_iTimerFlags[32]) +// These are replicated flags for player's timer (most timer data is local to it's own player). +// Note that these flags are mirrors of data local to the player - they are set to the player's +// state every frame and cannot be changed. + +#define TIMER_FLAG_INVALID (1 << 0) +#define TIMER_FLAG_ACTIVE (1 << 1) // We need to broadcast this because Timer_State is local only. +#define TIMER_FLAG_PAUSED (1 << 2) // A paused timer cannot be active and vice versa. + +// Environmental Attributes (player->m_iEnvironmentalAttributes) +#define PLAYER_ENV_ATTRIBUTES_BHOP (1 << 0) +#define PLAYER_ENV_ATTRIBUTES_SURF (1 << 1) +#define PLAYER_ENV_ATTRIBUTES_AUTOBHOP (1 << 2) +#define PLAYER_ENV_ATTRIBUTES_CSGOMOVEMENT (1 << 3) +#define PLAYER_ENV_ATTRIBUTES_CSGODUCKHULL (1 << 4) + +// Movement restriction flags (player->m_iMovementRestrictions) (new version of Environmental Restrictions below) +#define PLAYER_MOVEMENT_RESTRICTION_NOJUMP (1 << 0) +#define PLAYER_MOVEMENT_RESTRICTION_NOBHOP (1 << 1) +#define PLAYER_MOVEMENT_RESTRICTION_NODOUBLEDUCK (1 << 2) + +// OBSOLETE: ONLY IN OLD MAPS: Environmental Restrictions (player->m_iEnvironmentalRestrictions), note not flags, complete integer. +#define PLAYER_ENV_RESTRICTION_NOJUMP 1 +#define PLAYER_ENV_RESTRICTION_NOBHOP 2 +#define PLAYER_ENV_RESTRICTION_BOTH 3 + +// Cooperative status (player->m_Local.m_multiplayercoursedata.Player1Status, Player2Status etc) +#define COOPERATIVE_STATUS_NONE 0 +#define COOPERATIVE_STATUS_WAITING 1 +#define COOPERATIVE_STATUS_READY 2 +#define COOPERATIVE_STATUS_TIMER_ACTIVE 3 +#define COOPERATIVE_STATUS_TIMER_COMPLETE 4 +#define COOPERATIVE_STATUS_PLAYER_DISCONNECTED 5 // Player disconnected from server, waiting for them to reconnect. +#define COOPERATIVE_STATUS_TIMER_PAUSED 6 + +// Kreedz Climbing Button Constants +#define IN_CHECKPOINT (1 << 25) +#define IN_TELEPORT (1 << 26) +#define IN_SPECTATE (1 << 27) +//#define IN_AVAILABLE (1 << 28) // Unused +#define IN_HOOK (1 << 29) + +// converts the course id from the obsolete "player_starttimer" event into the course name +stock void GCCourseidToString(int courseid, char[] course, int size) +{ + char szCourseid[16]; + if (courseid < 1 || courseid > COURSE_CVAR_COUNT) + { + return; + } + FormatEx(szCourseid, sizeof(szCourseid), "Course%i", courseid); + FindConVar(szCourseid).GetString(course, size); +} + +stock void GCGetCurrentMapCourses(ArrayList &array) +{ + if (array == null) + { + // 1 for endurance bool + array = new ArrayList(ByteCountToCells(MAX_COURSE_SIZE) + 1); + } + else + { + array.Clear(); + } + + char course[MAX_COURSE_SIZE]; + + int ent; + while((ent = FindEntityByClassname(ent, "func_stoptimer")) != -1) + { + int courseid = GetEntProp(ent, Prop_Data, "CourseID"); + GCCourseidToString(courseid, course, sizeof(course)); + array.PushString(course); + + bool endurance = GCIsCourseEndurance(course, ent); + array.Set(array.Length - 1, endurance, ByteCountToCells(MAX_COURSE_SIZE)); + } + + int courseStringtableCount; + int courseNamesIdx = FindStringTable("CourseNames"); + courseStringtableCount = GetStringTableNumStrings(courseNamesIdx); + + for (int i; i < courseStringtableCount; i++) + { + ReadStringTable(courseNamesIdx, i, course, sizeof(course)); + array.PushString(course); + + bool endurance = GCIsCourseEndurance(course, ent); + array.Set(array.Length - 1, endurance, ByteCountToCells(MAX_COURSE_SIZE)); + } +} + +stock int GCGetTimerState(int client) +{ + return GetEntProp(client, Prop_Send, "Timer_Active"); +} + +stock void GCSetTimerState(int client, int timerstate) +{ + SetEntProp(client, Prop_Send, "Timer_Active", timerstate); +} + +stock int GCGetPlayerEnvAttributes(int client) +{ + return GetEntProp(client, Prop_Send, "m_iEnvironmentalAttributes"); +} + +stock void GCSetPlayerEnvAttributes(int client, int attributes) +{ + SetEntProp(client, Prop_Send, "m_iEnvironmentalAttributes", attributes); +} + +stock int GCGetPlayerMovementRestrictions(int client) +{ + return GetEntProp(client, Prop_Send, "m_iMovementRestrictions"); +} + +stock void GCSetPlayerMovementRestrictions(int client, int restrictions) +{ + SetEntProp(client, Prop_Send, "m_iMovementRestrictions", restrictions); +} + +stock void GCSetActiveCourse(int client, int course) +{ + int ent = FindEntityByClassname(0, "player_manager"); + int courseOffset = FindSendPropInfo("CPlayerResource", "m_iActiveCourse"); + SetEntData(ent, courseOffset + (client * 4), course); +} + +stock int GCGetTimerFlags(int client) +{ + int ent = FindEntityByClassname(0, "player_manager"); + int courseOffset = FindSendPropInfo("CPlayerResource", "m_iTimerFlags"); + return GetEntData(ent, courseOffset + (client * 4)); +} + +stock bool GCInvalidateTimer(int client) +{ + if (~GCGetTimerFlags(client) & TIMER_FLAG_INVALID) + { + FakeClientCommand(client, "InvalidateTimer 1"); + return true; + } + + return false; +} + +stock bool GCIsCourseEndurance(char[] course, int ent = -1) +{ + if (ent != -1) + { + if (IsValidEntity(ent)) + { + return !!(GetEntProp(ent, Prop_Data, "m_bEnduranceCourse")); + } + } + + while ((ent = FindEntityByClassname(ent, "point_climbtimer")) != -1) + { + if (IsValidEntity(ent)) + { + char buffer[MAX_COURSE_SIZE]; + GetEntPropString(ent, Prop_Data, "m_strCourseName", buffer, sizeof(buffer)); + + if (StrEqual(buffer, course)) + { + return !!(GetEntProp(ent, Prop_Data, "m_bEnduranceCourse")); + } + } + } + + while ((ent = FindEntityByClassname(ent, "func_stoptimer")) != -1) + { + if (IsValidEntity(ent)) + { + char buffer[MAX_COURSE_SIZE]; + int courseid = GetEntProp(ent, Prop_Data, "CourseID"); + GCCourseidToString(courseid, buffer, sizeof(buffer)); + + if (StrEqual(buffer, course)) + { + return !!(GetEntProp(ent, Prop_Data, "m_bEnduranceCourse")); + } + } + } + + return false; +}
\ No newline at end of file diff --git a/sourcemod/scripting/include/gamechaos/maths.inc b/sourcemod/scripting/include/gamechaos/maths.inc new file mode 100644 index 0000000..f3c94af --- /dev/null +++ b/sourcemod/scripting/include/gamechaos/maths.inc @@ -0,0 +1,362 @@ + +#if defined _gamechaos_stocks_maths_included + #endinput +#endif +#define _gamechaos_stocks_maths_included + +#define GC_MATHS_VERSION 0x02_00_00 +#define GC_MATHS_VERSION_STRING "2.0.0" + +#include <gamechaos/vectors> + +#define GC_PI 3.14159265359 + +#define GC_DEGREES(%1) ((%1) * 180.0 / GC_PI) // convert radians to degrees +#define GC_RADIANS(%1) ((%1) * GC_PI / 180.0) // convert degrees to radians + +#define GC_FLOAT_NAN view_as<float>(0xffffffff) +#define GC_FLOAT_INFINITY view_as<float>(0x7f800000) +#define GC_FLOAT_NEGATIVE_INFINITY view_as<float>(0xff800000) + +#define GC_FLOAT_LARGEST_POSITIVE view_as<float>(0x7f7fffff) +#define GC_FLOAT_SMALLEST_NEGATIVE view_as<float>(0xff7fffff) + +#define GC_FLOAT_SMALLEST_POSITIVE view_as<float>(0x00000001) +#define GC_FLOAT_LARGEST_NEGATIVE view_as<float>(0x80000001) + +#define GC_INT_MAX 0x7fffffff +#define GC_INT_MIN 0xffffffff + + +/** + * Credit: https://stackoverflow.com/questions/5666222/3d-line-plane-intersection + * Determines the point of intersection between a plane defined by a point and a normal vector and a line defined by a point and a direction vector. + * + * @param planePoint A point on the plane. + * @param planeNormal Normal vector of the plane. + * @param linePoint A point on the line. + * @param lineDirection Direction vector of the line. + * @param result Resultant vector. + */ +stock void GCLineIntersection(const float planePoint[3], const float planeNormal[3], const float linePoint[3], const float lineDirection[3], float result[3]) +{ + if (GetVectorDotProduct(planeNormal, lineDirection) == 0) + { + return; + } + + float t = (GetVectorDotProduct(planeNormal, planePoint) + - GetVectorDotProduct(planeNormal, linePoint)) + / GetVectorDotProduct(planeNormal, lineDirection); + + float lineDir[3]; + lineDir = lineDirection; + NormalizeVector(lineDir, lineDir); + + ScaleVector(lineDir, t); + + AddVectors(linePoint, lineDir, result); +} + +/** + * Calculates a point according to angles supplied that is a certain distance away. + * + * @param client Client index. + * @param result Resultant vector. + * @param distance Maximum distance to trace. + * @return True on success, false otherwise. + */ +stock void GCCalcPointAngleDistance(const float start[3], const float angle[3], float distance, float result[3]) +{ + float zsine = Sine(DegToRad(-angle[0])); + float zcos = Cosine(DegToRad(-angle[0])); + + result[0] = Cosine(DegToRad(angle[1])) * zcos; + result[1] = Sine(DegToRad(angle[1])) * zcos; + result[2] = zsine; + + ScaleVector(result, distance); + AddVectors(start, result, result); +} + +/** + * Compares how close 2 floats are. + * + * @param z1 Float 1 + * @param z2 Float 2 + * @param tolerance How close the floats have to be to return true. + * @return True on success, false otherwise. + */ +stock bool GCIsRoughlyEqual(float z1, float z2, float tolerance) +{ + return FloatAbs(z1 - z2) < tolerance; +} + +/** + * Checks if a float is within a range + * + * @param number Float to check. + * @param min Minimum range. + * @param max Maximum range. + * @return True on success, false otherwise. + */ +stock bool GCIsFloatInRange(float number, float min, float max) +{ + return number >= min && number <= max; +} + +/** + * Keeps the yaw angle within the range of -180 to 180. + * + * @param angle Angle. + * @return Normalised angle. + */ +stock float GCNormaliseYaw(float angle) +{ + if (angle <= -180.0) + { + angle += 360.0; + } + + if (angle > 180.0) + { + angle -= 360.0; + } + + return angle; +} + +/** + * Keeps the yaw angle within the range of -180 to 180. + * + * @param angle Angle. + * @return Normalised angle. + */ +stock float GCNormaliseYawRad(float angle) +{ + if (angle <= -FLOAT_PI) + { + angle += FLOAT_PI * 2; + } + + if (angle > FLOAT_PI) + { + angle -= FLOAT_PI * 2; + } + + return angle; +} + +/** + * Linearly interpolates between 2 values. + * + * @param f1 Float 1. + * @param f2 Float 2. + * @param fraction Amount to interpolate. + * @return Interpolated value. + */ +stock float GCInterpLinear(float f1, float f2, float fraction) +{ + float diff = f2 - f1; + + return diff * fraction + f1; +} + +/** + * Calculates the linear fraction from a value that was interpolated and 2 values it was interpolated from. + * + * @param f1 Float 1. + * @param f2 Float 2. + * @param fraction Interpolated value. + * @return Fraction. + */ +stock float GCCalcLerpFraction(float f1, float f2, float lerped) +{ + float diff = f2 - f1; + + float fraction = lerped - f1 / diff; + return fraction; +} + +/** + * Calculate absolute value of an integer. + * + * @param x Integer. + * @return Absolute value of integer. + */ +stock int GCIntAbs(int x) +{ + return x >= 0 ? x : -x; +} + +/** + * Get the maximum of 2 integers. + * + * @param n1 Integer. + * @param n2 Integer. + * @return The biggest of n1 and n2. + */ +stock int GCIntMax(int n1, int n2) +{ + return n1 > n2 ? n1 : n2; +} + +/** + * Get the minimum of 2 integers. + * + * @param n1 Integer. + * @param n2 Integer. + * @return The smallest of n1 and n2. + */ +stock int GCIntMin(int n1, int n2) +{ + return n1 < n2 ? n1 : n2; +} + +/** + * Checks if an integer is within a range + * + * @param number Integer to check. + * @param min Minimum range. + * @param max Maximum range. + * @return True on success, false otherwise. + */ +stock bool GCIsIntInRange(int number, int min, int max) +{ + return number >= min && number <= max; +} + +/** + * Calculates a float percentage from a common fraction. + * + * @param numerator Numerator. + * @param denominator Denominator. + * @return Float percentage. -1.0 on failure. + */ +stock float GCCalcIntPercentage(int numerator, int denominator) +{ + return float(numerator) / float(denominator) * 100.0; +} + +/** + * Integer power. + * Returns the base raised to the power of the exponent. + * Returns 0 if exponent is negative. + * + * @param base Base to be raised. + * @param exponent Value to raise the base. + * @return Value to the power of exponent. + */ +stock int GCIntPow(int base, int exponent) +{ + if (exponent < 0) + { + return 0; + } + + int result = 1; + for (;;) + { + if (exponent & 1) + { + result *= base + } + + exponent >>= 1; + + if (!exponent) + { + break; + } + + base *= base; + } + return result; +} + +/** + * Swaps the values of 2 variables. + * + * @param cell1 Cell 1. + * @param cell2 Cell 2. + */ +stock void GCSwapCells(any &cell1, any &cell2) +{ + any temp = cell1; + cell1 = cell2; + cell2 = temp; +} + +/** + * Clamps an int between min and max. + * + * @param value Float to clamp. + * @param min Minimum range. + * @param max Maximum range. + * @return Clamped value. + */ +stock int GCIntClamp(int value, int min, int max) +{ + if (value < min) + { + return min; + } + if (value > max) + { + return max; + } + return value; +} + +/** + * Returns the biggest of 2 values. + * + * @param num1 Number 1. + * @param num2 Number 2. + * @return Biggest number. + */ +stock float GCFloatMax(float num1, float num2) +{ + if (num1 > num2) + { + return num1; + } + return num2; +} + +/** + * Returns the smallest of 2 values. + * + * @param num1 Number 1. + * @param num2 Number 2. + * @return Smallest number. + */ +stock float GCFloatMin(float num1, float num2) +{ + if (num1 < num2) + { + return num1; + } + return num2; +} + +/** + * Clamps a float between min and max. + * + * @param value Float to clamp. + * @param min Minimum range. + * @param max Maximum range. + * @return Clamped value. + */ +stock float GCFloatClamp(float value, float min, float max) +{ + if (value < min) + { + return min; + } + if (value > max) + { + return max; + } + return value; +} diff --git a/sourcemod/scripting/include/gamechaos/misc.inc b/sourcemod/scripting/include/gamechaos/misc.inc new file mode 100644 index 0000000..f964862 --- /dev/null +++ b/sourcemod/scripting/include/gamechaos/misc.inc @@ -0,0 +1,245 @@ + +#if defined _gamechaos_stocks_misc_included + #endinput +#endif +#define _gamechaos_stocks_misc_included + +#define GC_MISC_VERSION 0x01_00_00 +#define GC_MISC_VERSION_STRING "1.0.0" + +/** + * Check if player is overlapping their MOVERIGHT and MOVELEFT buttons. + * + * @param x Buttons; + * @return True if overlapping, false otherwise. + */ +stock bool GCIsOverlapping(int buttons) +{ + return buttons & IN_MOVERIGHT && buttons & IN_MOVELEFT +} + +/** + * Checks if player gained speed. + * + * @param speed Current player speed. + * @param lastspeed Player speed from previous tick. + * @return True if player gained speed, false otherwise. + */ +stock bool GCIsStrafeSynced(float speed, float lastspeed) +{ + return speed > lastspeed; +} + +/** + * Checks if the player is not holding down their MOVERIGHT and MOVELEFT buttons. + * + * @param x Buttons. + * @return True if they're not holding either, false otherwise. + */ +stock bool GCIsDeadAirtime(int buttons) +{ + return !(buttons & IN_MOVERIGHT) && !(buttons & IN_MOVELEFT); +} + +/** +* Source: https://forums.alliedmods.net/showthread.php?p=2535972 +* Runs a single line of vscript code. +* NOTE: Dont use the "script" console command, it startes a new instance and leaks memory. Use this instead! +* +* @param code The code to run. +* @noreturn +*/ +stock void GCRunScriptCode(const char[] code, any ...) +{ + static int scriptLogic = INVALID_ENT_REFERENCE; + + if (scriptLogic == INVALID_ENT_REFERENCE || !IsValidEntity(scriptLogic)) + { + scriptLogic = EntIndexToEntRef(CreateEntityByName("logic_script")); + if (scriptLogic == INVALID_ENT_REFERENCE || !IsValidEntity(scriptLogic)) + { + SetFailState("Could not create a 'logic_script' entity."); + } + + DispatchSpawn(scriptLogic); + } + + char buffer[512]; + VFormat(buffer, sizeof(buffer), code, 2); + + SetVariantString(buffer); + AcceptEntityInput(scriptLogic, "RunScriptCode"); +} + +stock void GCTE_SendBeamBox(int client, + const float origin[3], + const float mins[3], + const float maxs[3], + int ModelIndex, + int HaloIndex = 0, + float Life = 3.0, + float Width = 2.0, + const int Colour[4] = { 255, 255, 255, 255 }, + float EndWidth = 2.0, + int StartFrame = 0, + int FrameRate = 0, + int FadeLength = 0, + float Amplitude = 0.0, + int Speed = 0) +{ + // credit to some bhop timer by shavit? thanks + int pairs[8][3] = { { 0, 0, 0 }, { 1, 0, 0 }, { 1, 1, 0 }, { 0, 1, 0 }, { 0, 0, 1 }, { 1, 0, 1 }, { 1, 1, 1 }, { 0, 1, 1 } }; + int edges[12][2] = { { 0, 1 }, { 0, 3 }, { 0, 4 }, { 2, 1 }, { 2, 3 }, { 2, 6 }, { 5, 4 }, { 5, 6 }, { 5, 1 }, { 7, 4 }, { 7, 6 }, { 7, 3 } }; + + float corners[8][3]; + float corner[2][3]; + + AddVectors(origin, mins, corner[0]); + AddVectors(origin, maxs, corner[1]); + + for (int i = 0; i < 8; i++) + { + corners[i][0] = corner[pairs[i][0]][0]; + corners[i][1] = corner[pairs[i][1]][1]; + corners[i][2] = corner[pairs[i][2]][2]; + } + + for (int i = 0; i < 12; i++) + { + TE_SetupBeamPoints(corners[edges[i][0]], + corners[edges[i][1]], + ModelIndex, + HaloIndex, + StartFrame, + FrameRate, + Life, + Width, + EndWidth, + FadeLength, + Amplitude, + Colour, + Speed); + TE_SendToClient(client); + } +} + +stock void GCTE_SendBeamCross(int client, + const float origin[3], + int ModelIndex, + int HaloIndex = 0, + float Life = 3.0, + float Width = 2.0, + const int Colour[4] = { 255, 255, 255, 255 }, + float EndWidth = 2.0, + int StartFrame = 0, + int FrameRate = 0, + int FadeLength = 0, + float Amplitude = 0.0, + int Speed = 0) +{ + float points[4][3]; + + for (int i; i < 4; i++) + { + points[i][2] = origin[2]; + } + + // -x; -y + points[0][0] = origin[0] - 8.0; + points[0][1] = origin[1] - 8.0; + + // +x; -y + points[1][0] = origin[0] + 8.0; + points[1][1] = origin[1] - 8.0; + + // +x; +y + points[2][0] = origin[0] + 8.0; + points[2][1] = origin[1] + 8.0; + + // -x; +y + points[3][0] = origin[0] - 8.0; + points[3][1] = origin[1] + 8.0; + + //draw cross + for (int corner; corner < 4; corner++) + { + TE_SetupBeamPoints(origin, points[corner], ModelIndex, HaloIndex, StartFrame, FrameRate, Life, Width, EndWidth, FadeLength, Amplitude, Colour, Speed); + TE_SendToClient(client); + } +} + +stock void GCTE_SendBeamRectangle(int client, + const float origin[3], + const float mins[3], + const float maxs[3], + int modelIndex, + int haloIndex = 0, + float life = 3.0, + float width = 2.0, + const int colour[4] = { 255, 255, 255, 255 }, + float endWidth = 2.0, + int startFrame = 0, + int frameRate = 0, + int fadeLength = 0, + float amplitude = 0.0, + int speed = 0) +{ + float vertices[4][3]; + GCRectangleVerticesFromPoint(vertices, origin, mins, maxs); + + // send the square + for (int i; i < 4; i++) + { + int j = (i == 3) ? (0) : (i + 1); + TE_SetupBeamPoints(vertices[i], + vertices[j], + modelIndex, + haloIndex, + startFrame, + frameRate, + life, + width, + endWidth, + fadeLength, + amplitude, + colour, + speed); + TE_SendToClient(client); + } +} + +/** + * Calculates vertices for a rectangle from a point, mins and maxs. + * + * @param result Vertex array result. + * @param origin Origin to offset mins and maxs by. + * @param mins Minimum size of the rectangle. + * @param maxs Maximum size of the rectangle. + * @return True if overlapping, false otherwise. + */ +stock void GCRectangleVerticesFromPoint(float result[4][3], const float origin[3], const float mins[3], const float maxs[3]) +{ + // Vertices are set clockwise starting from top left (-x; -y) + + // -x; -y + result[0][0] = origin[0] + mins[0]; + result[0][1] = origin[1] + mins[1]; + + // +x; -y + result[1][0] = origin[0] + maxs[0]; + result[1][1] = origin[1] + mins[1]; + + // +x; +y + result[2][0] = origin[0] + maxs[0]; + result[2][1] = origin[1] + maxs[1]; + + // -x; +y + result[3][0] = origin[0] + mins[0]; + result[3][1] = origin[1] + maxs[1]; + + // z is the same for every vertex + for (int vertex; vertex < 4; vertex++) + { + result[vertex][2] = origin[2]; + } +}
\ No newline at end of file diff --git a/sourcemod/scripting/include/gamechaos/strings.inc b/sourcemod/scripting/include/gamechaos/strings.inc new file mode 100644 index 0000000..8ffcb60 --- /dev/null +++ b/sourcemod/scripting/include/gamechaos/strings.inc @@ -0,0 +1,367 @@ + +#if defined _gamechaos_stocks_strings_included + #endinput +#endif +#define _gamechaos_stocks_strings_included + +// these are used for functions that return strings. +// you can change these if they're too small/big. +#define GC_FIXED_BUFFER_SIZE_SMALL 64 +#define GC_FIXED_BUFFER_SIZE_LARGE 4096 + +/** + * Puts the values from a string of integers into an array + * + * @param string + * @param separator + * @param array + * @param arraysize + */ +stock void GCSeparateIntsFromString(const char[] string, const char[] separator, int[] array, int arraysize) +{ + char[][] explodedbuffer = new char[arraysize][32]; + + ExplodeString(string, separator, explodedbuffer, arraysize, 32); + + for (int i; i < arraysize; i++) + { + array[i] = StringToInt(explodedbuffer[i]); + } +} + +/** + * Prints a message to all admins in the chat area. + * + * @param format Formatting rules. + * @param ... Variable number of format parameters. + */ +stock void GCPrintToChatAdmins(const char[] format, any ...) +{ + char buffer[256]; + + for (int i = 1; i <= MaxClients; i++) + { + if (GCIsValidClient(i)) + { + AdminId id = GetUserAdmin(i); + if (!GetAdminFlag(id, Admin_Generic)) + { + continue; + } + SetGlobalTransTarget(i); + VFormat(buffer, sizeof(buffer), format, 2); + PrintToChat(i, "%s", buffer); + } + } +} + +/** + * Removes trailings zeroes from a string. Also removes the decimal point if it can. + * + * @param buffer Buffer to trim. + * @return Whether anything was removed. + */ +stock bool GCRemoveTrailing0s(char[] buffer) +{ + bool removed; + int maxlen = strlen(buffer); + + if (maxlen == 0) + { + return removed; + } + + for (int i = maxlen - 1; i > 0 && (buffer[i] == '0' || buffer[i] == '.' || buffer[i] == 0); i--) + { + if (buffer[i] == 0) + { + continue; + } + if (buffer[i] == '.') + { + buffer[i] = 0; + removed = true; + break; + } + buffer[i] = 0; + removed = true; + } + return removed; +} + +/** + * Formats time by HHMMSS. Uses ticks for the time. + * + * @param timeInTicks Time in ticks. + * @param tickRate Tickrate. + * @param formattedTime String to use for formatting. + * @param size String size. + */ +stock void GCFormatTickTimeHHMMSS(int timeInTicks, float tickRate, char[] formattedTime, int size) +{ + if (timeInTicks <= 0) + { + FormatEx(formattedTime, size, "-00:00:00"); + return; + } + + int time = RoundFloat(float(timeInTicks) / tickRate * 100.0); // centiseconds + int iHours = time / 360000; + int iMinutes = time / 6000 - iHours * 6000; + int iSeconds = (time - iHours * 360000 - iMinutes * 6000) / 100; + int iCentiSeconds = time % 100; + + if (iHours != 0) + { + FormatEx(formattedTime, size, "%02i:", iHours); + } + if (iMinutes != 0) + { + Format(formattedTime, size, "%s%02i:", formattedTime, iMinutes); + } + + Format(formattedTime, size, "%s%02i.%02i", formattedTime, iSeconds, iCentiSeconds); +} + +/** + * Formats time by HHMMSS. Uses seconds. + * + * @param seconds Time in seconds. + * @param formattedTime String to use for formatting. + * @param size String size. + * @param decimals Amount of decimals to use for the fractional part. + */ +stock void GCFormatTimeHHMMSS(float seconds, char[] formattedTime, int size, int decimals) +{ + int iFlooredTime = RoundToFloor(seconds); + int iHours = iFlooredTime / 3600; + int iMinutes = iFlooredTime / 60 - iHours * 60; + int iSeconds = iFlooredTime - iHours * 3600 - iMinutes * 60; + int iFraction = RoundToFloor(FloatFraction(seconds) * Pow(10.0, float(decimals))); + + if (iHours != 0) + { + FormatEx(formattedTime, size, "%02i:", iHours); + } + if (iMinutes != 0) + { + Format(formattedTime, size, "%s%02i:", formattedTime, iMinutes); + } + char szFraction[32]; + FormatEx(szFraction, sizeof(szFraction), "%i", iFraction); + + int iTest = strlen(szFraction); + for (int i; i < decimals - iTest; i++) + { + Format(szFraction, sizeof(szFraction), "%s%s", "0", szFraction); + } + + Format(formattedTime, size, "%s%02i.%s", formattedTime, iSeconds, szFraction); +} + +/** + * Encodes and appends a number onto the end of a UTF-8 string. + * + * @param string String to append to. + * @param strsize String size. + * @param number Unicode codepoint to encode. + */ +stock void GCEncodeUtf8(char[] string, char strsize, int number) +{ + // UTF-8 octet sequence (only change digits marked with x) + /* + Char. number range | UTF-8 octet sequence + (hexadecimal) | (binary) + --------------------+--------------------------------------------- + 0000 0000-0000 007F | 0xxxxxxx + 0000 0080-0000 07FF | 110xxxxx 10xxxxxx + 0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx + 0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx + // byte 4 | byte 3 | byte 2 | byte 1*/ + + //char encodedChar = 0b_11110000_10000000_10000000_10000000; + + int zeropos = strlen(string); + + if (zeropos >= strsize - 1) // need one byte for null terminator + { + return; + } + + if (number < 0) + { + //PrintToServer("ERROR: Encode() - Can't encode negative numbers"); + return; + } + + if (number >= 0x110_000) + { + //PrintToServer("ERROR: Encode() - Number is too big to encode"); + return; + } + + // 1 byte + if (number < 0x80) + { + string[zeropos] = number; + string[zeropos + 1] = '\0'; + } + // 2 bytes + else if (number < 0x800) + { + // can't encode if we don't have enough room + if (zeropos + 2 >= strsize) + { + return; + } + + string[zeropos] = 0b_1100_0000 | (number >> 6); // don't need to mask out bits over 0x7FF + string[zeropos + 1] = 0b_1000_0000 | (number & 0b_0011_1111); + + string[zeropos + 2] = '\0'; + } + // 3 bytes + else if (number < 0x10_000) + { + // can't encode if we don't have enough room + if (zeropos + 3 >= strsize) + { + return; + } + + string[zeropos] = 0b_1110_0000 | (number >> 12); // don't need to mask out bits over 0xFFFF + string[zeropos + 1] = 0b_1000_0000 | ((number >> 6) & 0b_0011_1111); + string[zeropos + 2] = 0b_1000_0000 | (number & 0b_0011_1111); + + string[zeropos + 3] = '\0'; + } + // 4 bytes + else if (number < 0x110_000) + { + // can't encode if we don't have enough room + if (zeropos + 4 >= strsize) + { + return; + } + + string[zeropos] = 0b_1111_0000 | (number >> 18); // don't need to mask out bits over 0x10FFFF + string[zeropos + 1] = 0b_1000_0000 | ((number >> 12) & 0b_0011_1111); + string[zeropos + 2] = 0b_1000_0000 | ((number >> 6) & 0b_0011_1111); + string[zeropos + 3] = 0b_1000_0000 | (number & 0b_0011_1111); + + string[zeropos + 4] = '\0'; + } +} + +// decode a UTF-8 string into an array of unicode codepoints +/** + * Decodes a UTF-8 string into an array of unicode codepoints. + * + * @param string String to decode. + * @param strsize String size. + * @param codepoints Array to use to store the codepoints. + * @param cplength Array length. + */ +stock void GCDecodeUtf8(char[] string, int strsize, int[] codepoints, int cplength) +{ + int charindex; + int cpindex; + + while (charindex < strsize && cpindex < cplength) + { + if (string[charindex] == '\0') + { + break; + } + + int bytes = GetCharBytes(string[charindex]); + + switch (bytes) + { + case 1: + { + codepoints[cpindex] = string[charindex]; + } + case 2: + { + codepoints[cpindex] = (string[charindex++] & 0b_0001_1111) << 6; // byte 2 + codepoints[cpindex] |= string[charindex] & 0b_0011_1111; // byte 1 + } + case 3: + { + codepoints[cpindex] = (string[charindex++] & 0b_0000_1111) << 12; // byte 3 + codepoints[cpindex] |= (string[charindex++] & 0b_0011_1111) << 6; // byte 2 + codepoints[cpindex] |= string[charindex] & 0b_0011_1111; // byte 1 + } + case 4: + { + codepoints[cpindex] = (string[charindex++] & 0b_0000_0111) << 18; // byte 4 + codepoints[cpindex] |= (string[charindex++] & 0b_0011_1111) << 12; // byte 3 + codepoints[cpindex] |= (string[charindex++] & 0b_0011_1111) << 6; // byte 2 + codepoints[cpindex] |= string[charindex] & 0b_0011_1111; // byte 1 + } + } + + charindex++; + cpindex++; + } +} + +/** + * Converts an integer to a string. + * Same as IntToString, but it returns the string. + * + * @param num Integer to convert. + * @return String of the number. + */ +stock char[] GCIntToStringRet(int num) +{ + char string[GC_FIXED_BUFFER_SIZE_SMALL]; + IntToString(num, string, sizeof string); + return string; +} + + /** + * Converts a floating point number to a string. + * Same as FloatToString, but it returns the string. + * + * @param num Floating point number to convert. + * @return String of the number. + */ +stock char[] GCFloatToStringRet(float num) +{ + char string[GC_FIXED_BUFFER_SIZE_SMALL]; + FloatToString(num, string, sizeof string); + return string; +} + +/** + * Formats a string according to the SourceMod format rules (see documentation). + * Same as Format, except it returns the formatted string. + * + * @param format Formatting rules. + * @param ... Variable number of format parameters. + * @return Formatted string. + */ +stock char[] GCFormatReturn(const char[] format, any ...) +{ + char string[GC_FIXED_BUFFER_SIZE_LARGE]; + VFormat(string, sizeof string, format, 2); + return string; +} + +/** + * Removes whitespace characters from the beginning and end of a string. + * Same as TrimString, except it returns the formatted string and + * it doesn't modify the passed string. + * + * @param str The string to trim. + * @return Number of bytes written (UTF-8 safe). + */ +stock char[] GCTrimStringReturn(char[] str) +{ + char string[GC_FIXED_BUFFER_SIZE_LARGE]; + strcopy(string, sizeof string, str); + TrimString(string); + return string; +}
\ No newline at end of file diff --git a/sourcemod/scripting/include/gamechaos/tempents.inc b/sourcemod/scripting/include/gamechaos/tempents.inc new file mode 100644 index 0000000..7ec9b5a --- /dev/null +++ b/sourcemod/scripting/include/gamechaos/tempents.inc @@ -0,0 +1,62 @@ + +#if defined _gamechaos_stocks_tempents_included + #endinput +#endif +#define _gamechaos_stocks_tempents_included + +// improved api of some tempents + +#define GC_TEMPENTS_VERSION 0x01_00_00 +#define GC_TEMPENTS_VERSION_STRING "1.0.0" + +#include <sdktools_tempents> + +/** + * Sets up a point to point beam effect. + * + * @param start Start position of the beam. + * @param end End position of the beam. + * @param modelIndex Precached model index. + * @param life Time duration of the beam. + * @param width Initial beam width. + * @param endWidth Final beam width. + * @param colour Color array (r, g, b, a). + * @param haloIndex Precached model index. + * @param amplitude Beam amplitude. + * @param speed Speed of the beam. + * @param fadeLength Beam fade time duration. + * @param frameRate Beam frame rate. + * @param startFrame Initial frame to render. + */ +stock void GCTE_SetupBeamPoints(const float start[3], + const float end[3], + int modelIndex, + float life = 2.0, + float width = 2.0, + float endWidth = 2.0, + const int colour[4] = {255, 255, 255, 255}, + int haloIndex = 0, + float amplitude = 0.0, + int speed = 0, + int fadeLength = 0, + int frameRate = 0, + int startFrame = 0) +{ + TE_Start("BeamPoints"); + TE_WriteVector("m_vecStartPoint", start); + TE_WriteVector("m_vecEndPoint", end); + TE_WriteNum("m_nModelIndex", modelIndex); + TE_WriteNum("m_nHaloIndex", haloIndex); + TE_WriteNum("m_nStartFrame", startFrame); + TE_WriteNum("m_nFrameRate", frameRate); + TE_WriteFloat("m_fLife", life); + TE_WriteFloat("m_fWidth", width); + TE_WriteFloat("m_fEndWidth", endWidth); + TE_WriteFloat("m_fAmplitude", amplitude); + TE_WriteNum("r", colour[0]); + TE_WriteNum("g", colour[1]); + TE_WriteNum("b", colour[2]); + TE_WriteNum("a", colour[3]); + TE_WriteNum("m_nSpeed", speed); + TE_WriteNum("m_nFadeLength", fadeLength); +}
\ No newline at end of file diff --git a/sourcemod/scripting/include/gamechaos/tracing.inc b/sourcemod/scripting/include/gamechaos/tracing.inc new file mode 100644 index 0000000..65d54a8 --- /dev/null +++ b/sourcemod/scripting/include/gamechaos/tracing.inc @@ -0,0 +1,242 @@ + +#if defined _gamechaos_stocks_tracing_included + #endinput +#endif +#define _gamechaos_stocks_tracing_included + +#include <sdktools_trace> + +#define GC_TRACING_VERSION 0x01_00_00 +#define GC_TRACING_VERSION_STRING "1.0.0" + +/** + * Trace ray filter that filters players from being traced. + * + * @param entity Entity. + * @param data Data. + * @return True on success, false otherwise. + */ +stock bool GCTraceEntityFilterPlayer(int entity, any data) +{ + return entity > MAXPLAYERS; +} + +/** + * Traces the player hull beneath the player in the direction of + * the player's velocity. This should be used on the tick when the player lands + * + * @param client Player's index. + * @param pos Player's position vector. + * @param velocity Player's velocity vector. This shuold have the current tick's x and y velocities, but the previous tick's z velocity, since when you're on ground, your z velocity is 0. + * @param result Trace endpoint on success, player's position on failure. + * @param bugged Whether to add gravity to the player's velocity or not. + * @return True on success, false otherwise. + */ +stock bool GCTraceLandPos(int client, const float pos[3], const float velocity[3], float result[3], float fGravity, bool bugged = false) +{ + float newVel[3]; + newVel = velocity; + + if (bugged) + { + // add 0.5 gravity + newVel[2] -= fGravity * GetTickInterval() * 0.5; + } + else + { + // add 1.5 gravity + newVel[2] -= fGravity * GetTickInterval() * 1.5; + } + + ScaleVector(newVel, GetTickInterval() * 2.0); + float pos2[3]; + AddVectors(pos, newVel, pos2); + + float mins[3]; + float maxs[3]; + GetClientMins(client, mins); + GetClientMaxs(client, maxs); + + Handle trace = TR_TraceHullFilterEx(pos, pos2, mins, maxs, MASK_PLAYERSOLID, GCTraceEntityFilterPlayer); + + if (!TR_DidHit(trace)) + { + result = pos; + CloseHandle(trace); + return false; + } + + TR_GetEndPosition(result, trace); + CloseHandle(trace); + + return true; +} + +/** + * Traces the player hull 2 units straight down beneath the player. + * + * @param client Player's index. + * @param pos Player's position vector. + * @param result Trace endpoint on success, player's position on failure. + * @return True on success, false otherwise. + */ +stock bool GCTraceGround(int client, const float pos[3], float result[3]) +{ + float mins[3]; + float maxs[3]; + + GetClientMins(client, mins); + GetClientMaxs(client, maxs); + + float startpos[3]; + float endpos[3]; + + startpos = pos; + endpos = pos; + + endpos[2] -= 2.0; + + TR_TraceHullFilter(startpos, endpos, mins, maxs, MASK_PLAYERSOLID, GCTraceEntityFilterPlayer); + + if (TR_DidHit()) + { + TR_GetEndPosition(result); + return true; + } + else + { + result = endpos; + return false; + } +} + +/** + * Traces a hull between 2 positions. + * + * @param pos1 Position 1. + * @param pos2 Position 2 + * @param result Trace endpoint on success, player's position on failure. + * @return True on success, false otherwise. + */ +stock bool GCTraceBlock(const float pos1[3], const float pos2[3], float result[3]) +{ + float mins[3] = {-16.0, -16.0, -1.0}; + float maxs[3] = { 16.0, 16.0, 0.0}; + + TR_TraceHullFilter(pos1, pos2, mins, maxs, MASK_PLAYERSOLID, GCTraceEntityFilterPlayer); + + if (TR_DidHit()) + { + TR_GetEndPosition(result); + return true; + } + else + { + return false; + } +} + +/** + * Traces from player eye position in the direction of where the player is looking. + * + * @param client Client index. + * @param result Resultant vector. + * @return True on success, false otherwise. + */ +stock bool GCGetEyeRayPosition(int client, float result[3], TraceEntityFilter filter, any data = 0, int flags = MASK_PLAYERSOLID) +{ + float start[3]; + float angle[3]; + + GetClientEyePosition(client, start); + GetClientEyeAngles(client, angle); + + TR_TraceRayFilter(start, angle, flags, RayType_Infinite, filter, data); + + if (TR_DidHit(INVALID_HANDLE)) + { + TR_GetEndPosition(result, INVALID_HANDLE); + return true; + } + return false; +} + +/** + * Traces from player eye position in the direction of where the player is looking, up to a certain distance. + * + * @param client Client index. + * @param result Resultant vector. + * @param distance Maximum distance to trace. + * @return True on success, false otherwise. + */ +stock bool GCTraceEyeRayPositionDistance(int client, float result[3], float distance) +{ + float start[3]; + float angle[3]; + + GetClientEyePosition(client, start); + GetClientEyeAngles(client, angle); + + float endpoint[3]; + float zsine = Sine(DegToRad(-angle[0])); + float zcos = Cosine(DegToRad(-angle[0])); + + endpoint[0] = Cosine(DegToRad(angle[1])) * zcos; + endpoint[1] = Sine(DegToRad(angle[1])) * zcos; + endpoint[2] = zsine; + + ScaleVector(endpoint, distance); + AddVectors(start, endpoint, endpoint); + + TR_TraceRayFilter(start, endpoint, MASK_PLAYERSOLID, RayType_EndPoint, GCTraceEntityFilterPlayer, client); + + if (TR_DidHit()) + { + TR_GetEndPosition(result); + return true; + } + + result = endpoint; + return false; +} + +/** + * Traces a hull in a certain direction and distance. + * + * @param origin Position to trace from. + * @param direction Trace direction. + * @param mins Minimum size of the hull. + * @param maxs Maximum size of the hull. + * @param result Resultant vector. + * @return True on success, false otherwise. + */ +stock bool GCTraceHullDirection(const float origin[3], + const float direction[3], + const float mins[3], + const float maxs[3], + float result[3], + float distance, + TraceEntityFilter filter, + any data = 0, + int flags = MASK_PLAYERSOLID) +{ + float pos2[3]; + float zsine = Sine(DegToRad(-direction[0])); + float zcos = Cosine(DegToRad(-direction[0])); + + pos2[0] = Cosine(DegToRad(direction[1])) * zcos; + pos2[1] = Sine(DegToRad(direction[1])) * zcos; + pos2[2] = zsine; + + ScaleVector(pos2, distance); + AddVectors(origin, pos2, pos2); + + TR_TraceHullFilter(origin, pos2, mins, maxs, flags, filter, data); + if (TR_DidHit()) + { + TR_GetEndPosition(result); + return true; + } + result = pos2; + return false; +}
\ No newline at end of file diff --git a/sourcemod/scripting/include/gamechaos/vectors.inc b/sourcemod/scripting/include/gamechaos/vectors.inc new file mode 100644 index 0000000..79d5e8f --- /dev/null +++ b/sourcemod/scripting/include/gamechaos/vectors.inc @@ -0,0 +1,66 @@ + +#if defined _gamechaos_stocks_vectors_included + #endinput +#endif +#define _gamechaos_stocks_vectors_included + +#define GC_VECTORS_VERSION 0x01_00_01 +#define GC_VECTORS_VERSION_STRING "1.0.1" + +/** + * Calculates the horizontal (x, y) length of a vector. + * + * @param vec Vector. + * @return Vector length (magnitude). + */ +stock float GCGetVectorLength2D(const float vec[3]) +{ + float tempVec[3]; + tempVec = vec; + tempVec[2] = 0.0; + + return GetVectorLength(tempVec); +} + +/** + * Calculates the horizontal (x, y) distance between 2 vectors. + * + * @param x Vector 1. + * @param y Vector 2. + * @param tolerance How close the floats have to be to return true. + * @return True on success, false otherwise. + */ +stock float GCGetVectorDistance2D(const float x[3], const float y[3]) +{ + float x2[3]; + float y2[3]; + + x2 = x; + y2 = y; + + x2[2] = 0.0; + y2[2] = 0.0; + + return GetVectorDistance(x2, y2); +} + +/** + * Checks if 2 vectors are exactly equal. + * + * @param a Vector 1. + * @param b Vector 2. + * @return True on success, false otherwise. + */ +stock bool GCVectorsEqual(const float a[3], const float b[3]) +{ + bool result = true; + for (int i = 0; i < 3; i++) + { + if (a[i] != b[i]) + { + result = false; + break; + } + } + return result; +}
\ No newline at end of file diff --git a/sourcemod/scripting/include/glib/addressutils.inc b/sourcemod/scripting/include/glib/addressutils.inc new file mode 100644 index 0000000..bbe8f14 --- /dev/null +++ b/sourcemod/scripting/include/glib/addressutils.inc @@ -0,0 +1,54 @@ +#if defined _addressutils_included +#endinput +#endif +#define _addressutils_included + +methodmap AddressBase +{ + property Address Address + { + public get() { return view_as<Address>(this); } + } +} + +//-==Operator overloadings +stock Address operator+(Address l, int r) +{ + return l + view_as<Address>(r); +} + +stock Address operator+(int l, Address r) +{ + return view_as<Address>(l) + r; +} + +stock Address operator-(Address l, int r) +{ + return l - view_as<Address>(r); +} + +stock Address operator-(int l, Address r) +{ + return view_as<Address>(l) - r; +} + +stock Address operator*(Address l, int r) +{ + return l * view_as<Address>(r); +} + +stock Address operator*(int l, Address r) +{ + return view_as<Address>(l) * r; +} + +stock Address operator/(Address l, int r) +{ + return l / view_as<Address>(r); +} + +stock Address operator/(int l, Address r) +{ + return view_as<Address>(l) / r; +} +//Operator overloadings==-
\ No newline at end of file diff --git a/sourcemod/scripting/include/glib/assertutils.inc b/sourcemod/scripting/include/glib/assertutils.inc new file mode 100644 index 0000000..83cd90d --- /dev/null +++ b/sourcemod/scripting/include/glib/assertutils.inc @@ -0,0 +1,61 @@ +#if defined _assertutils_included +#endinput +#endif +#define _assertutils_included + +/* Compile time settings for this include. Should be defined before including this file. +* #define ASSERTUTILS_DISABLE //Disables all assertions +* #define ASSERTUTILS_FAILSTATE_FUNC //Define the name of the function that should be called when assertion is hit +*/ + +#if !defined SNAME +#define __SNAME "" +#else +#define __SNAME SNAME +#endif + +#define ASSERT_FMT_STRING_LEN 512 + +#if defined ASSERTUTILS_DISABLE + +#define ASSERT(%1)%2; +#define ASSERT_MSG(%1,%2)%3; +#define ASSERT_FMT(%1,%2)%3; +#define ASSERT_FINAL(%1)%2; +#define ASSERT_FINAL_MSG(%1,%2)%3; + +#elseif defined ASSERTUTILS_FAILSTATE_FUNC + +#define ASSERT(%1) if(!(%1)) ASSERTUTILS_FAILSTATE_FUNC(__SNAME..."Assertion failed: \""...#%1..."\"") +#define ASSERT_MSG(%1,%2) if(!(%1)) ASSERTUTILS_FAILSTATE_FUNC(__SNAME...%2) +#define ASSERT_FMT(%1,%2) if(!(%1)) ASSERTUTILS_FAILSTATE_FUNC(__SNAME...%2) +#define ASSERT_FINAL(%1) if(!(%1)) SetFailState(__SNAME..."Assertion failed: \""...#%1..."\"") +#define ASSERT_FINAL_MSG(%1,%2) if(!(%1)) SetFailState(__SNAME...%2) + +#else + +#define ASSERT(%1) if(!(%1)) SetFailState(__SNAME..."Assertion failed: \""...#%1..."\"") +#define ASSERT_MSG(%1,%2) if(!(%1)) SetFailState(__SNAME...%2) +#define ASSERT_FMT(%1,%2) if(!(%1)) SetFailState(__SNAME...%2) +#define ASSERT_FINAL(%1) ASSERT(%1) +#define ASSERT_FINAL_MSG(%1,%2) ASSERT_MSG(%1,%2) + +#endif + +// Might be redundant as default ASSERT_MSG accept format arguments just fine. +#if 0 +stock void ASSERT_FMT(bool result, char[] fmt, any ...) +{ +#if !defined ASSERTUTILS_DISABLE + if(!result) + { + char buff[ASSERT_FMT_STRING_LEN]; + VFormat(buff, sizeof(buff), fmt, 3); + + SetFailState(__SNAME..."%s", buff); + } +#endif +} +#endif + +#undef ASSERT_FMT_STRING_LEN
\ No newline at end of file diff --git a/sourcemod/scripting/include/glib/memutils.inc b/sourcemod/scripting/include/glib/memutils.inc new file mode 100644 index 0000000..5813d92 --- /dev/null +++ b/sourcemod/scripting/include/glib/memutils.inc @@ -0,0 +1,232 @@ +#if defined _memutils_included +#endinput +#endif +#define _memutils_included + +#include "glib/assertutils" +#include "glib/addressutils" + +/* Compile time settings for this include. Should be defined before including this file. +* #define MEMUTILS_PLUGINENDCALL //This should be defined if main plugin has OnPluginEnd() forward used. +*/ + +#define MEM_LEN_SAFE_THRESHOLD 2000 + +//-==PatchHandling methodmap +static StringMap gPatchStack; + +methodmap PatchHandler < AddressBase +{ + public PatchHandler(Address addr) + { + ASSERT(addr != Address_Null); + + if(!gPatchStack) + gPatchStack = new StringMap(); + + return view_as<PatchHandler>(addr); + } + + property any Any + { + public get() { return view_as<any>(this); } + } + + public void Save(int len) + { + ASSERT(gPatchStack); + ASSERT(len > 0 && len < MEM_LEN_SAFE_THRESHOLD); + + len++; + + int[] arr = new int[len]; + arr[0] = len; + + for(int i = 0; i < len - 1; i++) + arr[i + 1] = LoadFromAddress(this.Address + i, NumberType_Int8); + + char buff[32]; + IntToString(this.Any, buff, sizeof(buff)); + gPatchStack.SetArray(buff, arr, len); + } + + public void PatchNSave(int len, char byte = 0x90) + { + ASSERT(gPatchStack); + ASSERT(len > 0 && len < MEM_LEN_SAFE_THRESHOLD); + + len++; + + int[] arr = new int[len]; + arr[0] = len; + + for(int i = 0; i < len - 1; i++) + { + arr[i + 1] = LoadFromAddress(this.Address + i, NumberType_Int8); + StoreToAddress(this.Address + i, byte, NumberType_Int8); + } + + char buff[32]; + IntToString(this.Any, buff, sizeof(buff)); + gPatchStack.SetArray(buff, arr, len); + } + + public void PatchNSaveSeq(const char[] data, int len) + { + ASSERT(gPatchStack); + ASSERT(len > 0 && len < MEM_LEN_SAFE_THRESHOLD); + + len++; + + int[] arr = new int[len]; + arr[0] = len; + + for(int i = 0; i < len - 1; i++) + { + arr[i + 1] = LoadFromAddress(this.Address + i, NumberType_Int8); + StoreToAddress(this.Address + i, data[i], NumberType_Int8); + } + + char buff[32]; + IntToString(this.Any, buff, sizeof(buff)); + gPatchStack.SetArray(buff, arr, len); + } + + public void Restore() + { + if(!gPatchStack) + return; + + char buff[32]; + IntToString(this.Any, buff, sizeof(buff)); + + int arrSize[1]; + if(!gPatchStack.GetArray(buff, arrSize, sizeof(arrSize))) + return; + + int[] arr = new int[arrSize[0]]; + gPatchStack.GetArray(buff, arr, arrSize[0]); + gPatchStack.Remove(buff); + + for(int i = 0; i < arrSize[0] - 1; i++) + StoreToAddress(this.Address + i, arr[i + 1], NumberType_Int8); + + if(gPatchStack.Size == 0) + delete gPatchStack; + } +} + +public void OnPluginEnd() +{ + if(gPatchStack) + { + StringMapSnapshot sms = gPatchStack.Snapshot(); + char buff[32]; + Address addr; + + for(int i = 0; i < sms.Length; i++) + { + sms.GetKey(i, buff, sizeof(buff)); + addr = view_as<Address>(StringToInt(buff)); + view_as<PatchHandler>(addr).Restore(); + } + } + +#if defined MEMUTILS_PLUGINENDCALL + OnPluginEnd_MemUtilsRedefined(); +#endif +} +#undef OnPluginEnd +#if defined MEMUTILS_PLUGINENDCALL +#define OnPluginEnd OnPluginEnd_MemUtilsRedefined +#else +#define OnPluginEnd OnPluginEnd_Redifined(){}\ +void MEMUTILS_INCLUDE_WARNING_OnPluginEnd_REDIFINITION +#endif +//PatchHandling methodmap==- + +//-==Other util functions +stock int LoadStringFromAddress(Address addr, char[] buff, int length) +{ + int i; + for(i = 0; i < length && (buff[i] = LoadFromAddress(addr + i, NumberType_Int8)) != '\0'; i++) { } + buff[i == length ? i - 1 : i] = '\0'; + return i; +} + +stock void DumpOnAddress(Address addr, int len, int columns = 10) +{ + char buff[128], buff2[128]; + + ASSERT(addr != Address_Null); + ASSERT(len > 0 && len < MEM_LEN_SAFE_THRESHOLD); + + Format(buff, sizeof(buff), "[0x%08X]", addr); + char chr; + for(int i = 0; i < len; i++) + { + chr = LoadFromAddress(addr + i, NumberType_Int8); + Format(buff, sizeof(buff), "%s %02X", buff, chr); + Format(buff2, sizeof(buff2), "%s%c", buff2, (chr > ' ' && chr != 0x7F && chr != 0xFF ? chr : '.')); + if(i % columns == columns - 1) + { + PrintToServer(__SNAME..."%s %s", buff, buff2); + Format(buff, sizeof(buff), "[0x%08X]", addr + i); + buff2[0] = '\0'; + } + } + + if((len - 1) % columns != columns - 1) + PrintToServer(__SNAME..."%s %s", buff, buff2); +} + +//NO OVERLAPPING!! +stock void MoveBytes(Address from, Address to, int len, char replace_with = 0x90) +{ + ASSERT(from != Address_Null); + ASSERT(to != Address_Null); + ASSERT(len > 0 && len < MEM_LEN_SAFE_THRESHOLD); + ASSERT(to < from || to > from + len); + + if(from == to) + return; + + for(int i = 0; i < len; i++) + { + StoreToAddress(to + i, LoadFromAddress(from + i, NumberType_Int8), NumberType_Int8); + StoreToAddress(from + i, replace_with, NumberType_Int8); + } +} + +stock void CutNCopyBytes(Address from, Address to, int len, char replace_with = 0x90) +{ + ASSERT(from != Address_Null); + ASSERT(to != Address_Null); + ASSERT(len > 0 && len < MEM_LEN_SAFE_THRESHOLD); + + if(from == to) + return; + + int[] arr = new int[len]; + + for(int i = 0; i < len; i++) + { + arr[i] = LoadFromAddress(from + i, NumberType_Int8); + StoreToAddress(from + i, replace_with, NumberType_Int8); + } + + for(int i = 0; i < len; i++) + StoreToAddress(to + i, arr[i], NumberType_Int8); +} + +stock void PatchArea(Address addr, int len, char byte = 0x90) +{ + ASSERT(addr != Address_Null); + ASSERT(len > 0 && len < MEM_LEN_SAFE_THRESHOLD); + + for(int i = 0; i < len; i++) + StoreToAddress(addr + i, byte, NumberType_Int8); +} +//Other util functions==- + +#undef MEM_LEN_SAFE_THRESHOLD
\ No newline at end of file diff --git a/sourcemod/scripting/include/gokz.inc b/sourcemod/scripting/include/gokz.inc new file mode 100644 index 0000000..edbd896 --- /dev/null +++ b/sourcemod/scripting/include/gokz.inc @@ -0,0 +1,1097 @@ +/* + GOKZ General Include + + Website: https://bitbucket.org/kztimerglobalteam/gokz +*/ + +#if defined _gokz_included_ +#endinput +#endif +#define _gokz_included_ +#include <cstrike> +#include <movement> + +#include <gokz/version> + + + +// =====[ ENUMS ]===== + +enum ObsMode +{ + ObsMode_None = 0, // Not in spectator mode + ObsMode_DeathCam, // Special mode for death cam animation + ObsMode_FreezeCam, // Zooms to a target, and freeze-frames on them + ObsMode_Fixed, // View from a fixed camera position + ObsMode_InEye, // Follow a player in first person view + ObsMode_Chase, // Follow a player in third person view + ObsMode_Roaming // Free roaming +}; + + + +// =====[ CONSTANTS ]===== + +#define GOKZ_SOURCE_URL "https://github.com/KZGlobalTeam/gokz" +#define GOKZ_UPDATER_BASE_URL "http://updater.gokz.org/v2/" +#define GOKZ_COLLISION_GROUP_STANDARD 2 +#define GOKZ_COLLISION_GROUP_NOTRIGGER 1 +#define GOKZ_TP_FREEZE_TICKS 5 +#define EPSILON 0.000001 +#define PI 3.14159265359 +#define SPEED_NORMAL 250.0 +#define SPEED_NO_WEAPON 260.0 +#define FLOAT_MAX view_as<float>(0x7F7FFFFF) +#define SF_BUTTON_USE_ACTIVATES 1024 +#define IGNORE_JUMP_TIME 0.2 +stock float PLAYER_MINS[3] = {-16.0, -16.0, 0.0}; +stock float PLAYER_MAXS[3] = {16.0, 16.0, 72.0}; +stock float PLAYER_MAXS_DUCKED[3] = {16.0, 16.0, 54.0}; + + + +// =====[ STOCKS ]===== + +/** + * Represents a time float as a string e.g. 01:23.45. + * + * @param time Time in seconds. + * @param precise Whether to include fractional seconds. + * @return String representation of time. + */ +stock char[] GOKZ_FormatTime(float time, bool precise = true) +{ + char formattedTime[12]; + + int roundedTime = RoundFloat(time * 100); // Time rounded to number of centiseconds + + int centiseconds = roundedTime % 100; + roundedTime = (roundedTime - centiseconds) / 100; + int seconds = roundedTime % 60; + roundedTime = (roundedTime - seconds) / 60; + int minutes = roundedTime % 60; + roundedTime = (roundedTime - minutes) / 60; + int hours = roundedTime; + + if (hours == 0) + { + if (precise) + { + FormatEx(formattedTime, sizeof(formattedTime), "%02d:%02d.%02d", minutes, seconds, centiseconds); + } + else + { + FormatEx(formattedTime, sizeof(formattedTime), "%d:%02d", minutes, seconds); + } + } + else + { + if (precise) + { + FormatEx(formattedTime, sizeof(formattedTime), "%d:%02d:%02d.%02d", hours, minutes, seconds, centiseconds); + } + else + { + FormatEx(formattedTime, sizeof(formattedTime), "%d:%02d:%02d", hours, minutes, seconds); + } + } + return formattedTime; +} + +/** + * Checks if the value is a valid client entity index, if they are in-game and not GOTV. + * + * @param client Client index. + * @return Whether client is valid. + */ +stock bool IsValidClient(int client) +{ + return client >= 1 && client <= MaxClients && IsClientInGame(client) && !IsClientSourceTV(client); +} + +/** + * Returns the greater of two float values. + * + * @param value1 First value. + * @param value2 Second value. + * @return Greatest value. + */ +stock float FloatMax(float value1, float value2) +{ + if (value1 >= value2) + { + return value1; + } + return value2; +} + +/** + * Returns the lesser of two float values. + * + * @param value1 First value. + * @param value2 Second value. + * @return Lesser value. + */ +stock float FloatMin(float value1, float value2) +{ + if (value1 <= value2) + { + return value1; + } + return value2; +} + +/** + * Clamp a float value between an upper and lower bound. + * + * @param value Preferred value. + * @param min Minimum value. + * @param max Maximum value. + * @return The closest value to the preferred value. + */ +stock float FloatClamp(float value, float min, float max) +{ + if (value >= max) + { + return max; + } + if (value <= min) + { + return min; + } + return value; +} + + +/** + * Returns the greater of two int values. + * + * @param value1 First value. + * @param value2 Second value. + * @return Greatest value. + */ +stock int IntMax(int value1, int value2) +{ + if (value1 >= value2) + { + return value1; + } + return value2; +} + +/** + * Returns the lesser of two int values. + * + * @param value1 First value. + * @param value2 Second value. + * @return Lesser value. + */ +stock int IntMin(int value1, int value2) +{ + if (value1 <= value2) + { + return value1; + } + return value2; +} + +/** + * Rounds a float to the nearest specified power of 10. + * + * @param value Value to round. + * @param power Power of 10 to round to. + * @return Rounded value. + */ +stock float RoundToPowerOfTen(float value, int power) +{ + float pow = Pow(10.0, float(power)); + return RoundFloat(value / pow) * pow; +} + +/** + * Sets all characters in a string to lower case. + * + * @param input Input string. + * @param output Output buffer. + * @param size Maximum size of output. + */ +stock void String_ToLower(const char[] input, char[] output, int size) +{ + size--; + int i = 0; + while (input[i] != '\0' && i < size) + { + output[i] = CharToLower(input[i]); + i++; + } + output[i] = '\0'; +} + +/** + * Gets the client's observer mode. + * + * @param client Client index. + * @return Current observer mode. + */ +stock ObsMode GetObserverMode(int client) +{ + return view_as<ObsMode>(GetEntProp(client, Prop_Send, "m_iObserverMode")); +} + +/** + * Gets the player a client is spectating. + * + * @param client Client index. + * @return Client index of target, or -1 if not spectating anyone. + */ +stock int GetObserverTarget(int client) +{ + if (!IsValidClient(client)) + { + return -1; + } + ObsMode mode = GetObserverMode(client); + if (mode == ObsMode_InEye || mode == ObsMode_Chase) + { + return GetEntPropEnt(client, Prop_Send, "m_hObserverTarget"); + } + return -1; +} + +/** + * Emits a sound to other players that are spectating the client. + * + * @param client Client being spectated. + * @param sound Sound to play. + */ +stock void EmitSoundToClientSpectators(int client, const char[] sound) +{ + for (int i = 1; i <= MaxClients; i++) + { + if (IsValidClient(i) && GetObserverTarget(i) == client) + { + EmitSoundToClient(i, sound); + } + } +} + +/** + * Calculates the lowest angle from angle A to angle B. + * Input and result angles are between -180 and 180. + * + * @param angleA Angle A. + * @param angleB Angle B. + * @return Delta angle. + */ +stock float CalcDeltaAngle(float angleA, float angleB) +{ + float difference = angleB - angleA; + + if (difference > 180.0) + { + difference = difference - 360.0; + } + else if (difference <= -180.0) + { + difference = difference + 360.0; + } + + return difference; +} + +/** + * Strips all color control characters in a string. + * The Output buffer can be the same as the input buffer. + * Original code by Psychonic, thanks. + * Source: smlib + * + * @param input Input String. + * @param output Output String. + * @param size Max Size of the Output string + */ +stock void Color_StripFromChatText(const char[] input, char[] output, int size) +{ + int x = 0; + for (int i = 0; input[i] != '\0'; i++) { + + if (x + 1 == size) + { + break; + } + + int character = input[i]; + + if (character > 0x08) + { + output[x++] = character; + } + } + + output[x] = '\0'; +} + +/** + * Returns an integer as a string. + * + * @param num Integer to stringify. + * @return Integer as a string. + */ +stock char[] IntToStringEx(int num) +{ + char string[12]; + IntToString(num, string, sizeof(string)); + return string; +} + +/** + * Returns a float as a string. + * + * @param num Float to stringify. + * @return Float as a string. + */ +stock char[] FloatToStringEx(float num) +{ + char string[32]; + FloatToString(num, string, sizeof(string)); + return string; +} + +/** + * Increment an index, looping back to 0 if the max value is reached. + * + * @param index Current index. + * @param buffer Max value of index. + * @return Current index incremented, or 0 if max value is reached. + */ +stock int NextIndex(int index, int max) +{ + index++; + if (index == max) + { + return 0; + } + return index; +} + +/** + * Reorders an array with current index at the front, and previous + * values after, including looping back to the end after reaching + * the start of the array. + * + * @param input Array to reorder. + * @param inputSize Size of input array. + * @param buffer Output buffer. + * @param bufferSize Size of buffer. + * @param index Index of current/most recent value of input array. + */ +stock void SortByRecent(const int[] input, int inputSize, int[] buffer, int bufferSize, int index) +{ + int reorderedIndex = 0; + for (int i = index; reorderedIndex < bufferSize && i >= 0; i--) + { + buffer[reorderedIndex] = input[i]; + reorderedIndex++; + } + for (int i = inputSize - 1; reorderedIndex < bufferSize && i > index; i--) + { + buffer[reorderedIndex] = input[i]; + reorderedIndex++; + } +} + +/** + * Returns the Steam account ID for a given SteamID2. + * Checks for invalid input are not very extensive. + * + * @param steamID2 SteamID2 to convert. + * @return Steam account ID, or -1 if invalid. + */ +stock int Steam2ToSteamAccountID(const char[] steamID2) +{ + char pieces[3][16]; + if (ExplodeString(steamID2, ":", pieces, sizeof(pieces), sizeof(pieces[])) != 3) + { + return -1; + } + + int IDNumberPart1 = StringToInt(pieces[1]); + int IDNumberPart2 = StringToInt(pieces[2]); + if (pieces[1][0] != '0' && IDNumberPart1 == 0 || IDNumberPart1 != 0 && IDNumberPart1 != 1 || IDNumberPart2 <= 0) + { + return -1; + } + + return IDNumberPart1 + (IDNumberPart2 << 1); +} + +/** + * Teleports a player and removes their velocity and base velocity + * immediately and also every tick for the next 5 ticks. Automatically + * makes the player crouch if there is a ceiling above them. + * + * @param client Client index. + * @param origin Origin to teleport to. + * @param angles Eye angles to set. + */ +stock void TeleportPlayer(int client, const float origin[3], const float angles[3], bool setAngles = true, bool holdStill = true) +{ + // Clear the player's parent before teleporting to fix being + // teleported into seemingly random places if the player has a parent. + AcceptEntityInput(client, "ClearParent"); + + Movement_SetOrigin(client, origin); + Movement_SetVelocity(client, view_as<float>( { 0.0, 0.0, 0.0 } )); + Movement_SetBaseVelocity(client, view_as<float>( { 0.0, 0.0, 0.0 } )); + if (setAngles) + { + // NOTE: changing angles with TeleportEntity can fail due to packet loss!!! + // (Movement_SetEyeAngles is a thin wrapper of TeleportEntity) + Movement_SetEyeAngles(client, angles); + } + // Duck the player if there is something blocking them from above + Handle trace = TR_TraceHullFilterEx(origin, + origin, + view_as<float>( { -16.0, -16.0, 0.0 } ), // Standing players are 32 x 32 x 72 + view_as<float>( { 16.0, 16.0, 72.0 } ), + MASK_PLAYERSOLID, + TraceEntityFilterPlayers, + client); + bool ducked = TR_DidHit(trace); + + if (holdStill) + { + // Prevent noclip exploit + SetEntProp(client, Prop_Send, "m_CollisionGroup", GOKZ_COLLISION_GROUP_STANDARD); + + // Intelligently hold player still to prevent booster and trigger exploits + StartHoldStill(client, ducked); + } + else if (ducked) + { + ForcePlayerDuck(client); + } + + delete trace; +} + +static void StartHoldStill(int client, bool ducked) +{ + DataPack data = new DataPack(); + data.WriteCell(GetClientUserId(client)); + data.WriteCell(0); // tick counter + data.WriteCell(GOKZ_TP_FREEZE_TICKS); // number of ticks to hold still + data.WriteCell(ducked); + ContinueHoldStill(data); +} + +public void ContinueHoldStill(DataPack data) +{ + data.Reset(); + int client = GetClientOfUserId(data.ReadCell()); + int ticks = data.ReadCell(); + int tickCount = data.ReadCell(); + bool ducked = data.ReadCell(); + delete data; + + if (!IsValidClient(client)) + { + return; + } + + if (ticks < tickCount) + { + Movement_SetVelocity(client, view_as<float>( { 0.0, 0.0, 0.0 } )); + Movement_SetBaseVelocity(client, view_as<float>( { 0.0, 0.0, 0.0 } )); + Movement_SetGravity(client, 1.0); + + // Don't drop the player off of ladders. + // The game will automatically change the movetype back to MOVETYPE_WALK if it can't find a ladder. + // Don't change the movetype if it's currently MOVETYPE_NONE, as that means the player is paused. + if (Movement_GetMovetype(client) != MOVETYPE_NONE) + { + Movement_SetMovetype(client, MOVETYPE_LADDER); + } + + // Prevent noclip exploit + SetEntProp(client, Prop_Send, "m_CollisionGroup", GOKZ_COLLISION_GROUP_STANDARD); + + // Force duck on player and make sure that the player can't trigger triggers above them. + // they can still trigger triggers even when we force ducking. + if (ducked) + { + ForcePlayerDuck(client); + + if (ticks < tickCount - 1) + { + // Don't trigger triggers + SetEntProp(client, Prop_Send, "m_CollisionGroup", GOKZ_COLLISION_GROUP_NOTRIGGER); + } + else + { + // Let the player trigger triggers on the last tick + SetEntProp(client, Prop_Send, "m_CollisionGroup", GOKZ_COLLISION_GROUP_STANDARD); + } + } + + ++ticks; + data = new DataPack(); + data.WriteCell(GetClientUserId(client)); + data.WriteCell(ticks); + data.WriteCell(tickCount); + data.WriteCell(ducked); + RequestFrame(ContinueHoldStill, data); + } +} + +/** + * Forces the player to instantly duck. + * + * @param client Client index. + */ +stock void ForcePlayerDuck(int client) +{ + // these are both necessary, because on their own the player will sometimes still be in a state that isn't fully ducked. + SetEntPropFloat(client, Prop_Send, "m_flDuckAmount", 1.0, 0); + SetEntProp(client, Prop_Send, "m_bDucking", false); + SetEntProp(client, Prop_Send, "m_bDucked", true); +} + +/** + * Returns whether the player is stuck e.g. in a wall after noclipping. + * + * @param client Client index. + * @return Whether player is stuck. + */ +stock bool IsPlayerStuck(int client) +{ + float vecMin[3], vecMax[3], vecOrigin[3]; + + GetClientMins(client, vecMin); + GetClientMaxs(client, vecMax); + GetClientAbsOrigin(client, vecOrigin); + + TR_TraceHullFilter(vecOrigin, vecOrigin, vecMin, vecMax, MASK_PLAYERSOLID, TraceEntityFilterPlayers); + return TR_DidHit(); // head in wall ? +} + +/** + * Retrieves the absolute origin of an entity. + * + * @param entity Index of the entity. + * @param result Entity's origin if successful. + * @return Returns true if successful. + */ +stock bool GetEntityAbsOrigin(int entity, float result[3]) +{ + if (!IsValidEntity(entity)) + { + return false; + } + + if (!HasEntProp(entity, Prop_Data, "m_vecAbsOrigin")) + { + return false; + } + + GetEntPropVector(entity, Prop_Data, "m_vecAbsOrigin", result); + return true; +} + +/** + * Retrieves the name of an entity. + * + * @param entity Index of the entity. + * @param buffer Buffer to store the name. + * @param maxlength Maximum length of the buffer. + * @return Number of non-null bytes written. + */ +stock int GetEntityName(int entity, char[] buffer, int maxlength) +{ + return GetEntPropString(entity, Prop_Data, "m_iName", buffer, maxlength); +} + +/** + * Finds an entity by name or by name and classname. + * Taken from smlib https://github.com/bcserv/smlib + * This can take anywhere from ~0.2% to ~11% of frametime (i5-7600k) in the worst case scenario where + * every entity which has a name (4096 of them) is iterated over. Your mileage may vary. + * + * @param name Name of the entity to find. + * @param className Optional classname to match along with name. + * @param ignorePlayers Ignore player entities. + * @return Entity index if successful, INVALID_ENT_REFERENCE if not. + */ +stock int GOKZFindEntityByName(const char[] name, const char[] className = "", bool ignorePlayers = false) +{ + int result = INVALID_ENT_REFERENCE; + if (className[0] == '\0') + { + // HACK: Double the limit to get non-networked entities too. + // https://developer.valvesoftware.com/wiki/Entity_limit + int realMaxEntities = GetMaxEntities() * 2; + int startEntity = 1; + if (ignorePlayers) + { + startEntity = MaxClients + 1; + } + for (int entity = startEntity; entity < realMaxEntities; entity++) + { + if (!IsValidEntity(entity)) + { + continue; + } + + char entName[65]; + GetEntityName(entity, entName, sizeof(entName)); + if (StrEqual(entName, name)) + { + result = entity; + break; + } + } + } + else + { + int entity = INVALID_ENT_REFERENCE; + while ((entity = FindEntityByClassname(entity, className)) != INVALID_ENT_REFERENCE) + { + char entName[65]; + GetEntityName(entity, entName, sizeof(entName)); + if (StrEqual(entName, name)) + { + result = entity; + break; + } + } + } + return result; +} + +/** + * Gets the current map's display name in lower case. + * + * @param buffer Buffer to store the map name. + * @param maxlength Maximum length of buffer. + */ +stock void GetCurrentMapDisplayName(char[] buffer, int maxlength) +{ + char map[PLATFORM_MAX_PATH]; + GetCurrentMap(map, sizeof(map)); + GetMapDisplayName(map, map, sizeof(map)); + String_ToLower(map, buffer, maxlength); +} + +/** + * Gets the current map's file size. + */ +stock int GetCurrentMapFileSize() +{ + char mapBuffer[PLATFORM_MAX_PATH]; + GetCurrentMap(mapBuffer, sizeof(mapBuffer)); + Format(mapBuffer, sizeof(mapBuffer), "maps/%s.bsp", mapBuffer); + return FileSize(mapBuffer); +} + +/** + * Copies the elements of a source vector to a destination vector. + * + * @param src Source vector. + * @param dest Destination vector. + */ +stock void CopyVector(const any src[3], any dest[3]) +{ + dest[0] = src[0]; + dest[1] = src[1]; + dest[2] = src[2]; +} + +/** + * Returns whether the player is spectating. + * + * @param client Client index. + */ +stock bool IsSpectating(int client) +{ + int team = GetClientTeam(client); + return team == CS_TEAM_SPECTATOR || team == CS_TEAM_NONE; +} + +/** + * Rotate a vector on an axis. + * + * @param vec Vector to rotate. + * @param axis Axis to rotate around. + * @param theta Angle in radians. + * @param result Rotated vector. + */ +stock void RotateVectorAxis(float vec[3], float axis[3], float theta, float result[3]) +{ + float cosTheta = Cosine(theta); + float sinTheta = Sine(theta); + + float axisVecCross[3]; + GetVectorCrossProduct(axis, vec, axisVecCross); + + for (int i = 0; i < 3; i++) + { + result[i] = (vec[i] * cosTheta) + (axisVecCross[i] * sinTheta) + (axis[i] * GetVectorDotProduct(axis, vec)) * (1.0 - cosTheta); + } +} + +/** + * Rotate a vector by pitch and yaw. + * + * @param vec Vector to rotate. + * @param pitch Pitch angle (in degrees). + * @param yaw Yaw angle (in degrees). + * @param result Rotated vector. + */ +stock void RotateVectorPitchYaw(float vec[3], float pitch, float yaw, float result[3]) +{ + if (pitch != 0.0) + { + RotateVectorAxis(vec, view_as<float>({0.0, 1.0, 0.0}), DegToRad(pitch), result); + } + if (yaw != 0.0) + { + RotateVectorAxis(result, view_as<float>({0.0, 0.0, 1.0}), DegToRad(yaw), result); + } +} + +/** + * Attempts to return a valid spawn location. + * + * @param origin Spawn origin if found. + * @param angles Spawn angles if found. + * @return Whether a valid spawn point is found. + */ +stock bool GetValidSpawn(float origin[3], float angles[3]) +{ + // Return true if the spawn found is truly valid (not in the ground or out of bounds) + bool foundValidSpawn; + bool searchCT; + float spawnOrigin[3]; + float spawnAngles[3]; + int spawnEntity = -1; + while (!foundValidSpawn) + { + if (searchCT) + { + spawnEntity = FindEntityByClassname(spawnEntity, "info_player_counterterrorist"); + } + else + { + spawnEntity = FindEntityByClassname(spawnEntity, "info_player_terrorist"); + } + + if (spawnEntity != -1) + { + GetEntPropVector(spawnEntity, Prop_Data, "m_vecOrigin", spawnOrigin); + GetEntPropVector(spawnEntity, Prop_Data, "m_angRotation", spawnAngles); + if (IsSpawnValid(spawnOrigin)) + { + origin = spawnOrigin; + angles = spawnAngles; + foundValidSpawn = true; + } + } + else if (!searchCT) + { + searchCT = true; + } + else + { + break; + } + } + return foundValidSpawn; +} + +/** + * Check whether a position is a valid spawn location. + * A spawn location is considered valid if it is in bounds and not stuck inside the ground. + * + * @param origin Origin vector. + * @return Whether the origin is a valid spawn location. + */ +stock bool IsSpawnValid(float origin[3]) +{ + Handle trace = TR_TraceHullFilterEx(origin, origin, PLAYER_MINS, PLAYER_MAXS, MASK_PLAYERSOLID, TraceEntityFilterPlayers); + if (!TR_StartSolid(trace) && !TR_AllSolid(trace) && TR_GetFraction(trace) == 1.0) + { + delete trace; + return true; + } + delete trace; + return false; +} + +/** + * Get an entity's origin, angles, its bounding box's center and the distance from the center to its bounding box's edges. + * + * @param entity Index of the entity. + * @param origin Entity's origin. + * @param center Center of the entity's bounding box. + * @param angles Entity's angles. + * @param distFromCenter The distance between the center of the entity's bounding box and its edges. + */ +stock void GetEntityPositions(int entity, float origin[3], float center[3], float angles[3], float distFromCenter[3]) +{ + int ent = entity; + float maxs[3], mins[3]; + GetEntPropVector(ent, Prop_Send, "m_vecOrigin", origin); + // Take parent entities into account. + while (GetEntPropEnt(ent, Prop_Send, "moveparent") != -1) + { + ent = GetEntPropEnt(ent, Prop_Send, "moveparent"); + float tempOrigin[3]; + GetEntPropVector(ent, Prop_Send, "m_vecOrigin", tempOrigin); + for (int i = 0; i < 3; i++) + { + origin[i] += tempOrigin[i]; + } + } + + GetEntPropVector(ent, Prop_Data, "m_angRotation", angles); + + GetEntPropVector(ent, Prop_Send, "m_vecMaxs", maxs); + GetEntPropVector(ent, Prop_Send, "m_vecMins", mins); + for (int i = 0; i < 3; i++) + { + center[i] = origin[i] + (maxs[i] + mins[i]) / 2; + distFromCenter[i] = (maxs[i] - mins[i]) / 2; + } +} + +/** + * Find a valid position around a timer. + * + * @param entity Index of the timer entity. + * @param originDest Result origin if a valid position is found. + * @param anglesDest Result angles if a valid position is found. + * @return Whether a valid position is found. + */ +stock bool FindValidPositionAroundTimerEntity(int entity, float originDest[3], float anglesDest[3], bool isButton) +{ + float origin[3], center[3], angles[3], distFromCenter[3]; + GetEntityPositions(entity, origin, center, angles, distFromCenter); + float extraOffset[3]; + if (isButton) // Test several positions within button press range. + { + extraOffset[0] = 32.0; + extraOffset[1] = 32.0; + extraOffset[2] = 32.0; + } + else // Test positions at the inner surface of the zone. + { + extraOffset[0] = -(PLAYER_MAXS[0] - PLAYER_MINS[0]) - 1.03125; + extraOffset[1] = -(PLAYER_MAXS[1] - PLAYER_MINS[1]) - 1.03125; + extraOffset[2] = -(PLAYER_MAXS[2] - PLAYER_MINS[2]) - 1.03125; + } + if (FindValidPositionAroundCenter(center, distFromCenter, extraOffset, originDest, anglesDest)) + { + return true; + } + // Test the positions right next to the timer button/zones if the tests above fail. + // This can fail when the timer has a cover brush over it. + extraOffset[0] = 0.03125; + extraOffset[1] = 0.03125; + extraOffset[2] = 0.03125; + return FindValidPositionAroundCenter(center, distFromCenter, extraOffset, originDest, anglesDest); +} + +static bool FindValidPositionAroundCenter(float center[3], float distFromCenter[3], float extraOffset[3], float originDest[3], float anglesDest[3]) +{ + float testOrigin[3]; + int x, y; + + for (int i = 0; i < 3; i++) + { + // The search starts from the center then outwards to opposite directions. + x = i == 2 ? -1 : i; + for (int j = 0; j < 3; j++) + { + y = j == 2 ? -1 : j; + for (int z = -1; z <= 1; z++) + { + testOrigin = center; + testOrigin[0] = testOrigin[0] + (distFromCenter[0] + extraOffset[0]) * x + (PLAYER_MAXS[0] - PLAYER_MINS[0]) * x * 0.5; + testOrigin[1] = testOrigin[1] + (distFromCenter[1] + extraOffset[1]) * y + (PLAYER_MAXS[1] - PLAYER_MINS[1]) * y * 0.5; + testOrigin[2] = testOrigin[2] + (distFromCenter[2] + extraOffset[2]) * z + (PLAYER_MAXS[2] - PLAYER_MINS[2]) * z; + + // Check if there's a line of sight towards the zone as well. + if (IsSpawnValid(testOrigin) && CanSeeBox(testOrigin, center, distFromCenter)) + { + originDest = testOrigin; + // Always look towards the center. + float offsetVector[3]; + offsetVector[0] = -(distFromCenter[0] + extraOffset[0]) * x; + offsetVector[1] = -(distFromCenter[1] + extraOffset[1]) * y; + offsetVector[2] = -(distFromCenter[2] + extraOffset[2]) * z; + GetVectorAngles(offsetVector, anglesDest); + anglesDest[2] = 0.0; // Roll should always be 0.0 + return true; + } + } + } + } + return false; +} + +static bool CanSeeBox(float origin[3], float center[3], float distFromCenter[3]) +{ + float traceOrigin[3], traceDest[3], mins[3], maxs[3]; + + CopyVector(origin, traceOrigin); + + + SubtractVectors(center, distFromCenter, mins); + AddVectors(center, distFromCenter, maxs); + + for (int i = 0; i < 3; i++) + { + mins[i] += 0.03125; + maxs[i] -= 0.03125; + traceDest[i] = FloatClamp(traceOrigin[i], mins[i], maxs[i]); + } + int mask = (MASK_NPCSOLID_BRUSHONLY | MASK_OPAQUE_AND_NPCS) & ~CONTENTS_OPAQUE; + Handle trace = TR_TraceRayFilterEx(traceOrigin, traceDest, mask, RayType_EndPoint, TraceEntityFilterPlayers); + if (TR_DidHit(trace)) + { + float end[3]; + TR_GetEndPosition(end, trace); + for (int i = 0; i < 3; i++) + { + if (end[i] != traceDest[i]) + { + delete trace; + return false; + } + } + } + delete trace; + return true; +} + +/** + * Gets entity index from the address to an entity. + * + * @param pEntity Entity address. + * @return Entity index. + * @error Couldn't find offset for m_angRotation, m_vecViewOffset, couldn't confirm offset of m_RefEHandle. + */ +stock int GOKZGetEntityFromAddress(Address pEntity) +{ + static int offs_RefEHandle; + if (offs_RefEHandle) + { + return EntRefToEntIndex(LoadFromAddress(pEntity + view_as<Address>(offs_RefEHandle), NumberType_Int32) | (1 << 31)); + } + + // if we don't have it already, attempt to lookup offset based on SDK information + // CWorld is derived from CBaseEntity so it should have both offsets + int offs_angRotation = FindDataMapInfo(0, "m_angRotation"), offs_vecViewOffset = FindDataMapInfo(0, "m_vecViewOffset"); + if (offs_angRotation == -1) + { + SetFailState("Could not find offset for ((CBaseEntity) CWorld)::m_angRotation"); + } + else if (offs_vecViewOffset == -1) + { + SetFailState("Could not find offset for ((CBaseEntity) CWorld)::m_vecViewOffset"); + } + else if ((offs_angRotation + 0x0C) != (offs_vecViewOffset - 0x04)) + { + char game[32]; + GetGameFolderName(game, sizeof(game)); + SetFailState("Could not confirm offset of CBaseEntity::m_RefEHandle (incorrect assumption for game '%s'?)", game); + } + + // offset seems right, cache it for the next call + offs_RefEHandle = offs_angRotation + 0x0C; + return GOKZGetEntityFromAddress(pEntity); +} + +/** + * Gets client index from CGameMovement class. + * + * @param addr Address of CGameMovement class. + * @param offsetCGameMovement_player Offset of CGameMovement::player. + * @return Client index. + * @error Couldn't find offset for m_angRotation, m_vecViewOffset, couldn't confirm offset of m_RefEHandle. + */ +stock int GOKZGetClientFromGameMovementAddress(Address addr, int offsetCGameMovement_player) +{ + Address playerAddr = view_as<Address>(LoadFromAddress(view_as<Address>(view_as<int>(addr) + offsetCGameMovement_player), NumberType_Int32)); + return GOKZGetEntityFromAddress(playerAddr); +} + +/** + * Gets the nearest point in the oriented bounding box of an entity to a point. + * + * @param entity Entity index. + * @param origin Point's origin. + * @param result Result point. + */ +stock void CalcNearestPoint(int entity, float origin[3], float result[3]) +{ + float entOrigin[3], entMins[3], entMaxs[3], trueMins[3], trueMaxs[3]; + GetEntPropVector(entity, Prop_Send, "m_vecOrigin", entOrigin); + GetEntPropVector(entity, Prop_Send, "m_vecMaxs", entMaxs); + GetEntPropVector(entity, Prop_Send, "m_vecMins", entMins); + + AddVectors(entOrigin, entMins, trueMins); + AddVectors(entOrigin, entMaxs, trueMaxs); + + for (int i = 0; i < 3; i++) + { + result[i] = FloatClamp(origin[i], trueMins[i], trueMaxs[i]); + } +} + +/** + * Get the shortest distance from P to the (infinite) line through vLineA and vLineB. + * + * @param P Point's origin. + * @param vLineA Origin of the first point of the line. + * @param vLineB Origin of the first point of the line. + * @return The shortest distance from the point to the line. + */ +stock float CalcDistanceToLine(float P[3], float vLineA[3], float vLineB[3]) +{ + float vClosest[3]; + float vDir[3]; + float t; + float delta[3]; + SubtractVectors(vLineB, vLineA, vDir); + float div = GetVectorDotProduct(vDir, vDir); + if (div < EPSILON) + { + t = 0.0; + } + else + { + t = (GetVectorDotProduct(vDir, P) - GetVectorDotProduct(vDir, vLineA)) / div; + } + for (int i = 0; i < 3; i++) + { + vClosest[i] = vLineA[i] + vDir[i]*t; + } + SubtractVectors(P, vClosest, delta); + return GetVectorLength(delta); +} + +/** + * Gets the ideal amount of time the text should be held for HUD messages. + * + * The message buffer is only 16 slots long, and it is shared between 6 channels maximum. + * Assuming a message is sent every game frame, each channel used should be only taking around 2.5 slots on average. + * This also assumes all channels are used equally (so no other plugin taking all the channel buffer for itself). + * We want to use as much of the message buffer as possible to take into account latency variances. + * + * @param interval HUD message update interval, in tick intervals. + * @return How long the text should be held for. + */ +stock float GetTextHoldTime(int interval) +{ + return 3 * interval * GetTickInterval(); +} diff --git a/sourcemod/scripting/include/gokz/anticheat.inc b/sourcemod/scripting/include/gokz/anticheat.inc new file mode 100644 index 0000000..7fa5409 --- /dev/null +++ b/sourcemod/scripting/include/gokz/anticheat.inc @@ -0,0 +1,168 @@ +/* + gokz-anticheat Plugin Include + + Website: https://bitbucket.org/kztimerglobalteam/gokz +*/ + +#if defined _gokz_anticheat_included_ +#endinput +#endif +#define _gokz_anticheat_included_ + + + +// =====[ ENUMS ]===== + +enum ACReason: +{ + ACReason_BhopMacro = 0, + ACReason_BhopHack, + ACREASON_COUNT +}; + + + +// =====[ CONSTANTS ]===== + +#define AC_MAX_BUTTON_SAMPLES 40 +#define AC_MAX_BHOP_GROUND_TICKS 8 +#define AC_MAX_BHOP_SAMPLES 30 +#define AC_BINDEXCEPTION_SAMPLES 5 +#define AC_LOG_PATH "logs/gokz-anticheat.log" + +stock char gC_ACReasons[ACREASON_COUNT][] = +{ + "BHop Macro", + "BHop Hack" +}; + + + +// =====[ FORWARDS ]===== + +/** + * Called when gokz-anticheat suspects a player of cheating. + * + * @param client Client index. + * @param reason Reason for suspicion. + * @param notes Additional reasoning, description etc. + * @param stats Data supporting the suspicion e.g. scroll pattern. + */ +forward void GOKZ_AC_OnPlayerSuspected(int client, ACReason reason, const char[] notes, const char[] stats); + + + +// =====[ NATIVES ]===== + +/** + * Gets the number of recent bhop samples available for a player. + * + * @param client Client index. + * @return Number of bhop samples available. + */ +native int GOKZ_AC_GetSampleSize(int client); + +/** + * Gets whether a player hit a perfect bhop for a number of + * recent bhops. Buffer must be large enough to fit the sample + * size. + * + * @param client Client index. + * @param buffer Buffer for perfect bhop booleans, with the first element being the most recent bhop. + * @param sampleSize Maximum recent bhop samples. + * @return Number of bhop samples. + */ +native int GOKZ_AC_GetHitPerf(int client, bool[] buffer, int sampleSize); + +/** + * Gets a player's number of perfect bhops out of a sample + * size of bhops. + * + * @param client Client index. + * @param sampleSize Maximum recent bhop samples to include in calculation. + * @return Player's number of perfect bhops. + */ +native int GOKZ_AC_GetPerfCount(int client, int sampleSize); + +/** + * Gets a player's ratio of perfect bhops to normal bhops. + * + * @param client Client index. + * @param sampleSize Maximum recent bhop samples to include in calculation. + * @return Player's ratio of perfect bhops to normal bhops. + */ +native float GOKZ_AC_GetPerfRatio(int client, int sampleSize); + +/** + * Gets a player's jump input counts for a number of recent + * bhops. Buffer must be large enough to fit the sample size. + * + * @param client Client index. + * @param buffer Buffer for jump input counts, with the first element being the most recent bhop. + * @param sampleSize Maximum recent bhop samples. + * @return Number of bhop samples. + */ +native int GOKZ_AC_GetJumpInputs(int client, int[] buffer, int sampleSize); + +/** + * Gets a player's average number of jump inputs for a number + * of recent bhops. + * + * @param client Client index. + * @param sampleSize Maximum recent bhop samples to include in calculation. + * @return Player's average number of jump inputs. + */ +native float GOKZ_AC_GetAverageJumpInputs(int client, int sampleSize); + +/** + * Gets a player's jump input counts prior to a number of recent + * bhops. Buffer must be large enough to fit the sample size. + * Includes the jump input that resulted in the jump. + * + * @param client Client index. + * @param buffer Buffer for jump input counts, with the first element being the most recent bhop. + * @param sampleSize Maximum recent bhop samples. + * @return Number of bhop samples. + */ +native int GOKZ_AC_GetPreJumpInputs(int client, int[] buffer, int sampleSize); + +/** + * Gets a player's jump input counts after a number of recent + * bhops. Buffer must be large enough to fit the sample size. + * Excludes the jump input that resulted in the jump. + * + * @param client Client index. + * @param buffer Buffer for jump input counts, with the first element being the most recent bhop. + * @param sampleSize Maximum recent bhop samples. + * @return Number of bhop samples. + */ +native int GOKZ_AC_GetPostJumpInputs(int client, int[] buffer, int sampleSize); + + + +// =====[ DEPENDENCY ]===== + +public SharedPlugin __pl_gokz_anticheat = +{ + name = "gokz-anticheat", + file = "gokz-anticheat.smx", + #if defined REQUIRE_PLUGIN + required = 1, + #else + required = 0, + #endif +}; + +#if !defined REQUIRE_PLUGIN +public void __pl_gokz_anticheat_SetNTVOptional() +{ + MarkNativeAsOptional("GOKZ_AC_GetSampleSize"); + MarkNativeAsOptional("GOKZ_AC_GetHitPerf"); + MarkNativeAsOptional("GOKZ_AC_GetPerfCount"); + MarkNativeAsOptional("GOKZ_AC_GetPerfRatio"); + MarkNativeAsOptional("GOKZ_AC_GetJumpInputs"); + MarkNativeAsOptional("GOKZ_AC_GetAverageJumpInputs"); + MarkNativeAsOptional("GOKZ_AC_GetPreJumpInputs"); + MarkNativeAsOptional("GOKZ_AC_GetPostJumpInputs"); +} +#endif
\ No newline at end of file diff --git a/sourcemod/scripting/include/gokz/chat.inc b/sourcemod/scripting/include/gokz/chat.inc new file mode 100644 index 0000000..0264a57 --- /dev/null +++ b/sourcemod/scripting/include/gokz/chat.inc @@ -0,0 +1,45 @@ +/* + gokz-chat Plugin Include + + Website: https://bitbucket.org/kztimerglobalteam/gokz +*/ + +#if defined _gokz_chat_included_ +#endinput +#endif +#define _gokz_chat_included_ + + + +// =====[ NATIVES ]===== + +/** + * Gets whether a mode is loaded. + * + * @param client Client. + * @param tag Tag to prepend to the player name in chat. + * @param color Color to use for the tag. + */ +native void GOKZ_CH_SetChatTag(int client, const char[] tag, const char[] color); + + + +// =====[ DEPENDENCY ]===== + +public SharedPlugin __pl_gokz_chat = +{ + name = "gokz-chat", + file = "gokz-chat.smx", + #if defined REQUIRE_PLUGIN + required = 1, + #else + required = 0, + #endif +}; + +#if !defined REQUIRE_PLUGIN +public void __pl_gokz_chat_SetNTVOptional() +{ + MarkNativeAsOptional("GOKZ_CH_SetChatTag"); +} +#endif
\ No newline at end of file diff --git a/sourcemod/scripting/include/gokz/core.inc b/sourcemod/scripting/include/gokz/core.inc new file mode 100644 index 0000000..fb450d1 --- /dev/null +++ b/sourcemod/scripting/include/gokz/core.inc @@ -0,0 +1,1920 @@ +/* + gokz-core Plugin Include + + Website: https://bitbucket.org/kztimerglobalteam/gokz +*/ + +#if defined _gokz_core_included_ +#endinput +#endif +#define _gokz_core_included_ + +#include <cstrike> +#include <regex> +#include <topmenus> + +#include <gokz> + + + +// =====[ ENUMS ]===== + +enum +{ + TimeType_Nub = 0, + TimeType_Pro, + TIMETYPE_COUNT +}; + +enum +{ + MapPrefix_Other = 0, + MapPrefix_KZPro, + MAPPREFIX_COUNT +}; + +enum StartPositionType: +{ + StartPositionType_Spawn, + StartPositionType_Custom, + StartPositionType_MapButton, + StartPositionType_MapStart, + STARTPOSITIONTYPE_COUNT +}; + +enum CourseTimerType: +{ + CourseTimerType_None, + CourseTimerType_Default, + CourseTimerType_Button, + CourseTimerType_ZoneLegacy, + CourseTimerType_ZoneNew, + CourseTimerType_COUNT +}; + +enum OptionProp: +{ + OptionProp_Cookie = 0, + OptionProp_Type, + OptionProp_DefaultValue, + OptionProp_MinValue, + OptionProp_MaxValue, + OPTIONPROP_COUNT +}; + +enum OptionType: +{ + OptionType_Int = 0, + OptionType_Float +}; + +enum Option: +{ + OPTION_INVALID = -1, + Option_Mode, + Option_Style, + Option_CheckpointMessages, + Option_CheckpointSounds, + Option_TeleportSounds, + Option_ErrorSounds, + Option_VirtualButtonIndicators, + Option_TimerButtonZoneType, + Option_ButtonThroughPlayers, + Option_Safeguard, + OPTION_COUNT +}; + +enum +{ + Mode_Vanilla = 0, + Mode_SimpleKZ, + Mode_KZTimer, + MODE_COUNT +}; + +enum +{ + Style_Normal = 0, + STYLE_COUNT +}; + +enum +{ + CheckpointMessages_Disabled = 0, + CheckpointMessages_Enabled, + CHECKPOINTMESSAGES_COUNT +}; + +enum +{ + CheckpointSounds_Disabled = 0, + CheckpointSounds_Enabled, + CHECKPOINTSOUNDS_COUNT +}; + +enum +{ + TeleportSounds_Disabled = 0, + TeleportSounds_Enabled, + TELEPORTSOUNDS_COUNT +}; + +enum +{ + ErrorSounds_Disabled = 0, + ErrorSounds_Enabled, + ERRORSOUNDS_COUNT +}; + +enum +{ + VirtualButtonIndicators_Disabled = 0, + VirtualButtonIndicators_Enabled, + VIRTUALBUTTONINDICATORS_COUNT +}; + +enum +{ + TimerButtonZoneType_BothButtons = 0, + TimerButtonZoneType_EndZone, + TimerButtonZoneType_BothZones, + TIMERBUTTONZONETYPE_COUNT +}; + +enum +{ + ButtonThroughPlayers_Disabled = 0, + ButtonThroughPlayers_Enabled, + BUTTONTHROUGHPLAYERS_COUNT +}; + +enum +{ + Safeguard_Disabled = 0, + Safeguard_EnabledNUB, + Safeguard_EnabledPRO, + SAFEGUARD_COUNT +}; + +enum +{ + ModeCVar_Accelerate = 0, + ModeCVar_AccelerateUseWeaponSpeed, + ModeCVar_AirAccelerate, + ModeCVar_AirMaxWishSpeed, + ModeCVar_EnableBunnyhopping, + ModeCVar_Friction, + ModeCVar_Gravity, + ModeCVar_JumpImpulse, + ModeCVar_LadderScaleSpeed, + ModeCVar_LedgeMantleHelper, + ModeCVar_MaxSpeed, + ModeCVar_MaxVelocity, + ModeCVar_StaminaJumpCost, + ModeCVar_StaminaLandCost, + ModeCVar_StaminaMax, + ModeCVar_StaminaRecoveryRate, + ModeCVar_StandableNormal, + ModeCVar_TimeBetweenDucks, + ModeCVar_WalkableNormal, + ModeCVar_WaterAccelerate, + MoveCVar_WaterMoveSpeedMultiplier, + MoveCVar_WaterSwimMode, + MoveCVar_WeaponEncumbrancePerItem, + ModeCVar_WeaponEncumbranceScale, + MODECVAR_COUNT +}; + +// NOTE: gokz-core/map/entlump.sp +enum EntlumpTokenType +{ + EntlumpTokenType_OpenBrace, // { + EntlumpTokenType_CloseBrace, // } + EntlumpTokenType_Identifier, // everything that's inside quotations + EntlumpTokenType_Unknown, + EntlumpTokenType_EndOfStream +}; + +// NOTE: gokz-core/map/triggers.sp +// NOTE: corresponds to climb_teleport_type in kz_mapping_api.fgd +enum TeleportType +{ + TeleportType_Invalid = -1, + TeleportType_Normal, + TeleportType_MultiBhop, + TeleportType_SingleBhop, + TeleportType_SequentialBhop, + TELEPORTTYPE_COUNT +}; + +enum TriggerType +{ + TriggerType_Invalid = 0, + TriggerType_Teleport, + TriggerType_Antibhop +}; + + + +// =====[ CONSTANTS ]===== + +#define GOKZ_CHECKPOINT_VERSION 2 +#define GOKZ_MAX_CHECKPOINTS 2048 +#define GOKZ_MAX_COURSES 100 + +#define GOKZ_BHOP_NO_CHECKPOINT_TIME 0.15 +#define GOKZ_MULT_NO_CHECKPOINT_TIME 0.11 +#define GOKZ_LADDER_NO_CHECKPOINT_TIME 1.5 +#define GOKZ_PAUSE_COOLDOWN 1.0 +#define GOKZ_TIMER_START_NO_TELEPORT_TICKS 4 +#define GOKZ_TIMER_START_GROUND_TICKS 4 +#define GOKZ_TIMER_START_NOCLIP_TICKS 4 +#define GOKZ_JUMPSTATS_NOCLIP_RESET_TICKS 4 +#define GOKZ_TIMER_SOUND_COOLDOWN 0.15 +#define GOKZ_VIRTUAL_BUTTON_USE_DETECTION_TIME 2.0 +#define GOKZ_TURNBIND_COOLDOWN 0.3 + +#define GOKZ_MAPPING_API_VERSION_NONE 0 // the map doesn't have a mapping api version +#define GOKZ_MAPPING_API_VERSION 1 + +#define GOKZ_ANTI_BHOP_TRIGGER_DEFAULT_DELAY 0.2 +#define GOKZ_TELEPORT_TRIGGER_DEFAULT_TYPE TeleportType_Normal +#define GOKZ_TELEPORT_TRIGGER_DEFAULT_DELAY 0.0 +#define GOKZ_TELEPORT_TRIGGER_DEFAULT_USE_DEST_ANGLES true +#define GOKZ_TELEPORT_TRIGGER_DEFAULT_RESET_SPEED true +#define GOKZ_TELEPORT_TRIGGER_DEFAULT_RELATIVE_DESTINATION false +#define GOKZ_TELEPORT_TRIGGER_DEFAULT_REORIENT_PLAYER false +#define GOKZ_TELEPORT_TRIGGER_BHOP_MIN_DELAY 0.08 + +#define GOKZ_SOUND_CHECKPOINT "buttons/blip1.wav" +#define GOKZ_SOUND_TELEPORT "buttons/blip1.wav" +#define GOKZ_SOUND_TIMER_STOP "buttons/button18.wav" + +#define GOKZ_START_NAME "climb_start" +#define GOKZ_BONUS_START_NAME_REGEX "^climb_bonus(\\d+)_start$" +#define GOKZ_BONUS_END_NAME_REGEX "^climb_bonus(\\d+)_end$" + +#define GOKZ_START_BUTTON_NAME "climb_startbutton" +#define GOKZ_END_BUTTON_NAME "climb_endbutton" +#define GOKZ_BONUS_START_BUTTON_NAME_REGEX "^climb_bonus(\\d+)_startbutton$" +#define GOKZ_BONUS_END_BUTTON_NAME_REGEX "^climb_bonus(\\d+)_endbutton$" +#define GOKZ_ANTI_BHOP_TRIGGER_NAME "climb_anti_bhop" +#define GOKZ_ANTI_CP_TRIGGER_NAME "climb_anti_checkpoint" +#define GOKZ_ANTI_PAUSE_TRIGGER_NAME "climb_anti_pause" +#define GOKZ_ANTI_JUMPSTAT_TRIGGER_NAME "climb_anti_jumpstat" +#define GOKZ_BHOP_RESET_TRIGGER_NAME "climb_bhop_reset" +#define GOKZ_TELEPORT_TRIGGER_NAME "climb_teleport" + +#define GOKZ_START_ZONE_NAME "climb_startzone" +#define GOKZ_END_ZONE_NAME "climb_endzone" +#define GOKZ_BONUS_START_ZONE_NAME_REGEX "^climb_bonus(\\d+)_startzone$" +#define GOKZ_BONUS_END_ZONE_NAME_REGEX "^climb_bonus(\\d+)_endzone$" + +#define GOKZ_CFG_SERVER "sourcemod/gokz/gokz.cfg" +#define GOKZ_CFG_OPTIONS "cfg/sourcemod/gokz/options.cfg" +#define GOKZ_CFG_OPTIONS_SORTING "cfg/sourcemod/gokz/options_menu_sorting.cfg" +#define GOKZ_CFG_OPTIONS_ROOT "Options" +#define GOKZ_CFG_OPTIONS_DESCRIPTION "description" +#define GOKZ_CFG_OPTIONS_DEFAULT "default" + +#define GOKZ_OPTION_MAX_NAME_LENGTH 30 +#define GOKZ_OPTION_MAX_DESC_LENGTH 255 +#define GENERAL_OPTION_CATEGORY "General" + +// TODO: where do i put the defines? +#define GOKZ_BSP_HEADER_IDENTIFIER (('P' << 24) | ('S' << 16) | ('B' << 8) | 'V') +#define GOKZ_ENTLUMP_MAX_KEY 32 +#define GOKZ_ENTLUMP_MAX_VALUE 1024 + +#define GOKZ_MAX_MAPTRIGGERS_ERROR_LENGTH 256 + +#define CHAR_ESCAPE view_as<char>(27) + +#define GOKZ_SAFEGUARD_RESTART_MIN_DELAY 0.6 +#define GOKZ_SAFEGUARD_RESTART_MAX_DELAY 5.0 + +// Prevents the player from retouching a trigger too often. +#define GOKZ_MAX_RETOUCH_TRIGGER_COUNT 4 + +stock char gC_TimeTypeNames[TIMETYPE_COUNT][] = +{ + "NUB", + "PRO" +}; + +stock char gC_ModeNames[MODE_COUNT][] = +{ + "Vanilla", + "SimpleKZ", + "KZTimer" +}; + +stock char gC_ModeNamesShort[MODE_COUNT][] = +{ + "VNL", + "SKZ", + "KZT" +}; + +stock char gC_ModeKeys[MODE_COUNT][] = +{ + "vanilla", + "simplekz", + "kztimer" +}; + +stock float gF_ModeVirtualButtonRanges[MODE_COUNT] = +{ + 0.0, + 32.0, + 70.0 +}; + +stock char gC_ModeStartSounds[MODE_COUNT][] = +{ + "common/wpn_select.wav", + "buttons/button9.wav", + "buttons/button3.wav" +}; + +stock char gC_ModeEndSounds[MODE_COUNT][] = +{ + "common/wpn_select.wav", + "buttons/bell1.wav", + "buttons/button3.wav" +}; + +stock char gC_ModeFalseEndSounds[MODE_COUNT][] = +{ + "common/wpn_select.wav", + "buttons/button11.wav", + "buttons/button2.wav" +}; + +stock char gC_StyleNames[STYLE_COUNT][] = +{ + "Normal" +}; + +stock char gC_StyleNamesShort[STYLE_COUNT][] = +{ + "NRM" +}; + +stock char gC_CoreOptionNames[OPTION_COUNT][] = +{ + "GOKZ - Mode", + "GOKZ - Style", + "GOKZ - Checkpoint Messages", + "GOKZ - Checkpoint Sounds", + "GOKZ - Teleport Sounds", + "GOKZ - Error Sounds", + "GOKZ - VB Indicators", + "GOKZ - Timer Button Zone Type", + "GOKZ - Button Through Players", + "GOKZ - Safeguard" +}; + +stock char gC_CoreOptionDescriptions[OPTION_COUNT][] = +{ + "Movement Mode - 0 = Vanilla, 1 = SimpleKZ, 2 = KZTimer", + "Movement Style - 0 = Normal", + "Checkpoint Messages - 0 = Disabled, 1 = Enabled", + "Checkpoint Sounds - 0 = Disabled, 1 = Enabled", + "Teleport Sounds - 0 = Disabled, 1 = Enabled", + "Error Sounds - 0 = Disabled, 1 = Enabled", + "Virtual Button Indicators - 0 = Disabled, 1 = Enabled", + "Timer Button Zone Type - 0 = Both buttons, 1 = Only end zone, 2 = Both zones", + "Button Through Players - 0 = Disabled, 1 = Enabled", + "Safeguard - 0 = Disabled, 1 = Enabled (NUB), 2 = Enabled (PRO)" +}; + +stock char gC_CoreOptionPhrases[OPTION_COUNT][] = +{ + "Options Menu - Mode", + "Options Menu - Style", + "Options Menu - Checkpoint Messages", + "Options Menu - Checkpoint Sounds", + "Options Menu - Teleport Sounds", + "Options Menu - Error Sounds", + "Options Menu - Virtual Button Indicators", + "Options Menu - Timer Button Zone Type", + "Options Menu - Button Through Players", + "Options Menu - Safeguard" +}; + +stock char gC_TimerButtonZoneTypePhrases[TIMERBUTTONZONETYPE_COUNT][] = +{ + "Timer Button Zone Type - Both Buttons", + "Timer Button Zone Type - Only End Zone", + "Timer Button Zone Type - Both Zones" +}; + +stock char gC_SafeGuardPhrases[SAFEGUARD_COUNT][] = +{ + "Options Menu - Disabled", + "Safeguard - Enabled NUB", + "Safeguard - Enabled PRO" +} + +stock int gI_CoreOptionCounts[OPTION_COUNT] = +{ + MODE_COUNT, + STYLE_COUNT, + CHECKPOINTMESSAGES_COUNT, + CHECKPOINTSOUNDS_COUNT, + TELEPORTSOUNDS_COUNT, + ERRORSOUNDS_COUNT, + VIRTUALBUTTONINDICATORS_COUNT, + TIMERBUTTONZONETYPE_COUNT, + BUTTONTHROUGHPLAYERS_COUNT, + SAFEGUARD_COUNT +}; + +stock int gI_CoreOptionDefaults[OPTION_COUNT] = +{ + Mode_KZTimer, + Style_Normal, + CheckpointMessages_Disabled, + CheckpointSounds_Enabled, + TeleportSounds_Disabled, + ErrorSounds_Enabled, + VirtualButtonIndicators_Disabled, + TimerButtonZoneType_BothButtons, + ButtonThroughPlayers_Enabled, + Safeguard_Disabled +}; + +stock char gC_ModeCVars[MODECVAR_COUNT][] = +{ + "sv_accelerate", + "sv_accelerate_use_weapon_speed", + "sv_airaccelerate", + "sv_air_max_wishspeed", + "sv_enablebunnyhopping", + "sv_friction", + "sv_gravity", + "sv_jump_impulse", + "sv_ladder_scale_speed", + "sv_ledge_mantle_helper", + "sv_maxspeed", + "sv_maxvelocity", + "sv_staminajumpcost", + "sv_staminalandcost", + "sv_staminamax", + "sv_staminarecoveryrate", + "sv_standable_normal", + "sv_timebetweenducks", + "sv_walkable_normal", + "sv_wateraccelerate", + "sv_water_movespeed_multiplier", + "sv_water_swim_mode", + "sv_weapon_encumbrance_per_item", + "sv_weapon_encumbrance_scale" +}; + + +// =====[ STRUCTS ]===== + +enum struct Checkpoint +{ + float origin[3]; + float angles[3]; + float ladderNormal[3]; + bool onLadder; + int groundEnt; + + void Create(int client) + { + Movement_GetOrigin(client, this.origin); + Movement_GetEyeAngles(client, this.angles); + GetEntPropVector(client, Prop_Send, "m_vecLadderNormal", this.ladderNormal); + this.onLadder = Movement_GetMovetype(client) == MOVETYPE_LADDER; + this.groundEnt = GetEntPropEnt(client, Prop_Data, "m_hGroundEntity"); + } +} + +enum struct UndoTeleportData +{ + float tempOrigin[3]; + float tempAngles[3]; + float origin[3]; + float angles[3]; + // Undo TP properties + bool lastTeleportOnGround; + bool lastTeleportInBhopTrigger; + bool lastTeleportInAntiCpTrigger; + + void Init(int client, bool lastTeleportInBhopTrigger, bool lastTeleportOnGround, bool lastTeleportInAntiCpTrigger) + { + Movement_GetOrigin(client, this.tempOrigin); + Movement_GetEyeAngles(client, this.tempAngles); + this.lastTeleportInBhopTrigger = lastTeleportInBhopTrigger; + this.lastTeleportOnGround = lastTeleportOnGround; + this.lastTeleportInAntiCpTrigger = lastTeleportInAntiCpTrigger; + } + + void Update() + { + this.origin = this.tempOrigin; + this.angles = this.tempAngles; + } +} + + +// NOTE: gokz-core/map/entlump.sp +enum struct EntlumpToken +{ + EntlumpTokenType type; + char string[GOKZ_ENTLUMP_MAX_VALUE]; +} + +// NOTE: gokz-core/map/triggers.sp +enum struct AntiBhopTrigger +{ + int entRef; + int hammerID; + float time; +} + +enum struct TeleportTrigger +{ + int hammerID; + TeleportType type; + float delay; + char tpDestination[256]; + bool useDestAngles; + bool resetSpeed; + bool relativeDestination; + bool reorientPlayer; +} + +enum struct TouchedTrigger +{ + TriggerType triggerType; + int entRef; // entref of one of the TeleportTriggers + int startTouchTick; // tick where the player touched the trigger + int groundTouchTick; // tick where the player touched the ground +} + +// Legacy triggers that activate timer buttons. +enum struct TimerButtonTrigger +{ + int hammerID; + int course; + bool isStartTimer; +} + +// =====[ FORWARDS ]===== + +/** + * Called when a player's options values are loaded from clientprefs. + * + * @param client Client index. + */ +forward void GOKZ_OnOptionsLoaded(int client); + +/** + * Called when a player's option's value is changed. + * Only called if client is in game. + * + * @param client Client index. + * @param option Option name. + * @param newValue New value of the option. + */ +forward void GOKZ_OnOptionChanged(int client, const char[] option, any newValue); + +/** + * Called when a player starts their timer. + * + * @param client Client index. + * @param course Course number. + * @return Plugin_Handled or Plugin_Stop to block, Plugin_Continue to proceed. + */ +forward Action GOKZ_OnTimerStart(int client, int course); + +/** + * Called when a player has started their timer. + * + * @param client Client index. + * @param course Course number. + */ +forward void GOKZ_OnTimerStart_Post(int client, int course); + +/** + * Called when a player ends their timer. + * + * @param client Client index. + * @param course Course number. + * @param time Player's end time. + * @param teleportsUsed Number of teleports used by player. + * @return Plugin_Handled or Plugin_Stop to block, Plugin_Continue to proceed. + */ +forward Action GOKZ_OnTimerEnd(int client, int course, float time, int teleportsUsed); + +/** + * Called when a player has ended their timer. + * + * @param client Client index. + * @param course Course number. + * @param time Player's end time. + * @param teleportsUsed Number of teleports used by player. + */ +forward void GOKZ_OnTimerEnd_Post(int client, int course, float time, int teleportsUsed); + +/** + * Called when the end timer message is printed to chat. + * + * @param client Client index. + * @param course Course number. + * @param time Player's end time. + * @param teleportsUsed Number of teleports used by player. + * @return Plugin_Handled or Plugin_Stop to block, Plugin_Continue to proceed. + */ +forward Action GOKZ_OnTimerEndMessage(int client, int course, float time, int teleportsUsed); + +/** + * Called when a player's timer has been forcefully stopped. + * + * @param client Client index. + */ +forward void GOKZ_OnTimerStopped(int client); + +/** + * Called when a player pauses. + * + * @param client Client index. + * @return Plugin_Handled or Plugin_Stop to block, Plugin_Continue to proceed. + */ +forward Action GOKZ_OnPause(int client); + +/** + * Called when a player has paused. + * + * @param client Client index. + */ +forward void GOKZ_OnPause_Post(int client); + +/** + * Called when a player resumes. + * + * @param client Client index. + * @return Plugin_Handled or Plugin_Stop to block, Plugin_Continue to proceed. + */ +forward Action GOKZ_OnResume(int client); + +/** + * Called when a player has resumed. + * + * @param client Client index. + */ +forward void GOKZ_OnResume_Post(int client); + +/** + * Called when a player makes a checkpoint. + * + * @param client Client index. + * @return Plugin_Handled or Plugin_Stop to block, Plugin_Continue to proceed. + */ +forward Action GOKZ_OnMakeCheckpoint(int client); + +/** + * Called when a player has made a checkpoint. + * + * @param client Client index. + */ +forward void GOKZ_OnMakeCheckpoint_Post(int client); + +/** + * Called when a player teleports to their checkpoint. + * + * @param client Client index. + * @return Plugin_Handled or Plugin_Stop to block, Plugin_Continue to proceed. + */ +forward Action GOKZ_OnTeleportToCheckpoint(int client); + +/** + * Called when a player has teleported to their checkpoint. + * + * @param client Client index. + */ +forward void GOKZ_OnTeleportToCheckpoint_Post(int client); + +/** + * Called when a player goes to a previous checkpoint. + * + * @param client Client index. + * @return Plugin_Handled or Plugin_Stop to block, Plugin_Continue to proceed. + */ +forward Action GOKZ_OnPrevCheckpoint(int client); + +/** + * Called when a player has gone to a previous checkpoint. + * + * @param client Client index. + */ +forward void GOKZ_OnPrevCheckpoint_Post(int client); + +/** + * Called when a player goes to a next checkpoint. + * + * @param client Client index. + * @return Plugin_Handled or Plugin_Stop to block, Plugin_Continue to proceed. + */ +forward Action GOKZ_OnNextCheckpoint(int client); + +/** + * Called when a player has gone to a next checkpoint. + * + * @param client Client index. + */ +forward void GOKZ_OnNextCheckpoint_Post(int client); + +/** + * Called when a player teleports to start. + * + * @param client Client index. + * @param course Course index. + * @return Plugin_Handled or Plugin_Stop to block, Plugin_Continue to proceed. + */ +forward Action GOKZ_OnTeleportToStart(int client, int course); + +/** + * Called when a player has teleported to start. + * + * @param client Client index. + * @param course Course index. + */ +forward void GOKZ_OnTeleportToStart_Post(int client, int course); + +/** + * Called when a player teleports to end. + * + * @param client Client index. + * @param course Course index. + * @return Plugin_Handled or Plugin_Stop to block, Plugin_Continue to proceed. + */ +forward Action GOKZ_OnTeleportToEnd(int client, int course); + +/** + * Called when a player has teleported to end. + * + * @param client Client index. + * @param course Course index. + */ +forward void GOKZ_OnTeleportToEnd_Post(int client, int course); + +/** + * Called when a player undoes a teleport. + * + * @param client Client index. + * @return Plugin_Handled or Plugin_Stop to block, Plugin_Continue to proceed. + */ +forward Action GOKZ_OnUndoTeleport(int client); + +/** + * Called when a player has undone a teleport. + * + * @param client Client index. + */ +forward void GOKZ_OnUndoTeleport_Post(int client); + +/** + * Called when a player has performed a counted teleport (teleport count went up) + * i.e. a catch-all for teleport to checkpoint, teleport to start, undo teleport etc. + * + * @param client Client index. + */ +forward void GOKZ_OnCountedTeleport_Post(int client); + +/** + * Called when a player's start position is set. + * + * @param client Client index. + * @param type Start position type. + * @param origin Start position origin. + * @param angles Start position eye angles. + */ +forward void GOKZ_OnStartPositionSet_Post(int client, StartPositionType type, const float origin[3], const float angles[3]); + +/** + * Called when player's begins a jump that is deemed valid. + * A jump is deemed invalid if a player is teleported. + * + * @param client Client index. + * @param jumped Whether player jumped. + * @param ladderJump Whether it was a ladder jump. + * @param jumpbug Whether player performed a jumpbug. + */ +forward void GOKZ_OnJumpValidated(int client, bool jumped, bool ladderJump, bool jumpbug); + +/** + * Called when player's current jump is invalidated. + * A jump is deemed invalid if a player is teleported. + * + * @param client Client index. + */ +forward void GOKZ_OnJumpInvalidated(int client); + +/** + * Called when a player has been switched to a team. + * + * @param client Client index. + */ +forward void GOKZ_OnJoinTeam(int client, int team); + +/** + * Called the first time a player spawns in on a team. + * + * @param client Client index. + */ +forward void GOKZ_OnFirstSpawn(int client); + +/** + * Called when a mode has been loaded. + * + * @param mode Mode loaded. + */ +forward void GOKZ_OnModeLoaded(int mode); + +/** + * Called when a mode has been unloaded. + * + * @param mode Mode unloaded. + */ +forward void GOKZ_OnModeUnloaded(int mode); + +/** + * Called when a plugin other than gokz-core calls a native + * that may affect a player's timer or teleport count in + * their favour e.g. GOKZ_StartTimer, GOKZ_EndTimer, + * GOKZ_SetTime and GOKZ_SetTeleportCount. + * + * @param plugin Handle of the calling plugin. + * @param client Client index. + * @return Plugin_Handled or Plugin_Stop to block, Plugin_Continue to proceed. + */ +forward Action GOKZ_OnTimerNativeCalledExternally(Handle plugin, int client); + +/** + * Called when the options menu has been created and 3rd + * party plugins can grab the handle or add categories. + * + * @param topMenu Options top menu handle. + */ +forward void GOKZ_OnOptionsMenuCreated(TopMenu topMenu); + +/** + * Called when the options menu is ready to have items added. + * + * @param topMenu Options top menu handle. + */ +forward void GOKZ_OnOptionsMenuReady(TopMenu topMenu); + +/** + * Called when a course is registered. A course is registered if both the + * start and end of it (e.g. timer buttons) have been detected. + * + * @param course Course number. + */ +forward void GOKZ_OnCourseRegistered(int course); + +/** + * Called when a player's run becomes invalidated. + * An invalidated run doesn't necessarily stop the timer. + * + * @param client Client index. + */ +forward void GOKZ_OnRunInvalidated(int client); + +/** + * Called when a sound is emitted to the client via GOKZ Core. + * + * @param client Client index. + * @param sample Sound file name relative to the "sound" folder. + * @param volume Sound volume. + * @param description Optional description. + * @return Plugin_Continue to allow the sound to be played, Plugin_Stop to block it, + * Plugin_Changed when any parameter has been modified. + */ +forward Action GOKZ_OnEmitSoundToClient(int client, const char[] sample, float &volume, const char[] description); + + +// =====[ NATIVES ]===== + +/** + * Gets whether a mode is loaded. + * + * @param mode Mode. + * @return Whether mode is loaded. + */ +native bool GOKZ_GetModeLoaded(int mode); + +/** + * Gets the version number of a loaded mode. + * + * @param mode Mode. + * @return Version number of the mode, or -1 if not loaded. + */ +native int GOKZ_GetModeVersion(int mode); + +/** + * Sets whether a mode is loaded. To be used by mode plugins. + * + * @param mode Mode. + * @param loaded Whether mode is loaded. + * @param version Version number of the mode. + */ +native void GOKZ_SetModeLoaded(int mode, bool loaded, int version = -1); + +/** + * Gets the total number of loaded modes. + * + * @return Number of loaded modes. + */ +native int GOKZ_GetLoadedModeCount(); + +/** + * Sets the player's current mode. + * If the player's timer is running, it will be stopped. + * + * @param client Client index. + * @param mode Mode. + * @return Whether the operation was successful. + */ +native bool GOKZ_SetMode(int client, int mode); + +/** + * Gets the Handle to the options top menu. + * + * @return Handle to the options top menu, + * or null if not created yet. + */ +native TopMenu GOKZ_GetOptionsTopMenu(); + +/** + * Gets whether a course is registered. A course is registered if both the + * start and end of it (e.g. timer buttons) have been detected. + * + * @param course Course number. + * @return Whether course has been registered. + */ +native bool GOKZ_GetCourseRegistered(int course); + +/** + * Prints a message to a client's chat, formatting colours and optionally + * adding the chat prefix. If using the chat prefix, specify a colour at + * the beginning of the message e.g. "{default}Hello!". + * + * @param client Client index. + * @param addPrefix Whether to add the chat prefix. + * @param format Formatting rules. + * @param any Variable number of format parameters. + */ +native void GOKZ_PrintToChat(int client, bool addPrefix, const char[] format, any...); + +/** + * Prints a message to a client's chat, formatting colours and optionally + * adding the chat prefix. If using the chat prefix, specify a colour at + * the beginning of the message e.g. "{default}Hello!". Also prints the + * message to the server log. + * + * @param client Client index. + * @param addPrefix Whether to add the chat prefix. + * @param format Formatting rules. + * @param any Variable number of format parameters. + */ +native void GOKZ_PrintToChatAndLog(int client, bool addPrefix, const char[] format, any...); + +/** + * Starts a player's timer for a course on the current map. + * This can be blocked by OnTimerNativeCalledExternally(). + * + * @param client Client index. + * @param course Course number. + * @param allowMidair Whether player is allowed to start timer midair. + * @return Whether player's timer was started. + */ +native bool GOKZ_StartTimer(int client, int course, bool allowOffGround = false); + +/** + * Ends a player's timer for a course on the current map. + * This can be blocked by OnTimerNativeCalledExternally(). + * + * @param client Client index. + * @param course Course number. + * @return Whether player's timer was ended. + */ +native bool GOKZ_EndTimer(int client, int course); + +/** + * Forces a player's timer to stop. Intended for run invalidation. + * + * @param client Client index. + * @param playSound Whether to play the timer stop sound. + * @return Whether player's timer was stopped. + */ +native bool GOKZ_StopTimer(int client, bool playSound = true); + +/** + * Forces all players' timers to stop. Intended for run invalidation. + * + * @param playSound Whether to play the timer stop sound. + */ +native void GOKZ_StopTimerAll(bool playSound = true); + +/** + * Gets whether or not a player's timer is running i.e. isn't 'stopped'. + * + * @param client Client index. + * @return Whether player's timer is running. + */ +native bool GOKZ_GetTimerRunning(int client); + +/** + * Gets whether or not a player's timer is valid i.e the run is a valid run. + * + * @param client Client index. + * @return Whether player's timer is running. + */ +native bool GOKZ_GetValidTimer(int client); + +/** + * Gets the course a player is currently running. + * + * @param client Client index. + * @return Course number. + */ +native int GOKZ_GetCourse(int client); + +/** + * Set the player's current course. + * This can be blocked by OnTimerNativeCalledExternally(). + * + * @param client Client index. + * @param course Course number. + * @return Whether native was allowed to proceed. + */ +native bool GOKZ_SetCourse(int client, int course); + +/** + * Gets whether a player is paused. + * + * @param client Client index. + * @return Whether player is paused. + */ +native bool GOKZ_GetPaused(int client); + +/** + * Gets a player's current run time. + * + * @param client Client index. + * @return Player's current run time. + */ +native float GOKZ_GetTime(int client); + +/** + * Gets a player's current run time. + * This can be blocked by OnTimerNativeCalledExternally(). + * + * @param client Client index. + * @param time Run time to set to. + * @return Whether native was allowed to proceed. + */ +native bool GOKZ_SetTime(int client, float time); + +/** + * Mark a player's run as invalid without stopping the timer. + * + * @param client Client index. + */ +native void GOKZ_InvalidateRun(int client); + +/** + * Gets a player's current checkpoint count. + * + * @param client Client index. + * @return Player's current checkpoint count. + */ +native int GOKZ_GetCheckpointCount(int client); + +/** + * Sets a player's current checkpoint count. + * This can be blocked by OnTimerNativeCalledExternally(). + * + * @param client Client index. + * @param cpCount Checkpoint count to set to. + * @return Whether native was allowed to proceed. + */ +native int GOKZ_SetCheckpointCount(int client, int cpCount); + +/** + * Gets checkpoint data of a player. + * + * @param client Client index. + * @return Client's checkpoint data. + */ +native ArrayList GOKZ_GetCheckpointData(int client); + +/** + * Sets checkpoint data of a player. The checkpoint data is assumed to be ordered. + * This can be blocked by OnTimerNativeCalledExternally(). + * + * @param client Client index. + * @param checkpoints Checkpoint data. + * @param version Checkpoint version. + * @return Whether native was allowed to proceed and operation was successful. + */ +native bool GOKZ_SetCheckpointData(int client, ArrayList checkpoints, int version); + +/** + * Get undo teleport data of a player. + * + * @param client Client index. + * @return ArrayList of length 1 containing player's undo teleport data. + */ +native ArrayList GOKZ_GetUndoTeleportData(int client); + +/** + * Set undo teleport data of a player. + * This can be blocked by OnTimerNativeCalledExternally(). + * + * @param client Client index. + * @param undoTeleportDataArray ArrayList of length 1 containing player's undo teleport data. + * @param version Checkpoint version. + * @return Whether native was allowed to proceed and operation was successful. + */ +native bool GOKZ_SetUndoTeleportData(int client, ArrayList undoTeleportDataArray, int version); + +/** + * Gets a player's current teleport count. + * + * @param client Client index. + * @return Player's current teleport count. + */ +native int GOKZ_GetTeleportCount(int client); + +/** + * Sets a player's current teleport count. + * This can be blocked by OnTimerNativeCalledExternally(). + * + * @param client Client index. + * @param tpCount Teleport count to set to. + * @return Whether native was allowed to proceed. + */ +native bool GOKZ_SetTeleportCount(int client, int tpCount); + +/** + * Teleports a player to start, or respawns them. + * + * @param client Client index. + */ +native void GOKZ_TeleportToStart(int client); + +/** + * Teleports a player to the start zone/button of the specified course. + * + * @param client Client index. + * @param course Course index. + */ +native void GOKZ_TeleportToSearchStart(int client, int course); + +/** + * Gets the virtual button position the player currently has. + * + * @param client Client index. + * @param position Returns the client's virtual button position. + * @param isStart True to get the start button position, false for the end button. + * @return The course the button belongs to. + */ +native int GOKZ_GetVirtualButtonPosition(int client, float position[3], bool isStart); + +/** + * Sets the virtual button position the player currently has. + * + * @param client Client index. + * @param position The client's virtual button position. + * @param course The course the virtual button belongs to. + * @param isStart True to get the start button position, false for the end button. + */ +native void GOKZ_SetVirtualButtonPosition(int client, const float position[3], int course, bool isStart); + +/** + * Resets the player's virtual button. + * + * @param client Client index. + * @param isStart True to get the start button position, false for the end button. + */ +native void GOKZ_ResetVirtualButtonPosition(int client, bool isStart); + +/** + * Locks the virtual button position of a player. + * + * @param client Client index. + */ +native void GOKZ_LockVirtualButtons(int client); + +/** + * Gets the start position the player currently has. + * + * @param client Client index. + * @param position Returns the client's start position. + * @param angles Returns the client's start angles. + * @return Player's current start position type. + */ +native StartPositionType GOKZ_GetStartPosition(int client, float position[3], float angles[3]); + +/** + * Sets the start position the player currently has. + * + * @param client Client index. + * @param type The start position type. + * @param position The client's start position. + * @param angles The client's start angles. + */ +native void GOKZ_SetStartPosition(int client, StartPositionType type, const float position[3], const float angles[3]); + +/** + * Gets the type of start position the player currently has. + * The "Spawn" type means teleport to start will respawn the player. + * + * @param client Client index. + * @return Player's current start position type. + */ +native StartPositionType GOKZ_GetStartPositionType(int client); + +/** + * Set the start position of the player to the start of a course. + * + * @param client Client index. + * @param course Course index. + * + * @return False if the course start was not found. + */ +native bool GOKZ_SetStartPositionToMapStart(int client, int course); + +/** + * Teleports a player to end. + * + * @param client Client index. + * @param course Course index. + */ +native void GOKZ_TeleportToEnd(int client, int course); + +/** + * Set a new checkpoint at a player's current position. + * + * @param client Client index. + */ +native void GOKZ_MakeCheckpoint(int client); + +/** + * Gets whether a player can make a new checkpoint. + * @param client Client index. + * @return Whether player can set a checkpoint. + */ +native bool GOKZ_GetCanMakeCheckpoint(int client); + +/** + * Teleports a player to their last checkpoint. + * + * @param client Client index. + */ +native void GOKZ_TeleportToCheckpoint(int client); + +/** + * Gets whether a player can teleport to their checkpoint + * e.g. will return false if player has no checkpoints. + * + * @param client Client index. + * @return Whether player can teleport to checkpoint. + */ +native bool GOKZ_GetCanTeleportToCheckpoint(int client); + +/** + * Teleport a player back to a previous checkpoint. + * + * @param client Client index. + */ +native void GOKZ_PrevCheckpoint(int client); + +/** + * Gets whether a player can go to their previous checkpoint + * e.g. will return false if player has no checkpoints. + * + * @param client Client index. + * @return Whether player can go to previous checkpoint. + */ +native bool GOKZ_GetCanPrevCheckpoint(int client); + +/** + * Teleport a player to a more recent checkpoint. + * + * @param client Client index. + */ +native void GOKZ_NextCheckpoint(int client); + +/** + * Gets whether a player can go to their next checkpoint + * e.g. will return false if player has no checkpoints. + * + * @param client Client index. + * @return Whether player can go to next checkpoint. + */ +native bool GOKZ_GetCanNextCheckpoint(int client); + +/** + * Teleport a player to where they last teleported from. + * + * @param client Client index. + */ +native void GOKZ_UndoTeleport(int client); + +/** + * Gets whether a player can undo their teleport + * e.g. will return false if teleport was from midair. + * + * @param client Client index. + * @return Whether player can undo teleport. + */ +native bool GOKZ_GetCanUndoTeleport(int client); + +/** + * Pause a player's timer and freeze them. + * + * @param client Client index. + */ +native void GOKZ_Pause(int client); + +/** + * Gets whether a player can pause. Pausing is not allowed + * under some circumstance when the timer is running. + * + * @param client Client index. + */ +native bool GOKZ_GetCanPause(int client); + +/** + * Resumes a player's timer and unfreezes them. + * + * @param client Client index. + */ +native void GOKZ_Resume(int client); + +/** + * Gets whether a player can resume. Resuming is not allowed + * under some circumstance when the timer is running. + * + * @param client Client index. + */ +native bool GOKZ_GetCanResume(int client); + +/** + * Toggles the paused state of a player. + * + * @param client Client index. + */ +native void GOKZ_TogglePause(int client); + +/** + * Gets whether a player can teleport to start. + * + * @param client Client index. + * @return Whether player can teleport to start. + */ +native bool GOKZ_GetCanTeleportToStartOrEnd(int client); + +/** + * Plays the error sound to a player if they have the option enabled. + * + * @param client Client index. + */ +native void GOKZ_PlayErrorSound(int client); + +/** + * Set the origin of a player without invalidating any jumpstats. + * + * Only use this in plugins that create a new mode! + * + * @param client Client index. + * @param origin The new origin. + */ +native void GOKZ_SetValidJumpOrigin(int client, const float origin[3]); + +/** + * Registers an option with gokz-core, which uses clientprefs to + * keep track of the option's value and to save it to a database. + * This also effectively provides natives and forwards for other + * plugins to access any options that have been registered. + * + * @param name Option name. + * @param description Option description. + * @param type Type to treat the option value as. + * @param defaultValue Default value of option. + * @param minValue Minimum value of option. + * @param maxValue Maximum value of option. + * @return Whether registration was successful. + */ +native bool GOKZ_RegisterOption(const char[] name, const char[] description, OptionType type, any defaultValue, any minValue, any maxValue); + +/** + * Gets a property of a registered option. If used outside of + * gokz-core to get the cookie, a clone of its Handle is returned. + * + * @param option Option name. + * @param prop Option property to get. + * @return Value of property, or -1 (int) if option isn't registered. + */ +native any GOKZ_GetOptionProp(const char[] option, OptionProp prop); + +/** + * Sets a property of a registered option. For safety and simplicity, + * the cookie property is read-only and will fail to be set. + * + * @param option Option name. + * @param prop Option property to set. + * @param value Value to set the property to. + * @return Whether option property was successfully set. + */ +native bool GOKZ_SetOptionProp(const char[] option, OptionProp prop, any value); + +/** + * Gets the current value of a player's option. + * + * @param client Client index. + * @param option Option name. + * @return Current value of option, or -1 (int) if option isn't registered. + */ +native any GOKZ_GetOption(int client, const char[] option); + +/** + * Sets a player's option's value. Fails if option doesn't exist, + * or if desired value is outside the registered value range. + * + * @param client Client index. + * @param option Option name. + * @param value New option value. + * @return Whether option was successfully set. + */ +native bool GOKZ_SetOption(int client, const char[] option, any value); + +/** + * Gets whether player's last takeoff was a perfect bunnyhop as adjusted by GOKZ. + * + * @param client Client index. + * @return Whether player's last takeoff was a GOKZ perfect b-hop. + */ +native bool GOKZ_GetHitPerf(int client); + +/** + * Sets whether player's last takeoff was a perfect bunnyhop as adjusted by GOKZ. + * Intended to be called by GOKZ mode plugins only. + * + * @param client Client index. + * @param hitPerf Whether player's last takeoff was a GOKZ perfect b-hop. + */ +native void GOKZ_SetHitPerf(int client, bool hitPerf); + +/** + * Gets a player's horizontal speed at the time of their last takeoff as recorded by GOKZ. + * + * @param client Client index. + * @return Player's last takeoff speed as recorded by GOKZ. + */ +native float GOKZ_GetTakeoffSpeed(int client); + +/** + * Sets a player's recorded horizontal speed at the time of their last takeoff. + * Intended to be called by GOKZ mode plugins only. + * + * @param client Client index. + * @param takeoffSpeed Player's last takeoff speed as recorded by GOKZ. + */ +native void GOKZ_SetTakeoffSpeed(int client, float takeoffSpeed); + +/** + * Gets whether a player's current or last jump/airtime is valid. + * A jump is deemed invalid if the player is teleported. + * + * @param client Client index. + * @return Validity of player's current or last jump. + */ +native bool GOKZ_GetValidJump(int client); + +/** + * Has a player switch to a team via GOKZ Core. + * + * @param client Client index. + * @param team Which team to switch to. + * @param restorePos Whether to restore saved position if leaving spectators. + * @param forceBroadcast Force JoinTeam forward calling even if client's team did not change. + */ +native void GOKZ_JoinTeam(int client, int team, bool restorePos = true, bool forceBroadcast = false); + +/** + * Emit a sound to a player via GOKZ Core. + * Sounds emitted by this native will call GOKZ_OnEmitSoundToClient forward. + * + * @param client Client index. + * @param sample Sound file name relative to the "sound" folder. + * @param volume Sound volume. + * @param description Optional description. + */ +native void GOKZ_EmitSoundToClient(int client, const char[] sample, float volume = SNDVOL_NORMAL, const char[] description = ""); + + +// =====[ STOCKS ]===== + +/** + * Makes a player join a team if they aren't on one and respawns them. + * + * @param client Client index. + * @param team Which team to switch to if not on one. + * @param restorePos Whether to restore saved position if leaving spectators. + */ +stock void GOKZ_RespawnPlayer(int client, int team = CS_TEAM_T, bool restorePos = true) +{ + if (IsSpectating(client)) + { + GOKZ_JoinTeam(client, team, restorePos); + } + else + { + CS_RespawnPlayer(client); + } +} + +/** + * Prints a message to all client's chat, formatting colours and optionally + * adding the chat prefix. If using the chat prefix, specify a colour at + * the beginning of the message e.g. "{default}Hello!". + * + * @param addPrefix Whether to add the chat prefix. + * @param format Formatting rules. + * @param any Variable number of format parameters. + */ +stock void GOKZ_PrintToChatAll(bool addPrefix, const char[] format, any...) +{ + char buffer[1024]; + for (int client = 1; client <= MaxClients; client++) + { + if (IsClientInGame(client)) + { + SetGlobalTransTarget(client); + VFormat(buffer, sizeof(buffer), format, 3); + GOKZ_PrintToChat(client, addPrefix, buffer); + } + } +} + +/** + * Prints a chat message to those spectating the client, formatting colours + * and optionally adding the chat prefix. If using the chat prefix, specify + * a colour at the beginning of the message e.g. "{default}Hello!". + * + * @param client Client index. + * @param addPrefix Whether to add the chat prefix. + * @param format Formatting rules. + * @param any Variable number of format parameters. + */ +stock void GOKZ_PrintToChatSpectators(int client, bool addPrefix, const char[] format, any...) +{ + char buffer[1024]; + for (int target = 1; target <= MaxClients; target++) + { + if (IsClientInGame(target) && GetObserverTarget(target) == client) + { + SetGlobalTransTarget(target); + VFormat(buffer, sizeof(buffer), format, 4); + GOKZ_PrintToChat(target, addPrefix, buffer); + } + } +} + +/** + * Gets the player's current time type. + * + * @param client Client index. + * @return Player's current time type. + */ +stock int GOKZ_GetTimeType(int client) +{ + return GOKZ_GetTimeTypeEx(GOKZ_GetTeleportCount(client)); +} + +/** + * Gets the time type given a teleport count. + * + * @param teleports Teleport count. + * @return Time type. + */ +stock int GOKZ_GetTimeTypeEx(int teleportCount) +{ + if (teleportCount == 0) + { + return TimeType_Pro; + } + return TimeType_Nub; +} + +/** + * Clears and populates a menu with an item for each mode + * in order of the mode enumeration. Highlights the client's + * selected mode with an asterisk. + * + * @param client Client index to check selected mode. + * @param menu Menu to populate items with. + * @param disableUnloadedModes Draw items for unloaded modes as disabled. + */ +stock void GOKZ_MenuAddModeItems(int client, Menu menu, bool disableUnloadedModes) +{ + int selectedMode = GOKZ_GetCoreOption(client, Option_Mode); + char display[32]; + + menu.RemoveAllItems(); + + for (int mode = 0; mode < MODE_COUNT; mode++) + { + FormatEx(display, sizeof(display), "%s", gC_ModeNames[mode]); + // Add asterisk to selected mode + if (mode == selectedMode) + { + Format(display, sizeof(display), "%s*", display); + } + + if (GOKZ_GetModeLoaded(mode)) + { + menu.AddItem("", display, ITEMDRAW_DEFAULT); + } + else + { + menu.AddItem("", display, ITEMDRAW_DISABLED); + } + } +} + +/** + * Increment an (integer-type) option's value. + * Loops back to min. value if max. value is exceeded. + * + * @param client Client index. + * @param option Option name. + * @return Whether option was successfully set. + */ +stock bool GOKZ_CycleOption(int client, const char[] option) +{ + int maxValue = GOKZ_GetOptionProp(option, OptionProp_MaxValue); + if (maxValue == -1) + { + return false; + } + + int newValue = GOKZ_GetOption(client, option) + 1; + if (newValue > GOKZ_GetOptionProp(option, OptionProp_MaxValue)) + { + newValue = GOKZ_GetOptionProp(option, OptionProp_MinValue); + } + return GOKZ_SetOption(client, option, newValue); +} + +/** + * Returns whether an option is a gokz-core option. + * + * @param option Option name. + * @param optionEnum Variable to store enumerated gokz-core option (if it is one). + * @return Whether option is a gokz-core option. + */ +stock bool GOKZ_IsCoreOption(const char[] option, Option &optionEnum = OPTION_INVALID) +{ + for (Option i; i < OPTION_COUNT; i++) + { + if (StrEqual(option, gC_CoreOptionNames[i])) + { + optionEnum = i; + return true; + } + } + return false; +} + +/** + * Gets a property of a gokz-core option. + * + * @param coreOption gokz-core option. + * @param prop Option property to get. + * @return Value of property, or -1 if option isn't registered. + */ +stock any GOKZ_GetCoreOptionProp(Option option, OptionProp prop) +{ + return GOKZ_GetOptionProp(gC_CoreOptionNames[option], prop); +} + +/** + * Gets the current value of a player's gokz-core option. + * + * @param client Client index. + * @param option gokz-core option. + * @return Current value of option. + */ +stock any GOKZ_GetCoreOption(int client, Option option) +{ + return GOKZ_GetOption(client, gC_CoreOptionNames[option]); +} + +/** + * Sets the player's gokz-core option's value. + * + * @param client Client index. + * @param option gokz-core option. + * @param value New option value. + * @return Whether option was successfully set. + */ +stock bool GOKZ_SetCoreOption(int client, Option option, any value) +{ + return GOKZ_SetOption(client, gC_CoreOptionNames[option], value); +} + +/** + * Increment an integer-type gokz-core option's value. + * Loops back to '0' if max value is exceeded. + * + * @param client Client index. + * @param option gokz-core option. + * @return Whether option was successfully set. + */ +stock bool GOKZ_CycleCoreOption(int client, Option option) +{ + return GOKZ_CycleOption(client, gC_CoreOptionNames[option]); +} + +/** + * Gets the current default mode. + * + * @return Default mode. + */ +stock int GOKZ_GetDefaultMode() +{ + return GOKZ_GetCoreOptionProp(Option_Mode, OptionProp_DefaultValue); +} + +/** + * Returns whether a course number is a valid (within valid range). + * + * @param course Course number. + * @param bonus Whether to only consider bonus course numbers as valid. + * @return Whether course number is valid. + */ +stock bool GOKZ_IsValidCourse(int course, bool bonus = false) +{ + return (!bonus && course == 0) || (0 < course && course < GOKZ_MAX_COURSES); +} + +/** + * Returns an integer from an entity's name as matched using a regular expression. + * + * @param entity Entity index. + * @param re Regular expression to match the integer with. + * @param substringID ID of the substring that will contain the integer. + * @returns Integer found in the entity's name, or -1 if not found. + */ +stock int GOKZ_MatchIntFromEntityName(int entity, Regex re, int substringID) +{ + int num = -1; + char buffer[32]; + GetEntityName(entity, buffer, sizeof(buffer)); + + if (re.Match(buffer) > 0) + { + re.GetSubString(1, buffer, sizeof(buffer)); + num = StringToInt(buffer); + } + + return num; +} + +/** + * Emits a sound to other players that are spectating the client. + * Sounds emitted by this function will call GOKZ_OnEmitSoundToClient forward. + * + * @param sample Sound file name relative to the "sound" folder. + * @param volume Sound volume. + * @param description Optional description. + */ +stock void GOKZ_EmitSoundToAll(const char[] sample, float volume = SNDVOL_NORMAL, const char[] description = "") +{ + for (int client = 1; client <= MaxClients; client++) + { + if (IsClientInGame(client)) + { + GOKZ_EmitSoundToClient(client, sample, volume, description); + } + } +} + +/** + * Emits a sound to other players that are spectating the client. + * Sounds emitted by this function will call GOKZ_OnEmitSoundToClient forward. + * + * @param client Client being spectated. + * @param sample Sound file name relative to the "sound" folder. + * @param volume Sound volume. + * @param description Optional description. + */ +stock void GOKZ_EmitSoundToClientSpectators(int client, const char[] sample, float volume = SNDVOL_NORMAL, const char[] description = "") +{ + for (int i = 1; i <= MaxClients; i++) + { + if (IsValidClient(i) && GetObserverTarget(i) == client) + { + GOKZ_EmitSoundToClient(i, sample, volume); + } + } +} + +// =====[ DEPENDENCY ]===== + +public SharedPlugin __pl_gokz_core = +{ + name = "gokz-core", + file = "gokz-core.smx", + #if defined REQUIRE_PLUGIN + required = 1, + #else + required = 0, + #endif +}; + +#if !defined REQUIRE_PLUGIN +public void __pl_gokz_core_SetNTVOptional() +{ + MarkNativeAsOptional("GOKZ_GetModeLoaded"); + MarkNativeAsOptional("GOKZ_GetModeVersion"); + MarkNativeAsOptional("GOKZ_SetModeLoaded"); + MarkNativeAsOptional("GOKZ_GetLoadedModeCount"); + MarkNativeAsOptional("GOKZ_SetMode"); + MarkNativeAsOptional("GOKZ_GetOptionsTopMenu"); + MarkNativeAsOptional("GOKZ_GetCourseRegistered"); + MarkNativeAsOptional("GOKZ_PrintToChat"); + MarkNativeAsOptional("GOKZ_PrintToChatAndLog"); + MarkNativeAsOptional("GOKZ_StartTimer"); + MarkNativeAsOptional("GOKZ_EndTimer"); + MarkNativeAsOptional("GOKZ_StopTimer"); + MarkNativeAsOptional("GOKZ_StopTimerAll"); + MarkNativeAsOptional("GOKZ_TeleportToStart"); + MarkNativeAsOptional("GOKZ_TeleportToSearchStart"); + MarkNativeAsOptional("GOKZ_GetVirtualButtonPosition"); + MarkNativeAsOptional("GOKZ_SetVirtualButtonPosition"); + MarkNativeAsOptional("GOKZ_ResetVirtualButtonPosition"); + MarkNativeAsOptional("GOKZ_LockVirtualButtons"); + MarkNativeAsOptional("GOKZ_GetStartPosition"); + MarkNativeAsOptional("GOKZ_SetStartPosition"); + MarkNativeAsOptional("GOKZ_GetStartPositionType"); + MarkNativeAsOptional("GOKZ_SetStartPositionToMapStart"); + MarkNativeAsOptional("GOKZ_TeleportToEnd"); + MarkNativeAsOptional("GOKZ_MakeCheckpoint"); + MarkNativeAsOptional("GOKZ_GetCanMakeCheckpoint"); + MarkNativeAsOptional("GOKZ_TeleportToCheckpoint"); + MarkNativeAsOptional("GOKZ_GetCanTeleportToCheckpoint"); + MarkNativeAsOptional("GOKZ_PrevCheckpoint"); + MarkNativeAsOptional("GOKZ_GetCanPrevCheckpoint"); + MarkNativeAsOptional("GOKZ_NextCheckpoint"); + MarkNativeAsOptional("GOKZ_GetCanNextCheckpoint"); + MarkNativeAsOptional("GOKZ_UndoTeleport"); + MarkNativeAsOptional("GOKZ_GetCanUndoTeleport"); + MarkNativeAsOptional("GOKZ_Pause"); + MarkNativeAsOptional("GOKZ_GetCanPause"); + MarkNativeAsOptional("GOKZ_Resume"); + MarkNativeAsOptional("GOKZ_GetCanResume"); + MarkNativeAsOptional("GOKZ_TogglePause"); + MarkNativeAsOptional("GOKZ_GetCanTeleportToStartOrEnd"); + MarkNativeAsOptional("GOKZ_PlayErrorSound"); + MarkNativeAsOptional("GOKZ_SetValidJumpOrigin"); + MarkNativeAsOptional("GOKZ_GetTimerRunning"); + MarkNativeAsOptional("GOKZ_GetCourse"); + MarkNativeAsOptional("GOKZ_SetCourse"); + MarkNativeAsOptional("GOKZ_GetPaused"); + MarkNativeAsOptional("GOKZ_GetTime"); + MarkNativeAsOptional("GOKZ_SetTime"); + MarkNativeAsOptional("GOKZ_InvalidateRun"); + MarkNativeAsOptional("GOKZ_GetCheckpointCount"); + MarkNativeAsOptional("GOKZ_SetCheckpointCount"); + MarkNativeAsOptional("GOKZ_GetCheckpointData"); + MarkNativeAsOptional("GOKZ_SetCheckpointData"); + MarkNativeAsOptional("GOKZ_GetUndoTeleportData"); + MarkNativeAsOptional("GOKZ_SetUndoTeleportData"); + MarkNativeAsOptional("GOKZ_GetTeleportCount"); + MarkNativeAsOptional("GOKZ_SetTeleportCount"); + MarkNativeAsOptional("GOKZ_RegisterOption"); + MarkNativeAsOptional("GOKZ_GetOptionProp"); + MarkNativeAsOptional("GOKZ_SetOptionProp"); + MarkNativeAsOptional("GOKZ_GetOption"); + MarkNativeAsOptional("GOKZ_SetOption"); + MarkNativeAsOptional("GOKZ_GetHitPerf"); + MarkNativeAsOptional("GOKZ_SetHitPerf"); + MarkNativeAsOptional("GOKZ_GetTakeoffSpeed"); + MarkNativeAsOptional("GOKZ_SetTakeoffSpeed"); + MarkNativeAsOptional("GOKZ_GetValidJump"); + MarkNativeAsOptional("GOKZ_JoinTeam"); + MarkNativeAsOptional("GOKZ_EmitSoundToClient"); +} +#endif
\ No newline at end of file diff --git a/sourcemod/scripting/include/gokz/global.inc b/sourcemod/scripting/include/gokz/global.inc new file mode 100644 index 0000000..0f23a0c --- /dev/null +++ b/sourcemod/scripting/include/gokz/global.inc @@ -0,0 +1,317 @@ +/* + gokz-global Plugin Include + + Website: https://bitbucket.org/kztimerglobalteam/gokz +*/ + +#if defined _gokz_global_included_ +#endinput +#endif +#define _gokz_global_included_ + +#include <GlobalAPI> + + + +// =====[ ENUMS ]===== + +enum +{ + EnforcedCVar_Cheats = 0, + EnforcedCVar_ClampUnsafeVelocities, + EnforcedCVar_DropKnifeEnable, + EnforcedCVar_AutoBunnyhopping, + EnforcedCVar_MinUpdateRate, + EnforcedCVar_MaxUpdateRate, + EnforcedCVar_MinCmdRate, + EnforcedCVar_MaxCmdRate, + EnforcedCVar_ClientCmdrateDifference, + EnforcedCVar_Turbophysics, + ENFORCEDCVAR_COUNT +}; + +enum +{ + BannedPluginCommand_Funcommands = 0, + BannedPluginCommand_Playercommands, + BANNEDPLUGINCOMMAND_COUNT +}; + +enum +{ + BannedPlugin_Funcommands = 0, + BannedPlugin_Playercommands, + BANNEDPLUGIN_COUNT +}; + +enum GlobalMode +{ + GlobalMode_Invalid = -1, + GlobalMode_KZTimer = 200, + GlobalMode_KZSimple, + GlobalMode_Vanilla +} + + + +// =====[ CONSTANTS ]===== + +#define GL_SOUND_NEW_RECORD "gokz/holyshit.mp3" +#define GL_FPS_MAX_CHECK_INTERVAL 1.0 +#define GL_FPS_MAX_KICK_TIMEOUT 10.0 +#define GL_FPS_MAX_MIN_VALUE 120 +#define GL_MYAW_MAX_VALUE 0.3 + +stock char gC_EnforcedCVars[ENFORCEDCVAR_COUNT][] = +{ + "sv_cheats", + "sv_clamp_unsafe_velocities", + "mp_drop_knife_enable", + "sv_autobunnyhopping", + "sv_minupdaterate", + "sv_maxupdaterate", + "sv_mincmdrate", + "sv_maxcmdrate", + "sv_client_cmdrate_difference", + "sv_turbophysics" +}; + +stock char gC_BannedPluginCommands[BANNEDPLUGINCOMMAND_COUNT][] = +{ + "sm_beacon", + "sm_slap" +}; + +stock char gC_BannedPlugins[BANNEDPLUGIN_COUNT][] = +{ + "Fun Commands", + "Player Commands" +}; + +stock float gF_EnforcedCVarValues[ENFORCEDCVAR_COUNT] = +{ + 0.0, + 0.0, + 0.0, + 0.0, + 128.0, + 128.0, + 128.0, + 128.0, + 0.0, + 0.0 +}; + + + +// =====[ FORWARDS ]===== + +/** + * Called when a player sets a new global top time. + * + * @param client Client index. + * @param course Course number e.g. 0=main, 1='bonus1' etc. + * @param mode Player's movement mode. + * @param timeType Time type i.e. NUB or PRO. + * @param rank Ranking within the same time type. + * @param rankOverall Overall (NUB and PRO) ranking (0 if not ranked high enough). + * @param runTime Player's end time. + */ +forward void GOKZ_GL_OnNewTopTime(int client, int course, int mode, int timeType, int rank, int rankOverall, float runTime); + + + +// =====[ NATIVES ]===== + +/** + * Prints to chat the global records for a map, course and mode. + * + * @param client Client index. + * @param map Map name or "" for current map. + * @param course Course number e.g. 0=main, 1='bonus1' etc. + * @param mode GOKZ mode. + */ +native void GOKZ_GL_PrintRecords(int client, const char[] map = "", int course, int mode, const char[] steamid = DEFAULT_STRING); + +/** + * Opens up the global map top menu for a map, course and mode. + * + * @param client Client index. + * @param map Map name or "" for current map. + * @param course Course number e.g. 0=main, 1='bonus1' etc. + * @param mode GOKZ mode. + * @param timeType Type of time i.e. NUB or PRO. + */ +native void GOKZ_GL_DisplayMapTopMenu(int client, const char[] map = "", int course, int mode, int timeType); + +/** + * Get the total global points of a player. + * + * @param client Client index. + * @param mode GOKZ mode. + * @param timeType Type of time i.e. NUB or PRO. + */ +native void GOKZ_GL_GetPoints(int client, int mode, int timeType); + +/** + * Get the global points on the main coruse of the current map. + * + * @param client Client index. + * @param mode GOKZ mode. + * @param timeType Type of time i.e. NUB or PRO. + */ +native void GOKZ_GL_GetMapPoints(int client, int mode, int timeType); + +/** + * Get the total global ranking points of a player. + * + * @param client Client index. + * @param mode GOKZ mode. + * @return The points. + */ +native int GOKZ_GL_GetRankPoints(int client, int mode); + +/** + * Get the amount of maps a player finished. + * + * @param client Client index. + * @param mode GOKZ mode. + * @param timeType Type of time i.e. NUB or PRO. + */ +native void GOKZ_GL_GetFinishes(int client, int mode, int timeType); + +/** + * Fetch the points a player got from the Global API. + * + * @param client Client index. -1 to update all indices. + * @param mode GOKZ mode. -1 to update all modes. + */ +native void GOKZ_GL_UpdatePoints(int client = -1, int mode = -1); + +/** + * Gets whether the Global API key is valid or not for global status. + * + * @return True if the API key is valid, false otherwise or if there is no connection to the Global API. + */ +native bool GOKZ_GL_GetAPIKeyValid(); + +/** + * Gets whether the running plugins are valid or not for global status. + * + * @return True if the plugins are valid, false otherwise. + */ +native bool GOKZ_GL_GetPluginsValid(); + +/** + * Gets whether the setting enforcer is valid or not for global status. + * + * @return True if the setting enforcer is valid, false otherwise. + */ +native bool GOKZ_GL_GetSettingsEnforcerValid(); + +/** + * Gets whether the current map is valid or not for global status. + * + * @return True if the map is valid, false otherwise or if there is no connection to the Global API. + */ +native bool GOKZ_GL_GetMapValid(); + +/** + * Gets whether the current player is valid or not for global status. + * + * @param client Client index. + * @return True if the player is valid, false otherwise or if there is no connection to the Global API. + */ +native bool GOKZ_GL_GetPlayerValid(int client); + + + +// =====[ STOCKS ]===== + +/** + * Gets the global mode enumeration equivalent for the GOKZ mode. + * + * @param mode GOKZ mode. + * @return Global mode enumeration equivalent. + */ +stock GlobalMode GOKZ_GL_GetGlobalMode(int mode) +{ + switch (mode) + { + case Mode_Vanilla:return GlobalMode_Vanilla; + case Mode_SimpleKZ:return GlobalMode_KZSimple; + case Mode_KZTimer:return GlobalMode_KZTimer; + } + return GlobalMode_Invalid; +} + +/** + * Gets the global mode enumeration equivalent for the GOKZ mode. + * + * @param mode GOKZ mode. + * @return Global mode enumeration equivalent. + */ +stock int GOKZ_GL_FromGlobalMode(GlobalMode mode) +{ + switch (mode) + { + case GlobalMode_Vanilla:return Mode_Vanilla; + case GlobalMode_KZSimple:return Mode_SimpleKZ; + case GlobalMode_KZTimer:return Mode_KZTimer; + } + return -1; +} + +/** + * Gets the string representation of a mode. + * + * @param mode GOKZ mode. + * @param mode_str String version of the mode. + * @param size Max length of mode_str. + * @return True if the conversion was successful. + */ +stock bool GOKZ_GL_GetModeString(int mode, char[] mode_str, int size) +{ + switch (mode) + { + case Mode_Vanilla:strcopy(mode_str, size, "kz_vanilla"); + case Mode_SimpleKZ:strcopy(mode_str, size, "kz_simple"); + case Mode_KZTimer:strcopy(mode_str, size, "kz_timer"); + default:return false; + } + return true; +} + + + +// =====[ DEPENDENCY ]===== + +public SharedPlugin __pl_gokz_global = +{ + name = "gokz-global", + file = "gokz-global.smx", + #if defined REQUIRE_PLUGIN + required = 1, + #else + required = 0, + #endif +}; + +#if !defined REQUIRE_PLUGIN +public void __pl_gokz_global_SetNTVOptional() +{ + MarkNativeAsOptional("GOKZ_GL_PrintRecords"); + MarkNativeAsOptional("GOKZ_GL_DisplayMapTopMenu"); + MarkNativeAsOptional("GOKZ_GL_UpdatePoints"); + MarkNativeAsOptional("GOKZ_GL_GetAPIKeyValid"); + MarkNativeAsOptional("GOKZ_GL_GetPluginsValid"); + MarkNativeAsOptional("GOKZ_GL_GetSettingsEnforcerValid"); + MarkNativeAsOptional("GOKZ_GL_GetMapValid"); + MarkNativeAsOptional("GOKZ_GL_GetPlayerValid"); + MarkNativeAsOptional("GOKZ_GL_GetPoints"); + MarkNativeAsOptional("GOKZ_GL_GetMapPoints"); + MarkNativeAsOptional("GOKZ_GL_GetRankPoints"); + MarkNativeAsOptional("GOKZ_GL_GetFinishes"); + MarkNativeAsOptional("GOKZ_GL_UpdatePoints"); +} +#endif
\ No newline at end of file diff --git a/sourcemod/scripting/include/gokz/hud.inc b/sourcemod/scripting/include/gokz/hud.inc new file mode 100644 index 0000000..5d658ff --- /dev/null +++ b/sourcemod/scripting/include/gokz/hud.inc @@ -0,0 +1,468 @@ +/* + gokz-hud Plugin Include + + Website: https://bitbucket.org/kztimerglobalteam/gokz +*/ + +#if defined _gokz_hud_included_ +#endinput +#endif +#define _gokz_hud_included_ + + + +// =====[ ENUMS ]===== + +enum HUDOption: +{ + HUDOPTION_INVALID = -1, + HUDOption_TPMenu, + HUDOption_InfoPanel, + HUDOption_ShowKeys, + HUDOption_TimerText, + HUDOption_TimerStyle, + HUDOption_TimerType, + HUDOption_SpeedText, + HUDOption_ShowWeapon, + HUDOption_ShowControls, + HUDOption_DeadstrafeColor, + HUDOption_ShowSpectators, + HUDOption_SpecListPosition, + HUDOption_UpdateRate, + HUDOption_DynamicMenu, + HUDOPTION_COUNT +}; + +enum +{ + TPMenu_Disabled = 0, + TPMenu_Simple, + TPMenu_Advanced, + TPMENU_COUNT +}; + +enum +{ + InfoPanel_Disabled = 0, + InfoPanel_Enabled, + INFOPANEL_COUNT +}; + +enum +{ + ShowKeys_Spectating = 0, + ShowKeys_Always, + ShowKeys_Disabled, + SHOWKEYS_COUNT +}; + +enum +{ + TimerText_Disabled = 0, + TimerText_InfoPanel, + TimerText_TPMenu, + TimerText_Bottom, + TimerText_Top, + TIMERTEXT_COUNT +}; + +enum +{ + TimerStyle_Standard = 0, + TimerStyle_Precise, + TIMERSTYLE_COUNT +}; + +enum +{ + TimerType_Disabled = 0, + TimerType_Enabled, + TIMERTYPE_COUNT +}; + +enum +{ + SpeedText_Disabled = 0, + SpeedText_InfoPanel, + SpeedText_Bottom, + SPEEDTEXT_COUNT +}; + +enum +{ + ShowWeapon_Disabled = 0, + ShowWeapon_Enabled, + SHOWWEAPON_COUNT +}; + +enum +{ + ReplayControls_Disabled = 0, + ReplayControls_Enabled, + REPLAYCONTROLS_COUNT +}; + +enum +{ + DeadstrafeColor_Disabled = 0, + DeadstrafeColor_Enabled, + DEADSTRAFECOLOR_COUNT +}; + +enum +{ + ShowSpecs_Disabled = 0, + ShowSpecs_Number, + ShowSpecs_Full, + SHOWSPECS_COUNT +}; + +enum +{ + SpecListPosition_TPMenu = 0, + SpecListPosition_InfoPanel, + SPECLISTPOSITION_COUNT +} + +enum +{ + UpdateRate_Slow = 0, + UpdateRate_Fast, + UPDATERATE_COUNT, +}; + +enum +{ + DynamicMenu_Legacy = 0, + DynamicMenu_Disabled, + DynamicMenu_Enabled, + DYNAMICMENU_COUNT +}; + +// =====[ STRUCTS ]====== + +enum struct HUDInfo +{ + bool TimerRunning; + int TimeType; + float Time; + bool Paused; + bool OnGround; + bool OnLadder; + bool Noclipping; + bool Ducking; + bool HitBhop; + bool IsTakeoff; + float Speed; + int ID; + bool Jumped; + bool HitPerf; + bool HitJB; + float TakeoffSpeed; + int Buttons; + int CurrentTeleport; +} + + + +// =====[ CONSTANTS ]===== + +#define HUD_OPTION_CATEGORY "HUD" +#define HUD_MAX_BHOP_GROUND_TICKS 5 +#define HUD_MAX_HINT_SIZE 227 + +stock char gC_HUDOptionNames[HUDOPTION_COUNT][] = +{ + "GOKZ HUD - Teleport Menu", + "GOKZ HUD - Centre Panel", + "GOKZ HUD - Show Keys", + "GOKZ HUD - Timer Text", + "GOKZ HUD - Timer Style", + "GOKZ HUD - Show Time Type", + "GOKZ HUD - Speed Text", + "GOKZ HUD - Show Weapon", + "GOKZ HUD - Show Controls", + "GOKZ HUD - Dead Strafe", + "GOKZ HUD - Show Spectators", + "GOKZ HUD - Spec List Pos", + "GOKZ HUD - Update Rate", + "GOKZ HUD - Dynamic Menu" +}; + +stock char gC_HUDOptionDescriptions[HUDOPTION_COUNT][] = +{ + "Teleport Menu - 0 = Disabled, 1 = Simple, 2 = Advanced", + "Centre Information Panel - 0 = Disabled, 1 = Enabled", + "Key Press Display - 0 = Spectating, 1 = Always, 2 = Disabled", + "Timer Display - 0 = Disabled, 1 = Centre Panel, 2 = Teleport Menu, 3 = Bottom, 4 = Top", + "Timer Style - 0 = Standard, 1 = Precise", + "Timer Type - 0 = Disabled, 1 = Enabled", + "Speed Display - 0 = Disabled, 1 = Centre Panel, 2 = Bottom", + "Weapon Viewmodel - 0 = Disabled, 1 = Enabled", + "Replay Controls Display - 0 = Disbled, 1 = Enabled", + "Dead Strafe Indicator - 0 = Disabled, 1 = Enabled", + "Show Spectators - 0 = Disabled, 1 = Number Only, 2 = Number and Names", + "Spectator List Position - 0 = Teleport Menu, 2 = Center Panel", + "HUD Update Rate - 0 = Slow, 1 = Fast", + "Dynamic Menu - 0 = Legacy, 1 = Disabled, 2 = Enabled" +}; + +stock char gC_HUDOptionPhrases[HUDOPTION_COUNT][] = +{ + "Options Menu - Teleport Menu", + "Options Menu - Info Panel", + "Options Menu - Show Keys", + "Options Menu - Timer Text", + "Options Menu - Timer Style", + "Options Menu - Timer Type", + "Options Menu - Speed Text", + "Options Menu - Show Weapon", + "Options Menu - Show Controls", + "Options Menu - Dead Strafe Indicator", + "Options Menu - Show Spectators", + "Options Menu - Spectator List Position", + "Options Menu - Update Rate", + "Options Menu - Dynamic Menu" +}; + +stock int gI_HUDOptionCounts[HUDOPTION_COUNT] = +{ + TPMENU_COUNT, + INFOPANEL_COUNT, + SHOWKEYS_COUNT, + TIMERTEXT_COUNT, + TIMERSTYLE_COUNT, + TIMERTYPE_COUNT, + SPEEDTEXT_COUNT, + SHOWWEAPON_COUNT, + REPLAYCONTROLS_COUNT, + DEADSTRAFECOLOR_COUNT, + SHOWSPECS_COUNT, + SPECLISTPOSITION_COUNT, + UPDATERATE_COUNT, + DYNAMICMENU_COUNT +}; + +stock int gI_HUDOptionDefaults[HUDOPTION_COUNT] = +{ + TPMenu_Advanced, + InfoPanel_Enabled, + ShowKeys_Spectating, + TimerText_InfoPanel, + TimerStyle_Standard, + TimerType_Enabled, + SpeedText_InfoPanel, + ShowWeapon_Enabled, + ReplayControls_Enabled, + DeadstrafeColor_Disabled, + ShowSpecs_Disabled, + SpecListPosition_TPMenu, + UpdateRate_Slow, + DynamicMenu_Legacy +}; + +stock char gC_TPMenuPhrases[TPMENU_COUNT][] = +{ + "Options Menu - Disabled", + "Options Menu - Simple", + "Options Menu - Advanced" +}; + +stock char gC_ShowKeysPhrases[SHOWKEYS_COUNT][] = +{ + "Options Menu - Spectating", + "Options Menu - Always", + "Options Menu - Disabled" +}; + +stock char gC_TimerTextPhrases[TIMERTEXT_COUNT][] = +{ + "Options Menu - Disabled", + "Options Menu - Info Panel", + "Options Menu - Teleport Menu", + "Options Menu - Bottom", + "Options Menu - Top" +}; + +stock char gC_TimerTypePhrases[TIMERTYPE_COUNT][] = +{ + "Options Menu - Disabled", + "Options Menu - Enabled" +}; + +stock char gC_SpeedTextPhrases[SPEEDTEXT_COUNT][] = +{ + "Options Menu - Disabled", + "Options Menu - Info Panel", + "Options Menu - Bottom" +}; + +stock char gC_ShowControlsPhrases[REPLAYCONTROLS_COUNT][] = +{ + "Options Menu - Disabled", + "Options Menu - Enabled" +}; + +stock char gC_DeadstrafeColorPhrases[DEADSTRAFECOLOR_COUNT][] = +{ + "Options Menu - Disabled", + "Options Menu - Enabled" +}; + +stock char gC_ShowSpecsPhrases[SHOWSPECS_COUNT][] = +{ + "Options Menu - Disabled", + "Options Menu - Number", + "Options Menu - Number and Names" +}; + +stock char gC_SpecListPositionPhrases[SPECLISTPOSITION_COUNT][] = +{ + "Options Menu - Teleport Menu", + "Options Menu - Info Panel" +}; + +stock char gC_HUDUpdateRatePhrases[UPDATERATE_COUNT][]= +{ + "Options Menu - Slow", + "Options Menu - Fast" +}; + +stock char gC_DynamicMenuPhrases[DYNAMICMENU_COUNT][]= +{ + "Options Menu - Legacy", + "Options Menu - Disabled", + "Options Menu - Enabled" +}; + +// =====[ NATIVES ]===== + +/** + * Returns whether the GOKZ HUD menu is showing for a client. + * + * @param client Client index. + * @return Whether the GOKZ HUD menu is showing. + */ +native bool GOKZ_HUD_GetMenuShowing(int client); + +/** + * Sets whether the GOKZ HUD menu would be showing for a client. + * + * @param client Client index. + * @param value Whether the GOKZ HUD menu would be showing for a client. + */ +native void GOKZ_HUD_SetMenuShowing(int client, bool value); + +/** + * Gets the spectator text for the menu. Used by GOKZ-replays. + * + * @param client Client index. + * @param value Whether the GOKZ HUD menu would be showing for a client. + */ +native void GOKZ_HUD_GetMenuSpectatorText(int client, any[] info, char[] buffer, int size); + +/** + * Forces the client's TP menu to update. + * + * @param client Client index. + */ +native void GOKZ_HUD_ForceUpdateTPMenu(int client); + +// =====[ STOCKS ]===== + +/** + * Returns whether an option is a gokz-hud option. + * + * @param option Option name. + * @param optionEnum Variable to store enumerated gokz-hud option (if it is one). + * @return Whether option is a gokz-hud option. + */ +stock bool GOKZ_HUD_IsHUDOption(const char[] option, HUDOption &optionEnum = HUDOPTION_INVALID) +{ + for (HUDOption i; i < HUDOPTION_COUNT; i++) + { + if (StrEqual(option, gC_HUDOptionNames[i])) + { + optionEnum = i; + return true; + } + } + return false; +} + +/** + * Gets the current value of a player's gokz-hud option. + * + * @param client Client index. + * @param option gokz-hud option. + * @return Current value of option. + */ +stock any GOKZ_HUD_GetOption(int client, HUDOption option) +{ + return GOKZ_GetOption(client, gC_HUDOptionNames[option]); +} + +/** + * Sets a player's gokz-hud option's value. + * + * @param client Client index. + * @param option gokz-hud option. + * @param value New option value. + * @return Whether option was successfully set. + */ +stock bool GOKZ_HUD_SetOption(int client, HUDOption option, any value) +{ + return GOKZ_SetOption(client, gC_HUDOptionNames[option], value); +} + +/** + * Increment an integer-type gokz-hud option's value. + * Loops back to '0' if max value is exceeded. + * + * @param client Client index. + * @param option gokz-hud option. + * @return Whether option was successfully set. + */ +stock bool GOKZ_HUD_CycleOption(int client, HUDOption option) +{ + return GOKZ_CycleOption(client, gC_HUDOptionNames[option]); +} + +/** + * Represents a time float as a string e.g. 01:23.45 + * and according to the client's HUD options. + * + * @param client Client index. + * @param time Time in seconds. + * @return String representation of time. + */ +stock char[] GOKZ_HUD_FormatTime(int client, float time) +{ + bool precise = GOKZ_HUD_GetOption(client, HUDOption_TimerStyle) == TimerStyle_Precise; + return GOKZ_FormatTime(time, precise); +} + + + +// =====[ DEPENDENCY ]===== + +public SharedPlugin __pl_gokz_hud = +{ + name = "gokz-hud", + file = "gokz-hud.smx", + #if defined REQUIRE_PLUGIN + required = 1, + #else + required = 0, + #endif +}; + +#if !defined REQUIRE_PLUGIN +public void __pl_gokz_hud_SetNTVOptional() +{ + MarkNativeAsOptional("GOKZ_HUD_GetMenuShowing"); + MarkNativeAsOptional("GOKZ_HUD_SetMenuShowing"); + MarkNativeAsOptional("GOKZ_HUD_GetMenuSpectatorText"); + MarkNativeAsOptional("GOKZ_HUD_ForceUpdateTPMenu"); +} +#endif diff --git a/sourcemod/scripting/include/gokz/jumpbeam.inc b/sourcemod/scripting/include/gokz/jumpbeam.inc new file mode 100644 index 0000000..1b92479 --- /dev/null +++ b/sourcemod/scripting/include/gokz/jumpbeam.inc @@ -0,0 +1,148 @@ +/* + gokz-jumpbeam Plugin Include + + Website: https://bitbucket.org/kztimerglobalteam/gokz +*/ + +#if defined _gokz_jumpbeam_included_ +#endinput +#endif +#define _gokz_jumpbeam_included_ + + + +// =====[ ENUMS ]===== + +enum JBOption: +{ + JBOPTION_INVALID = -1, + JBOption_Type, + JBOPTION_COUNT +}; + +enum +{ + JBType_Disabled = 0, + JBType_Feet, + JBType_Head, + JBType_FeetAndHead, + JBType_Ground, + JBTYPE_COUNT +}; + + + +// =====[ CONSTANTS ]===== + +#define JB_BEAM_LIFETIME 4.0 + +stock char gC_JBOptionNames[JBOPTION_COUNT][] = +{ + "GOKZ JB - Jump Beam Type" +}; + +stock char gC_JBOptionDescriptions[JBOPTION_COUNT][] = +{ + "Jump Beam Type - 0 = Disabled, 1 = Feet, 2 = Head, 3 = Feet & Head, 4 = Ground" +}; + +stock int gI_JBOptionDefaultValues[JBOPTION_COUNT] = +{ + JBType_Disabled +}; + +stock int gI_JBOptionCounts[JBOPTION_COUNT] = +{ + JBTYPE_COUNT +}; + +stock char gC_JBOptionPhrases[JBOPTION_COUNT][] = +{ + "Options Menu - Jump Beam" +}; + +stock char gC_JBTypePhrases[JBTYPE_COUNT][] = +{ + "Options Menu - Disabled", + "Options Menu - Feet", + "Options Menu - Head", + "Options Menu - Feet and Head", + "Options Menu - Ground" +}; + + + +// =====[ STOCKS ]===== + +/** + * Returns whether an option is a gokz-jumpbeam option. + * + * @param option Option name. + * @param optionEnum Variable to store enumerated gokz-jumpbeam option (if it is one). + * @return Whether option is a gokz-jumpbeam option. + */ +stock bool GOKZ_JB_IsJBOption(const char[] option, JBOption &optionEnum = JBOPTION_INVALID) +{ + for (JBOption i; i < JBOPTION_COUNT; i++) + { + if (StrEqual(option, gC_JBOptionNames[i])) + { + optionEnum = i; + return true; + } + } + return false; +} + +/** + * Gets the current value of a player's gokz-jumpbeam option. + * + * @param client Client index. + * @param option gokz-jumpbeam option. + * @return Current value of option. + */ +stock any GOKZ_JB_GetOption(int client, JBOption option) +{ + return GOKZ_GetOption(client, gC_JBOptionNames[option]); +} + +/** + * Sets a player's gokz-jumpbeam option's value. + * + * @param client Client index. + * @param option gokz-jumpbeam option. + * @param value New option value. + * @return Whether option was successfully set. + */ +stock bool GOKZ_JB_SetOption(int client, JBOption option, any value) +{ + return GOKZ_SetOption(client, gC_JBOptionNames[option], value); +} + +/** + * Increment an integer-type gokz-jumpbeam option's value. + * Loops back to '0' if max value is exceeded. + * + * @param client Client index. + * @param option gokz-jumpbeam option. + * @return Whether option was successfully set. + */ +stock bool GOKZ_JB_CycleOption(int client, JBOption option) +{ + return GOKZ_CycleOption(client, gC_JBOptionNames[option]); +} + + + +// =====[ DEPENDENCY ]===== + +public SharedPlugin __pl_gokz_jumpbeam = +{ + name = "gokz-jumpbeam", + file = "gokz-jumpbeam.smx", + #if defined REQUIRE_PLUGIN + required = 1, + #else + required = 0, + #endif +};
\ No newline at end of file diff --git a/sourcemod/scripting/include/gokz/jumpstats.inc b/sourcemod/scripting/include/gokz/jumpstats.inc new file mode 100644 index 0000000..452ae28 --- /dev/null +++ b/sourcemod/scripting/include/gokz/jumpstats.inc @@ -0,0 +1,442 @@ +/* + gokz-jumpstats Plugin Include + + Website: https://bitbucket.org/kztimerglobalteam/gokz +*/ + +#if defined _gokz_jumpstats_included_ +#endinput +#endif +#define _gokz_jumpstats_included_ + + + +// =====[ ENUMS ]===== + +enum +{ + JumpType_FullInvalid = -1, + JumpType_LongJump, + JumpType_Bhop, + JumpType_MultiBhop, + JumpType_WeirdJump, + JumpType_LadderJump, + JumpType_Ladderhop, + JumpType_Jumpbug, + JumpType_LowpreBhop, + JumpType_LowpreWeirdJump, + JumpType_Fall, + JumpType_Other, + JumpType_Invalid, + JUMPTYPE_COUNT +}; + +enum +{ + StrafeDirection_None, + StrafeDirection_Left, + StrafeDirection_Right +}; + +enum +{ + DistanceTier_None = 0, + DistanceTier_Meh, + DistanceTier_Impressive, + DistanceTier_Perfect, + DistanceTier_Godlike, + DistanceTier_Ownage, + DistanceTier_Wrecker, + DISTANCETIER_COUNT +}; + +enum JSOption: +{ + JSOPTION_INVALID = -1, + JSOption_JumpstatsMaster, + JSOption_MinChatTier, + JSOption_MinConsoleTier, + JSOption_MinSoundTier, + JSOption_FailstatsConsole, + JSOption_FailstatsChat, + JSOption_JumpstatsAlways, + JSOption_ExtendedChatReport, + JSOption_MinChatBroadcastTier, + JSOption_MinSoundBroadcastTier, + JSOPTION_COUNT +}; + +enum +{ + JSToggleOption_Disabled = 0, + JSToggleOption_Enabled, + JSTOGGLEOPTION_COUNT +}; + + + +// =====[ CONSTANTS ]===== + +#define JS_CFG_TIERS "cfg/sourcemod/gokz/gokz-jumpstats-tiers.cfg" +#define JS_CFG_SOUNDS "cfg/sourcemod/gokz/gokz-jumpstats-sounds.cfg" +#define JS_CFG_BROADCAST "cfg/sourcemod/gokz/gokz-jumpstats-broadcast.cfg" +#define JS_OPTION_CATEGORY "Jumpstats" +#define JS_MAX_LADDERJUMP_OFFSET 2.0 +#define JS_MAX_BHOP_GROUND_TICKS 5 +#define JS_MAX_DUCKBUG_RESET_TICKS 6 +#define JS_MAX_WEIRDJUMP_FALL_OFFSET 64.0 +#define JS_TOUCH_GRACE_TICKS 3 +#define JS_MAX_TRACKED_STRAFES 48 +#define JS_MIN_BLOCK_DISTANCE 186 +#define JS_MIN_LAJ_BLOCK_DISTANCE 50 +#define JS_MAX_LAJ_FAILSTAT_DISTANCE 250 +#define JS_TOP_RECORD_COUNT 20 +#define JS_MAX_JUMP_DISTANCE 500 +#define JS_FAILSTATS_MAX_TRACKED_TICKS 128 +#define JS_MIN_TELEPORT_DELAY 5 +#define JS_SPEED_MODIFICATION_TOLERANCE 0.1 +#define JS_OFFSET_EPSILON 0.03125 + +stock char gC_JumpTypes[JUMPTYPE_COUNT][] = +{ + "Long Jump", + "Bunnyhop", + "Multi Bunnyhop", + "Weird Jump", + "Ladder Jump", + "Ladderhop", + "Jumpbug", + "Lowpre Bunnyhop", + "Lowpre Weird Jump", + "Fall", + "Unknown Jump", + "Invalid Jump" +}; + +stock char gC_JumpTypesShort[JUMPTYPE_COUNT][] = +{ + "LJ", + "BH", + "MBH", + "WJ", + "LAJ", + "LAH", + "JB", + "LBH", + "LWJ", + "FL", + "UNK", + "INV" +}; + +stock char gC_JumpTypeKeys[JUMPTYPE_COUNT][] = +{ + "longjump", + "bhop", + "multibhop", + "weirdjump", + "ladderjump", + "ladderhop", + "jumpbug", + "lowprebhop", + "lowpreweirdjump", + "fall", + "unknown", + "invalid" +}; + +stock char gC_DistanceTiers[DISTANCETIER_COUNT][] = +{ + "None", + "Meh", + "Impressive", + "Perfect", + "Godlike", + "Ownage", + "Wrecker" +}; + +stock char gC_DistanceTierKeys[DISTANCETIER_COUNT][] = +{ + "none", + "meh", + "impressive", + "perfect", + "godlike", + "ownage", + "wrecker" +}; + +stock char gC_DistanceTierChatColours[DISTANCETIER_COUNT][] = +{ + "{grey}", + "{grey}", + "{blue}", + "{green}", + "{darkred}", + "{gold}", + "{orchid}" +}; + +stock char gC_JSOptionNames[JSOPTION_COUNT][] = +{ + "GOKZ JS - Master Switch", + "GOKZ JS - Chat Report", + "GOKZ JS - Console Report", + "GOKZ JS - Sounds", + "GOKZ JS - Failstats Console", + "GOKZ JS - Failstats Chat", + "GOKZ JS - Jumpstats Always", + "GOKZ JS - Ext Chat Report", + "GOKZ JS - Min Chat Broadcast", + "GOKZ JS - Min Sound Broadcast" +}; + +stock char gC_JSOptionDescriptions[JSOPTION_COUNT][] = +{ + "Master Switch for All Jumpstats Functionality - 0 = Disabled, 1 = Enabled", + "Minimum Tier for Jumpstats Chat Report - 0 = Disabled, 1 = Meh+, 2 = Impressive+, 3 = Perfect+, 4 = Godlike+, 5 = Ownage+, 6 = Wrecker", + "Minimum Tier for Jumpstats Console report - 0 = Disabled, 1 = Meh+, 2 = Impressive+, 3 = Perfect+, 4 = Godlike+, 5 = Ownage+, 6 = Wrecker", + "Minimum Tier for Jumpstats Sounds - 0 = Disabled, 2 = Impressive+, 3 = Perfect+, 4 = Godlike+, 5 = Ownage+, 6 = Wrecker", + "Print Failstats To Console - 0 = Disabled, 1 = Enabled", + "Print Failstats To Chat - 0 = Disabled, 1 = Enabled", + "Always show jumpstats, even for invalid jumps - 0 = Disabled, 1 = Enabled", + "Extended Chat Report - 0 = Disabled, 1 = Enabled", + "Minimum Jump Tier for Jumpstat Chat Broadcast - 0 = Disabled, 1 = Meh+, 2 = Impressive+, 3 = Perfect+, 4 = Godlike+, 5 = Ownage+, 6 = Wrecker", + "Minimum Jump Tier for Jumpstat Sound Broadcast - 0 = Disabled, 1 = Meh+, 2 = Impressive+, 3 = Perfect+, 4 = Godlike+, 5 = Ownage+, 6 = Wrecker" +}; + +stock char gI_JSOptionPhrases[JSOPTION_COUNT][] = +{ + "Options Menu - Jumpstats Master Switch", + "Options Menu - Jumpstats Chat Report", + "Options Menu - Jumpstats Console Report", + "Options Menu - Jumpstats Sounds", + "Options Menu - Failstats Console Report", + "Options Menu - Failstats Chat Report", + "Options Menu - Jumpstats Always", + "Options Menu - Extended Jump Chat Report", + "Options Menu - Minimal Jump Chat Broadcast Tier", + "Options Menu - Minimal Jump Sound Broadcast Tier" +}; + +stock int gI_JSOptionDefaults[JSOPTION_COUNT] = +{ + JSToggleOption_Enabled, + DistanceTier_Meh, + DistanceTier_Meh, + DistanceTier_Impressive, + JSToggleOption_Enabled, + JSToggleOption_Disabled, + JSToggleOption_Disabled, + JSToggleOption_Disabled, + DistanceTier_Ownage, + DistanceTier_None +}; + +stock int gI_JSOptionCounts[JSOPTION_COUNT] = +{ + JSTOGGLEOPTION_COUNT, + DISTANCETIER_COUNT, + DISTANCETIER_COUNT, + DISTANCETIER_COUNT, + JSTOGGLEOPTION_COUNT, + JSTOGGLEOPTION_COUNT, + JSTOGGLEOPTION_COUNT, + JSTOGGLEOPTION_COUNT, + DISTANCETIER_COUNT, + DISTANCETIER_COUNT +}; + + + +// =====[ STRUCTS ]===== + +enum struct Jump +{ + int jumper; + int block; + int crouchRelease; + int crouchTicks; + int deadair; + int duration; + int originalType; + int overlap; + int releaseW; + int strafes; + int type; + float deviation; + float distance; + float edge; + float height; + float maxSpeed; + float offset; + float preSpeed; + float sync; + float width; + + // For the 'always' stats + float miss; + + // We can't make a separate enum struct for that cause it won't let us + // index an array of enum structs. + int strafes_gainTicks[JS_MAX_TRACKED_STRAFES]; + int strafes_deadair[JS_MAX_TRACKED_STRAFES]; + int strafes_overlap[JS_MAX_TRACKED_STRAFES]; + int strafes_ticks[JS_MAX_TRACKED_STRAFES]; + float strafes_gain[JS_MAX_TRACKED_STRAFES]; + float strafes_loss[JS_MAX_TRACKED_STRAFES]; + float strafes_sync[JS_MAX_TRACKED_STRAFES]; + float strafes_width[JS_MAX_TRACKED_STRAFES]; +} + + + +// =====[ FORWARDS ]===== + +/** + * Called when a player begins their jump. + * + * @param client Client index. + * @param jumpType Type of jump. + */ +forward void GOKZ_JS_OnTakeoff(int client, int jumpType); + +/** + * Called when a player lands their jump. + * + * @param jump The jumpstats. + */ +forward void GOKZ_JS_OnLanding(Jump jump); + +/** + * Called when player's current jump has been declared an invalid jumpstat. + * + * @param client Client index. + */ +forward void GOKZ_JS_OnJumpInvalidated(int client); + +/** + * Called when a player fails a blockjump. + * + * @param jump The jumpstats. + */ +forward void GOKZ_JS_OnFailstat(Jump jump); + +/** + * Called when a player lands a jump and has always-on jumpstats enabled. + * + * @param jump The jumpstats. + */ +forward void GOKZ_JS_OnJumpstatAlways(Jump jump); + +/** + * Called when a player fails a jump and has always-on failstats enabled. + * + * @param jump The failstats. + */ +forward void GOKZ_JS_OnFailstatAlways(Jump jump); + + + +// =====[ NATIVES ]===== + +/** + * Gets the default jumpstats option value as set by a config file. + * + * @param option GOKZ Jumpstats option. + * @return Default option value. + */ +native int GOKZ_JS_GetDefaultOption(JSOption option); + +/** + * Declare a player's current jump an invalid jumpstat. + * + * @param client Client index. + */ +native void GOKZ_JS_InvalidateJump(int client); + + + +// =====[ STOCKS ]===== + +/** + * Returns whether an option is a gokz-jumpstats option. + * + * @param option Option name. + * @param optionEnum Variable to store enumerated gokz-jumpstats option (if it is one). + * @return Whether option is a gokz-jumpstats option. + */ +stock bool GOKZ_JS_IsJSOption(const char[] option, JSOption &optionEnum = JSOPTION_INVALID) +{ + for (JSOption i; i < JSOPTION_COUNT; i++) + { + if (StrEqual(option, gC_JSOptionNames[i])) + { + optionEnum = i; + return true; + } + } + return false; +} + +/** + * Gets the current value of a player's gokz-jumpstats option. + * + * @param client Client index. + * @param option gokz-jumpstats option. + * @return Current value of option. + */ +stock any GOKZ_JS_GetOption(int client, JSOption option) +{ + return GOKZ_GetOption(client, gC_JSOptionNames[option]); +} + +/** + * Sets a player's gokz-jumpstats option's value. + * + * @param client Client index. + * @param option gokz-jumpstats option. + * @param value New option value. + * @return Whether option was successfully set. + */ +stock bool GOKZ_JS_SetOption(int client, JSOption option, any value) +{ + return GOKZ_SetOption(client, gC_JSOptionNames[option], value); +} + +/** + * Increment an integer-type gokz-jumpstats option's value. + * Loops back to '0' if max value is exceeded. + * + * @param client Client index. + * @param option gokz-jumpstats option. + * @return Whether option was successfully set. + */ +stock bool GOKZ_JS_CycleOption(int client, JSOption option) +{ + return GOKZ_CycleOption(client, gC_JSOptionNames[option]); +} + + + +// =====[ DEPENDENCY ]===== + +public SharedPlugin __pl_gokz_jumpstats = +{ + name = "gokz-jumpstats", + file = "gokz-jumpstats.smx", + #if defined REQUIRE_PLUGIN + required = 1, + #else + required = 0, + #endif +}; + +#if !defined REQUIRE_PLUGIN +public void __pl_gokz_jumpstats_SetNTVOptional() +{ + MarkNativeAsOptional("GOKZ_JS_GetDefaultOption"); + MarkNativeAsOptional("GOKZ_JS_InvalidateJump"); +} +#endif diff --git a/sourcemod/scripting/include/gokz/kzplayer.inc b/sourcemod/scripting/include/gokz/kzplayer.inc new file mode 100644 index 0000000..8176d39 --- /dev/null +++ b/sourcemod/scripting/include/gokz/kzplayer.inc @@ -0,0 +1,584 @@ +/* + GOKZ KZPlayer Methodmap Include + + Website: https://bitbucket.org/kztimerglobalteam/gokz +*/ + +#if defined _gokz_kzplayer_included_ +#endinput +#endif +#define _gokz_kzplayer_included_ + +#include <movementapi> + +#include <gokz> + + + +methodmap KZPlayer < MovementAPIPlayer { + + public KZPlayer(int client) { + return view_as<KZPlayer>(MovementAPIPlayer(client)); + } + + + + // =====[ GENERAL ]===== + + property bool Valid { + public get() { + return IsValidClient(this.ID); + } + } + + property bool InGame { + public get() { + return IsClientInGame(this.ID); + } + } + + property bool Authorized { + public get() { + return IsClientAuthorized(this.ID); + } + } + + property bool Fake { + public get() { + return IsFakeClient(this.ID); + } + } + + property bool Alive { + public get() { + return IsPlayerAlive(this.ID); + } + } + + property ObsMode ObserverMode { + public get() { + return GetObserverMode(this.ID); + } + } + + property int ObserverTarget { + public get() { + return GetObserverTarget(this.ID); + } + } + + + + // =====[ CORE ]===== + #if defined _gokz_core_included_ + + public void StartTimer(int course) { + GOKZ_StartTimer(this.ID, course); + } + + public void EndTimer(int course) { + GOKZ_EndTimer(this.ID, course); + } + + public bool StopTimer() { + return GOKZ_StopTimer(this.ID); + } + + public void TeleportToStart() { + GOKZ_TeleportToStart(this.ID); + } + + public void TeleportToSearchStart(int course) { + GOKZ_TeleportToSearchStart(this.ID, course); + } + + public void TeleportToEnd(int course) { + GOKZ_TeleportToEnd(this.ID, course); + } + + property StartPositionType StartPositionType { + public get() { + return GOKZ_GetStartPositionType(this.ID); + } + } + + public void MakeCheckpoint() { + GOKZ_MakeCheckpoint(this.ID); + } + + property bool CanMakeCheckpoint { + public get() { + return GOKZ_GetCanMakeCheckpoint(this.ID); + } + } + + public void TeleportToCheckpoint() { + GOKZ_TeleportToCheckpoint(this.ID); + } + + property bool CanTeleportToCheckpoint { + public get() { + return GOKZ_GetCanTeleportToCheckpoint(this.ID); + } + } + + public void PrevCheckpoint() { + GOKZ_PrevCheckpoint(this.ID); + } + + property bool CanPrevCheckpoint { + public get() { + return GOKZ_GetCanPrevCheckpoint(this.ID); + } + } + + public void NextCheckpoint() { + GOKZ_NextCheckpoint(this.ID); + } + + property bool CanNextCheckpoint { + public get() { + return GOKZ_GetCanNextCheckpoint(this.ID); + } + } + + public void UndoTeleport() { + GOKZ_UndoTeleport(this.ID); + } + + property bool CanUndoTeleport { + public get() { + return GOKZ_GetCanUndoTeleport(this.ID); + } + } + + public void Pause() { + GOKZ_Pause(this.ID); + } + + property bool CanPause { + public get() { + return GOKZ_GetCanPause(this.ID); + } + } + + public void Resume() { + GOKZ_Resume(this.ID); + } + + property bool CanResume { + public get() { + return GOKZ_GetCanResume(this.ID); + } + } + + public void TogglePause() { + GOKZ_TogglePause(this.ID); + } + + public void PlayErrorSound() { + GOKZ_PlayErrorSound(this.ID); + } + + property bool TimerRunning { + public get() { + return GOKZ_GetTimerRunning(this.ID); + } + } + + property int Course { + public get() { + return GOKZ_GetCourse(this.ID); + } + } + + property bool Paused { + public get() { + return GOKZ_GetPaused(this.ID); + } + public set(bool pause) { + if (pause) { + this.Pause(); + } + else { + this.Resume(); + } + } + } + + property bool CanTeleportToStart { + public get() { + return GOKZ_GetCanTeleportToStartOrEnd(this.ID); + } + } + + property float Time { + public get() { + return GOKZ_GetTime(this.ID); + } + public set(float value) { + GOKZ_SetTime(this.ID, value); + } + } + + property int CheckpointCount { + public get() { + return GOKZ_GetCheckpointCount(this.ID); + } + public set(int cpCount) { + GOKZ_SetCheckpointCount(this.ID, cpCount); + } + } + + property ArrayList CheckpointData { + public get() { + return GOKZ_GetCheckpointData(this.ID); + } + public set(ArrayList checkpoints) { + GOKZ_SetCheckpointData(this.ID, checkpoints, GOKZ_CHECKPOINT_VERSION); + } + } + + property int TeleportCount { + public get() { + return GOKZ_GetTeleportCount(this.ID); + } + public set(int value) { + GOKZ_SetTeleportCount(this.ID, value); + } + } + + property int TimeType { + public get() { + return GOKZ_GetTimeType(this.ID); + } + } + + property bool GOKZHitPerf { + public get() { + return GOKZ_GetHitPerf(this.ID); + } + public set(bool value) { + GOKZ_SetHitPerf(this.ID, value); + } + } + + property float GOKZTakeoffSpeed { + public get() { + return GOKZ_GetTakeoffSpeed(this.ID); + } + public set(float value) { + GOKZ_SetTakeoffSpeed(this.ID, value); + } + } + + property bool ValidJump { + public get() { + return GOKZ_GetValidJump(this.ID); + } + } + + public any GetOption(const char[] option) { + return GOKZ_GetOption(this.ID, option); + } + + public bool SetOption(const char[] option, any value) { + return GOKZ_SetOption(this.ID, option, value); + } + + public bool CycleOption(const char[] option) { + return GOKZ_CycleOption(this.ID, option); + } + + public any GetCoreOption(Option option) { + return GOKZ_GetCoreOption(this.ID, option); + } + + public bool SetCoreOption(Option option, int value) { + return GOKZ_SetCoreOption(this.ID, option, value); + } + + public bool CycleCoreOption(Option option) { + return GOKZ_CycleCoreOption(this.ID, option); + } + + property int Mode { + public get() { + return this.GetCoreOption(Option_Mode); + } + public set(int value) { + this.SetCoreOption(Option_Mode, value); + } + } + + property int Style { + public get() { + return this.GetCoreOption(Option_Style); + } + public set(int value) { + this.SetCoreOption(Option_Style, value); + } + } + + property int CheckpointMessages { + public get() { + return this.GetCoreOption(Option_CheckpointMessages); + } + public set(int value) { + this.SetCoreOption(Option_CheckpointMessages, value); + } + } + + property int CheckpointSounds { + public get() { + return this.GetCoreOption(Option_CheckpointSounds); + } + public set(int value) { + this.SetCoreOption(Option_CheckpointSounds, value); + } + } + + property int TeleportSounds { + public get() { + return this.GetCoreOption(Option_TeleportSounds); + } + public set(int value) { + this.SetCoreOption(Option_TeleportSounds, value); + } + } + + property int ErrorSounds { + public get() { + return this.GetCoreOption(Option_ErrorSounds); + } + public set(int value) { + this.SetCoreOption(Option_ErrorSounds, value); + } + } + + #endif + // =====[ END CORE ]===== + + + + // =====[ HUD ]===== + #if defined _gokz_hud_included_ + + public any GetHUDOption(HUDOption option) { + return GOKZ_HUD_GetOption(this.ID, option); + } + + public bool SetHUDOption(HUDOption option, any value) { + return GOKZ_HUD_SetOption(this.ID, option, value); + } + + public bool CycleHUDOption(HUDOption option) { + return GOKZ_HUD_CycleOption(this.ID, option); + } + + property int TPMenu { + public get() { + return this.GetHUDOption(HUDOption_TPMenu); + } + public set(int value) { + this.SetHUDOption(HUDOption_TPMenu, value); + } + } + + property int InfoPanel { + public get() { + return this.GetHUDOption(HUDOption_InfoPanel); + } + public set(int value) { + this.SetHUDOption(HUDOption_InfoPanel, value); + } + } + + property int ShowKeys { + public get() { + return this.GetHUDOption(HUDOption_ShowKeys); + } + public set(int value) { + this.SetHUDOption(HUDOption_ShowKeys, value); + } + } + + property int TimerText { + public get() { + return this.GetHUDOption(HUDOption_TimerText); + } + public set(int value) { + this.SetHUDOption(HUDOption_TimerText, value); + } + } + + property int TimerStyle { + public get() { + return this.GetHUDOption(HUDOption_TimerStyle); + } + public set(int value) { + this.SetHUDOption(HUDOption_TimerStyle, value); + } + } + + property int SpeedText { + public get() { + return this.GetHUDOption(HUDOption_SpeedText); + } + public set(int value) { + this.SetHUDOption(HUDOption_SpeedText, value); + } + } + + property int ShowWeapon { + public get() { + return this.GetHUDOption(HUDOption_ShowWeapon); + } + public set(int value) { + this.SetHUDOption(HUDOption_ShowWeapon, value); + } + } + + property int ReplayControls { + public get() { + return this.GetHUDOption(HUDOption_ShowControls); + } + public set(int value) { + this.SetHUDOption(HUDOption_ShowControls, value); + } + } + + property int ShowSpectators { + public get() { + return this.GetHUDOption(HUDOption_ShowSpectators); + } + public set(int value) { + this.SetHUDOption(HUDOption_ShowSpectators, value); + } + } + + property int SpecListPosition { + public get() { + return this.GetHUDOption(HUDOption_SpecListPosition); + } + public set(int value){ + this.SetHUDOption(HUDOption_SpecListPosition, value); + } + } + + property bool MenuShowing { + public get() { + return GOKZ_HUD_GetMenuShowing(this.ID); + } + public set(bool value) { + GOKZ_HUD_SetMenuShowing(this.ID, value); + } + } + property int DynamicMenu { + public get() { + return this.GetHUDOption(HUDOption_DynamicMenu); + } + public set(int value) { + this.SetHUDOption(HUDOption_DynamicMenu, value); + } + } + #endif + // =====[ END HUD ]===== + + + + // =====[ PISTOL ]===== + #if defined _gokz_pistol_included_ + + property int Pistol { + public get() { + return this.GetOption(PISTOL_OPTION_NAME); + } + public set(int value) { + this.SetOption(PISTOL_OPTION_NAME, value); + } + } + + #endif + // =====[ END PISTOL ]===== + + + + // =====[ JUMP BEAM ]===== + #if defined _gokz_jumpbeam_included_ + + public any GetJBOption(JBOption option) { + return GOKZ_JB_GetOption(this.ID, option); + } + + public bool SetJBOption(JBOption option, any value) { + return GOKZ_JB_SetOption(this.ID, option, value); + } + + public bool CycleJBOption(JBOption option) { + return GOKZ_JB_CycleOption(this.ID, option); + } + + property int JBType { + public get() { + return this.GetJBOption(JBOption_Type); + } + public set(int value) { + this.SetJBOption(JBOption_Type, value); + } + } + + #endif + // =====[ END JUMP BEAM ]===== + + + + // =====[ TIPS ]===== + #if defined _gokz_tips_included_ + + property int Tips { + public get() { + return this.GetOption(TIPS_OPTION_NAME); + } + public set(int value) { + this.SetOption(TIPS_OPTION_NAME, value); + } + } + + #endif + // =====[ END TIPS ]===== + + + + // =====[ QUIET ]===== + #if defined _gokz_quiet_included_ + + property int ShowPlayers { + public get() { + return this.GetOption(gC_QTOptionNames[QTOption_ShowPlayers]); + } + public set(int value) { + this.SetOption(gC_QTOptionNames[QTOption_ShowPlayers], value); + } + } + + #endif + // =====[ END QUIET ]===== + + + + // =====[ SLAY ON END ]===== + #if defined _gokz_slayonend_included_ + + property int SlayOnEnd { + public get() { + return this.GetOption(SLAYONEND_OPTION_NAME); + } + public set(int value) { + this.SetOption(SLAYONEND_OPTION_NAME, value); + } + } + + #endif + // =====[ END SLAY ON END ]===== +}
\ No newline at end of file diff --git a/sourcemod/scripting/include/gokz/localdb.inc b/sourcemod/scripting/include/gokz/localdb.inc new file mode 100644 index 0000000..472a120 --- /dev/null +++ b/sourcemod/scripting/include/gokz/localdb.inc @@ -0,0 +1,353 @@ +/* + gokz-localdb Plugin Include + + Website: https://bitbucket.org/kztimerglobalteam/gokz +*/ + +#if defined _gokz_localdb_included_ +#endinput +#endif +#define _gokz_localdb_included_ + + + +// =====[ ENUMS ]===== + +enum DatabaseType +{ + DatabaseType_None = -1, + DatabaseType_MySQL, + DatabaseType_SQLite +}; + +enum +{ + JumpstatDB_Lookup_JumpID = 0, + JumpstatDB_Lookup_Distance, + JumpstatDB_Lookup_Block +}; + +enum +{ + JumpstatDB_FindPlayer_SteamID32 = 0, + JumpstatDB_FindPlayer_Alias +}; + +enum +{ + JumpstatDB_Top20_JumpID = 0, + JumpstatDB_Top20_SteamID, + JumpstatDB_Top20_Alias, + JumpstatDB_Top20_Block, + JumpstatDB_Top20_Distance, + JumpstatDB_Top20_Strafes, + JumpstatDB_Top20_Sync, + JumpstatDB_Top20_Pre, + JumpstatDB_Top20_Max, + JumpstatDB_Top20_Air +}; + +enum +{ + JumpstatDB_PBMenu_JumpID = 0, + JumpstatDB_PBMenu_JumpType, + JumpstatDB_PBMenu_Distance, + JumpstatDB_PBMenu_Strafes, + JumpstatDB_PBMenu_Sync, + JumpstatDB_PBMenu_Pre, + JumpstatDB_PBMenu_Max, + JumpstatDB_PBMenu_Air +}; + +enum +{ + JumpstatDB_BlockPBMenu_JumpID = 0, + JumpstatDB_BlockPBMenu_JumpType, + JumpstatDB_BlockPBMenu_Block, + JumpstatDB_BlockPBMenu_Distance, + JumpstatDB_BlockPBMenu_Strafes, + JumpstatDB_BlockPBMenu_Sync, + JumpstatDB_BlockPBMenu_Pre, + JumpstatDB_BlockPBMenu_Max, + JumpstatDB_BlockPBMenu_Air +}; + +enum +{ + JumpstatDB_Cache_Distance = 0, + JumpstatDB_Cache_Block, + JumpstatDB_Cache_BlockDistance, + JUMPSTATDB_CACHE_COUNT +}; + +enum +{ + TimerSetupDB_GetVBPos_SteamID = 0, + TimerSetupDB_GetVBPos_MapID, + TimerSetupDB_GetVBPos_Course, + TimerSetupDB_GetVBPos_IsStart, + TimerSetupDB_GetVBPos_PositionX, + TimerSetupDB_GetVBPos_PositionY, + TimerSetupDB_GetVBPos_PositionZ +}; + +enum +{ + TimerSetupDB_GetStartPos_SteamID = 0, + TimerSetupDB_GetStartPos_MapID, + TimerSetupDB_GetStartPos_PositionX, + TimerSetupDB_GetStartPos_PositionY, + TimerSetupDB_GetStartPos_PositionZ, + TimerSetupDB_GetStartPos_Angle0, + TimerSetupDB_GetStartPos_Angle1 +}; + +enum DBOption: +{ + DBOption_AutoLoadTimerSetup = 0, + DBOPTION_COUNT +}; + +enum +{ + DBOption_Disabled = 0, + DBOption_Enabled, + DBOPTIONBOOL_COUNT +}; + + + +// =====[ CONSTANTS ]===== + +#define GOKZ_DB_JS_DISTANCE_PRECISION 10000 +#define GOKZ_DB_JS_SYNC_PRECISION 100 +#define GOKZ_DB_JS_PRE_PRECISION 100 +#define GOKZ_DB_JS_MAX_PRECISION 100 +#define GOKZ_DB_JS_AIRTIME_PRECISION 10000 +#define GOKZ_DB_JS_MAX_JUMPS_PER_PLAYER 100 + +stock char gC_DBOptionNames[DBOPTION_COUNT][] = +{ + "GOKZ DB - Auto Load Setup" +}; + +stock char gC_DBOptionDescriptions[DBOPTION_COUNT][] = +{ + "Automatically load timer setup on map start - 0 = Disabled, 1 = Enabled" +} + +stock int gI_DBOptionDefaultValues[DBOPTION_COUNT] = +{ + DBOption_Disabled +}; + +stock int gI_DBOptionCounts[DBOPTION_COUNT] = +{ + DBOPTIONBOOL_COUNT +}; + +stock char gC_DBOptionPhrases[DBOPTION_COUNT][] = +{ + "Options Menu - Auto Load Timer Setup" +}; + + + +// =====[ TYPES ]===== + +typeset GetVBPositionCallback +{ + function Action(int client, const float position[3], int course, bool isStart); +}; + +typeset GetStartPositionCallback +{ + function Action(int client, const float position[3], const float angles[3]); +}; + + + +// =====[ FORWARDS ]===== + +/** + * Called when gokz-localdb has connected to the database. + * Use GOKZ_DB_GetDatabase to get a clone of the database handle. + * + * @param DBType Database type. + */ +forward void GOKZ_DB_OnDatabaseConnect(DatabaseType DBType); + +/** + * Called when a player is ready for database interaction. + * At this point, the player is present and updated in the "Players" table. + * + * @param client Client index. + * @param steamID SteamID32 of the player (from GetSteamAccountID()). + * @param cheater Whether player is marked as a cheater in the database. + */ +forward void GOKZ_DB_OnClientSetup(int client, int steamID, bool cheater); + +/** + * Called when the current map is ready for database interaction. + * At this point, the map is present and updated in the "Maps" table. + * + * @param mapID MapID from the "Maps" table. + */ +forward void GOKZ_DB_OnMapSetup(int mapID); + +/** + * Called when a time has been inserted into the database. + * + * @param client Client index. + * @param steamID SteamID32 of the player (from GetSteamAccountID()). + * @param mapID MapID from the "Maps" table. + * @param course Course number e.g. 0=main, 1='bonus1' etc. + * @param mode Player's movement mode. + * @param style Player's movement style. + * @param runTimeMS Player's end time in milliseconds. + * @param teleportsUsed Number of teleports used by player. + */ +forward void GOKZ_DB_OnTimeInserted( + int client, + int steamID, + int mapID, + int course, + int mode, + int style, + int runTimeMS, + int teleportsUsed); + +/** + * Called when jumpstat PB has been achieved. + * + * @param client Client index. + * @param jumptype Type of the jump. + * @param mode Mode the jump was performed in. + * @param distance Distance jumped. + * @param block The size of the block jumped across. + * @param strafes The amount of strafes used. + * @param sync Keyboard/mouse synchronisation of the jump. + * @param pre Speed at takeoff. + * @param max Maximum speed during the jump. + * @param airtime Amount of time spend airborne. + */ +forward void GOKZ_DB_OnJumpstatPB( + int client, + int jumptype, + int mode, + float distance, + int block, + int strafes, + float sync, + float pre, + float max, + int airtime); + + +// =====[ NATIVES ]===== + +/** + * Gets a clone of the GOKZ local database handle. + * + * @param database Database handle, or null if connection hasn't been made. + */ +native Database GOKZ_DB_GetDatabase(); + +/** + * Gets the GOKZ local database type. + * + * @return Database type. + */ +native DatabaseType GOKZ_DB_GetDatabaseType(); + +/** + * Gets whether client has been set up for GOKZ Local DB. + * + * @param client Client index. + * @return Whether GOKZ Local DB has set up the client. + */ +native bool GOKZ_DB_IsClientSetUp(int client); + +/** + * Gets whether GOKZ Local DB is set up for the current map. + * + * @return Whether GOKZ Local DB has set up the current map. + */ +native bool GOKZ_DB_IsMapSetUp(); + +/** + * Gets the current map's MapID as in the "Maps" table. + * + * @return MapID from the "Maps" table. + */ +native int GOKZ_DB_GetCurrentMapID(); + +/** + * Gets whether player is marked as a cheater in the database. + * + * @param client Client index. + * @return Whether player is marked as a cheater in the database. + */ +native bool GOKZ_DB_IsCheater(int client); + +/** + * Sets wheter player is marked as a cheater in the database. + * + * @param client Client index. + * @param cheater Whether to mark the player as a cheater. + */ +native void GOKZ_DB_SetCheater(int client, bool cheater); + + + +// =====[ STOCKS ]===== + +/** + * Converts a time float (seconds) to an integer (milliseconds). + * + * @param time Time in seconds. + * @return Time in milliseconds. + */ +stock int GOKZ_DB_TimeFloatToInt(float time) +{ + return RoundFloat(time * 1000.0); +} + +/** + * Converts a time integer (milliseconds) to a float (seconds). + * + * @param time Time in milliseconds. + * @return Time in seconds. + */ +stock float GOKZ_DB_TimeIntToFloat(int time) +{ + return time / 1000.0; +} + + + +// =====[ DEPENDENCY ]===== + +public SharedPlugin __pl_gokz_localdb = +{ + name = "gokz-localdb", + file = "gokz-localdb.smx", + #if defined REQUIRE_PLUGIN + required = 1, + #else + required = 0, + #endif +}; + +#if !defined REQUIRE_PLUGIN +public void __pl_gokz_localdb_SetNTVOptional() +{ + MarkNativeAsOptional("GOKZ_DB_GetDatabase"); + MarkNativeAsOptional("GOKZ_DB_GetDatabaseType"); + MarkNativeAsOptional("GOKZ_DB_IsClientSetUp"); + MarkNativeAsOptional("GOKZ_DB_IsMapSetUp"); + MarkNativeAsOptional("GOKZ_DB_GetCurrentMapID"); + MarkNativeAsOptional("GOKZ_DB_IsCheater"); + MarkNativeAsOptional("GOKZ_DB_SetCheater"); +} +#endif diff --git a/sourcemod/scripting/include/gokz/localranks.inc b/sourcemod/scripting/include/gokz/localranks.inc new file mode 100644 index 0000000..914c6cb --- /dev/null +++ b/sourcemod/scripting/include/gokz/localranks.inc @@ -0,0 +1,176 @@ +/* + gokz-localranks Plugin Include + + Website: https://bitbucket.org/kztimerglobalteam/gokz +*/ + +#if defined _gokz_localranks_included_ +#endinput +#endif +#define _gokz_localranks_included_ + + + +// =====[ ENUMS ]===== + +enum +{ + RecordType_Nub = 0, + RecordType_Pro, + RecordType_NubAndPro, + RECORDTYPE_COUNT +}; + + + +// =====[ CONSTANTS ]===== + +#define LR_CFG_MAP_POOL "cfg/sourcemod/gokz/gokz-localranks-mappool.cfg" +#define LR_CFG_SOUNDS "cfg/sourcemod/gokz/gokz-localranks-sounds.cfg" +#define LR_COMMAND_COOLDOWN 2.5 +#define LR_MAP_TOP_CUTOFF 20 +#define LR_PLAYER_TOP_CUTOFF 20 + + + +// =====[ FORWARDS ]===== + +/** + * Called when a player's time has been processed by GOKZ Local Ranks. + * + * @param client Client index. + * @param steamID SteamID32 of the player (from GetSteamAccountID()). + * @param mapID MapID from the "Maps" database table. + * @param course Course number e.g. 0=main, 1='bonus1' etc. + * @param mode Player's movement mode. + * @param style Player's movement style. + * @param runTime Player's end time. + * @param teleportsUsed Number of teleportsUsed used by player. + * @param firstTime Whether this is player's first time on this course. + * @param pbDiff Difference between new time and PB in seconds (-'ve means beat PB). + * @param rank New rank of the player's PB time. + * @param maxRank New total number of players with times. + * @param firstTimePro Whether this is player's first PRO time on this course. + * @param pbDiffPro Difference between new time and PRO PB in seconds (-'ve means beat PB). + * @param rankPro New rank of the player's PB PRO time. + * @param maxRankPro New total number of players with PRO times. + */ +forward void GOKZ_LR_OnTimeProcessed( + int client, + int steamID, + int mapID, + int course, + int mode, + int style, + float runTime, + int teleportsUsed, + bool firstTime, + float pbDiff, + int rank, + int maxRank, + bool firstTimePro, + float pbDiffPro, + int rankPro, + int maxRankPro); + +/** + * Called when a player sets a new local record. + * + * @param client Client index. + * @param steamID SteamID32 of the player (from GetSteamAccountID()). + * @param mapID MapID from the "Maps" table. + * @param course Course number e.g. 0=main, 1='bonus1' etc. + * @param mode Player's movement mode. + * @param style Player's movement style. + * @param recordType Type of record. + */ +forward void GOKZ_LR_OnNewRecord( + int client, + int steamID, + int mapID, + int course, + int mode, + int style, + int recordType, + float pbDiff, + int teleportsUsed); + +/** + * Called when a player misses the server record time. + * Is called regardless of player's current run type. + * + * @param client Client index. + * @param recordTime Record time missed. + * @param course Course number e.g. 0=main, 1='bonus1' etc. + * @param mode Player's movement mode. + * @param style Player's movement style. + * @param recordType Type of record. + */ +forward void GOKZ_LR_OnRecordMissed(int client, float recordTime, int course, int mode, int style, int recordType); + +/** + * Called when a player misses their personal best time. + * Is called regardless of player's current run type. + * + * @param client Client index. + * @param pbTime Personal best time missed. + * @param course Course number e.g. 0=main, 1='bonus1' etc. + * @param mode Player's movement mode. + * @param style Player's movement style. + * @param recordType Type of record. + */ +forward void GOKZ_LR_OnPBMissed(int client, float pbTime, int course, int mode, int style, int recordType); + + + +// =====[ NATIVES ]===== + +/** + * Gets whether player has missed the server record time. + * + * @param client Client index. + * @param timeType Which record time i.e. NUB or PRO. + * @return Whether player has missed the server record time. + */ +native bool GOKZ_LR_GetRecordMissed(int client, int timeType); + +/** + * Gets whether player has missed their personal best time. + * + * @param client Client index. + * @param timeType Which PB time i.e. NUB or PRO. + * @return Whether player has missed their PB time. + */ +native bool GOKZ_LR_GetPBMissed(int client, int timeType); + +/** + * Reopens the map top menu with the already selected parameters. + * Don't use if the client hasn't opened the map top menu before. + * + * @param client Client index. + */ +native void GOKZ_LR_ReopenMapTopMenu(int client); + + + +// =====[ DEPENDENCY ]===== + +public SharedPlugin __pl_gokz_localranks = +{ + name = "gokz-localranks", + file = "gokz-localranks.smx", + #if defined REQUIRE_PLUGIN + required = 1, + #else + required = 0, + #endif +}; + +#if !defined REQUIRE_PLUGIN +public void __pl_gokz_localranks_SetNTVOptional() +{ + MarkNativeAsOptional("GOKZ_LR_GetRecordMissed"); + MarkNativeAsOptional("GOKZ_LR_GetPBMissed"); + MarkNativeAsOptional("GOKZ_LR_ReopenMapTopMenu"); +} +#endif
\ No newline at end of file diff --git a/sourcemod/scripting/include/gokz/momsurffix.inc b/sourcemod/scripting/include/gokz/momsurffix.inc new file mode 100644 index 0000000..65f603e --- /dev/null +++ b/sourcemod/scripting/include/gokz/momsurffix.inc @@ -0,0 +1,23 @@ +/* + gokz-momsurffix Plugin Include + + Website: https://bitbucket.org/kztimerglobalteam/gokz +*/ + +#if defined _gokz_momsurffix_included_ +#endinput +#endif +#define _gokz_momsurffix_included_ + +// =====[ DEPENDENCY ]===== + +public SharedPlugin __pl_gokz_momsurffix = +{ + name = "gokz-momsurffix", + file = "gokz-momsurffix.smx", + #if defined REQUIRE_PLUGIN + required = 1, + #else + required = 0, + #endif +};
\ No newline at end of file diff --git a/sourcemod/scripting/include/gokz/paint.inc b/sourcemod/scripting/include/gokz/paint.inc new file mode 100644 index 0000000..19f4fb5 --- /dev/null +++ b/sourcemod/scripting/include/gokz/paint.inc @@ -0,0 +1,114 @@ +/* + gokz-paint Plugin Include + + Website: https://bitbucket.org/kztimerglobalteam/gokz +*/ + +#if defined _gokz_paint_included_ +#endinput +#endif +#define _gokz_paint_included_ + + + +// =====[ ENUMS ]===== + +enum PaintOption: +{ + PAINTOPTION_INVALID = -1, + PaintOption_Color, + PaintOption_Size, + PAINTOPTION_COUNT +}; + +enum +{ + PaintColor_Red = 0, + PaintColor_White, + PaintColor_Black, + PaintColor_Blue, + PaintColor_Brown, + PaintColor_Green, + PaintColor_Yellow, + PaintColor_Purple, + PAINTCOLOR_COUNT +}; + +enum +{ + PaintSize_Small = 0, + PaintSize_Medium, + PaintSize_Big, + PAINTSIZE_COUNT +}; + + + +// =====[ CONSTANTS ]===== + +#define PAINT_OPTION_CATEGORY "Paint" +#define MIN_PAINT_SPACING 1.0 + +stock char gC_PaintOptionNames[PAINTOPTION_COUNT][] = +{ + "GOKZ Paint - Color", + "GOKZ Paint - Size" +}; + +stock char gC_PaintOptionDescriptions[PAINTOPTION_COUNT][] = +{ + "Paint Color", + "Paint Size - 0 = Small, 1 = Medium, 2 = Big" +}; + +stock char gC_PaintOptionPhrases[PAINTOPTION_COUNT][] = +{ + "Options Menu - Paint Color", + "Options Menu - Paint Size" +}; + +stock int gI_PaintOptionCounts[PAINTOPTION_COUNT] = +{ + PAINTCOLOR_COUNT, + PAINTSIZE_COUNT +}; + +stock int gI_PaintOptionDefaults[PAINTOPTION_COUNT] = +{ + PaintColor_Red, + PaintSize_Medium +}; + +stock char gC_PaintColorPhrases[PAINTCOLOR_COUNT][] = +{ + "Options Menu - Red", + "Options Menu - White", + "Options Menu - Black", + "Options Menu - Blue", + "Options Menu - Brown", + "Options Menu - Green", + "Options Menu - Yellow", + "Options Menu - Purple" +}; + +stock char gC_PaintSizePhrases[PAINTSIZE_COUNT][] = +{ + "Options Menu - Small", + "Options Menu - Medium", + "Options Menu - Big" +}; + + + +// =====[ DEPENDENCY ]===== + +public SharedPlugin __pl_gokz_paint = +{ + name = "gokz-paint", + file = "gokz-paint.smx", + #if defined REQUIRE_PLUGIN + required = 1, + #else + required = 0, + #endif +}; diff --git a/sourcemod/scripting/include/gokz/pistol.inc b/sourcemod/scripting/include/gokz/pistol.inc new file mode 100644 index 0000000..1edd5f9 --- /dev/null +++ b/sourcemod/scripting/include/gokz/pistol.inc @@ -0,0 +1,93 @@ +/* + gokz-pistol Plugin Include + + Website: https://bitbucket.org/kztimerglobalteam/gokz +*/ + +#if defined _gokz_pistol_included_ +#endinput +#endif +#define _gokz_pistol_included_ + + + +// =====[ ENUMS ]===== + +enum +{ + Pistol_Disabled = 0, + Pistol_USPS, + Pistol_Glock18, + Pistol_DualBerettas, + Pistol_P250, + Pistol_FiveSeveN, + Pistol_Tec9, + Pistol_CZ75Auto, + Pistol_DesertEagle, + Pistol_R8Revolver, + PISTOL_COUNT +}; + + + +// =====[ CONSTANTS ]===== + +#define PISTOL_OPTION_NAME "GOKZ - Pistol" +#define PISTOL_OPTION_DESCRIPTION "Pistol - 0 = Disabled, 1 = USP-S / P2000, 2 = Glock-18, 3 = Dual Berettas, 4 = P250, 5 = Five-SeveN, 6 = Tec-9, 7 = CZ75-Auto, 8 = Desert Eagle, 9 = R8 Revolver" + +stock char gC_PistolNames[PISTOL_COUNT][] = +{ + "", // Disabled + "USP-S / P2000", + "Glock-18", + "Dual Berettas", + "P250", + "Five-SeveN", + "Tec-9", + "CZ75-Auto", + "Desert Eagle", + "R8 Revolver" +}; + +stock char gC_PistolClassNames[PISTOL_COUNT][] = +{ + "", // Disabled + "weapon_hkp2000", + "weapon_glock", + "weapon_elite", + "weapon_p250", + "weapon_fiveseven", + "weapon_tec9", + "weapon_cz75a", + "weapon_deagle", + "weapon_revolver" +}; + +stock int gI_PistolTeams[PISTOL_COUNT] = +{ + CS_TEAM_NONE, // Disabled + CS_TEAM_CT, + CS_TEAM_T, + CS_TEAM_NONE, + CS_TEAM_NONE, + CS_TEAM_CT, + CS_TEAM_T, + CS_TEAM_NONE, + CS_TEAM_NONE, + CS_TEAM_NONE +}; + + + +// =====[ DEPENDENCY ]===== + +public SharedPlugin __pl_gokz_pistol = +{ + name = "gokz-pistol", + file = "gokz-pistol.smx", + #if defined REQUIRE_PLUGIN + required = 1, + #else + required = 0, + #endif +};
\ No newline at end of file diff --git a/sourcemod/scripting/include/gokz/profile.inc b/sourcemod/scripting/include/gokz/profile.inc new file mode 100644 index 0000000..70d314a --- /dev/null +++ b/sourcemod/scripting/include/gokz/profile.inc @@ -0,0 +1,291 @@ +/* + gokz-profile Plugin Include + + Website: https://bitbucket.org/kztimerglobalteam/gokz +*/ + +#if defined _gokz_profile_included_ +#endinput +#endif +#define _gokz_profile_included_ + + +// =====[ RANKS ]===== + +#define RANK_COUNT 23 + +stock int gI_rankThreshold[MODE_COUNT][RANK_COUNT] = { + { + 0, + 1, + 500, + 1000, + + 2000, + 5000, + 10000, + + 20000, + 30000, + 40000, + + 60000, + 70000, + 80000, + + 100000, + 120000, + 140000, + + 160000, + 180000, + 200000, + + 250000, + 300000, + 400000, + 600000 + }, + { + 0, + 1, + 500, + 1000, + + 2000, + 5000, + 10000, + + 20000, + 30000, + 40000, + + 60000, + 70000, + 80000, + + 100000, + 120000, + 150000, + + 200000, + 230000, + 250000, + + 300000, + 400000, + 500000, + 800000 + }, + { + 0, + 1, + 500, + 1000, + + 2000, + 5000, + 10000, + + 20000, + 30000, + 40000, + + 60000, + 70000, + 80000, + + 100000, + 120000, + 150000, + + 200000, + 230000, + 250000, + + 400000, + 600000, + 800000, + 1000000 + }, +}; + +stock char gC_rankName[RANK_COUNT][] = { + "New", + "Beginner-", + "Beginner", + "Beginner+", + "Amateur-", + "Amateur", + "Amateur+", + "Casual-", + "Casual", + "Casual+", + "Regular-", + "Regular", + "Regular+", + "Skilled-", + "Skilled", + "Skilled+", + "Expert-", + "Expert", + "Expert+", + "Semipro", + "Pro", + "Master", + "Legend" +}; + +stock char gC_rankColor[RANK_COUNT][] = { + "{grey}", + "{default}", + "{default}", + "{default}", + "{blue}", + "{blue}", + "{blue}", + "{lightgreen}", + "{lightgreen}", + "{lightgreen}", + "{green}", + "{green}", + "{green}", + "{purple}", + "{purple}", + "{purple}", + "{orchid}", + "{orchid}", + "{orchid}", + "{lightred}", + "{lightred}", + "{red}", + "{gold}" +}; + + +// =====[ ENUMS ]===== + +enum ProfileOption: +{ + PROFILEOPTION_INVALID = -1, + ProfileOption_ShowRankChat, + ProfileOption_ShowRankClanTag, + ProfileOption_TagType, + PROFILEOPTION_COUNT +}; + +enum +{ + ProfileOptionBool_Disabled = 0, + ProfileOptionBool_Enabled, + PROFILEOPTIONBOOL_COUNT +}; + +enum +{ + ProfileTagType_Rank = 0, + ProfileTagType_Admin, + ProfileTagType_VIP, + PROFILETAGTYPE_COUNT +}; + + + +// =====[ CONSTANTS ]===== + +stock char gC_ProfileOptionNames[PROFILEOPTION_COUNT][] = +{ + "GOKZ Profile - Show Rank Chat", + "GOKZ Profile - Show Rank Clan", + "GOKZ Profile - Tag Type" +}; + +stock char gC_ProfileOptionDescriptions[PROFILEOPTION_COUNT][] = +{ + "Show Rank Tag in Chat - 0 = Disabled, 1 = Enabled", + "Show Rank in Clan - 0 = Disabled, 1 = Enabled", + "Type of Tag to Show - 0 = Rank, 1 = Admin, 2 = VIP" +}; + +stock char gC_ProfileOptionPhrases[PROFILEOPTION_COUNT][] = +{ + "Options Menu - Show Rank Chat", + "Options Menu - Show Rank Clan", + "Options Menu - Tag Type", +}; + +stock char gC_ProfileBoolPhrases[PROFILEOPTIONBOOL_COUNT][] = +{ + "Options Menu - Disabled", + "Options Menu - Enabled" +}; + +stock char gC_ProfileTagTypePhrases[PROFILETAGTYPE_COUNT][] = +{ + "Options Menu - Tag Rank", + "Options Menu - Tag Admin", + "Options Menu - Tag VIP" +}; + +stock int gI_ProfileOptionCounts[PROFILEOPTION_COUNT] = +{ + PROFILEOPTIONBOOL_COUNT, + PROFILEOPTIONBOOL_COUNT, + PROFILETAGTYPE_COUNT +}; + +stock int gI_ProfileOptionDefaults[PROFILEOPTION_COUNT] = +{ + ProfileOptionBool_Enabled, + ProfileOptionBool_Enabled, + ProfileTagType_Rank +}; + +#define PROFILE_OPTION_CATEGORY "Profile" +#define TAG_COLOR_ADMIN "{red}" +#define TAG_COLOR_VIP "{purple}" + + +// =====[ FORWARDS ]===== + + +/** + * Called when the rank of a player is updated. + * + * @param client Client index. + * @param mode Game mode. + * @param rank The new rank. + */ +forward void GOKZ_PF_OnRankUpdated(int client, int mode, int rank); + +// =====[ NATIVES ]===== + +/** + * Gets whether a mode is loaded. + * + * @param client Client. + * @param tag Mode. + * @returns Integer representing the player rank. + */ +native int GOKZ_PF_GetRank(int client, int mode); + + +// =====[ DEPENDENCY ]===== + +public SharedPlugin __pl_gokz_profile = +{ + name = "gokz-profile", + file = "gokz-profile.smx", + #if defined REQUIRE_PLUGIN + required = 1, + #else + required = 0, + #endif +}; + +#if !defined REQUIRE_PLUGIN +public void __pl_gokz_profile_SetNTVOptional() +{ + MarkNativeAsOptional("GOKZ_PF_GetRank"); +} +#endif
\ No newline at end of file diff --git a/sourcemod/scripting/include/gokz/quiet.inc b/sourcemod/scripting/include/gokz/quiet.inc new file mode 100644 index 0000000..a328b7e --- /dev/null +++ b/sourcemod/scripting/include/gokz/quiet.inc @@ -0,0 +1,205 @@ +/* + gokz-quiet Plugin Include + + Website: https://bitbucket.org/kztimerglobalteam/gokz +*/ + +#if defined _gokz_quiet_included_ +#endinput +#endif +#define _gokz_quiet_included_ + + + +// =====[ ENUMS ]===== + +enum QTOption: +{ + QTOPTION_INVALID = -1, + QTOption_ShowPlayers, + QTOption_Soundscapes, + QTOption_FallDamageSound, + QTOption_AmbientSounds, + QTOption_CheckpointVolume, + QTOption_TeleportVolume, + QTOption_TimerVolume, + QTOption_ErrorVolume, + QTOption_ServerRecordVolume, + QTOption_WorldRecordVolume, + QTOption_JumpstatsVolume, + QTOPTION_COUNT +}; + +enum +{ + ShowPlayers_Disabled = 0, + ShowPlayers_Enabled, + SHOWPLAYERS_COUNT +}; + +enum +{ + Soundscapes_Disabled = 0, + Soundscapes_Enabled, + SOUNDSCAPES_COUNT +}; + +// =====[ CONSTANTS ]===== + +#define QUIET_OPTION_CATEGORY "Quiet" +#define DEFAULT_VOLUME 10 +#define VOLUME_COUNT 21 // Maximum of 200% + +#define EFFECT_IMPACT 8 +#define EFFECT_KNIFESLASH 2 +#define BLANK_SOUNDSCAPEINDEX 482 // Search for "coopcementplant.missionselect_blank" id with sv_soundscape_printdebuginfo. + +stock char gC_QTOptionNames[QTOPTION_COUNT][] = +{ + "GOKZ QT - Show Players", + "GOKZ QT - Soundscapes", + "GOKZ QT - Fall Damage Sound", + "GOKZ QT - Ambient Sounds", + "GOKZ QT - Checkpoint Volume", + "GOKZ QT - Teleport Volume", + "GOKZ QT - Timer Volume", + "GOKZ QT - Error Volume", + "GOKZ QT - Server Record Volume", + "GOKZ QT - World Record Volume", + "GOKZ QT - Jumpstats Volume" +}; + +stock char gC_QTOptionDescriptions[QTOPTION_COUNT][] = +{ + "Visibility of Other Players - 0 = Disabled, 1 = Enabled", + "Play Soundscapes - 0 = Disabled, 1 = Enabled", + "Play Fall Damage Sound - 0 to 20 = 0% to 200%", + "Play Ambient Sounds - 0 to 20 = 0% to 200%", + "Checkpoint Volume - 0 to 20 = 0% to 200%", + "Teleport Volume - 0 to 20 = 0% to 200%", + "Timer Volume - 0 to 20 = 0% to 200%", + "Error Volume - 0 to 20 = 0% to 200%", + "Server Record Volume - 0 to 20 = 0% to 200%", + "World Record Volume - 0 to 20 = 0% to 200%", + "Jumpstats Volume - 0 to 20 = 0% to 200%" +}; + +stock int gI_QTOptionDefaultValues[QTOPTION_COUNT] = +{ + ShowPlayers_Enabled, + Soundscapes_Enabled, + DEFAULT_VOLUME, // Fall damage volume + DEFAULT_VOLUME, // Ambient volume + DEFAULT_VOLUME, // Checkpoint volume + DEFAULT_VOLUME, // Teleport volume + DEFAULT_VOLUME, // Timer volume + DEFAULT_VOLUME, // Error volume + DEFAULT_VOLUME, // Server Record Volume + DEFAULT_VOLUME, // World Record Volume + DEFAULT_VOLUME // Jumpstats Volume +}; + +stock int gI_QTOptionCounts[QTOPTION_COUNT] = +{ + SHOWPLAYERS_COUNT, + SOUNDSCAPES_COUNT, + VOLUME_COUNT, // Fall damage volume + VOLUME_COUNT, // Ambient volume + VOLUME_COUNT, // Checkpoint volume + VOLUME_COUNT, // Teleport volume + VOLUME_COUNT, // Timer volume + VOLUME_COUNT, // Error volume + VOLUME_COUNT, // Server Record volume + VOLUME_COUNT, // World Record volume + VOLUME_COUNT // Jumpstats volume +}; + +stock char gC_QTOptionPhrases[QTOPTION_COUNT][] = +{ + "Options Menu - Show Players", + "Options Menu - Soundscapes", + "Options Menu - Fall Damage Sounds", + "Options Menu - Ambient Sounds", + "Options Menu - Checkpoint Volume", + "Options Menu - Teleport Volume", + "Options Menu - Timer Volume", + "Options Menu - Error Volume", + "Options Menu - Server Record Volume", + "Options Menu - World Record Volume", + "Options Menu - Jumpstats Volume" +}; + +// =====[ STOCKS ]===== + +/** + * Returns whether an option is a gokz-quiet option. + * + * @param option Option name. + * @param optionEnum Variable to store enumerated gokz-quiet option (if it is one). + * @return Whether option is a gokz-quiet option. + */ +stock bool GOKZ_QT_IsQTOption(const char[] option, QTOption &optionEnum = QTOPTION_INVALID) +{ + for (QTOption i; i < QTOPTION_COUNT; i++) + { + if (StrEqual(option, gC_QTOptionNames[i])) + { + optionEnum = i; + return true; + } + } + return false; +} + +/** + * Gets the current value of a player's gokz-quiet option. + * + * @param client Client index. + * @param option gokz-quiet option. + * @return Current value of option. + */ +stock any GOKZ_QT_GetOption(int client, QTOption option) +{ + return GOKZ_GetOption(client, gC_QTOptionNames[option]); +} + +/** + * Sets a player's gokz-quiet option's value. + * + * @param client Client index. + * @param option gokz-quiet option. + * @param value New option value. + * @return Whether option was successfully set. + */ +stock bool GOKZ_QT_SetOption(int client, QTOption option, any value) +{ + return GOKZ_SetOption(client, gC_QTOptionNames[option], value); +} + +/** + * Increment an integer-type gokz-quiet option's value. + * Loops back to '0' if max value is exceeded. + * + * @param client Client index. + * @param option gokz-quiet option. + * @return Whether option was successfully set. + */ +stock bool GOKZ_QT_CycleOption(int client, QTOption option) +{ + return GOKZ_CycleOption(client, gC_QTOptionNames[option]); +} + + + +// =====[ DEPENDENCY ]===== + +public SharedPlugin __pl_gokz_quiet = +{ + name = "gokz-quiet", + file = "gokz-quiet.smx", + #if defined REQUIRE_PLUGIN + required = 1, + #else + required = 0, + #endif +};
\ No newline at end of file diff --git a/sourcemod/scripting/include/gokz/racing.inc b/sourcemod/scripting/include/gokz/racing.inc new file mode 100644 index 0000000..d6819ea --- /dev/null +++ b/sourcemod/scripting/include/gokz/racing.inc @@ -0,0 +1,189 @@ +/* + gokz-racing Plugin Include + + Website: https://bitbucket.org/kztimerglobalteam/gokz +*/ + +#if defined _gokz_racing_included_ +#endinput +#endif +#define _gokz_racing_included_ + + + +// =====[ ENUMS ]===== + +enum RaceInfo: +{ + RaceInfo_ID, + RaceInfo_Status, + RaceInfo_HostUserID, + RaceInfo_FinishedRacerCount, + RaceInfo_Type, + RaceInfo_Course, + RaceInfo_Mode, + RaceInfo_CheckpointRule, + RaceInfo_CooldownRule, + RACEINFO_COUNT +}; + +enum +{ + RaceType_Normal, + RaceType_Duel, + RACETYPE_COUNT +}; + +enum +{ + RaceStatus_Pending, + RaceStatus_Countdown, + RaceStatus_Started, + RaceStatus_Aborting, + RACESTATUS_COUNT +}; + +enum +{ + RacerStatus_Available, + RacerStatus_Pending, + RacerStatus_Accepted, + RacerStatus_Racing, + RacerStatus_Finished, + RacerStatus_Surrendered, + RACERSTATUS_COUNT +}; + +enum +{ + CheckpointRule_None, + CheckpointRule_Limit, + CheckpointRule_Cooldown, + CheckpointRule_Unlimited, + CHECKPOINTRULE_COUNT +}; + + + +// =====[ CONSTANTS ]===== + +#define RC_COUNTDOWN_TIME 10.0 +#define RC_REQUEST_TIMEOUT_TIME 15.0 + + + +// =====[ FORWARDS ]===== + +/** + * Called when a player has finished their race. + * + * @param client Client index. + * @param raceID ID of the race. + * @param place Final placement in the race. + */ +forward void GOKZ_RC_OnFinish(int client, int raceID, int place); + +/** + * Called when a player has surrendered their race. + * + * @param client Client index. + * @param raceID ID of the race. + */ +forward void GOKZ_RC_OnSurrender(int client, int raceID); + +/** + * Called when a player receives a race request. + * + * @param client Client index. + * @param raceID ID of the race. + */ +forward void GOKZ_RC_OnRequestReceived(int client, int raceID) + +/** + * Called when a player accepts a race request. + * + * @param client Client index. + * @param raceID ID of the race. + */ +forward void GOKZ_RC_OnRequestAccepted(int client, int raceID) + +/** + * Called when a player declines a race request. + * + * @param client Client index. + * @param raceID ID of the race. + * @param timeout Whether the client was too late to respond. + */ +forward void GOKZ_RC_OnRequestDeclined(int client, int raceID, bool timeout) + +/** + * Called when a race has been registered. + * The initial status of a race is RaceStatus_Pending. + * + * @param raceID ID of the race. + */ +forward void GOKZ_RC_OnRaceRegistered(int raceID); + +/** + * Called when a race's info property has changed. + * + * @param raceID ID of the race. + * @param prop Info property that was changed. + * @param oldValue Previous value. + * @param newValue New value. + */ +forward void GOKZ_RC_OnRaceInfoChanged(int raceID, RaceInfo prop, int oldValue, int newValue); + + + +// =====[ NATIVES ]===== + +/** + * Gets the value of a race info property. + * + * @param raceID Race index. + * @param prop Info property to get. + * @return Value of the info property. + */ +native int GOKZ_RC_GetRaceInfo(int raceID, RaceInfo prop); + +/** + * Gets a player's racer status. + * Refer to the RacerStatus enumeration. + * + * @param client Client index. + * @return Racer status of the client. + */ +native int GOKZ_RC_GetStatus(int client); + +/** + * Gets the ID of the race a player is in. + * + * @param client Client index. + * @return ID of the race the player is in, or -1 if not in a race. + */ +native int GOKZ_RC_GetRaceID(int client); + + + +// =====[ DEPENDENCY ]===== + +public SharedPlugin __pl_gokz_racing = +{ + name = "gokz-racing", + file = "gokz-racing.smx", + #if defined REQUIRE_PLUGIN + required = 1, + #else + required = 0, + #endif +}; + +#if !defined REQUIRE_PLUGIN +public void __pl_gokz_racing_SetNTVOptional() +{ + MarkNativeAsOptional("GOKZ_RC_GetRaceInfo"); + MarkNativeAsOptional("GOKZ_RC_GetStatus"); + MarkNativeAsOptional("GOKZ_RC_GetRaceID"); +} +#endif
\ No newline at end of file diff --git a/sourcemod/scripting/include/gokz/replays.inc b/sourcemod/scripting/include/gokz/replays.inc new file mode 100644 index 0000000..6aabdbd --- /dev/null +++ b/sourcemod/scripting/include/gokz/replays.inc @@ -0,0 +1,275 @@ +/* + gokz-replays Plugin Include + + Website: https://bitbucket.org/kztimerglobalteam/gokz +*/ + +#if defined _gokz_replays_included_ +#endinput +#endif +#define _gokz_replays_included_ + +// Bit of a hack, but need it for other plugins that depend on replays to compile +#if defined REQUIRE_PLUGIN +#undef REQUIRE_PLUGIN +#include <gokz/anticheat> +#define REQUIRE_PLUGIN +#else +#include <gokz/anticheat> +#endif + + + +// =====[ ENUMS ]===== +enum +{ + ReplayType_Run = 0, + ReplayType_Cheater, + ReplayType_Jump, + REPLAYTYPE_COUNT +}; + +enum ReplaySaveState +{ + ReplaySave_Local = 0, + ReplaySave_Temp, + ReplaySave_Disabled +}; + +// NOTE: Replays use delta compression for storage. +// This enum is the indices of the ReplayTickData enum struct. +// NOTE: This has to match the ReplayTickData enum struct!!! +enum +{ + RPDELTA_DELTAFLAGS = 0, + RPDELTA_DELTAFLAGS2, + RPDELTA_VEL_X, + RPDELTA_VEL_Y, + RPDELTA_VEL_Z, + RPDELTA_MOUSE_X, + RPDELTA_MOUSE_Y, + RPDELTA_ORIGIN_X, + RPDELTA_ORIGIN_Y, + RPDELTA_ORIGIN_Z, + RPDELTA_ANGLES_X, + RPDELTA_ANGLES_Y, + RPDELTA_ANGLES_Z, + RPDELTA_VELOCITY_X, + RPDELTA_VELOCITY_Y, + RPDELTA_VELOCITY_Z, + RPDELTA_FLAGS, + RPDELTA_PACKETSPERSECOND, + RPDELTA_LAGGEDMOVEMENTVALUE, + RPDELTA_BUTTONSFORCED, + + RP_V2_TICK_DATA_BLOCKSIZE +}; + + + +// =====[ STRUCTS ] ===== + +enum struct GeneralReplayHeader +{ + int magicNumber; + int formatVersion; + int replayType; + char gokzVersion[32]; + char mapName[64]; + int mapFileSize; + int serverIP; + int timestamp; + char playerAlias[MAX_NAME_LENGTH]; + int playerSteamID; + int mode; + int style; + float playerSensitivity; + float playerMYaw; + float tickrate; + int tickCount; + int equippedWeapon; + int equippedKnife; +} + +enum struct JumpReplayHeader +{ + int jumpType; + float distance; + int blockDistance; + int strafeCount; + float sync; + float pre; + float max; + int airtime; +} + +enum struct CheaterReplayHeader +{ + ACReason ACReason; +} + +enum struct RunReplayHeader +{ + float time; + int course; + int teleportsUsed; +} + +// NOTE: Make sure to change the RPDELTA_* enum, TickDataToArray() and TickDataFromArray() when adding/removing stuff from this!!! +enum struct ReplayTickData +{ + int deltaFlags; + int deltaFlags2; + float vel[3]; + int mouse[2]; + float origin[3]; + float angles[3]; + float velocity[3]; + int flags; + float packetsPerSecond; + float laggedMovementValue; + int buttonsForced; +} + + + +// =====[ CONSTANTS ]===== + +#define RP_DIRECTORY "data/gokz-replays" // In Path_SM +#define RP_DIRECTORY_RUNS "data/gokz-replays/_runs" // In Path_SM +#define RP_DIRECTORY_RUNS_TEMP "data/gokz-replays/_tempRuns" // In Path_SM +#define RP_DIRECTORY_CHEATERS "data/gokz-replays/_cheaters" // In Path_SM +#define RP_DIRECTORY_JUMPS "data/gokz-replays/_jumps" // In Path_SM +#define RP_DIRECTORY_BLOCKJUMPS "blocks" +#define RP_FILE_EXTENSION "replay" +#define RP_MAGIC_NUMBER 0x676F6B7A +#define RP_FORMAT_VERSION 0x02 +#define RP_NAV_FILE "maps/gokz-replays.nav" +#define RP_V1_TICK_DATA_BLOCKSIZE 7 +#define RP_CACHE_BLOCKSIZE 4 +#define RP_MAX_BOTS 4 +#define RP_PLAYBACK_BREATHER_TIME 2.0 +#define RP_MIN_CHEATER_REPLAY_LENGTH 30 // 30 seconds +#define RP_MAX_CHEATER_REPLAY_LENGTH 120 // 2 minutes +#define RP_MAX_BHOP_GROUND_TICKS 5 +#define RP_SKIP_TIME 10 // 10 seconds +#define RP_MAX_DURATION 6451200 // 14 hours on 128 tick +#define RP_JUMP_STEP_SOUND_THRESHOLD 140.0 +#define RP_PLAYER_ACCELSPEED 450.0 + +#define RP_MOVETYPE_MASK (0xF) +#define RP_IN_ATTACK (1 << 4) +#define RP_IN_ATTACK2 (1 << 5) +#define RP_IN_JUMP (1 << 6) +#define RP_IN_DUCK (1 << 7) +#define RP_IN_FORWARD (1 << 8) +#define RP_IN_BACK (1 << 9) +#define RP_IN_LEFT (1 << 10) +#define RP_IN_RIGHT (1 << 11) +#define RP_IN_MOVELEFT (1 << 12) +#define RP_IN_MOVERIGHT (1 << 13) +#define RP_IN_RELOAD (1 << 14) +#define RP_IN_SPEED (1 << 15) +#define RP_IN_USE (1 << 16) +#define RP_IN_BULLRUSH (1 << 17) +#define RP_FL_ONGROUND (1 << 18) +#define RP_FL_DUCKING (1 << 19) +#define RP_FL_SWIM (1 << 20) +#define RP_UNDER_WATER (1 << 21) +#define RP_TELEPORT_TICK (1 << 22) +#define RP_TAKEOFF_TICK (1 << 23) +#define RP_HIT_PERF (1 << 24) +#define RP_SECONDARY_EQUIPPED (1 << 25) + + + +// =====[ FORWARDS ]===== + +/** + * Called when a replay of a player is written to disk. + * This includes replays of cheaters which are saved if + * the player is marked as a cheater by gokz-localdb. + * + * @param client The client ID of the player who completed the run. + * @param replayType The type of the replay (Run/Jump/Cheater). + * @param map The name of the map the run was completed on. + * @param course The specific course on the map the run was completed on. + * @param timeType The type of time (Pro/Nub). + * @param time The time the run was completed in. + * @param filePath Replay file path. + * @param tempReplay Whether the replay file should only be temporaily stored. + * @return Plugin_Handled to take over the temporary replay deletion, Plugin_Continue to allow temporary replay deletion by the replay plugin. + */ +forward Action GOKZ_RP_OnReplaySaved(int client, int replayType, const char[] map, int course, int timeType, float time, const char[] filePath, bool tempReplay); + +/** + * Called when a currently being recorded replay is discarded from + * memory and recording has been stopped (without writing it to disk). + * + * @param client Client index. + */ +forward void GOKZ_RP_OnReplayDiscarded(int client); + +/** + * Called when a player has ended their timer, and gokz-replays has + * processed the time and has possibly written a replay to disk. + * + * @param client Client index. + * @param filePath Replay file path, or "" if no replay saved. + * @param course Course number. + * @param time Player's end time. + * @param teleportsUsed Number of teleports used by player. + */ +forward void GOKZ_RP_OnTimerEnd_Post(int client, const char[] filePath, int course, float time, int teleportsUsed); + + + +// =====[ NATIVES ]==== + +/** + * Called by the HUD to get the state of the current replay. + * + * @param client Client index. + * @param info Struct to pass the values into. + * @return If successful + */ +native int GOKZ_RP_GetPlaybackInfo(int client, any[] info); + +/** + * Called by the LocalDB to initiate a replay of a jump + * + * @param client Client index. + * @param path Path to the replay file. + * @return The client ID of the bot performing the replay. + */ +native int GOKZ_RP_LoadJumpReplay(int client, char[] path); + +/** + * Called by the HUD to show the replay control menu. + * + * @param client Client index. + */ +native bool GOKZ_RP_UpdateReplayControlMenu(int client); + + +// =====[ DEPENDENCY ]===== + +public SharedPlugin __pl_gokz_replays = +{ + name = "gokz-replays", + file = "gokz-replays.smx", + #if defined REQUIRE_PLUGIN + required = 1, + #else + required = 0, + #endif +}; + +#if !defined REQUIRE_PLUGIN +public void __pl_gokz_replays_SetNTVOptional() +{ + MarkNativeAsOptional("GOKZ_RP_GetPlaybackInfo"); + MarkNativeAsOptional("GOKZ_RP_LoadJumpReplay"); + MarkNativeAsOptional("GOKZ_RP_UpdateReplayControlMenu"); +} +#endif diff --git a/sourcemod/scripting/include/gokz/slayonend.inc b/sourcemod/scripting/include/gokz/slayonend.inc new file mode 100644 index 0000000..2ed01e5 --- /dev/null +++ b/sourcemod/scripting/include/gokz/slayonend.inc @@ -0,0 +1,43 @@ +/* + gokz-slayonend Plugin Include + + Website: https://bitbucket.org/kztimerglobalteam/gokz +*/ + +#if defined _gokz_slayonend_included_ +#endinput +#endif +#define _gokz_slayonend_included_ + + + +// =====[ ENUMS ]===== + +enum +{ + SlayOnEnd_Disabled = 0, + SlayOnEnd_Enabled, + SLAYONEND_COUNT +}; + + + +// =====[ CONSTANTS ]===== + +#define SLAYONEND_OPTION_NAME "GOKZ - Slay On End" +#define SLAYONEND_OPTION_DESCRIPTION "Automatic Slaying Upon Ending Timer - 0 = Disabled, 1 = Enabled" + + + +// =====[ DEPENDENCY ]===== + +public SharedPlugin __pl_gokz_slayonend = +{ + name = "gokz-slayonend", + file = "gokz-slayonend.smx", + #if defined REQUIRE_PLUGIN + required = 1, + #else + required = 0, + #endif +};
\ No newline at end of file diff --git a/sourcemod/scripting/include/gokz/tips.inc b/sourcemod/scripting/include/gokz/tips.inc new file mode 100644 index 0000000..b5e2d3e --- /dev/null +++ b/sourcemod/scripting/include/gokz/tips.inc @@ -0,0 +1,59 @@ +/* + gokz-tips Plugin Include + + Website: https://bitbucket.org/kztimerglobalteam/gokz +*/ + +#if defined _gokz_tips_included_ +#endinput +#endif +#define _gokz_tips_included_ + + + +// =====[ ENUMS ]===== + +enum +{ + Tips_Disabled = 0, + Tips_Enabled, + TIPS_COUNT +}; + + + +// =====[ CONSTANTS ]===== + +#define TIPS_PLUGINS_COUNT 9 +#define TIPS_CORE "gokz-tips-core.phrases.txt" +#define TIPS_TIPS "gokz-tips-tips.phrases.txt" +#define TIPS_OPTION_NAME "GOKZ - Tips" +#define TIPS_OPTION_DESCRIPTION "Random Tips Periodically in Chat - 0 = Disabled, 1 = Enabled" + +stock char gC_PluginsWithTips[TIPS_PLUGINS_COUNT][] = +{ + "goto", + "hud", + "jumpstats", + "localranks", + "measure", + "pistol", + "quiet", + "replays", + "spec" +} + + + +// =====[ DEPENDENCY ]===== + +public SharedPlugin __pl_gokz_tips = +{ + name = "gokz-tips", + file = "gokz-tips.smx", + #if defined REQUIRE_PLUGIN + required = 1, + #else + required = 0, + #endif +};
\ No newline at end of file diff --git a/sourcemod/scripting/include/gokz/tpanglefix.inc b/sourcemod/scripting/include/gokz/tpanglefix.inc new file mode 100644 index 0000000..fc8faa2 --- /dev/null +++ b/sourcemod/scripting/include/gokz/tpanglefix.inc @@ -0,0 +1,40 @@ +/* + gokz-tpanglefix Plugin Include + + Website: https://github.com/KZGlobalTeam/gokz +*/ + +#if defined _gokz_tpanglefix_included_ +#endinput +#endif +#define _gokz_tpanglefix_included_ + + +// =====[ ENUMS ]===== + +enum +{ + TPAngleFix_Disabled = 0, + TPAngleFix_Enabled, + TPANGLEFIX_COUNT +}; + + +// =====[ CONSTANTS ]===== + +#define TPANGLEFIX_OPTION_NAME "GOKZ - TPAngleFix" +#define TPANGLEFIX_OPTION_DESCRIPTION "TPAngleFix - 0 = Disabled, 1 = Enabled" + + +// =====[ DEPENDENCY ]===== + +public SharedPlugin __pl_gokz_tpanglefix = +{ + name = "gokz-tpanglefix", + file = "gokz-tpanglefix.smx", + #if defined REQUIRE_PLUGIN + required = 1, + #else + required = 0, + #endif +};
\ No newline at end of file diff --git a/sourcemod/scripting/include/gokz/version.inc b/sourcemod/scripting/include/gokz/version.inc new file mode 100644 index 0000000..34cdb19 --- /dev/null +++ b/sourcemod/scripting/include/gokz/version.inc @@ -0,0 +1,12 @@ +/* + GOKZ Version Include + + Website: https://bitbucket.org/kztimerglobalteam/gokz + + You should not need to edit this file. + This file is overwritten in the build pipeline for versioning. +*/ + + + +#define GOKZ_VERSION ""
\ No newline at end of file diff --git a/sourcemod/scripting/include/json.inc b/sourcemod/scripting/include/json.inc new file mode 100644 index 0000000..ebc46e4 --- /dev/null +++ b/sourcemod/scripting/include/json.inc @@ -0,0 +1,473 @@ +/** + * vim: set ts=4 : + * ============================================================================= + * sm-json + * Provides a pure SourcePawn implementation of JSON encoding and decoding. + * https://github.com/clugg/sm-json + * + * sm-json (C)2019 James Dickens. (clug) + * SourceMod (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>. + */ + +#if defined _json_included + #endinput +#endif +#define _json_included + +#include <string> +#include <json/definitions> +#include <json/helpers/decode> +#include <json/helpers/encode> +#include <json/helpers/string> +#include <json/object> + +/** + * Encodes a JSON instance into its string representation. + * + * @param obj Object to encode. + * @param output String buffer to store output. + * @param max_size Maximum size of string buffer. + * @param pretty_print Should the output be pretty printed (newlines and spaces)? [default: false] + * @param depth The current depth of the encoder. [default: 0] + */ +stock void json_encode( + JSON_Object obj, + char[] output, + int max_size, + bool pretty_print = false, + int depth = 0 +) +{ + bool is_array = obj.IsArray; + bool is_empty = true; + int builder_size; + + // used in key iterator + int str_length = 1; + int int_value; + int cell_length = 0; + + StringMapSnapshot snap = null; + int json_size = 0; + if (is_array) { + json_size = obj.CurrentIndex; + + strcopy(output, max_size, "["); + } else { + snap = obj.Snapshot(); + json_size = snap.Length; + + strcopy(output, max_size, "{"); + } + + int key_length = 0; + for (int i = 0; i < json_size; ++i) { + key_length = (is_array) ? JSON_INDEX_BUFFER_SIZE : snap.KeyBufferSize(i); + char[] key = new char[key_length]; + + if (is_array) { + obj.GetIndexString(key, key_length, i); + } else { + snap.GetKey(i, key, key_length); + } + + // skip meta-keys + if (json_is_meta_key(key)) { + continue; + } + + // skip keys that are marked as hidden + if (obj.GetKeyHidden(key)) { + continue; + } + + JSON_CELL_TYPE type = obj.GetKeyType(key); + // skip keys of unknown type + if (type == Type_Invalid) { + continue; + } + + // if we are dealing with a string, prepare the str_value variable for fetching + if (type == Type_String) { + str_length = obj.GetKeyLength(key); + } + char[] str_value = new char[str_length + 1]; + + // determine the length of the char[] needed to represent our cell data + cell_length = 0; + switch (type) { + case Type_String: { + // get the string value early, as its cell_length is determined by its contents + obj.GetString(key, str_value, str_length + 1); + cell_length = json_cell_string_size(str_length); + } + case Type_Int: { + // get the int value early, as its cell_length is determined by its contents + int_value = obj.GetInt(key); + cell_length = json_cell_int_size(int_value); + } + case Type_Float: { + cell_length = json_cell_float_size(); + } + case Type_Bool: { + cell_length = json_cell_bool_size(); + } + case Type_Null: { + cell_length = json_cell_null_size(); + } + case Type_Object: { + cell_length = max_size; + } + } + + // fit the contents into the cell + char[] cell = new char[cell_length]; + switch (type) { + case Type_String: { + json_cell_string(str_value, cell, cell_length); + } + case Type_Int: { + json_cell_int(int_value, cell, cell_length); + } + case Type_Float: { + float value = obj.GetFloat(key); + json_cell_float(value, cell, cell_length); + } + case Type_Bool: { + bool value = obj.GetBool(key); + json_cell_bool(value, cell, cell_length); + } + case Type_Null: { + json_cell_null(cell, cell_length); + } + case Type_Object: { + JSON_Object child = obj.GetObject(key); + json_encode(child, cell, cell_length, pretty_print, depth + 1); + } + } + + // make the builder fit our key:value + // use previously determined cell length and + 1 for , + builder_size = cell_length + 1; + if (! is_array) { + // get the length of the key and + 1 for : + builder_size += json_cell_string_size(strlen(key)) + 1; + + if (pretty_print) { + builder_size += strlen(JSON_PP_AFTER_COLON); + } + } + + char[] builder = new char[builder_size]; + strcopy(builder, builder_size, ""); + + // add the key if we're working with an object + if (! is_array) { + json_cell_string(key, builder, builder_size); + StrCat(builder, builder_size, ":"); + + if (pretty_print) { + StrCat(builder, builder_size, JSON_PP_AFTER_COLON); + } + } + + // add the value and a trailing comma + StrCat(builder, builder_size, cell); + StrCat(builder, builder_size, ","); + + // prepare pretty printing then send builder to output afterwards + if (pretty_print) { + StrCat(output, max_size, JSON_PP_NEWLINE); + + for (int j = 0; j < depth + 1; ++j) { + StrCat(output, max_size, JSON_PP_INDENT); + } + } + + StrCat(output, max_size, builder); + + is_empty = false; + } + + if (snap != null) { + delete snap; + } + + if (! is_empty) { + // remove the final comma + output[strlen(output) - 1] = '\0'; + + if (pretty_print) { + StrCat(output, max_size, JSON_PP_NEWLINE); + + for (int j = 0; j < depth; ++j) { + StrCat(output, max_size, JSON_PP_INDENT); + } + } + } + + // append closing bracket + StrCat(output, max_size, (is_array) ? "]" : "}"); +} + +/** + * Decodes a JSON string into a JSON instance. + * + * @param buffer Buffer to decode. + * @param result Object to store output in. Setting this allows loading over + * an existing JSON instance, 'refreshing' it as opposed to + * creating a new one. [default: null] + * @param pos Current position of the decoder as a bytes offset into the buffer. + * @param depth Current depth of the decoder as child elements in the object. + * @returns JSON instance or null if decoding failed (buffer didn't contain valid JSON). + */ +stock JSON_Object json_decode( + const char[] buffer, + JSON_Object result = null, + int &pos = 0, + int depth = 0 +) +{ + int length = strlen(buffer); + bool is_array = false; + + // skip preceding whitespace + if (! json_skip_whitespace(buffer, length, pos)) { + LogError("json_decode: buffer ended early at position %d", pos); + + return null; + } + + if (json_is_object(buffer[pos])) { + is_array = false; + } else if (json_is_array(buffer[pos])) { + is_array = true; + } else { + LogError("json_decode: character not identified as object or array at position %d", pos); + + return null; + } + + if (result == null) { + result = new JSON_Object(is_array); + } + + bool empty_checked = false; + char[] key = new char[length]; + char[] cell = new char[length]; + + // while we haven't reached the end of our structure + while (! is_array && ! json_is_object_end(buffer[pos]) + || is_array && ! json_is_array_end(buffer[pos])) { + // pos is either an opening structure or comma, so increment past it + ++pos; + + // skip any whitespace preceding the element + if (! json_skip_whitespace(buffer, length, pos)) { + LogError("json_decode: buffer ended early at position %d", pos); + + return null; + } + + // if we are at the end of an object or array + // and haven't checked for empty yet, we can stop here (empty structure) + if ((! is_array && json_is_object_end(buffer[pos]) + || is_array && json_is_array_end(buffer[pos])) + && ! empty_checked) { + break; + } + + empty_checked = true; + + // if dealing with an object, look for the key + if (! is_array) { + if (! json_is_string(buffer[pos])) { + LogError("json_decode: expected key string at position %d", pos); + + return null; + } + + // extract the key from the buffer + json_extract_string(buffer, length, pos, key, length, is_array); + + // skip any whitespace following the key + if (! json_skip_whitespace(buffer, length, pos)) { + LogError("json_decode: buffer ended early at position %d", pos); + + return null; + } + + // ensure that we find a colon + if (buffer[pos++] != ':') { + LogError("json_decode: expected colon after key at position %d", pos); + + return null; + } + + // skip any whitespace following the colon + if (! json_skip_whitespace(buffer, length, pos)) { + LogError("json_decode: buffer ended early at position %d", pos); + + return null; + } + } + + if (json_is_object(buffer[pos]) || json_is_array(buffer[pos])) { + // if we are dealing with an object or array + // fetch the existing object if one exists at the key + JSON_Object current = (! is_array) ? result.GetObject(key) : null; + + // decode recursively + JSON_Object value = json_decode(buffer, current, pos, depth + 1); + + // decoding failed, error will be logged in json_decode + if (value == null) { + return null; + } + + if (is_array) { + result.PushObject(value); + } else { + result.SetObject(key, value); + } + } else if (json_is_string(buffer[pos])) { + // if we are dealing with a string, attempt to extract it + if (! json_extract_string(buffer, length, pos, cell, length, is_array)) { + LogError("json_decode: couldn't extract string at position %d", pos); + + return null; + } + + if (is_array) { + result.PushString(cell); + } else { + result.SetString(key, cell); + } + } else { + if (! json_extract_until_end(buffer, length, pos, cell, length, is_array)) { + LogError("json_decode: couldn't extract until end at position %d", pos); + + return null; + } + + if (strlen(cell) == 0) { + LogError("json_decode: empty cell encountered at position %d", pos); + + return null; + } + + if (json_is_int(cell)) { + int value = json_extract_int(cell); + if (is_array) { + result.PushInt(value); + } else { + result.SetInt(key, value); + } + } else if (json_is_float(cell)) { + float value = json_extract_float(cell); + if (is_array) { + result.PushFloat(value); + } else { + result.SetFloat(key, value); + } + } else if (json_is_bool(cell)) { + bool value = json_extract_bool(cell); + if (is_array) { + result.PushBool(value); + } else { + result.SetBool(key, value); + } + } else if (json_is_null(cell)) { + if (is_array) { + result.PushHandle(null); + } else { + result.SetHandle(key, null); + } + } else { + LogError("json_decode: unknown type encountered at position %d: %s", pos, cell); + + return null; + } + } + + if (! json_skip_whitespace(buffer, length, pos)) { + LogError("json_decode: buffer ended early at position %d", pos); + + return null; + } + } + + // skip remaining whitespace and ensure we're at the end of the buffer + ++pos; + if (json_skip_whitespace(buffer, length, pos) && depth == 0) { + LogError("json_decode: unexpected data after end of structure at position %d", pos); + + return null; + } + + return result; +} + +/** + * Recursively cleans up a JSON instance and any JSON instances stored within. + * + * @param obj JSON instance to clean up. + */ +stock void json_cleanup(JSON_Object obj) +{ + bool is_array = obj.IsArray; + + int key_length = 0; + StringMapSnapshot snap = obj.Snapshot(); + for (int i = 0; i < snap.Length; ++i) { + key_length = snap.KeyBufferSize(i); + char[] key = new char[key_length]; + + // ignore meta keys + snap.GetKey(i, key, key_length); + if (json_is_meta_key(key)) { + continue; + } + + // only clean up objects + JSON_CELL_TYPE type = obj.GetKeyType(key); + if (type != Type_Object) { + continue; + } + + JSON_Object nested_obj = obj.GetObject(key); + if (nested_obj != null) { + nested_obj.Cleanup(); + delete nested_obj; + } + } + + obj.Clear(); + delete snap; + + if (is_array) { + obj.SetValue(JSON_ARRAY_INDEX_KEY, 0); + } +} diff --git a/sourcemod/scripting/include/json/decode_helpers.inc b/sourcemod/scripting/include/json/decode_helpers.inc new file mode 100644 index 0000000..0032cc3 --- /dev/null +++ b/sourcemod/scripting/include/json/decode_helpers.inc @@ -0,0 +1,312 @@ +/** + * vim: set ts=4 : + * ============================================================================= + * sm-json + * Provides a pure SourcePawn implementation of JSON encoding and decoding. + * https://github.com/clugg/sm-json + * + * sm-json (C)2018 James D. (clug) + * SourceMod (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>. + */ + +#if defined _json_decode_helpers_included + #endinput +#endif +#define _json_decode_helpers_included + +#include <string> + +/** + * @section Analysing format of incoming JSON cells. + */ + +/** + * Checks whether the character at the given + * position in the buffer is whitespace. + * + * @param buffer String buffer of data. + * @param pos Position to check in buffer. + * @return True if buffer[pos] is whitespace, false otherwise. + */ +stock bool json_is_whitespace(const char[] buffer, int &pos) { + return buffer[pos] == ' ' || buffer[pos] == '\t' || + buffer[pos] == '\r' || buffer[pos] == '\n'; +} + +/** + * Checks whether the character at the beginning + * of the buffer is the start of a string. + * + * @param buffer String buffer of data. + * @return True if buffer[0] is the start of a string, false otherwise. + */ +stock bool json_is_string(const char[] buffer) { + return buffer[0] == '"'; +} + +/** + * Checks whether the buffer provided contains an int. + * + * @param buffer String buffer of data. + * @return True if buffer contains an int, false otherwise. + */ +stock bool json_is_int(const char[] buffer) { + int length = strlen(buffer); + if (buffer[0] != '+' && buffer[0] != '-' && !IsCharNumeric(buffer[0])) { + return false; + } + + for (int i = 0; i < length; ++i) { + if (!IsCharNumeric(buffer[i])) return false; + } + + return true; +} + +/** + * Checks whether the buffer provided contains a float. + * + * @param buffer String buffer of data. + * @return True if buffer contains a float, false otherwise. + */ +stock bool json_is_float(const char[] buffer) { + bool has_decimal = false; + int length = strlen(buffer); + if (buffer[0] != '+' && buffer[0] != '-' && buffer[0] != '.' && !IsCharNumeric(buffer[0])) { + return false; + } + + for (int i = 0; i < length; ++i) { + if (buffer[i] == '.') { + if (has_decimal) { + return false; + } + + has_decimal = true; + } else if (!IsCharNumeric(buffer[i])) { + return false; + } + } + + return true; +} + +/** + * Checks whether the buffer provided contains a bool. + * + * @param buffer String buffer of data. + * @return True if buffer contains a bool, false otherwise. + */ +stock bool json_is_bool(const char[] buffer) { + return StrEqual(buffer, "true") || + StrEqual(buffer, "false"); +} + +/** + * Checks whether the buffer provided contains null. + * + * @param buffer String buffer of data. + * @return True if buffer contains null, false otherwise. + */ +stock bool json_is_null(const char[] buffer) { + return StrEqual(buffer, "null"); +} + +/** + * Checks whether the character at the beginning + * of the buffer is the start of an object. + * + * @param buffer String buffer of data. + * @return True if buffer[0] is the start of an object, false otherwise. + */ +stock bool json_is_object(const char[] buffer) { + return buffer[0] == '{'; +} + +/** + * Checks whether the character at the beginning + * of the buffer is the end of an object. + * + * @param buffer String buffer of data. + * @return True if buffer[0] is the end of an object, false otherwise. + */ +stock bool json_is_object_end(const char[] buffer) { + return buffer[0] == '}'; +} + +/** + * Checks whether the character at the beginning + * of the buffer is the start of an array. + * + * @param buffer String buffer of data. + * @return True if buffer[0] is the start of an array, false otherwise. + */ +stock bool json_is_array(const char[] buffer) { + return buffer[0] == '['; +} + +/** + * Checks whether the character at the beginning + * of the buffer is the start of an array. + * + * @param buffer String buffer of data. + * @return True if buffer[0] is the start of an array, false otherwise. + */ +stock bool json_is_array_end(const char[] buffer) { + return buffer[0] == ']'; +} + +/** + * Checks whether the character at the given position in the buffer + * is considered a valid 'end point' for some data, such as a + * colon (indicating a key), a comma (indicating a new element), + * or the end of an object or array. + * + * @param buffer String buffer of data. + * @param pos Position to check in buffer. + * @return True if buffer[pos] is a valid data end point, false otherwise. + */ +stock bool json_is_at_end(const char[] buffer, int &pos, bool is_array) { + return buffer[pos] == ',' || + (!is_array && buffer[pos] == ':') || + json_is_object_end(buffer[pos]) || + json_is_array_end(buffer[pos]); +} + +/** + * Moves the position until it reaches a non-whitespace + * character or the end of the buffer's maximum size. + * + * @param buffer String buffer of data. + * @param maxlen Maximum size of string buffer. + * @param pos Position to increment. + * @return True if pos is not at the end of the buffer, false otherwise. + */ +stock bool json_skip_whitespace(const char[] buffer, int maxlen, int &pos) { + while (json_is_whitespace(buffer, pos) && pos < maxlen) { + ++pos; + } + + return pos < maxlen; +} + +/** + * Extracts a JSON cell from the buffer until + * a valid end point is reached. + * + * @param buffer String buffer of data. + * @param maxlen Maximum size of string buffer. + * @param pos Position to increment. + * @param output String buffer to store output. + * @param output_maxlen Maximum size of output string buffer. + * @param is_array Whether the decoder is currently processing an array. + * @return True if pos is not at the end of the buffer, false otherwise. + */ +stock bool json_extract_until_end(const char[] buffer, int maxlen, int &pos, char[] output, int output_maxlen, bool is_array) { + // extracts a string from current pos until a valid 'end point' + strcopy(output, output_maxlen, ""); + + int start = pos; + while (!json_is_whitespace(buffer, pos) && !json_is_at_end(buffer, pos, is_array) && pos < maxlen) { + ++pos; + } + int end = pos - 1; + + // skip trailing whitespace + json_skip_whitespace(buffer, maxlen, pos); + + if (!json_is_at_end(buffer, pos, is_array)) return false; + strcopy(output, end - start + 2, buffer[start]); + + return pos < maxlen; +} + + +/** + * Extracts a JSON string from the buffer until + * a valid end point is reached. + * + * @param buffer String buffer of data. + * @param maxlen Maximum size of string buffer. + * @param pos Position to increment. + * @param output String buffer to store output. + * @param output_maxlen Maximum size of output string buffer. + * @param is_array Whether the decoder is currently processing an array. + * @return True if pos is not at the end of the buffer, false otherwise. + */ +stock bool json_extract_string(const char[] buffer, int maxlen, int &pos, char[] output, int output_maxlen, bool is_array) { + // extracts a string which needs to be quote-escaped + strcopy(output, output_maxlen, ""); + + ++pos; + int start = pos; + while (!(buffer[pos] == '"' && buffer[pos - 1] != '\\') && pos < maxlen) { + ++pos; + } + int end = pos - 1; + + // jump 1 ahead since we ended on " instead of an ending char + ++pos; + + // skip trailing whitespace + json_skip_whitespace(buffer, maxlen, pos); + + if (!json_is_at_end(buffer, pos, is_array)) return false; + // copy only from start with length end - start + 2 (+2 for NULL terminator and something else) + strcopy(output, end - start + 2, buffer[start]); + json_unescape_string(output, maxlen); + + return pos < maxlen; +} + +/** + * Extracts an int from the buffer. + * + * @param buffer String buffer of data. + * @return Int value of the buffer. + */ +stock int json_extract_int(const char[] buffer) { + return StringToInt(buffer); +} + +/** + * Extracts a float from the buffer. + * + * @param buffer String buffer of data. + * @return Float value of the buffer. + */ +stock float json_extract_float(const char[] buffer) { + return StringToFloat(buffer); +} + +/** + * Extracts a bool from the buffer. + * + * @param buffer String buffer of data. + * @return Bool value of the buffer. + */ +stock bool json_extract_bool(const char[] buffer) { + return StrEqual(buffer, "true"); +} diff --git a/sourcemod/scripting/include/json/definitions.inc b/sourcemod/scripting/include/json/definitions.inc new file mode 100644 index 0000000..63063d3 --- /dev/null +++ b/sourcemod/scripting/include/json/definitions.inc @@ -0,0 +1,103 @@ +/** + * vim: set ts=4 : + * ============================================================================= + * sm-json + * Provides a pure SourcePawn implementation of JSON encoding and decoding. + * https://github.com/clugg/sm-json + * + * sm-json (C)2019 James Dickens. (clug) + * SourceMod (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>. + */ + +#if defined _json_definitions_included + #endinput +#endif +#define _json_definitions_included + +#include <string> +#include <json/helpers/string> + +/** + * @section Pretty Print Constants + * + * Used to determine how pretty printed JSON should be formatted when encoded. + * You can modify these if you prefer your JSON formatted differently. + */ + +#define JSON_PP_AFTER_COLON " " +#define JSON_PP_INDENT " " +#define JSON_PP_NEWLINE "\n" + +/** + * @section Buffer Size Constants + * + * You may need to change these if you are working with very large arrays or floating point numbers. + */ + +#define JSON_FLOAT_BUFFER_SIZE 32 +#define JSON_INDEX_BUFFER_SIZE 16 + +/** + * @section Meta-key Constants + * + * Used to store metadata for each key in an object. + * You shouldn't need to change these unless working with keys that may clash with them. + */ + +#define JSON_ARRAY_INDEX_KEY "__array_index" +#define JSON_META_TYPE_KEY ":type" +#define JSON_META_LENGTH_KEY ":length" +#define JSON_META_HIDDEN_KEY ":hidden" + +/** + * @section General + */ + +/** + * Types of cells within a JSON object + */ +enum JSON_CELL_TYPE { + Type_Invalid = -1, + Type_String = 0, + Type_Int, + Type_Float, + Type_Bool, + Type_Null, + Type_Object +}; + +/** + * Checks whether the key provided is a meta-key that should only be used internally. + * + * @param key Key to check. + * @returns True when it is a meta-key, false otherwise. + */ +stock bool json_is_meta_key(char[] key) +{ + return json_string_endswith(key, JSON_META_TYPE_KEY) + || json_string_endswith(key, JSON_META_LENGTH_KEY) + || json_string_endswith(key, JSON_META_HIDDEN_KEY) + || StrEqual(key, JSON_ARRAY_INDEX_KEY); +} diff --git a/sourcemod/scripting/include/json/encode_helpers.inc b/sourcemod/scripting/include/json/encode_helpers.inc new file mode 100644 index 0000000..37cb83d --- /dev/null +++ b/sourcemod/scripting/include/json/encode_helpers.inc @@ -0,0 +1,164 @@ +/** + * vim: set ts=4 : + * ============================================================================= + * sm-json + * Provides a pure SourcePawn implementation of JSON encoding and decoding. + * https://github.com/clugg/sm-json + * + * sm-json (C)2018 James D. (clug) + * SourceMod (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>. + */ + +#if defined _json_encode_helpers_included + #endinput +#endif +#define _json_encode_helpers_included + +#include <string> + +/** + * @section Calculating buffer sizes for JSON cells. + */ + +/** + * Calculates the maximum buffer length required to + * store the JSON cell representation of a string. + * + * @param maxlen The string's current length or buffer size. + * @return Maximum buffer length. + */ +stock int json_cell_string_size(int maxlen) { + return (maxlen * 2) + 3; // * 2 for potential escaping, + 2 for surrounding quotes + NULL +} + +/** + * Calculates the maximum buffer length required to + * store the JSON cell representation of an int. + * + * @param input The int. + * @return Maximum buffer length. + */ +stock int json_cell_int_size(int input) { + if (input == 0) { + return 2; // "0" + NULL + } + + return ((input < 0) ? 1 : 0) + RoundToFloor(Logarithm(FloatAbs(float(input)), 10.0)) + 2; // neg sign + number of digits + NULL +} + +/** + * Calculates the maximum buffer length required to + * store the JSON cell representation of a float. + * + * @return Maximum buffer length. + */ +stock int json_cell_float_size() { + return JSON_FLOAT_BUFFER_SIZE; // fixed-length +} + +/** + * Calculates the maximum buffer length required to + * store the JSON cell representation of a bool. + * + * @return Maximum buffer length. + */ +stock int json_cell_bool_size() { + return 6; // "true|false" + NULL +} + +/** + * Calculates the maximum buffer length required to + * store the JSON cell representation of null. + * + * @return Maximum buffer length. + */ +stock int json_cell_null_size() { + return 5; // "null" + NULL +} + +/** + * @section Generating JSON cells. + */ + +/** + * Generates the JSON cell representation of a string. + * + * @param input Value to generate output for. + * @param output String buffer to store output. + * @param maxlen Maximum size of string buffer. + */ +stock void json_cell_string(const char[] input, char[] output, int maxlen) { + strcopy(output, maxlen, "_"); // add dummy char at start so first quotation isn't escaped + StrCat(output, maxlen, input); // add input string to output + // escape everything according to JSON spec + json_escape_string(output, maxlen); + + // surround string with quotations + output[0] = '"'; + StrCat(output, maxlen, "\""); +} + +/** + * Generates the JSON cell representation of an int. + * + * @param input Value to generate output for. + * @param output String buffer to store output. + * @param maxlen Maximum size of string buffer. + */ +stock void json_cell_int(int input, char[] output, int maxlen) { + IntToString(input, output, maxlen); +} + +/** + * Generates the JSON cell representation of a float. + * + * @param input Value to generate output for. + * @param output String buffer to store output. + * @param maxlen Maximum size of string buffer. + */ +stock void json_cell_float(float input, char[] output, int maxlen) { + FloatToString(input, output, maxlen); +} + +/** + * Generates the JSON cell representation of a bool. + * + * @param input Value to generate output for. + * @param output String buffer to store output. + * @param maxlen Maximum size of string buffer. + */ +stock void json_cell_bool(bool input, char[] output, int maxlen) { + strcopy(output, maxlen, (input) ? "true" : "false"); +} + +/** + * Generates the JSON cell representation of null. + * + * @param output String buffer to store output. + * @param maxlen Maximum size of string buffer. + */ +stock void json_cell_null(char[] output, int maxlen) { + strcopy(output, maxlen, "null"); +} diff --git a/sourcemod/scripting/include/json/helpers/decode.inc b/sourcemod/scripting/include/json/helpers/decode.inc new file mode 100644 index 0000000..f420222 --- /dev/null +++ b/sourcemod/scripting/include/json/helpers/decode.inc @@ -0,0 +1,502 @@ +/** + * vim: set ts=4 : + * ============================================================================= + * sm-json + * Provides a pure SourcePawn implementation of JSON encoding and decoding. + * https://github.com/clugg/sm-json + * + * sm-json (C)2019 James Dickens. (clug) + * SourceMod (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>. + */ + +#if defined _json_helpers_decode_included + #endinput +#endif +#define _json_helpers_decode_included + +#include <string> + +/** + * @section Determine Buffer Contents + */ + +/** + * Checks whether the character at the beginning + * of the buffer is whitespace. + * + * @param buffer String buffer of data. + * @returns True if the first character in the buffer + * is whitespace, false otherwise. + */ +stock bool json_is_whitespace(const char[] buffer) +{ + return buffer[0] == ' ' + || buffer[0] == '\t' + || buffer[0] == '\r' + || buffer[0] == '\n'; +} + +/** + * Checks whether the character at the beginning + * of the buffer is the start of a string. + * + * @param buffer String buffer of data. + * @returns True if the first character in the buffer + * is the start of a string, false otherwise. + */ +stock bool json_is_string(const char[] buffer) +{ + return buffer[0] == '"'; +} + +/** + * Checks whether the buffer provided contains an int. + * + * @param buffer String buffer of data. + * @returns True if buffer contains an int, false otherwise. + */ +stock bool json_is_int(const char[] buffer) +{ + bool starts_with_zero = false; + bool has_digit_gt_zero = false; + + int length = strlen(buffer); + for (int i = 0; i < length; ++i) { + // allow minus as first character only + if (i == 0 && buffer[i] == '-') { + continue; + } + + if (IsCharNumeric(buffer[i])) { + if (buffer[i] == '0') { + if (starts_with_zero) { + // detect repeating leading zeros + return false; + } else if (! has_digit_gt_zero) { + starts_with_zero = true; + } + } else { + has_digit_gt_zero = true; + } + } else { + return false; + } + } + + // buffer must start with zero and have no other numerics before decimal + // OR not start with zero and have other numerics + return (starts_with_zero && ! has_digit_gt_zero) + || (! starts_with_zero && has_digit_gt_zero); +} + +/** + * Checks whether the buffer provided contains a float. + * + * @param buffer String buffer of data. + * @returns True if buffer contains a float, false otherwise. + */ +stock bool json_is_float(const char[] buffer) +{ + bool starts_with_zero = false; + bool has_digit_gt_zero = false; + bool after_decimal = false; + bool has_digit_after_decimal = false; + bool after_exponent = false; + bool has_digit_after_exponent = false; + + int length = strlen(buffer); + for (int i = 0; i < length; ++i) { + // allow minus as first character only + if (i == 0 && buffer[i] == '-') { + continue; + } + + // if we haven't encountered a decimal or exponent yet + if (! after_decimal && ! after_exponent) { + if (buffer[i] == '.') { + // if we encounter a decimal before any digits + if (! starts_with_zero && ! has_digit_gt_zero) { + return false; + } + + after_decimal = true; + } else if (buffer[i] == 'e' || buffer[i] == 'E') { + // if we encounter an exponent before any non-zero digits + if (starts_with_zero && ! has_digit_gt_zero) { + return false; + } + + after_exponent = true; + } else if (IsCharNumeric(buffer[i])) { + if (buffer[i] == '0') { + if (starts_with_zero) { + // detect repeating leading zeros + return false; + } else if (! has_digit_gt_zero) { + starts_with_zero = true; + } + } else { + has_digit_gt_zero = true; + } + } else { + return false; + } + } else if (after_decimal && ! after_exponent) { + // after decimal has been encountered, allow any numerics + if (IsCharNumeric(buffer[i])) { + has_digit_after_decimal = true; + } else if (buffer[i] == 'e' || buffer[i] == 'E') { + if (! has_digit_after_decimal) { + // detect exponents directly after decimal + return false; + } + + after_exponent = true; + } else { + return false; + } + } else if (after_exponent) { + if ( + (buffer[i] == '+' || buffer[i] == '-') + && (buffer[i - 1] == 'e' || buffer[i - 1] == 'E') + ) { + // allow + or - directly after exponent + continue; + } else if (IsCharNumeric(buffer[i])) { + has_digit_after_exponent = true; + } else { + return false; + } + } + } + + if (starts_with_zero && has_digit_gt_zero) { + /* if buffer starts with zero, there should + be no other digits before the decimal */ + return false; + } + + // if we have a decimal, there should be digit(s) after it + if (after_decimal) { + if (! has_digit_after_decimal) { + return false; + } + } + + // if we have an exponent, there should be digit(s) after it + if (after_exponent) { + if (! has_digit_after_exponent) { + return false; + } + } + + /* we should have reached an exponent, decimal, or both. + otherwise, this number can be handled by the int parser */ + return after_decimal || after_exponent; +} + +/** + * Checks whether the buffer provided contains a bool. + * + * @param buffer String buffer of data. + * @returns True if buffer contains a bool, false otherwise. + */ +stock bool json_is_bool(const char[] buffer) +{ + return StrEqual(buffer, "true") || StrEqual(buffer, "false"); +} + +/** + * Checks whether the buffer provided contains null. + * + * @param buffer String buffer of data. + * @returns True if buffer contains null, false otherwise. + */ +stock bool json_is_null(const char[] buffer) +{ + return StrEqual(buffer, "null"); +} + +/** + * Checks whether the character at the beginning + * of the buffer is the start of an object. + * + * @param buffer String buffer of data. + * @returns True if the first character in the buffer is + * the start of an object, false otherwise. + */ +stock bool json_is_object(const char[] buffer) +{ + return buffer[0] == '{'; +} + +/** + * Checks whether the character at the beginning + * of the buffer is the end of an object. + * + * @param buffer String buffer of data. + * @returns True if the first character in the buffer is + * the end of an object, false otherwise. + */ +stock bool json_is_object_end(const char[] buffer) +{ + return buffer[0] == '}'; +} + +/** + * Checks whether the character at the beginning + * of the buffer is the start of an array. + * + * @param buffer String buffer of data. + * @returns True if the first character in the buffer is + * the start of an array, false otherwise. + */ +stock bool json_is_array(const char[] buffer) +{ + return buffer[0] == '['; +} + +/** + * Checks whether the character at the beginning + * of the buffer is the end of an array. + * + * @param buffer String buffer of data. + * @returns True if the first character in the buffer is + * the end of an array, false otherwise. + */ +stock bool json_is_array_end(const char[] buffer) +{ + return buffer[0] == ']'; +} + +/** + * Checks whether the character at the beginning of the buffer + * is considered a valid 'end point' for some data, such as a + * colon (indicating a key), a comma (indicating a new element), + * or the end of an object or array. + * + * @param buffer String buffer of data. + * @returns True if the first character in the buffer + * is a valid data end point, false otherwise. + */ +stock bool json_is_at_end(const char[] buffer, bool is_array) +{ + return buffer[0] == ',' + || (! is_array && buffer[0] == ':') + || json_is_object_end(buffer[0]) + || json_is_array_end(buffer[0]); +} + +/** + * @section Extract Contents from Buffer + */ + +/** + * Moves the position until it reaches a non-whitespace + * character or the end of the buffer's maximum size. + * + * @param buffer String buffer of data. + * @param max_size Maximum size of string buffer. + * @param pos Position to increment. + * @returns True if pos has not reached the end + * of the buffer, false otherwise. + */ +stock bool json_skip_whitespace(const char[] buffer, int max_size, int &pos) +{ + while (json_is_whitespace(buffer[pos]) && pos < max_size) { + ++pos; + } + + return pos < max_size; +} + +/** + * Extracts a JSON cell from the buffer until + * a valid end point is reached. + * + * @param buffer String buffer of data. + * @param max_size Maximum size of string buffer. + * @param pos Position to increment. + * @param output String buffer to store output. + * @param output_max_size Maximum size of output string buffer. + * @param is_array Whether the decoder is processing an array. + * @returns True if pos has not reached the end + * of the buffer, false otherwise. + */ +stock bool json_extract_until_end( + const char[] buffer, + int max_size, + int &pos, + char[] output, + int output_max_size, + bool is_array +) { + strcopy(output, output_max_size, ""); + + // set start to position of first character in cell + int start = pos; + + // while we haven't hit whitespace, an end point or the end of the buffer + while ( + ! json_is_whitespace(buffer[pos]) + && ! json_is_at_end(buffer[pos], is_array) + && pos < max_size + ) { + ++pos; + } + + // set end to the current position + int end = pos; + + // skip any following whitespace + json_skip_whitespace(buffer, max_size, pos); + + // if we aren't at a valid endpoint, extraction has failed + if (! json_is_at_end(buffer[pos], is_array)) { + return false; + } + + // copy only from start with length end - start + NULL terminator + strcopy(output, end - start + 1, buffer[start]); + + return pos < max_size; +} + +/** + * Extracts a JSON string from the buffer until + * a valid end point is reached. + * + * @param buffer String buffer of data. + * @param max_size Maximum size of string buffer. + * @param pos Position to increment. + * @param output String buffer to store output. + * @param output_max_size Maximum size of output string buffer. + * @param is_array Whether the decoder is processing an array. + * @returns True if pos has not reached the end + * of the buffer, false otherwise. + */ +stock bool json_extract_string( + const char[] buffer, + int max_size, + int &pos, + char[] output, + int output_max_size, + bool is_array +) { + strcopy(output, output_max_size, ""); + + // increment past opening quote + ++pos; + + // set start to position of first character in string + int start = pos; + + // while we haven't hit the end of the buffer + while (pos < max_size) { + // check for unescaped control characters + if ( + buffer[pos] == '\b' + || buffer[pos] == '\f' + || buffer[pos] == '\n' + || buffer[pos] == '\r' + || buffer[pos] == '\t' + ) { + return false; + } + + if (buffer[pos] == '"') { + // count preceding backslashes to check if quote is escaped + int search_pos = pos; + int preceding_backslashes = 0; + while (search_pos > 0 && buffer[--search_pos] == '\\') { + ++preceding_backslashes; + } + + // if we have an even number of backslashes, the quote is not escaped + if (preceding_backslashes % 2 == 0) { + break; + } + } + + // pass over the character as it is part of the string + ++pos; + } + + // set end to the current position + int end = pos; + + // increment past closing quote + ++pos; + + // skip trailing whitespace + if (! json_skip_whitespace(buffer, max_size, pos)) { + return false; + } + + // if we haven't reached an ending character at the end of the cell, + // there is likely junk data not encapsulated by a string + if (! json_is_at_end(buffer[pos], is_array)) { + return false; + } + + // copy only from start with length end - start + NULL terminator + strcopy(output, end - start + 1, buffer[start]); + json_unescape_string(output, max_size); + + return pos < max_size; +} + +/** + * Extracts an int from the buffer. + * + * @param buffer String buffer of data. + * @returns Int value of the buffer. + */ +stock int json_extract_int(const char[] buffer) +{ + return StringToInt(buffer); +} + +/** + * Extracts a float from the buffer. + * + * @param buffer String buffer of data. + * @returns Float value of the buffer. + */ +stock float json_extract_float(const char[] buffer) +{ + return StringToFloat(buffer); +} + +/** + * Extracts a bool from the buffer. + * + * @param buffer String buffer of data. + * @returns Bool value of the buffer. + */ +stock bool json_extract_bool(const char[] buffer) +{ + return StrEqual(buffer, "true"); +} diff --git a/sourcemod/scripting/include/json/helpers/encode.inc b/sourcemod/scripting/include/json/helpers/encode.inc new file mode 100644 index 0000000..ceae54d --- /dev/null +++ b/sourcemod/scripting/include/json/helpers/encode.inc @@ -0,0 +1,200 @@ +/** + * vim: set ts=4 : + * ============================================================================= + * sm-json + * Provides a pure SourcePawn implementation of JSON encoding and decoding. + * https://github.com/clugg/sm-json + * + * sm-json (C)2019 James Dickens. (clug) + * SourceMod (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>. + */ + +#if defined _json_helpers_encode_included + #endinput +#endif +#define _json_helpers_encode_included + +#include <string> + +/** + * @section Calculate Buffer Size for Value + */ + +/** + * Calculates the maximum buffer length required to + * store the JSON cell representation of a string. + * + * @param length The length of the string. + * @returns Maximum buffer length. + */ +stock int json_cell_string_size(int length) +{ + // double for potential escaping, + 2 for outside quotes + NULL terminator + return (length * 2) + 3; +} + +/** + * Calculates the maximum buffer length required to + * store the JSON cell representation of an int. + * + * @param input Value to calculate maximum buffer length for. + * @returns Maximum buffer length. + */ +stock int json_cell_int_size(int input) +{ + if (input == 0) { + // "0" + NULL terminator + return 2; + } + + int result = 0; + if (input < 0) { + // negative sign + result += 1; + } + + // calculate number of digits in number + result += RoundToFloor(Logarithm(FloatAbs(float(input)), 10.0)) + 1; + + // NULL terminator + result += 1; + + return result; +} + +/** + * Calculates the maximum buffer length required to + * store the JSON cell representation of a float. + * + * @returns Maximum buffer length. + */ +stock int json_cell_float_size() +{ + return JSON_FLOAT_BUFFER_SIZE; +} + +/** + * Calculates the maximum buffer length required to + * store the JSON cell representation of a bool. + * + * @returns Maximum buffer length. + */ +stock int json_cell_bool_size() +{ + // "true"|"false" + NULL terminator + return 6; +} + +/** + * Calculates the maximum buffer length required to + * store the JSON cell representation of null. + * + * @returns Maximum buffer length. + */ +stock int json_cell_null_size() +{ + // "null" + NULL terminator + return 5; +} + +/** + * @section Convert Values to JSON Cells + */ + +/** + * Generates the JSON cell representation of a string. + * + * @param input Value to generate output for. + * @param output String buffer to store output. + * @param max_size Maximum size of string buffer. + */ +stock void json_cell_string(const char[] input, char[] output, int max_size) +{ + // add dummy char that won't be escaped to replace with a quote later + strcopy(output, max_size, "?"); + + // add input string to output + StrCat(output, max_size, input); + + // escape the output string + json_escape_string(output, max_size); + + // surround string with quotations + output[0] = '"'; + StrCat(output, max_size, "\""); +} + +/** + * Generates the JSON cell representation of an int. + * + * @param input Value to generate output for. + * @param output String buffer to store output. + * @param max_size Maximum size of string buffer. + */ +stock void json_cell_int(int input, char[] output, int max_size) +{ + IntToString(input, output, max_size); +} + +/** + * Generates the JSON cell representation of a float. + * + * @param input Value to generate output for. + * @param output String buffer to store output. + * @param max_size Maximum size of string buffer. + */ +stock void json_cell_float(float input, char[] output, int max_size) +{ + FloatToString(input, output, max_size); + + // trim trailing 0s from float output up until decimal point + int last_char = strlen(output) - 1; + while (output[last_char] == '0' && output[last_char - 1] != '.') { + output[last_char--] = '\0'; + } +} + +/** + * Generates the JSON cell representation of a bool. + * + * @param input Value to generate output for. + * @param output String buffer to store output. + * @param max_size Maximum size of string buffer. + */ +stock void json_cell_bool(bool input, char[] output, int max_size) +{ + strcopy(output, max_size, (input) ? "true" : "false"); +} + +/** + * Generates the JSON cell representation of null. + * + * @param output String buffer to store output. + * @param max_size Maximum size of string buffer. + */ +stock void json_cell_null(char[] output, int max_size) +{ + strcopy(output, max_size, "null"); +} diff --git a/sourcemod/scripting/include/json/helpers/string.inc b/sourcemod/scripting/include/json/helpers/string.inc new file mode 100644 index 0000000..14fe38a --- /dev/null +++ b/sourcemod/scripting/include/json/helpers/string.inc @@ -0,0 +1,133 @@ +/** + * vim: set ts=4 : + * ============================================================================= + * sm-json + * Provides a pure SourcePawn implementation of JSON encoding and decoding. + * https://github.com/clugg/sm-json + * + * sm-json (C)2019 James Dickens. (clug) + * SourceMod (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>. + */ + +#if defined _json_helpers_string_included + #endinput +#endif +#define _json_helpers_string_included + +/** + * Mapping characters to their escaped form. + */ +char JSON_STRING_NORMAL[][] = { + "\\", "\"", "/", "\b", "\f", "\n", "\r", "\t" +}; +char JSON_STRING_ESCAPED[][] = { + "\\\\", "\\\"", "\\/", "\\b", "\\f", "\\n", "\\r", "\\t" +}; + +/** + * Escapes a string in-place in a buffer. + * + * @param buffer String buffer. + * @param max_size Maximum size of string buffer. + */ +stock void json_escape_string(char[] buffer, int max_size) +{ + for (int i = 0; i < sizeof(JSON_STRING_NORMAL); ++i) { + ReplaceString( + buffer, + max_size, + JSON_STRING_NORMAL[i], + JSON_STRING_ESCAPED[i] + ); + } +} + +/** + * Unescapes a string in-place in a buffer. + * + * @param buffer String buffer. + * @param max_size Maximum size of string buffer. + */ +stock void json_unescape_string(char[] buffer, int max_size) +{ + for (int i = 0; i < sizeof(JSON_STRING_NORMAL); ++i) { + ReplaceString( + buffer, + max_size, + JSON_STRING_ESCAPED[i], + JSON_STRING_NORMAL[i] + ); + } +} + +/** + * Checks if a string starts with another string. + * + * @param haystack String to check that starts with needle. + * @param max_size Maximum size of string buffer. + * @param needle String to check that haystack starts with. + * @returns True if haystack begins with needle, false otherwise. + */ +stock bool json_string_startswith(const char[] haystack, const char[] needle) +{ + int haystack_length = strlen(haystack); + int needle_length = strlen(needle); + if (needle_length > haystack_length) { + return false; + } + + for (int i = 0; i < needle_length; ++i) { + if (haystack[i] != needle[i]) { + return false; + } + } + + return true; +} + +/** + * Checks if a string ends with another string. + * + * @param haystack String to check that ends with needle. + * @param max_size Maximum size of string buffer. + * @param needle String to check that haystack ends with. + * @returns True if haystack ends with needle, false otherwise. + */ +stock bool json_string_endswith(const char[] haystack, const char[] needle) +{ + int haystack_length = strlen(haystack); + int needle_length = strlen(needle); + if (needle_length > haystack_length) { + return false; + } + + for (int i = 0; i < needle_length; ++i) { + if (haystack[haystack_length - needle_length + i] != needle[i]) { + return false; + } + } + + return true; +} diff --git a/sourcemod/scripting/include/json/object.inc b/sourcemod/scripting/include/json/object.inc new file mode 100644 index 0000000..8d17568 --- /dev/null +++ b/sourcemod/scripting/include/json/object.inc @@ -0,0 +1,1014 @@ +/** + * vim: set ts=4 : + * ============================================================================= + * sm-json + * Provides a pure SourcePawn implementation of JSON encoding and decoding. + * https://github.com/clugg/sm-json + * + * sm-json (C)2019 James Dickens. (clug) + * SourceMod (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>. + */ + +#if defined _json_object_included + #endinput +#endif +#define _json_object_included + +#include <string> +#include <json/definitions> +#include <json/helpers/encode> +#include <json> + +methodmap JSON_Object < StringMap +{ + /** + * Creates a new JSON_Object. + * + * @param is_array Should the object created be an array? [default: false] + * @returns A new JSON_Object. + */ + public JSON_Object(bool is_array = false) + { + StringMap self = CreateTrie(); + if (is_array) { + self.SetValue(JSON_ARRAY_INDEX_KEY, 0); + } + + return view_as<JSON_Object>(self); + } + + /** + * Checks whether the object has a key. + * + * @param key Key to check existence of. + * @returns True if the key exists, false otherwise. + */ + public bool HasKey(const char[] key) + { + int dummy_int; + char dummy_str[1]; + + return this.GetValue(key, dummy_int) + || this.GetString(key, dummy_str, sizeof(dummy_str)); + } + + /** + * @section Array helpers. + */ + + /** + * Whether the current object is an array. + */ + property bool IsArray { + public get() + { + return this.HasKey(JSON_ARRAY_INDEX_KEY); + } + } + + /** + * The current index of the object if it is an array, or -1 otherwise. + */ + property int CurrentIndex { + public get() + { + if (! this.IsArray) { + return -1; + } + + int result; + return (this.GetValue(JSON_ARRAY_INDEX_KEY, result)) ? result : -1; + } + + public set(int value) + { + this.SetValue(JSON_ARRAY_INDEX_KEY, value); + } + } + + /** + * The number of items in the object if it is an array, + * or the number of keys (including meta-keys) otherwise. + */ + property int Length { + public get() + { + StringMapSnapshot snap = this.Snapshot(); + int length = (this.IsArray) ? this.CurrentIndex : snap.Length; + delete snap; + + return length; + } + } + + /** + * Increments the current index of the object. + * + * @returns True on success, false if the current object is not an array. + */ + public bool IncrementIndex() + { + if (! this.HasKey(JSON_ARRAY_INDEX_KEY)) { + return false; + } + + this.CurrentIndex += 1; + + return true; + } + + /** + * Checks whether the object has an index. + * + * @param index Index to check existence of. + * @returns True if the index exists, false otherwise. + */ + public bool HasIndex(int index) + { + return index >= 0 && index < this.Length; + } + + /** + * Gets the string representation of an array index. + * + * @param output String buffer to store output. + * @param max_size Maximum size of string buffer. + * @param key Key to get string for. [default: current index] + * @returns True on success, false otherwise. + */ + public int GetIndexString(char[] output, int max_size, int key = -1) + { + key = (key == -1) ? this.CurrentIndex : key; + if (key == -1) { + return false; + } + + return IntToString(key, output, max_size); + } + + /** + * @section Internal Getters + */ + + /** + * Gets the cell type stored at a key. + * + * @param key Key to get value type for. + * @returns Value type for key provided, + * or Type_Invalid if it does not exist. + */ + public JSON_CELL_TYPE GetKeyType(const char[] key) + { + int max_size = strlen(key) + strlen(JSON_META_TYPE_KEY) + 1; + char[] type_key = new char[max_size]; + Format(type_key, max_size, "%s%s", key, JSON_META_TYPE_KEY); + + JSON_CELL_TYPE type; + return (this.GetValue(type_key, type)) ? type : Type_Invalid; + } + + /** + * Gets the cell type stored at an index. + * + * @param index Index to get value type for. + * @returns Value type for index provided, or Type_Invalid if it does not exist. + */ + public JSON_CELL_TYPE GetKeyTypeIndexed(int index) + { + char key[JSON_INDEX_BUFFER_SIZE]; + if (! this.GetIndexString(key, sizeof(key), index)) { + return Type_Invalid; + } + + return this.GetKeyType(key); + } + + /** + * Gets the length of the string stored at a key. + * + * @param key Key to get string length for. + * @returns Length of string at key provided, + * or -1 if it is not a string/does not exist. + */ + public int GetKeyLength(const char[] key) + { + int max_size = strlen(key) + strlen(JSON_META_LENGTH_KEY) + 1; + char[] length_key = new char[max_size]; + Format(length_key, max_size, "%s%s", key, JSON_META_LENGTH_KEY); + + int length; + return (this.GetValue(length_key, length)) ? length : -1; + } + + /** + * Gets the length of the string stored at an index. + * + * @param index Index to get string length for. + * @returns Length of string at index provided, or -1 if it is not a string/does not exist. + */ + public int GetKeyLengthIndexed(int index) + { + char key[JSON_INDEX_BUFFER_SIZE]; + if (! this.GetIndexString(key, sizeof(key), index)) { + return -1; + } + + return this.GetKeyLength(key); + } + + /** + * Gets whether the key should be hidden from encoding. + * + * @param key Key to get hidden state for. + * @returns Whether or not the key should be hidden. + */ + public bool GetKeyHidden(const char[] key) + { + int max_size = strlen(key) + strlen(JSON_META_HIDDEN_KEY) + 1; + char[] length_key = new char[max_size]; + Format(length_key, max_size, "%s%s", key, JSON_META_HIDDEN_KEY); + + bool hidden; + return (this.GetValue(length_key, hidden)) ? hidden : false; + } + + /** + * Gets whether the index should be hidden from encoding. + * + * @param index Index to get hidden state for. + * @returns Whether or not the index should be hidden. + */ + public bool GetKeyHiddenIndexed(int index) + { + char key[JSON_INDEX_BUFFER_SIZE]; + if (! this.GetIndexString(key, sizeof(key), index)) { + return false; + } + + return this.GetKeyHidden(key); + } + + /** + * @section Internal Setters + */ + + /** + * Sets the cell type stored at a key. + * + * @param key Key to set value type for. + * @param type Type to set key to. + * @returns True on success, false otherwise. + */ + public bool SetKeyType(const char[] key, JSON_CELL_TYPE type) + { + int max_size = strlen(key) + strlen(JSON_META_TYPE_KEY) + 1; + char[] type_key = new char[max_size]; + Format(type_key, max_size, "%s%s", key, JSON_META_TYPE_KEY); + + return this.SetValue(type_key, type); + } + + /** + * Sets the cell type stored at an index. + * + * @param index Index to set value type for. + * @param type Type to set index to. + * @returns True on success, false otherwise. + */ + public bool SetKeyTypeIndexed(int index, JSON_CELL_TYPE value) + { + char key[JSON_INDEX_BUFFER_SIZE]; + if (! this.GetIndexString(key, sizeof(key), index)) { + return false; + } + + return this.SetKeyType(key, value); + } + + /** + * Sets the length of the string stored at a key. + * + * @param key Key to set string length for. + * @param length Length to set string to. + * @returns True on success, false otherwise. + */ + public bool SetKeyLength(const char[] key, int length) + { + int max_size = strlen(key) + strlen(JSON_META_LENGTH_KEY) + 1; + char[] length_key = new char[max_size]; + Format(length_key, max_size, "%s%s", key, JSON_META_LENGTH_KEY); + + return this.SetValue(length_key, length); + } + + /** + * Sets the length of the string stored at an index. + * + * @param index Index to set string length for. + * @param length Length to set string to. + * @returns True on success, false otherwise. + */ + public bool SetKeyLengthIndexed(int index, int length) + { + char key[JSON_INDEX_BUFFER_SIZE]; + if (! this.GetIndexString(key, sizeof(key), index)) { + return false; + } + + return this.SetKeyLength(key, length); + } + + /** + * Sets whether the key should be hidden from encoding. + * + * @param key Key to set hidden state for. + * @param hidden Wheter or not the key should be hidden. + * @returns True on success, false otherwise. + */ + public bool SetKeyHidden(const char[] key, bool hidden) + { + int max_size = strlen(key) + strlen(JSON_META_HIDDEN_KEY) + 1; + char[] hidden_key = new char[max_size]; + Format(hidden_key, max_size, "%s%s", key, JSON_META_HIDDEN_KEY); + + return this.SetValue(hidden_key, hidden); + } + + /** + * Sets whether the index should be hidden from encoding. + * + * @param index Index to set hidden state for. + * @param hidden Wheter or not the index should be hidden. + * @returns True on success, false otherwise. + */ + public bool SetKeyHiddenIndexed(int index, bool hidden) + { + char key[JSON_INDEX_BUFFER_SIZE]; + if (! this.GetIndexString(key, sizeof(key), index)) { + return false; + } + + return this.SetKeyHidden(key, hidden); + } + + /** + * @section Getters + */ + + // GetValue is implemented natively by StringMap + + /** + * Retrieves the value stored at an index. + * + * @param index Index to retrieve value for. + * @param value Variable to store value. + * @returns Value stored at index. + */ + public bool GetValueIndexed(int index, any &value) + { + char key[JSON_INDEX_BUFFER_SIZE]; + if (! this.GetIndexString(key, sizeof(key), index)) { + return false; + } + + return this.GetValue(key, value); + } + + // GetString is implemented natively by StringMap + + /** + * Retrieves the string stored at an index. + * + * @param index Index to retrieve string value for. + * @param value String buffer to store output. + * @param max_size Maximum size of string buffer. + * @returns True on success. False if the key is not set, or the key is set as a value or array (not a string). + */ + public bool GetStringIndexed(int index, char[] value, int max_size) + { + char key[JSON_INDEX_BUFFER_SIZE]; + if (! this.GetIndexString(key, sizeof(key), index)) { + return false; + } + + return this.GetString(key, value, max_size); + } + + /** + * Retrieves the int stored at a key. + * + * @param key Key to retrieve int value for. + * @returns Value stored at key. + */ + public int GetInt(const char[] key) + { + int value; + return (this.GetValue(key, value)) ? value : -1; + } + + /** + * Retrieves the int stored at an index. + * + * @param index Index to retrieve int value for. + * @returns Value stored at index. + */ + public int GetIntIndexed(int index) + { + char key[JSON_INDEX_BUFFER_SIZE]; + if (! this.GetIndexString(key, sizeof(key), index)) { + return -1; + } + + return this.GetInt(key); + } + + /** + * Retrieves the float stored at a key. + * + * @param key Key to retrieve float value for. + * @returns Value stored at key. + */ + public float GetFloat(const char[] key) + { + float value; + return (this.GetValue(key, value)) ? value : -1.0; + } + + /** + * Retrieves the float stored at an index. + * + * @param index Index to retrieve float value for. + * @returns Value stored at index. + */ + public float GetFloatIndexed(int index) + { + char key[JSON_INDEX_BUFFER_SIZE]; + if (! this.GetIndexString(key, sizeof(key), index)) { + return -1.0; + } + + return this.GetFloat(key); + } + + /** + * Retrieves the bool stored at a key. + * + * @param key Key to retrieve bool value for. + * @returns Value stored at key. + */ + public bool GetBool(const char[] key) + { + bool value; + return (this.GetValue(key, value)) ? value : false; + } + + /** + * Retrieves the bool stored at an index. + * + * @param index Index to retrieve bool value for. + * @returns Value stored at index. + */ + public bool GetBoolIndexed(int index) + { + char key[JSON_INDEX_BUFFER_SIZE]; + if (! this.GetIndexString(key, sizeof(key), index)) { + return false; + } + + return this.GetBool(key); + } + + /** + * Retrieves the handle stored at a key. + * + * @param key Key to retrieve handle value for. + * @returns Value stored at key. + */ + public Handle GetHandle(const char[] key) + { + Handle value; + return (this.GetValue(key, value)) ? value : null; + } + + /** + * Retrieves the handle stored at an index. + * + * @param index Index to retrieve handle value for. + * @returns Value stored at index. + */ + public Handle GetHandleIndexed(int index) + { + char key[JSON_INDEX_BUFFER_SIZE]; + if (! this.GetIndexString(key, sizeof(key), index)) { + return null; + } + + return this.GetHandle(key); + } + + /** + * Retrieves the JSON object stored at a key. + * + * @param key Key to retrieve object value for. + * @returns Value stored at key. + */ + public JSON_Object GetObject(const char[] key) + { + return view_as<JSON_Object>(this.GetHandle(key)); + } + + /** + * Retrieves the object stored at an index. + * + * @param index Index to retrieve object value for. + * @returns Value stored at index. + */ + public JSON_Object GetObjectIndexed(int index) + { + char key[JSON_INDEX_BUFFER_SIZE]; + if (! this.GetIndexString(key, sizeof(key), index)) { + return null; + } + + return this.GetObject(key); + } + + /** + * @section Setters + */ + + /** + * Sets the string stored at a key. + * + * @param key Key to set to string value. + * @param value Value to set. + * @returns True on success, false otherwise. + */ + public bool SetString(const char[] key, const char[] value, bool replace = true) + { + return this.SetString(key, value, replace) + && this.SetKeyType(key, Type_String) + && this.SetKeyLength(key, strlen(value)); + } + + /** + * Sets the string stored at an index. + * + * @param index Index to set to string value. + * @param value Value to set. + * @returns True on success, false otherwise. + */ + public bool SetStringIndexed(int index, const char[] value, bool replace = true) + { + char key[JSON_INDEX_BUFFER_SIZE]; + if (! this.GetIndexString(key, sizeof(key), index)) { + return false; + } + + return this.SetString(key, value, replace); + } + + /** + * Sets the int stored at a key. + * + * @param key Key to set to int value. + * @param value Value to set. + * @returns True on success, false otherwise. + */ + public bool SetInt(const char[] key, int value, bool replace = true) + { + return this.SetValue(key, value, replace) + && this.SetKeyType(key, Type_Int); + } + + /** + * Sets the int stored at an index. + * + * @param index Index to set to int value. + * @param value Value to set. + * @returns True on success, false otherwise. + */ + public bool SetIntIndexed(int index, int value, bool replace = true) + { + char key[JSON_INDEX_BUFFER_SIZE]; + if (! this.GetIndexString(key, sizeof(key), index)) { + return false; + } + + return this.SetInt(key, value, replace); + } + + /** + * Sets the float stored at a key. + * + * @param key Key to set to float value. + * @param value Value to set. + * @returns True on success, false otherwise. + */ + public bool SetFloat(const char[] key, float value, bool replace = true) + { + return this.SetValue(key, value, replace) + && this.SetKeyType(key, Type_Float); + } + + /** + * Sets the float stored at an index. + * + * @param index Index to set to float value. + * @param value Value to set. + * @returns True on success, false otherwise. + */ + public bool SetFloatIndexed(int index, float value, bool replace = true) + { + char key[JSON_INDEX_BUFFER_SIZE]; + if (! this.GetIndexString(key, sizeof(key), index)) { + return false; + } + + return this.SetFloat(key, value, replace); + } + + /** + * Sets the bool stored at a key. + * + * @param key Key to set to bool value. + * @param value Value to set. + * @returns True on success, false otherwise. + */ + public bool SetBool(const char[] key, bool value, bool replace = true) + { + return this.SetValue(key, value, replace) + && this.SetKeyType(key, Type_Bool); + } + + /** + * Sets the bool stored at an index. + * + * @param index Index to set to bool value. + * @param value Value to set. + * @returns True on success, false otherwise. + */ + public bool SetBoolIndexed(int index, bool value, bool replace = true) + { + char key[JSON_INDEX_BUFFER_SIZE]; + if (! this.GetIndexString(key, sizeof(key), index)) { + return false; + } + + return this.SetBool(key, value, replace); + } + + /** + * Sets the handle stored at a key. + * + * @param key Key to set to handle value. + * @param value Value to set. + * @returns True on success, false otherwise. + */ + public bool SetHandle(const char[] key, Handle value = null, bool replace = true) + { + return this.SetValue(key, value, replace) + && this.SetKeyType(key, Type_Null); + } + + /** + * Sets the handle stored at an index. + * + * @param index Index to set to handle value. + * @param value Value to set. + * @returns True on success, false otherwise. + */ + public bool SetHandleIndexed(int index, Handle value = null, bool replace = true) + { + char key[JSON_INDEX_BUFFER_SIZE]; + if (! this.GetIndexString(key, sizeof(key), index)) { + return false; + } + + return this.SetHandle(key, value, replace); + } + + /** + * Sets the object stored at a key. + * + * @param key Key to set to object value. + * @param value Value to set. + * @returns True on success, false otherwise. + */ + public bool SetObject(const char[] key, JSON_Object value, bool replace = true) + { + return this.SetValue(key, value, replace) + && this.SetKeyType(key, Type_Object); + } + + /** + * Sets the object stored at an index. + * + * @param index Index to set to object value. + * @param value Value to set. + * @returns True on success, false otherwise. + */ + public bool SetObjectIndexed(int index, JSON_Object value, bool replace = true) + { + char key[JSON_INDEX_BUFFER_SIZE]; + if (! this.GetIndexString(key, sizeof(key), index)) { + return false; + } + + return this.SetObject(key, value, replace); + } + + /** + * @section Array setters. + */ + + /** + * Pushes a string to the end of the array. + * + * @param value Value to push. + * @returns True on success, false otherwise. + */ + public bool PushString(const char[] value) + { + return this.SetStringIndexed(this.CurrentIndex, value) + && this.IncrementIndex(); + } + + /** + * Pushes an int to the end of the array. + * + * @param value Value to push. + * @returns True on success, false otherwise. + */ + public bool PushInt(int value) + { + return this.SetIntIndexed(this.CurrentIndex, value) + && this.IncrementIndex(); + } + + /** + * Pushes a float to the end of the array. + * + * @param value Value to push. + * @returns True on success, false otherwise. + */ + public bool PushFloat(float value) + { + return this.SetFloatIndexed(this.CurrentIndex, value) + && this.IncrementIndex(); + } + + /** + * Pushes a bool to the end of the array. + * + * @param value Value to push. + * @returns True on success, false otherwise. + */ + public bool PushBool(bool value) + { + return this.SetBoolIndexed(this.CurrentIndex, value) + && this.IncrementIndex(); + } + + /** + * Pushes a handle to the end of the array. + * + * @param value Value to push. + * @returns True on success, false otherwise. + */ + public bool PushHandle(Handle value = null) + { + return this.SetHandleIndexed(this.CurrentIndex, value) + && this.IncrementIndex(); + } + + /** + * Pushes an object to the end of the array. + * + * @param value Value to push. + * @returns True on success, false otherwise. + */ + public bool PushObject(JSON_Object value) + { + return this.SetObjectIndexed(this.CurrentIndex, value) + && this.IncrementIndex(); + } + + /** + * @section Generic. + */ + + /** + * Finds the index of a value in the array. + * + * @param value Value to search for. + * @returns The index of the value if it is found, -1 otherwise. + */ + public int IndexOf(any value) + { + any current; + for (int i = 0; i < this.CurrentIndex; ++i) { + if (this.GetValueIndexed(i, current) && value == current) { + return i; + } + } + + return -1; + } + + /** + * Finds the index of a string in the array. + * + * @param value String to search for. + * @returns The index of the string if it is found, -1 otherwise. + */ + public int IndexOfString(const char[] value) + { + for (int i = 0; i < this.CurrentIndex; ++i) { + if (this.GetKeyTypeIndexed(i) != Type_String) { + continue; + } + + int current_size = this.GetKeyLengthIndexed(i) + 1; + char[] current = new char[current_size]; + this.GetStringIndexed(i, current, current_size); + if (StrEqual(value, current)) { + return i; + } + } + + return -1; + } + + /** + * Determines whether the array contains a value. + * + * @param value Value to search for. + * @returns True if the value is found, false otherwise. + */ + public bool Contains(any value) + { + return this.IndexOf(value) != -1; + } + + /** + * Determines whether the array contains a string. + * + * @param value String to search for. + * @returns True if the string is found, false otherwise. + */ + public bool ContainsString(const char[] value) + { + return this.IndexOfString(value) != -1; + } + + /** + * Removes an item from the object by key. + * + * @param key Key of object to remove. + * @returns True on success, false if the value was never set. + */ + public bool Remove(const char[] key) { + static char meta_keys[][] = { + JSON_META_TYPE_KEY, JSON_META_LENGTH_KEY, JSON_META_HIDDEN_KEY + }; + + // create a new char[] which will fit the longest meta-key + int meta_key_size = strlen(key) + 8; + char[] meta_key = new char[meta_key_size]; + + // view ourselves as a StringMap so we can call underlying Remove() method + StringMap self = view_as<StringMap>(this); + + bool success = true; + for (int i = 0; i < sizeof(meta_keys); ++i) { + Format(meta_key, meta_key_size, "%s%s", key, meta_keys[i]); + + if (this.HasKey(meta_key)) { + success = success && self.Remove(meta_key); + } + } + + return success && self.Remove(key); + } + + /** + * Removes a key and its related meta-keys from the object. + * + * @param key Key to remove. + * @returns True on success, false if the value was never set. + */ + public bool RemoveIndexed(int index) + { + if (! this.HasIndex(index)) { + return false; + } + + char key[JSON_INDEX_BUFFER_SIZE]; + if (! this.GetIndexString(key, sizeof(key), index)) { + return false; + } + + if (! this.Remove(key)) { + return false; + } + + for (int i = index + 1; i < this.CurrentIndex; ++i) { + if (! this.GetIndexString(key, sizeof(key), i)) { + return false; + } + + int target = i - 1; + + JSON_CELL_TYPE type = this.GetKeyTypeIndexed(i); + + switch (type) { + case Type_String: { + int str_length = this.GetKeyLengthIndexed(i); + char[] str_value = new char[str_length]; + + this.GetStringIndexed(i, str_value, str_length + 1); + this.SetStringIndexed(target, str_value); + } + case Type_Int: { + this.SetIntIndexed(target, this.GetIntIndexed(i)); + } + case Type_Float: { + this.SetFloatIndexed(target, this.GetFloatIndexed(i)); + } + case Type_Bool: { + this.SetBoolIndexed(target, this.GetBoolIndexed(i)); + } + case Type_Null: { + this.SetHandleIndexed(target, this.GetHandleIndexed(i)); + } + case Type_Object: { + this.SetObjectIndexed(target, this.GetObjectIndexed(i)); + } + } + + if (this.GetKeyHiddenIndexed(i)) { + this.SetKeyHiddenIndexed(target, true); + } + + this.Remove(key); + } + + this.CurrentIndex -= 1; + + return true; + } + + /** + * Encodes the instance into its string representation. + * + * @param output String buffer to store output. + * @param max_size Maximum size of string buffer. + * @param pretty_print Should the output be pretty printed (newlines and spaces)? [default: false] + * @param depth The current depth of the encoder. [default: 0] + */ + public void Encode(char[] output, int max_size, bool pretty_print = false, int depth = 0) + { + json_encode(this, output, max_size, pretty_print, depth); + } + + /** + * Decodes a JSON string into this object. + * + * @param buffer Buffer to decode. + */ + public void Decode(const char[] buffer) + { + json_decode(buffer, this); + } + + /** + * Recursively cleans up the object and any objects referenced within. + */ + public void Cleanup() + { + json_cleanup(this); + } +}; diff --git a/sourcemod/scripting/include/json/string_helpers.inc b/sourcemod/scripting/include/json/string_helpers.inc new file mode 100644 index 0000000..6063d47 --- /dev/null +++ b/sourcemod/scripting/include/json/string_helpers.inc @@ -0,0 +1,77 @@ +/** + * vim: set ts=4 : + * ============================================================================= + * sm-json + * Provides a pure SourcePawn implementation of JSON encoding and decoding. + * https://github.com/clugg/sm-json + * + * sm-json (C)2018 James D. (clug) + * SourceMod (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>. + */ + +#if defined _json_string_helpers_included + #endinput +#endif +#define _json_string_helpers_included + +/** + * Checks if a string starts with another string. + * + * @param haystack String to check that starts with needle. + * @param maxlen Maximum size of string buffer. + * @param needle String to check that haystack starts with. + * @return True if haystack begins with needle, false otherwise. + */ +stock bool json_string_startswith(const char[] haystack, const char[] needle) { + int haystack_length = strlen(haystack); + int needle_length = strlen(needle); + if (needle_length > haystack_length) return false; + + for (int i = 0; i < needle_length; ++i) { + if (haystack[i] != needle[i]) return false; + } + + return true; +} + +/** + * Checks if a string ends with another string. + * + * @param haystack String to check that ends with needle. + * @param maxlen Maximum size of string buffer. + * @param needle String to check that haystack ends with. + * @return True if haystack ends with needle, false otherwise. + */ +stock bool json_string_endswith(const char[] haystack, const char[] needle) { + int haystack_length = strlen(haystack); + int needle_length = strlen(needle); + if (needle_length > haystack_length) return false; + + for (int i = 0; i < needle_length; ++i) { + if (haystack[haystack_length - needle_length + i] != needle[i]) return false; + } + + return true; +}
\ No newline at end of file diff --git a/sourcemod/scripting/include/movement.inc b/sourcemod/scripting/include/movement.inc new file mode 100644 index 0000000..7cc5b29 --- /dev/null +++ b/sourcemod/scripting/include/movement.inc @@ -0,0 +1,530 @@ +/* + MovementAPI Function Stock Library + + Website: https://github.com/danzayau/MovementAPI +*/ + +#if defined _movement_included_ + #endinput +#endif +#define _movement_included_ + +#include <sdktools> + + + +// =====[ STOCKS ]===== + +/** + * Calculates the horizontal distance between two vectors. + * + * @param vec1 First vector. + * @param vec2 Second vector. + * @return Vector horizontal distance. + */ +stock float GetVectorHorizontalDistance(const float vec1[3], const float vec2[3]) +{ + return SquareRoot(Pow(vec2[0] - vec1[0], 2.0) + Pow(vec2[1] - vec1[1], 2.0)); +} + +/** + * Calculates a vector's horizontal length. + * + * @param vec Vector. + * @return Vector horizontal length (magnitude). + */ +stock float GetVectorHorizontalLength(const float vec[3]) +{ + return SquareRoot(Pow(vec[0], 2.0) + Pow(vec[1], 2.0)); +} + +/** + * Scales a vector to a certain horizontal length. + * + * @param vec Vector. + * @param length New horizontal length. + */ +stock void SetVectorHorizontalLength(float vec[3], float length) +{ + float newVec[3]; + newVec = vec; + newVec[2] = 0.0; + NormalizeVector(newVec, newVec); + ScaleVector(newVec, length); + newVec[2] = vec[2]; + vec = newVec; +} + +/** + * Gets a player's currently pressed buttons. + * + * @param client Client index. + * @return Bitsum of buttons. + */ +stock int Movement_GetButtons(int client) +{ + return GetClientButtons(client); +} + +/** + * Gets a player's origin. + * + * @param client Client index. + * @param result Resultant vector. + */ +stock void Movement_GetOrigin(int client, float result[3]) +{ + GetClientAbsOrigin(client, result); +} + +/** + * Gets a player's origin. + * If the player is on the ground, a trace hull is used to find the + * exact height of the ground the player is standing on. This is thus + * more accurate than Movement_GetOrigin when player is on ground. + * + * @param client Client index. + * @param result Resultant vector. + */ +stock void Movement_GetOriginEx(int client, float result[3]) +{ + if (!Movement_GetOnGround(client)) + { + GetClientAbsOrigin(client, result); + return; + } + + // Get the coordinate of the solid beneath the player's origin + // More accurate than GetClientAbsOrigin when on ground + float startPosition[3], endPosition[3]; + GetClientAbsOrigin(client, startPosition); + endPosition = startPosition; + endPosition[2] = startPosition[2] - 2.0; // Should be less than 2.0 units away + Handle trace = TR_TraceHullFilterEx( + startPosition, + endPosition, + view_as<float>( { -16.0, -16.0, 0.0 } ), // Players are 32 x 32 x 72 + view_as<float>( { 16.0, 16.0, 72.0 } ), + MASK_PLAYERSOLID, + TraceEntityFilterPlayers, + client); + if (TR_DidHit(trace)) + { + TR_GetEndPosition(result, trace); + // Do not get rid of the offset. The offset is correct, as the player must be + // at least 0.03125 units away from the ground. + } + else + { + result = startPosition; // Fallback to GetClientAbsOrigin + } + delete trace; +} + +public bool TraceEntityFilterPlayers(int entity, int contentsMask) +{ + return entity > MaxClients; +} + +/** + * Sets a player's origin by teleporting them. + * + * @param client Client index. + * @param origin New origin. + */ +stock void Movement_SetOrigin(int client, const float origin[3]) +{ + TeleportEntity(client, origin, NULL_VECTOR, NULL_VECTOR); +} + +/** + * Gets a player's velocity. + * + * @param client Client index. + * @param result Resultant vector. + */ +stock void Movement_GetVelocity(int client, float result[3]) +{ + GetEntPropVector(client, Prop_Data, "m_vecVelocity", result); +} + +/** + * Sets a player's velocity by teleporting them. + * + * @param client Client index. + * @param velocity New velocity. + */ +stock void Movement_SetVelocity(int client, const float velocity[3]) +{ + TeleportEntity(client, NULL_VECTOR, NULL_VECTOR, velocity); +} + +/** + * Gets a player's horizontal speed. + * + * @param client Client index. + * @return Player's horizontal speed. + */ +stock float Movement_GetSpeed(int client) +{ + float velocity[3]; + Movement_GetVelocity(client, velocity); + return GetVectorHorizontalLength(velocity); +} + +/** + * Sets a player's horizontal speed. + * + * @param client Client index. + * @param value New horizontal speed. + * @param applyBaseVel Whether to apply base velocity as well. + */ +stock void Movement_SetSpeed(int client, float value, bool applyBaseVel = false) +{ + float velocity[3]; + Movement_GetVelocity(client, velocity); + SetVectorHorizontalLength(velocity, value) + if (applyBaseVel) + { + float baseVelocity[3]; + Movement_GetBaseVelocity(client, baseVelocity); + AddVectors(velocity, baseVelocity, velocity); + } + Movement_SetVelocity(client, velocity); +} + +/** + * Gets a player's vertical velocity. + * + * @param client Client index. + * @return Player's vertical velocity. + */ +stock float Movement_GetVerticalVelocity(int client) +{ + float velocity[3]; + Movement_GetVelocity(client, velocity); + return velocity[2]; +} + +/** + * Sets a player's vertical velocity. + * + * @param client Client index. + * @param value New vertical velocity. + */ +stock void Movement_SetVerticalVelocity(int client, float value) +{ + float velocity[3]; + Movement_GetVelocity(client, velocity); + velocity[2] = value; + Movement_SetVelocity(client, velocity); +} + +/** + * Gets a player's base velocity. + * + * @param client Client index. + * @param result Resultant vector. + */ +stock void Movement_GetBaseVelocity(int client, float result[3]) +{ + GetEntPropVector(client, Prop_Data, "m_vecBaseVelocity", result); +} + +/** + * Sets a player's base velocity. + * + * @param client Client index. + * @param baseVelocity New base velocity. + */ +stock void Movement_SetBaseVelocity(int client, const float baseVelocity[3]) +{ + SetEntPropVector(client, Prop_Data, "m_vecBaseVelocity", baseVelocity); +} + +/** + * Gets a player's eye angles. + * + * @param client Client index. + * @param result Resultant vector. + */ +stock void Movement_GetEyeAngles(int client, float result[3]) +{ + GetClientEyeAngles(client, result); +} + +/** + * Sets a player's eye angles by teleporting them. + * + * @param client Client index. + * @param eyeAngles New eye angles. + */ +stock void Movement_SetEyeAngles(int client, const float eyeAngles[3]) +{ + TeleportEntity(client, NULL_VECTOR, eyeAngles, NULL_VECTOR); +} + +/** + * Gets whether a player is on the ground. + * + * @param client Client index. + * @return Whether player is on the ground. + */ +stock bool Movement_GetOnGround(int client) +{ + return view_as<bool>(GetEntityFlags(client) & FL_ONGROUND); +} + +/** + * Gets whether a player is ducking or ducked. + * + * @param client Client index. + * @return Whether player is ducking or ducked. + */ +stock bool Movement_GetDucking(int client) +{ + return GetEntProp(client, Prop_Send, "m_bDucked") || GetEntProp(client, Prop_Send, "m_bDucking"); +} + +/** + * Gets a player's "m_flDuckSpeed" value. + * + * @param client Client index. + * @return Value of "m_flDuckSpeed". + */ +stock float Movement_GetDuckSpeed(int client) +{ + return GetEntPropFloat(client, Prop_Send, "m_flDuckSpeed"); +} + +/** + * Sets a player's "m_flDuckSpeed" value. + * + * @param client Client index. + * @param value New "m_flDuckSpeed" value. + */ +stock void Movement_SetDuckSpeed(int client, float value) +{ + SetEntPropFloat(client, Prop_Send, "m_flDuckSpeed", value); +} + +/** + * Gets a player's "m_flVelocityModifier" value. + * + * @param client Client index. + * @return Value of "m_flVelocityModifier". + */ +stock float Movement_GetVelocityModifier(int client) +{ + return GetEntPropFloat(client, Prop_Send, "m_flVelocityModifier"); +} + +/** + * Sets a player's "m_flVelocityModifier" value. + * + * @param client Client index. + * @param value New "m_flVelocityModifier" value. + */ +stock void Movement_SetVelocityModifier(int client, float value) +{ + SetEntPropFloat(client, Prop_Send, "m_flVelocityModifier", value); +} + +/** + * Gets a player's gravity scale factor. + * + * @param client Client index. + * @return Gravity scale factor. + */ +stock float Movement_GetGravity(int client) +{ + return GetEntityGravity(client); +} + +/** + * Sets a player's gravity scale factor. + * + * @param client Client index. + * @param value Desired gravity scale factor. + */ +stock void Movement_SetGravity(int client, float value) +{ + SetEntityGravity(client, value); +} + +/** + * Gets a player's movetype. + * + * @param client Client index. + * @return Player's movetype. + */ +stock MoveType Movement_GetMovetype(int client) +{ + return GetEntityMoveType(client); +} + +/** + * Sets a player's movetype. + * + * @param client Client index. + * @param movetype New movetype. + */ +stock void Movement_SetMovetype(int client, MoveType movetype) +{ + SetEntityMoveType(client, movetype); +} + +/** + * Gets whether a player is on a ladder. + * + * @param client Client index. + * @return Whether player is on a ladder. + */ +stock bool Movement_GetOnLadder(int client) +{ + return GetEntityMoveType(client) == MOVETYPE_LADDER; +} + +/** + * Gets whether a player is noclipping. + * + * @param client Client index. + * @return Whether player is noclipping. + */ +stock bool Movement_GetNoclipping(int client) +{ + return GetEntityMoveType(client) == MOVETYPE_NOCLIP; +} + + + +// =====[ METHODMAP ]===== + +methodmap MovementPlayer { + + public MovementPlayer(int client) { + return view_as<MovementPlayer>(client); + } + + property int ID { + public get() { + return view_as<int>(this); + } + } + + property int Buttons { + public get() { + return Movement_GetButtons(this.ID); + } + } + + public void GetOrigin(float result[3]) { + Movement_GetOrigin(this.ID, result); + } + + public void SetOrigin(const float origin[3]) { + Movement_SetOrigin(this.ID, origin); + } + + public void GetVelocity(float result[3]) { + Movement_GetVelocity(this.ID, result); + } + + public void SetVelocity(const float velocity[3]) { + Movement_SetVelocity(this.ID, velocity); + } + + property float Speed { + public get() { + return Movement_GetSpeed(this.ID); + } + public set(float value) { + Movement_SetSpeed(this.ID, value); + } + } + + property float VerticalVelocity { + public get() { + return Movement_GetVerticalVelocity(this.ID); + } + public set(float value) { + Movement_SetVerticalVelocity(this.ID, value); + } + } + + public void GetBaseVelocity(float result[3]) { + Movement_GetBaseVelocity(this.ID, result); + } + + public void SetBaseVelocity(const float baseVelocity[3]) { + Movement_SetBaseVelocity(this.ID, baseVelocity); + } + + public void GetEyeAngles(float result[3]) { + Movement_GetEyeAngles(this.ID, result); + } + + public void SetEyeAngles(const float eyeAngles[3]) { + Movement_SetEyeAngles(this.ID, eyeAngles); + } + + property bool OnGround { + public get() { + return Movement_GetOnGround(this.ID); + } + } + + property bool Ducking { + public get() { + return Movement_GetDucking(this.ID); + } + } + + property float DuckSpeed { + public get() { + return Movement_GetDuckSpeed(this.ID); + } + public set(float value) { + Movement_SetDuckSpeed(this.ID, value); + } + } + + property float VelocityModifier { + public get() { + return Movement_GetVelocityModifier(this.ID); + } + public set(float value) { + Movement_SetVelocityModifier(this.ID, value); + } + } + + property float Gravity { + public get() { + return Movement_GetGravity(this.ID); + } + public set(float value) { + Movement_SetGravity(this.ID, value); + } + } + + property MoveType Movetype { + public get() { + return Movement_GetMovetype(this.ID); + } + public set(MoveType movetype) { + Movement_SetMovetype(this.ID, movetype); + } + } + + property bool OnLadder { + public get() { + return Movement_GetOnLadder(this.ID); + } + } + + property bool Noclipping { + public get() { + return Movement_GetNoclipping(this.ID); + } + } +} diff --git a/sourcemod/scripting/include/movementapi.inc b/sourcemod/scripting/include/movementapi.inc new file mode 100644 index 0000000..290c3f2 --- /dev/null +++ b/sourcemod/scripting/include/movementapi.inc @@ -0,0 +1,663 @@ +/* + MovementAPI Plugin Include + + Website: https://github.com/danzayau/MovementAPI +*/ + +#if defined _movementapi_included_ + #endinput +#endif +#define _movementapi_included_ + +#include <movement> + + + +/* + Terminology + + Takeoff + Becoming airborne, including jumping, falling, getting off a ladder and leaving noclip. + + Landing + Leaving the air, including landing on the ground, grabbing a ladder and entering noclip. + + Perfect Bunnyhop (Perf) + When the player has jumped in the tick after landing and keeps their speed. + + Duckbug/Crouchbug + When the player sucessfully lands due to uncrouching from mid air and not by falling + down. This causes no stamina loss or fall damage upon landing. + + Jumpbug + This is achieved by duckbugging and jumping at the same time. The player is never seen + as 'on ground' when bunnyhopping from a tick by tick perspective. A jumpbug inherits + the same behavior as a duckbug/crouchbug, along with its effects such as maintaining + speed due to no stamina loss. + + Distbug + Landing behavior varies depending on whether the player lands close to the edge of a + block or not: + + 1. If the player lands close to the edge of a block, this causes the jump duration to + be one tick longer and the player can "slide" on the ground during the landing tick, + using the position post-tick as landing position becomes inaccurate. + + 2. On the other hand, if the player does not land close to the edge, the player will + be considered on the ground one tick earlier, using this position as landing position + is not accurate as the player has yet to be fully on the ground. + + In scenario 1, GetNobugLandingOrigin calculates the correct landing position of the + player before the sliding effect takes effect. + + In scenario 2, GetNobugLandingOrigin attempts to extrapolate the player's fully on + ground position to make landing positions consistent across scenarios. +*/ + + + +// =====[ FORWARDS ]===== + +/** + * Called when a player's movetype changes. + * + * @param client Client index. + * @param oldMovetype Player's old movetype. + * @param newMovetype Player's new movetype. + */ +forward void Movement_OnChangeMovetype(int client, MoveType oldMovetype, MoveType newMovetype); + +/** + * Called when a player touches the ground. + * + * @param client Client index. + */ +forward void Movement_OnStartTouchGround(int client); + +/** + * Called when a player leaves the ground. + * + * @param client Client index. + * @param jumped Whether player jumped to leave ground. + * @param ladderJump Whether player jumped from a ladder. + * @param jumpbug Whether player performed a jumpbug. + */ +forward void Movement_OnStopTouchGround(int client, bool jumped, bool ladderJump, bool jumpbug); + +/** + * Called when a player starts ducking. + * + * @param client Client index. + */ +forward void Movement_OnStartDucking(int client); + +/** + * Called when a player stops ducking. + * + * @param client Client index. + */ +forward void Movement_OnStopDucking(int client); + +/** + * Called when a player jumps (player_jump event), including 'jumpbugs'. + * Setting velocity when this is called may not be effective. + * + * @param client Client index. + * @param jumpbug Whether player 'jumpbugged'. + */ +forward void Movement_OnPlayerJump(int client, bool jumpbug); + +/** + * Called before PlayerMove movement function is called. + * Modifying origin or velocity parameters will change player's origin and velocity accordingly. + * + * @param client Client index. + * @param origin Player origin. + * @param velocity Player velocity. + * @return Plugin_Changed if origin or velocity is changed, Plugin_Continue otherwise. + */ +forward Action Movement_OnPlayerMovePre(int client, float origin[3], float velocity[3]); + +/** + * Called after PlayerMove movement function is called. + * Modifying origin or velocity parameters will change player's origin and velocity accordingly. + * + * @param client Client index. + * @param origin Player origin. + * @param velocity Player velocity. + * @return Plugin_Changed if origin or velocity is changed, Plugin_Continue otherwise. + */ +forward Action Movement_OnPlayerMovePost(int client, float origin[3], float velocity[3]); + +/** + * Called before Duck movement function is called. + * Modifying origin or velocity parameters will change player's origin and velocity accordingly. + * + * @param client Client index. + * @param origin Player origin. + * @param velocity Player velocity. + * @return Plugin_Changed if origin or velocity is changed, Plugin_Continue otherwise. + */ +forward Action Movement_OnDuckPre(int client, float origin[3], float velocity[3]); + +/** + * Called after Duck movement function is called. + * Modifying origin or velocity parameters will change player's origin and velocity accordingly. + * + * @param client Client index. + * @param origin Player origin. + * @param velocity Player velocity. + * @return Plugin_Changed if origin or velocity is changed, Plugin_Continue otherwise. + */ +forward Action Movement_OnDuckPost(int client, float origin[3], float velocity[3]); + +/** + * Called before LadderMove movement function is called. + * Modifying origin or velocity parameters will change player's origin and velocity accordingly. + * + * @param client Client index. + * @param origin Player origin. + * @param velocity Player velocity. + * @return Plugin_Changed if origin or velocity is changed, Plugin_Continue otherwise. + */ +forward Action Movement_OnLadderMovePre(int client, float origin[3], float velocity[3]); + +/** + * Called after LadderMove movement function is called. + * Modifying origin or velocity parameters will change player's origin and velocity accordingly. + * + * @param client Client index. + * @param origin Player origin. + * @param velocity Player velocity. + * @return Plugin_Changed if origin or velocity is changed, Plugin_Continue otherwise. + */ +forward Action Movement_OnLadderMovePost(int client, float origin[3], float velocity[3]); + +/** + * Called before FullLadderMove movement function is called. + * Modifying origin or velocity parameters will change player's origin and velocity accordingly. + * + * @param client Client index. + * @param origin Player origin. + * @param velocity Player velocity. + * @return Plugin_Changed if origin or velocity is changed, Plugin_Continue otherwise. + */ +forward Action Movement_OnFullLadderMovePre(int client, float origin[3], float velocity[3]); + +/** + * Called after FullLadderMove movement function is called. + * Modifying origin or velocity parameters will change player's origin and velocity accordingly. + * + * @param client Client index. + * @param origin Player origin. + * @param velocity Player velocity. + * @return Plugin_Changed if origin or velocity is changed, Plugin_Continue otherwise. + */ +forward Action Movement_OnFullLadderMovePost(int client, float origin[3], float velocity[3]); + +/** + * Called after the player jumps, but before jumping stamina is applied and takeoff variables are not set yet. + * Modifying origin or velocity parameters will change player's origin and velocity accordingly. + * + * @param client Client index. + * @param origin Player origin. + * @param velocity Player velocity. + * @return Plugin_Changed if origin or velocity is changed, Plugin_Continue otherwise. + */ +forward Action Movement_OnJumpPre(int client, float origin[3], float velocity[3]); + +/** + * Called after the player jumps and after jumping stamina is applied and takeoff variables are already set here. + * Modifying origin or velocity parameters will change player's origin and velocity accordingly. + * + * @param client Client index. + * @param origin Player origin. + * @param velocity Player velocity. + * @return Plugin_Changed if origin or velocity is changed, Plugin_Continue otherwise. + */ +forward Action Movement_OnJumpPost(int client, float origin[3], float velocity[3]); + +/** + * Called before AirAccelerate movement function is called. + * Modifying origin or velocity parameters will change player's origin and velocity accordingly. + * + * @param client Client index. + * @param origin Player origin. + * @param velocity Player velocity. + * @return Plugin_Changed if origin or velocity is changed, Plugin_Continue otherwise. + */ +forward Action Movement_OnAirAcceleratePre(int client, float origin[3], float velocity[3]); + +/** + * Called after AirAccelerate movement function is called. + * Modifying origin or velocity parameters will change player's origin and velocity accordingly. + * + * @param client Client index. + * @param origin Player origin. + * @param velocity Player velocity. + * @return Plugin_Changed if origin or velocity is changed, Plugin_Continue otherwise. + */ +forward Action Movement_OnAirAcceleratePost(int client, float origin[3], float velocity[3]); + +/** + * Called before WalkMove movement function is called. + * Modifying origin or velocity parameters will change player's origin and velocity accordingly. + * + * @param client Client index. + * @param origin Player origin. + * @param velocity Player velocity. + * @return Plugin_Changed if origin or velocity is changed, Plugin_Continue otherwise. + */ +forward Action Movement_OnWalkMovePre(int client, float origin[3], float velocity[3]); + +/** + * Called after WalkMove movement function is called. + * Modifying origin or velocity parameters will change player's origin and velocity accordingly. + * + * @param client Client index. + * @param origin Player origin. + * @param velocity Player velocity. + * @return Plugin_Changed if origin or velocity is changed, Plugin_Continue otherwise. + */ +forward Action Movement_OnWalkMovePost(int client, float origin[3], float velocity[3]); + +/** + * Called before CategorizePosition movement function is called. + * Modifying origin or velocity parameters will change player's origin and velocity accordingly. + * + * @param client Client index. + * @param origin Player origin. + * @param velocity Player velocity. + * @return Plugin_Changed if origin or velocity is changed, Plugin_Continue otherwise. + */ +forward Action Movement_OnCategorizePositionPre(int client, float origin[3], float velocity[3]); + +/** + * Called after CategorizePosition movement function is called. + * Modifying origin or velocity parameters will change player's origin and velocity accordingly. + * + * @param client Client index. + * @param origin Player origin. + * @param velocity Player velocity. + * @return Plugin_Changed if origin or velocity is changed, Plugin_Continue otherwise. + */ +forward Action Movement_OnCategorizePositionPost(int client, float origin[3], float velocity[3]); + +// =====[ NATIVES ]===== + +/** + * Gets whether a player's last takeoff was a jump. + * + * @param client Client index. + * @return Whether player's last takeoff was a jump. + */ +native bool Movement_GetJumped(int client); + +/** + * Gets whether a player's last takeoff was a perfect bunnyhop. + * + * @param client Client index. + * @return Whether player's last takeoff was a perfect bunnyhop. + */ +native bool Movement_GetHitPerf(int client); + +/** + * Gets a player's origin at the time of their last takeoff. + * + * @param client Client index. + * @param result Resultant vector. + */ +native void Movement_GetTakeoffOrigin(int client, float result[3]); + +/** + * Gets a player's velocity at the time of their last takeoff. + * + * If sv_enablebunnyhopping is 0, CS:GO may adjust the player's + * velocity after the takeoff velocity has already been measured. + * + * @param client Client index. + * @param result Resultant vector. + */ +native void Movement_GetTakeoffVelocity(int client, float result[3]); + +/** + * Gets a player's horizontal speed at the time of their last takeoff. + * + * If sv_enablebunnyhopping is 0, CS:GO may adjust the player's + * velocity after the takeoff velocity has already been measured. + * + * @param client Client index. + * @return Player's last takeoff speed. + */ +native float Movement_GetTakeoffSpeed(int client); + +/** + * Gets a player's 'tickcount' at the time of their last takeoff. + * + * @param client Client index. + * @return Player's last takeoff 'tickcount'. + */ +native int Movement_GetTakeoffTick(int client); + +/** + * Gets a player's 'cmdnum' at the time of their last takeoff. + * + * @param client Client index. + * @return Player's last takeoff 'cmdnum'. + */ +native int Movement_GetTakeoffCmdNum(int client); + +/** + * Gets a player's origin at the time of their last landing with the distbug fixed. + * + * @param client Client index. + * @param result Resultant vector. + */ +native void Movement_GetNobugLandingOrigin(int client, float result[3]); + +/** + * Gets a player's origin at the time of their last landing. + * + * @param client Client index. + * @param result Resultant vector. + */ +native void Movement_GetLandingOrigin(int client, float result[3]); + +/** + * Gets a player's velocity at the time of their last landing. + * + * @param client Client index. + * @param result Resultant vector. + */ +native void Movement_GetLandingVelocity(int client, float result[3]); + +/** + * Gets a player's horizontal speed at the time of their last landing. + * + * @param client Client index. + * @return Last landing speed of the player (horizontal). + */ +native float Movement_GetLandingSpeed(int client); + +/** + * Gets a player's 'tickcount' at the time of their last landing. + * + * @param client Client index. + * @return Player's last landing 'tickcount'. + */ +native int Movement_GetLandingTick(int client); + +/** + * Gets a player's 'cmdnum' at the time of their last landing. + * + * @param client Client index. + * @return Player's last landing 'cmdnum'. + */ +native int Movement_GetLandingCmdNum(int client); + +/** + * Gets whether a player is turning their aim horizontally. + * + * @param client Client index. + * @return Whether player is turning their aim horizontally. + */ +native bool Movement_GetTurning(int client); + +/** + * Gets whether a player is turning their aim left. + * + * @param client Client index. + * @return Whether player is turning their aim left. + */ +native bool Movement_GetTurningLeft(int client); + +/** + * Gets whether a player is turning their aim right. + * + * @param client Client index. + * @return Whether player is turning their aim right. + */ +native bool Movement_GetTurningRight(int client); + +/** + * Gets result of CCSPlayer::GetPlayerMaxSpeed(client), which + * is the player's max speed as limited by their weapon. + * + * @param client Client index. + * @return Player's max speed as limited by their weapon. + */ +native float Movement_GetMaxSpeed(int client); + +/** + * Gets whether a player duckbugged on this tick. + * + * @param client Client index. + * @return Whether a player duckbugged on this tick. + */ +native bool Movement_GetDuckbugged(int client); + +/** + * Gets whether a player jumpbugged on this tick. + * + * @param client Client index. + * @return Whether a player jumpbugged on this tick. + */ +native bool Movement_GetJumpbugged(int client); + +/** + * Get the player's origin during movement processing. + * + * @param client Client index. + * @param result Resultant vector. + */ +native void Movement_GetProcessingOrigin(int client, float result[3]); + +/** + * Get the player's velocity during movement processing. + * + * @param client Param description + * @param result Resultant vector. + */ +native void Movement_GetProcessingVelocity(int client, float result[3]); + +/** + * Set the player's takeoff origin. + * + * @param client Client index. + * @param origin Desired origin. + */ +native void Movement_SetTakeoffOrigin(int client, float origin[3]); + +/** + * Set the player's takeoff velocity. + * + * @param client Client index. + * @param origin Desired velocity. + */ +native void Movement_SetTakeoffVelocity(int client, float velocity[3]); + +/** + * Set the player's landing origin. + * + * @param client Client index. + * @param origin Desired origin. + */ +native void Movement_SetLandingOrigin(int client, float origin[3]); + +/** + * Set the player's landing velocity. + * + * @param client Client index. + * @param origin Desired velocity. + */ +native void Movement_SetLandingVelocity(int client, float velocity[3]); + +// =====[ METHODMAP ]===== + +methodmap MovementAPIPlayer < MovementPlayer { + + public MovementAPIPlayer(int client) { + return view_as<MovementAPIPlayer>(MovementPlayer(client)); + } + + property bool Jumped { + public get() { + return Movement_GetJumped(this.ID); + } + } + + property bool HitPerf { + public get() { + return Movement_GetHitPerf(this.ID); + } + } + + public void GetTakeoffOrigin(float buffer[3]) { + Movement_GetTakeoffOrigin(this.ID, buffer); + } + + public void GetTakeoffVelocity(float buffer[3]) { + Movement_GetTakeoffVelocity(this.ID, buffer); + } + + public void SetTakeoffOrigin(float buffer[3]) + { + Movement_SetTakeoffOrigin(this.ID, buffer); + } + + public void SetTakeoffVelocity(float buffer[3]) + { + Movement_SetTakeoffVelocity(this.ID, buffer); + } + + property float TakeoffSpeed { + public get() { + return Movement_GetTakeoffSpeed(this.ID); + } + } + + property int TakeoffTick { + public get() { + return Movement_GetTakeoffTick(this.ID); + } + } + + property int TakeoffCmdNum { + public get() { + return Movement_GetTakeoffCmdNum(this.ID); + } + } + + public void GetLandingOrigin(float buffer[3]) { + Movement_GetLandingOrigin(this.ID, buffer); + } + + public void GetLandingVelocity(float buffer[3]) { + Movement_GetLandingVelocity(this.ID, buffer); + } + + public void SetLandingOrigin(float buffer[3]) + { + Movement_SetLandingOrigin(this.ID, buffer); + } + + public void SetLandingVelocity(float buffer[3]) + { + Movement_SetLandingVelocity(this.ID, buffer); + } + + property float LandingSpeed { + public get() { + return Movement_GetLandingSpeed(this.ID); + } + } + + property int LandingTick { + public get() { + return Movement_GetLandingTick(this.ID); + } + } + + property int LandingCmdNum { + public get() { + return Movement_GetLandingCmdNum(this.ID); + } + } + + property bool Turning { + public get() { + return Movement_GetTurning(this.ID); + } + } + + property bool TurningLeft { + public get() { + return Movement_GetTurningLeft(this.ID); + } + } + + property bool TurningRight { + public get() { + return Movement_GetTurningRight(this.ID); + } + } + + property float MaxSpeed { + public get() { + return Movement_GetMaxSpeed(this.ID); + } + } + + public void GetProcessingVelocity(float buffer[3]) + { + Movement_GetProcessingVelocity(this.ID, buffer); + } + + public void GetProcessingOrigin(float buffer[3]) + { + Movement_GetProcessingOrigin(this.ID, buffer); + } +} + + + +// =====[ DEPENDENCY ]===== + +public SharedPlugin __pl_movementapi = +{ + name = "movementapi", + file = "movementapi.smx", + #if defined REQUIRE_PLUGIN + required = 1, + #else + required = 0, + #endif +}; + +#if !defined REQUIRE_PLUGIN +public void __pl_movementapi_SetNTVOptional() +{ + MarkNativeAsOptional("Movement_GetJumped"); + MarkNativeAsOptional("Movement_GetHitPerf"); + MarkNativeAsOptional("Movement_GetTakeoffOrigin"); + MarkNativeAsOptional("Movement_GetTakeoffVelocity"); + MarkNativeAsOptional("Movement_GetTakeoffSpeed"); + MarkNativeAsOptional("Movement_GetTakeoffTick"); + MarkNativeAsOptional("Movement_GetTakeoffCmdNum"); + MarkNativeAsOptional("Movement_GetLandingOrigin"); + MarkNativeAsOptional("Movement_GetLandingVelocity"); + MarkNativeAsOptional("Movement_GetLandingSpeed"); + MarkNativeAsOptional("Movement_GetLandingTick"); + MarkNativeAsOptional("Movement_GetLandingCmdNum"); + MarkNativeAsOptional("Movement_GetTurning"); + MarkNativeAsOptional("Movement_GetTurningLeft"); + MarkNativeAsOptional("Movement_GetTurningRight"); + MarkNativeAsOptional("Movement_GetMaxSpeed"); + MarkNativeAsOptional("Movement_GetProcessingOrigin"); + MarkNativeAsOptional("Movement_GetProcessingVelocity"); + MarkNativeAsOptional("Movement_SetTakeoffOrigin"); + MarkNativeAsOptional("Movement_SetTakeoffVelocity"); + MarkNativeAsOptional("Movement_SetLandingOrigin"); + MarkNativeAsOptional("Movement_SetLandingVelocity"); +} +#endif
\ No newline at end of file diff --git a/sourcemod/scripting/include/smjansson.inc b/sourcemod/scripting/include/smjansson.inc new file mode 100644 index 0000000..029a492 --- /dev/null +++ b/sourcemod/scripting/include/smjansson.inc @@ -0,0 +1,1328 @@ +#if defined _jansson_included_ + #endinput +#endif +#define _jansson_included_ + + +/** + * --- Type + * + * The JSON specification (RFC 4627) defines the following data types: + * object, array, string, number, boolean, and null. + * JSON types are used dynamically; arrays and objects can hold any + * other data type, including themselves. For this reason, Jansson�s + * type system is also dynamic in nature. There�s one Handle type to + * represent all JSON values, and the referenced structure knows the + * type of the JSON value it holds. + * + */ +enum json_type { + JSON_OBJECT, + JSON_ARRAY, + JSON_STRING, + JSON_INTEGER, + JSON_REAL, + JSON_TRUE, + JSON_FALSE, + JSON_NULL +} + +/** + * Return the type of the JSON value. + * + * @param hObj Handle to the JSON value + * + * @return json_type of the value. + */ +native json_type:json_typeof(Handle:hObj); + +/** + * The type of a JSON value is queried and tested using these macros + * + * @param %1 Handle to the JSON value + * + * @return True if the value has the correct type. + */ +#define json_is_object(%1) ( json_typeof(%1) == JSON_OBJECT ) +#define json_is_array(%1) ( json_typeof(%1) == JSON_ARRAY ) +#define json_is_string(%1) ( json_typeof(%1) == JSON_STRING ) +#define json_is_integer(%1) ( json_typeof(%1) == JSON_INTEGER ) +#define json_is_real(%1) ( json_typeof(%1) == JSON_REAL ) +#define json_is_true(%1) ( json_typeof(%1) == JSON_TRUE ) +#define json_is_false(%1) ( json_typeof(%1) == JSON_FALSE ) +#define json_is_null(%1) ( json_typeof(%1) == JSON_NULL ) +#define json_is_number(%1) ( json_typeof(%1) == JSON_INTEGER || json_typeof(%1) == JSON_REAL ) +#define json_is_boolean(%1) ( json_typeof(%1) == JSON_TRUE || json_typeof(%1) == JSON_FALSE ) + +/** + * Saves json_type as a String in output + * + * @param input json_type value to convert to string + * @param output Buffer to store the json_type value + * @param maxlength Maximum length of string buffer. + * + * @return False if the type does not exist. + */ +stock bool:Stringify_json_type(json_type:input, String:output[], maxlength) { + switch(input) { + case JSON_OBJECT: strcopy(output, maxlength, "Object"); + case JSON_ARRAY: strcopy(output, maxlength, "Array"); + case JSON_STRING: strcopy(output, maxlength, "String"); + case JSON_INTEGER: strcopy(output, maxlength, "Integer"); + case JSON_REAL: strcopy(output, maxlength, "Real"); + case JSON_TRUE: strcopy(output, maxlength, "True"); + case JSON_FALSE: strcopy(output, maxlength, "False"); + case JSON_NULL: strcopy(output, maxlength, "Null"); + default: return false; + } + + return true; +} + + + +/** + * --- Equality + * + * - Two integer or real values are equal if their contained numeric + * values are equal. An integer value is never equal to a real value, + * though. + * - Two strings are equal if their contained UTF-8 strings are equal, + * byte by byte. Unicode comparison algorithms are not implemented. + * - Two arrays are equal if they have the same number of elements and + * each element in the first array is equal to the corresponding + * element in the second array. + * - Two objects are equal if they have exactly the same keys and the + * value for each key in the first object is equal to the value of + * the corresponding key in the second object. + * - Two true, false or null values have no "contents", so they are + * equal if their types are equal. + * + */ + +/** + * Test whether two JSON values are equal. + * + * @param hObj Handle to the first JSON object + * @param hOther Handle to the second JSON object + * + * @return Returns false if they are inequal or one + * or both of the pointers are NULL. + */ +native bool:json_equal(Handle:hObj, Handle:hOther); + + + + +/** + * --- Copying + * + * Jansson supports two kinds of copying: shallow and deep. There is + * a difference between these methods only for arrays and objects. + * + * Shallow copying only copies the first level value (array or object) + * and uses the same child values in the copied value. + * + * Deep copying makes a fresh copy of the child values, too. Moreover, + * all the child values are deep copied in a recursive fashion. + * + */ + +/** + * Get a shallow copy of the passed object + * + * @param hObj Handle to JSON object to be copied + * + * @return Returns a shallow copy of the object, + * or INVALID_HANDLE on error. + */ +native Handle:json_copy(Handle:hObj); + +/** + * Get a deep copy of the passed object + * + * @param hObj Handle to JSON object to be copied + * + * @return Returns a deep copy of the object, + * or INVALID_HANDLE on error. + */ +native Handle:json_deep_copy(Handle:hObj); + + + + +/** + * --- Objects + * + * A JSON object is a dictionary of key-value pairs, where the + * key is a Unicode string and the value is any JSON value. + * + */ + +/** + * Returns a handle to a new JSON object, or INVALID_HANDLE on error. + * Initially, the object is empty. + * + * @return Handle to a new JSON object. + */ +native Handle:json_object(); + +/** + * Returns the number of elements in hObj + * + * @param hObj Handle to JSON object + * + * @return Number of elements in hObj, + * or 0 if hObj is not a JSON object. + */ +native json_object_size(Handle:hObj); + +/** + * Get a value corresponding to sKey from hObj + * + * @param hObj Handle to JSON object to get a value from + * @param sKey Key to retrieve + * + * @return Handle to a the JSON object or + * INVALID_HANDLE on error. + */ +native Handle:json_object_get(Handle:hObj, const String:sKey[]); + +/** + * Set the value of sKey to hValue in hObj. + * If there already is a value for key, it is replaced by the new value. + * + * @param hObj Handle to JSON object to set a value on + * @param sKey Key to store in the object + * Must be a valid null terminated UTF-8 encoded + * Unicode string. + * @param hValue Value to store in the object + * + * @return True on success. + */ +native bool:json_object_set(Handle:hObj, const String:sKey[], Handle:hValue); + +/** + * Set the value of sKey to hValue in hObj. + * If there already is a value for key, it is replaced by the new value. + * This function automatically closes the Handle to the value object. + * + * @param hObj Handle to JSON object to set a value on + * @param sKey Key to store in the object + * Must be a valid null terminated UTF-8 encoded + * Unicode string. + * @param hValue Value to store in the object + * + * @return True on success. + */ +native bool:json_object_set_new(Handle:hObj, const String:sKey[], Handle:hValue); + +/** + * Delete sKey from hObj if it exists. + * + * @param hObj Handle to JSON object to delete a key from + * @param sKey Key to delete + * + * @return True on success. + */ +native bool:json_object_del(Handle:hObj, const String:sKey[]); + +/** + * Remove all elements from hObj. + * + * @param hObj Handle to JSON object to remove all + * elements from. + * + * @return True on success. + */ +native bool:json_object_clear(Handle:hObj); + +/** + * Update hObj with the key-value pairs from hOther, overwriting + * existing keys. + * + * @param hObj Handle to JSON object to update + * @param hOther Handle to JSON object to get update + * keys/values from. + * + * @return True on success. + */ +native bool:json_object_update(Handle:hObj, Handle:hOther); + +/** + * Like json_object_update(), but only the values of existing keys + * are updated. No new keys are created. + * + * @param hObj Handle to JSON object to update + * @param hOther Handle to JSON object to get update + * keys/values from. + * + * @return True on success. + */ +native bool:json_object_update_existing(Handle:hObj, Handle:hOther); + +/** + * Like json_object_update(), but only new keys are created. + * The value of any existing key is not changed. + * + * @param hObj Handle to JSON object to update + * @param hOther Handle to JSON object to get update + * keys/values from. + * + * @return True on success. + */ +native bool:json_object_update_missing(Handle:hObj, Handle:hOther); + + + + +/** + * Object iteration + * + * Example code: + * - We assume hObj is a Handle to a valid JSON object. + * + * + * new Handle:hIterator = json_object_iter(hObj); + * while(hIterator != INVALID_HANDLE) + * { + * new String:sKey[128]; + * json_object_iter_key(hIterator, sKey, sizeof(sKey)); + * + * new Handle:hValue = json_object_iter_value(hIterator); + * + * // Do something with sKey and hValue + * + * CloseHandle(hValue); + * + * hIterator = json_object_iter_next(hObj, hIterator); + * } + * + */ + +/** + * Returns a handle to an iterator which can be used to iterate over + * all key-value pairs in hObj. + * If you are not iterating to the end of hObj make sure to close the + * handle to the iterator manually. + * + * @param hObj Handle to JSON object to get an iterator + * for. + * + * @return Handle to JSON object iterator, + * or INVALID_HANDLE on error. + */ +native Handle:json_object_iter(Handle:hObj); + +/** + * Like json_object_iter(), but returns an iterator to the key-value + * pair in object whose key is equal to key. + * Iterating forward to the end of object only yields all key-value + * pairs of the object if key happens to be the first key in the + * underlying hash table. + * + * @param hObj Handle to JSON object to get an iterator + * for. + * @param sKey Start key for the iterator + * + * @return Handle to JSON object iterator, + * or INVALID_HANDLE on error. + */ +native Handle:json_object_iter_at(Handle:hObj, const String:key[]); + +/** + * Returns an iterator pointing to the next key-value pair in object. + * This automatically closes the Handle to the iterator hIter. + * + * @param hObj Handle to JSON object. + * @param hIter Handle to JSON object iterator. + * + * @return Handle to JSON object iterator, + * or INVALID_HANDLE on error, or if the + * whole object has been iterated through. + */ +native Handle:json_object_iter_next(Handle:hObj, Handle:hIter); + +/** + * Extracts the associated key of hIter as a null terminated UTF-8 + * encoded string in the passed buffer. + * + * @param hIter Handle to the JSON String object + * @param sKeyBuffer Buffer to store the value of the String. + * @param maxlength Maximum length of string buffer. + * @error Invalid JSON Object Iterator. + * @return Length of the returned string or -1 on error. + */ +native json_object_iter_key(Handle:hIter, String:sKeyBuffer[], maxlength); + +/** + * Returns a handle to the value hIter is pointing at. + * + * @param hIter Handle to JSON object iterator. + * + * @return Handle to value or INVALID_HANDLE on error. + */ +native Handle:json_object_iter_value(Handle:hIter); + +/** + * Set the value of the key-value pair in hObj, that is pointed to + * by hIter, to hValue. + * + * @param hObj Handle to JSON object. + * @param hIter Handle to JSON object iterator. + * @param hValue Handle to JSON value. + * + * @return True on success. + */ +native bool:json_object_iter_set(Handle:hObj, Handle:hIter, Handle:hValue); + +/** + * Set the value of the key-value pair in hObj, that is pointed to + * by hIter, to hValue. + * This function automatically closes the Handle to the value object. + * + * @param hObj Handle to JSON object. + * @param hIter Handle to JSON object iterator. + * @param hValue Handle to JSON value. + * + * @return True on success. + */ +native bool:json_object_iter_set_new(Handle:hObj, Handle:hIter, Handle:hValue); + + + + +/** + * Arrays + * + * A JSON array is an ordered collection of other JSON values. + * + */ + +/** + * Returns a handle to a new JSON array, or INVALID_HANDLE on error. + * + * @return Handle to the new JSON array + */ +native Handle:json_array(); + +/** + * Returns the number of elements in hArray + * + * @param hObj Handle to JSON array + * + * @return Number of elements in hArray, + * or 0 if hObj is not a JSON array. + */ +native json_array_size(Handle:hArray); + +/** + * Returns the element in hArray at position iIndex. + * + * @param hArray Handle to JSON array to get a value from + * @param iIndex Position to retrieve + * + * @return Handle to a the JSON object or + * INVALID_HANDLE on error. + */ +native Handle:json_array_get(Handle:hArray, iIndex); + +/** + * Replaces the element in array at position iIndex with hValue. + * The valid range for iIndex is from 0 to the return value of + * json_array_size() minus 1. + * + * @param hArray Handle to JSON array + * @param iIndex Position to replace + * @param hValue Value to store in the array + * + * @return True on success. + */ +native bool:json_array_set(Handle:hArray, iIndex, Handle:hValue); + +/** + * Replaces the element in array at position iIndex with hValue. + * The valid range for iIndex is from 0 to the return value of + * json_array_size() minus 1. + * This function automatically closes the Handle to the value object. + * + * @param hArray Handle to JSON array + * @param iIndex Position to replace + * @param hValue Value to store in the array + * + * @return True on success. + */ +native bool:json_array_set_new(Handle:hArray, iIndex, Handle:hValue); + +/** + * Appends value to the end of array, growing the size of array by 1. + * + * @param hArray Handle to JSON array + * @param hValue Value to append to the array + * + * @return True on success. + */ +native bool:json_array_append(Handle:hArray, Handle:hValue); + +/** + * Appends value to the end of array, growing the size of array by 1. + * This function automatically closes the Handle to the value object. + * + * @param hArray Handle to JSON array + * @param hValue Value to append to the array + * + * @return True on success. + */ +native bool:json_array_append_new(Handle:hArray, Handle:hValue); + +/** + * Inserts value to hArray at position iIndex, shifting the elements at + * iIndex and after it one position towards the end of the array. + * + * @param hArray Handle to JSON array + * @param iIndex Position to insert at + * @param hValue Value to store in the array + * + * @return True on success. + */ +native bool:json_array_insert(Handle:hArray, iIndex, Handle:hValue); + +/** + * Inserts value to hArray at position iIndex, shifting the elements at + * iIndex and after it one position towards the end of the array. + * This function automatically closes the Handle to the value object. + * + * @param hArray Handle to JSON array + * @param iIndex Position to insert at + * @param hValue Value to store in the array + * + * @return True on success. + */ +native bool:json_array_insert_new(Handle:hArray, iIndex, Handle:hValue); + +/** + * Removes the element in hArray at position iIndex, shifting the + * elements after iIndex one position towards the start of the array. + * + * @param hArray Handle to JSON array + * @param iIndex Position to insert at + * + * @return True on success. + */ +native bool:json_array_remove(Handle:hArray, iIndex); + +/** + * Removes all elements from hArray. + * + * @param hArray Handle to JSON array + * + * @return True on success. + */ +native bool:json_array_clear(Handle:hArray); + +/** + * Appends all elements in hOther to the end of hArray. + * + * @param hArray Handle to JSON array to be extended + * @param hOther Handle to JSON array, source to copy from + * + * @return True on success. + */ +native bool:json_array_extend(Handle:hArray, Handle:hOther); + + + + +/** + * Booleans & NULL + * + */ + +/** + * Returns a handle to a new JSON Boolean with value true, + * or INVALID_HANDLE on error. + * + * @return Handle to the new Boolean object + */ +native Handle:json_true(); + +/** + * Returns a handle to a new JSON Boolean with value false, + * or INVALID_HANDLE on error. + * + * @return Handle to the new Boolean object + */ +native Handle:json_false(); + +/** + * Returns a handle to a new JSON Boolean with the value passed + * in bState or INVALID_HANDLE on error. + * + * @param bState Value for the new Boolean object + * @return Handle to the new Boolean object + */ +native Handle:json_boolean(bool:bState); + +/** + * Returns a handle to a new JSON NULL or INVALID_HANDLE on error. + * + * @return Handle to the new NULL object + */ +native Handle:json_null(); + + + + +/** + * Strings + * + * Jansson uses UTF-8 as the character encoding. All JSON strings must + * be valid UTF-8 (or ASCII, as it�s a subset of UTF-8). Normal null + * terminated C strings are used, so JSON strings may not contain + * embedded null characters. + * + */ + +/** + * Returns a handle to a new JSON string, or INVALID_HANDLE on error. + * + * @param sValue Value for the new String object + * Must be a valid UTF-8 encoded Unicode string. + * @return Handle to the new String object + */ +native Handle:json_string(const String:sValue[]); + +/** + * Saves the associated value of hString as a null terminated UTF-8 + * encoded string in the passed buffer. + * + * @param hString Handle to the JSON String object + * @param sValueBuffer Buffer to store the value of the String. + * @param maxlength Maximum length of string buffer. + * @error Invalid JSON String Object. + * @return Length of the returned string or -1 on error. + */ +native json_string_value(Handle:hString, String:sValueBuffer[], maxlength); + +/** + * Sets the associated value of JSON String object to value. + * + * @param hString Handle to the JSON String object + * @param sValue Value to set the object to. + * Must be a valid UTF-8 encoded Unicode string. + * @error Invalid JSON String Object. + * @return True on success. + */ +native bool:json_string_set(Handle:hString, String:sValue[]); + + + + +/** + * Numbers + * + * The JSON specification only contains one numeric type, 'number'. + * The C (and Pawn) programming language has distinct types for integer + * and floating-point numbers, so for practical reasons Jansson also has + * distinct types for the two. They are called 'integer' and 'real', + * respectively. (Whereas 'real' is a 'Float' for Pawn). + * Therefore a number is represented by either a value of the type + * JSON_INTEGER or of the type JSON_REAL. + * + */ + +/** + * Returns a handle to a new JSON integer, or INVALID_HANDLE on error. + * + * @param iValue Value for the new Integer object + * @return Handle to the new Integer object + */ +native Handle:json_integer(iValue); + +/** + * Returns the associated value of a JSON Integer Object. + * + * @param hInteger Handle to the JSON Integer object + * @error Invalid JSON Integer Object. + * @return Value of the hInteger, + * or 0 if hInteger is not a JSON integer. + */ +native json_integer_value(Handle:hInteger); + +/** + * Sets the associated value of JSON Integer to value. + * + * @param hInteger Handle to the JSON Integer object + * @param iValue Value to set the object to. + * @error Invalid JSON Integer Object. + * @return True on success. + */ +native bool:json_integer_set(Handle:hInteger, iValue); + +/** + * Returns a handle to a new JSON real, or INVALID_HANDLE on error. + * + * @param fValue Value for the new Real object + * @return Handle to the new String object + */ +native Handle:json_real(Float:fValue); + +/** + * Returns the associated value of a JSON Real. + * + * @param hReal Handle to the JSON Real object + * @error Invalid JSON Real Object. + * @return Float value of hReal, + * or 0.0 if hReal is not a JSON Real. + */ +native Float:json_real_value(Handle:hReal); + +/** + * Sets the associated value of JSON Real to fValue. + * + * @param hReal Handle to the JSON Integer object + * @param fValue Value to set the object to. + * @error Invalid JSON Real handle. + * @return True on success. + */ +native bool:json_real_set(Handle:hReal, Float:value); + +/** + * Returns the associated value of a JSON integer or a + * JSON Real, cast to Float regardless of the actual type. + * + * @param hNumber Handle to the JSON Number + * @error Not a JSON Real or JSON Integer + * @return Float value of hNumber, + * or 0.0 on error. + */ +native Float:json_number_value(Handle:hNumber); + + + + +/** + * Decoding + * + * This sections describes the functions that can be used to decode JSON text + * to the Jansson representation of JSON data. The JSON specification requires + * that a JSON text is either a serialized array or object, and this + * requirement is also enforced with the following functions. In other words, + * the top level value in the JSON text being decoded must be either array or + * object. + * + */ + +/** + * Decodes the JSON string sJSON and returns the array or object it contains. + * Errors while decoding can be found in the sourcemod error log. + * + * @param sJSON String containing valid JSON + + * @return Handle to JSON object or array. + * or INVALID_HANDLE on error. + */ +native Handle:json_load(const String:sJSON[]); + +/** + * Decodes the JSON string sJSON and returns the array or object it contains. + * This function provides additional error feedback and does not log errors + * to the sourcemod error log. + * + * @param sJSON String containing valid JSON + * @param sErrorText This buffer will be filled with the error + * message. + * @param maxlen Size of the buffer + * @param iLine This int will contain the line of the error + * @param iColumn This int will contain the column of the error + * + * @return Handle to JSON object or array. + * or INVALID_HANDLE on error. + */ +native Handle:json_load_ex(const String:sJSON[], String:sErrorText[], maxlen, &iLine, &iColumn); + +/** + * Decodes the JSON text in file sFilePath and returns the array or object + * it contains. + * Errors while decoding can be found in the sourcemod error log. + * + * @param sFilePath Path to a file containing pure JSON + * + * @return Handle to JSON object or array. + * or INVALID_HANDLE on error. + */ +native Handle:json_load_file(const String:sFilePath[PLATFORM_MAX_PATH]); + +/** + * Decodes the JSON text in file sFilePath and returns the array or object + * it contains. + * This function provides additional error feedback and does not log errors + * to the sourcemod error log. + * + * @param sFilePath Path to a file containing pure JSON + * @param sErrorText This buffer will be filled with the error + * message. + * @param maxlen Size of the buffer + * @param iLine This int will contain the line of the error + * @param iColumn This int will contain the column of the error + * + * @return Handle to JSON object or array. + * or INVALID_HANDLE on error. + */ +native Handle:json_load_file_ex(const String:sFilePath[PLATFORM_MAX_PATH], String:sErrorText[], maxlen, &iLine, &iColumn); + + + +/** + * Encoding + * + * This sections describes the functions that can be used to encode values + * to JSON. By default, only objects and arrays can be encoded directly, + * since they are the only valid root values of a JSON text. + * + */ + +/** + * Saves the JSON representation of hObject in sJSON. + * + * @param hObject String containing valid JSON + * @param sJSON Buffer to store the created JSON string. + * @param maxlength Maximum length of string buffer. + * @param iIndentWidth Indenting with iIndentWidth spaces. + * The valid range for this is between 0 and 31 (inclusive), + * other values result in an undefined output. If this is set + * to 0, no newlines are inserted between array and object items. + * @param bEnsureAscii If this is set, the output is guaranteed + * to consist only of ASCII characters. This is achieved + * by escaping all Unicode characters outside the ASCII range. + * @param bSortKeys If this flag is used, all the objects in output are sorted + * by key. This is useful e.g. if two JSON texts are diffed + * or visually compared. + * @param bPreserveOrder If this flag is used, object keys in the output are sorted + * into the same order in which they were first inserted to + * the object. For example, decoding a JSON text and then + * encoding with this flag preserves the order of object keys. + * @return Length of the returned string or -1 on error. + */ +native json_dump(Handle:hObject, String:sJSON[], maxlength, iIndentWidth = 4, bool:bEnsureAscii = false, bool:bSortKeys = false, bool:bPreserveOrder = false); + +/** + * Write the JSON representation of hObject to the file sFilePath. + * If sFilePath already exists, it is overwritten. + * + * @param hObject String containing valid JSON + * @param sFilePath Buffer to store the created JSON string. + * @param iIndentWidth Indenting with iIndentWidth spaces. + * The valid range for this is between 0 and 31 (inclusive), + * other values result in an undefined output. If this is set + * to 0, no newlines are inserted between array and object items. + * @param bEnsureAscii If this is set, the output is guaranteed + * to consist only of ASCII characters. This is achieved + * by escaping all Unicode characters outside the ASCII range. + * @param bSortKeys If this flag is used, all the objects in output are sorted + * by key. This is useful e.g. if two JSON texts are diffed + * or visually compared. + * @param bPreserveOrder If this flag is used, object keys in the output are sorted + * into the same order in which they were first inserted to + * the object. For example, decoding a JSON text and then + * encoding with this flag preserves the order of object keys. + * @return Length of the returned string or -1 on error. + */ +native bool:json_dump_file(Handle:hObject, const String:sFilePath[], iIndentWidth = 4, bool:bEnsureAscii = false, bool:bSortKeys = false, bool:bPreserveOrder = false); + + + +/** + * Convenience stocks + * + * These are some custom functions to ease the development using this + * extension. + * + */ + +/** + * Returns a handle to a new JSON string, or INVALID_HANDLE on error. + * Formats the string according to the SourceMod format rules. + * The result must be a valid UTF-8 encoded Unicode string. + * + * @param sFormat Formatting rules. + * @param ... Variable number of format parameters. + * @return Handle to the new String object + */ +stock Handle:json_string_format(const String:sFormat[], any:...) { + new String:sTmp[4096]; + VFormat(sTmp, sizeof(sTmp), sFormat, 2); + + return json_string(sTmp); +} + +/** + * Returns a handle to a new JSON string, or INVALID_HANDLE on error. + * This stock allows to specify the size of the temporary buffer used + * to create the string. Use this if the default of 4096 is not enough + * for your string. + * Formats the string according to the SourceMod format rules. + * The result must be a valid UTF-8 encoded Unicode string. + * + * @param tmpBufferLength Size of the temporary buffer + * @param sFormat Formatting rules. + * @param ... Variable number of format parameters. + * @return Handle to the new String object + */ +stock Handle:json_string_format_ex(tmpBufferLength, const String:sFormat[], any:...) { + new String:sTmp[tmpBufferLength]; + VFormat(sTmp, sizeof(sTmp), sFormat, 3); + + return json_string(sTmp); +} + + +/** + * Returns the boolean value of the element in hArray at position iIndex. + * + * @param hArray Handle to JSON array to get a value from + * @param iIndex Position to retrieve + * + * @return True if it's a boolean and TRUE, + * false otherwise. + */ +stock bool:json_array_get_bool(Handle:hArray, iIndex) { + new Handle:hElement = json_array_get(hArray, iIndex); + + new bool:bResult = (json_is_true(hElement) ? true : false); + + CloseHandle(hElement); + return bResult; +} + +/** + * Returns the float value of the element in hArray at position iIndex. + * + * @param hArray Handle to JSON array to get a value from + * @param iIndex Position to retrieve + * + * @return Float value, + * or 0.0 if element is not a JSON Real. + */ +stock Float:json_array_get_float(Handle:hArray, iIndex) { + new Handle:hElement = json_array_get(hArray, iIndex); + + new Float:fResult = (json_is_number(hElement) ? json_number_value(hElement) : 0.0); + + CloseHandle(hElement); + return fResult; +} + +/** + * Returns the integer value of the element in hArray at position iIndex. + * + * @param hArray Handle to JSON array to get a value from + * @param iIndex Position to retrieve + * + * @return Integer value, + * or 0 if element is not a JSON Integer. + */ +stock json_array_get_int(Handle:hArray, iIndex) { + new Handle:hElement = json_array_get(hArray, iIndex); + + new iResult = (json_is_integer(hElement) ? json_integer_value(hElement) : 0); + + CloseHandle(hElement); + return iResult; +} + +/** + * Saves the associated value of the element in hArray at position iIndex + * as a null terminated UTF-8 encoded string in the passed buffer. + * + * @param hArray Handle to JSON array to get a value from + * @param iIndex Position to retrieve + * @param sBuffer Buffer to store the value of the String. + * @param maxlength Maximum length of string buffer. + * + * @error Element is not a JSON String. + * @return Length of the returned string or -1 on error. + */ +stock json_array_get_string(Handle:hArray, iIndex, String:sBuffer[], maxlength) { + new Handle:hElement = json_array_get(hArray, iIndex); + + new iResult = -1; + if(json_is_string(hElement)) { + iResult = json_string_value(hElement, sBuffer, maxlength); + } + CloseHandle(hElement); + + return iResult; +} + +/** + * Returns the boolean value of the element in hObj at entry sKey. + * + * @param hObj Handle to JSON object to get a value from + * @param sKey Entry to retrieve + * + * @return True if it's a boolean and TRUE, + * false otherwise. + */ +stock bool:json_object_get_bool(Handle:hObj, const String:sKey[]) { + new Handle:hElement = json_object_get(hObj, sKey); + + new bool:bResult = (json_is_true(hElement) ? true : false); + + CloseHandle(hElement); + return bResult; +} + +/** + * Returns the float value of the element in hObj at entry sKey. + * + * @param hObj Handle to JSON object to get a value from + * @param sKey Position to retrieve + * + * @return Float value, + * or 0.0 if element is not a JSON Real. + */ +stock Float:json_object_get_float(Handle:hObj, const String:sKey[]) { + new Handle:hElement = json_object_get(hObj, sKey); + + new Float:fResult = (json_is_number(hElement) ? json_number_value(hElement) : 0.0); + + CloseHandle(hElement); + return fResult; +} + +/** + * Returns the integer value of the element in hObj at entry sKey. + * + * @param hObj Handle to JSON object to get a value from + * @param sKey Position to retrieve + * + * @return Integer value, + * or 0 if element is not a JSON Integer. + */ +stock json_object_get_int(Handle:hObj, const String:sKey[]) { + new Handle:hElement = json_object_get(hObj, sKey); + + new iResult = (json_is_integer(hElement) ? json_integer_value(hElement) : 0); + + CloseHandle(hElement); + return iResult; +} + +/** + * Saves the associated value of the element in hObj at entry sKey + * as a null terminated UTF-8 encoded string in the passed buffer. + * + * @param hObj Handle to JSON object to get a value from + * @param sKey Entry to retrieve + * @param sBuffer Buffer to store the value of the String. + * @param maxlength Maximum length of string buffer. + * + * @error Element is not a JSON String. + * @return Length of the returned string or -1 on error. + */ +stock json_object_get_string(Handle:hObj, const String:sKey[], String:sBuffer[], maxlength) { + new Handle:hElement = json_object_get(hObj, sKey); + + new iResult = -1; + if(json_is_string(hElement)) { + iResult = json_string_value(hElement, sBuffer, maxlength); + } + CloseHandle(hElement); + + return iResult; +} + + + +/** + * Pack String Rules + * + * Here�s the full list of format characters: + * n Output a JSON null value. No argument is consumed. + * s Output a JSON string, consuming one argument. + * b Output a JSON bool value, consuming one argument. + * i Output a JSON integer value, consuming one argument. + * f Output a JSON real value, consuming one argument. + * r Output a JSON real value, consuming one argument. + * [] Build an array with contents from the inner format string, + * recursive value building is supported. + * No argument is consumed. + * {} Build an array with contents from the inner format string. + * The first, third, etc. format character represent a key, + * and must be s (as object keys are always strings). The + * second, fourth, etc. format character represent a value. + * Recursive value building is supported. + * No argument is consumed. + * + */ + +/** + * This method can be used to create json objects/arrays directly + * without having to create the structure. + * See 'Pack String Rules' for more details. + * + * @param sPackString Pack string similiar to Format()s fmt. + * See 'Pack String Rules'. + * @param hParams ADT Array containing all keys and values + * in the order they appear in the pack string. + * + * @error Invalid pack string or pack string and + * ADT Array don't match up regarding type + * or size. + * @return Handle to JSON element. + */ +stock Handle:json_pack(const String:sPackString[], Handle:hParams) { + new iPos = 0; + return json_pack_element_(sPackString, iPos, hParams); +} + + + + + +/** +* Internal stocks used by json_pack(). Don't use these directly! +* +*/ +stock Handle:json_pack_array_(const String:sFormat[], &iPos, Handle:hParams) { + new Handle:hObj = json_array(); + new iStrLen = strlen(sFormat); + for(; iPos < iStrLen;) { + new this_char = sFormat[iPos]; + + if(this_char == 32 || this_char == 58 || this_char == 44) { + // Skip whitespace, ',' and ':' + iPos++; + continue; + } + + if(this_char == 93) { + // array end + iPos++; + break; + } + + // Get the next entry as value + // This automatically increments the position! + new Handle:hValue = json_pack_element_(sFormat, iPos, hParams); + + // Append the value to the array. + json_array_append_new(hObj, hValue); + } + + return hObj; +} + +stock Handle:json_pack_object_(const String:sFormat[], &iPos, Handle:hParams) { + new Handle:hObj = json_object(); + new iStrLen = strlen(sFormat); + for(; iPos < iStrLen;) { + new this_char = sFormat[iPos]; + + if(this_char == 32 || this_char == 58 || this_char == 44) { + // Skip whitespace, ',' and ':' + iPos++; + continue; + } + + if(this_char == 125) { + // } --> object end + iPos++; + break; + } + + if(this_char != 115) { + LogError("Object keys must be strings at %d.", iPos); + return INVALID_HANDLE; + } + + // Get the key string for this object from + // the hParams array. + decl String:sKey[255]; + GetArrayString(hParams, 0, sKey, sizeof(sKey)); + RemoveFromArray(hParams, 0); + + // Advance one character in the pack string, + // because we've just read the Key string for + // this object. + iPos++; + + // Get the next entry as value + // This automatically increments the position! + new Handle:hValue = json_pack_element_(sFormat, iPos, hParams); + + // Insert into object + json_object_set_new(hObj, sKey, hValue); + } + + return hObj; +} + +stock Handle:json_pack_element_(const String:sFormat[], &iPos, Handle:hParams) { + new this_char = sFormat[iPos]; + while(this_char == 32 || this_char == 58 || this_char == 44) { + iPos++; + this_char = sFormat[iPos]; + } + + // Advance one character in the pack string + iPos++; + + switch(this_char) { + case 91: { + // { --> Array + return json_pack_array_(sFormat, iPos, hParams); + } + + case 123: { + // { --> Object + return json_pack_object_(sFormat, iPos, hParams); + + } + + case 98: { + // b --> Boolean + new iValue = GetArrayCell(hParams, 0); + RemoveFromArray(hParams, 0); + + return json_boolean(bool:iValue); + } + + case 102, 114: { + // r,f --> Real (Float) + new Float:iValue = GetArrayCell(hParams, 0); + RemoveFromArray(hParams, 0); + + return json_real(iValue); + } + + case 110: { + // n --> NULL + return json_null(); + } + + case 115: { + // s --> String + decl String:sKey[255]; + GetArrayString(hParams, 0, sKey, sizeof(sKey)); + RemoveFromArray(hParams, 0); + + return json_string(sKey); + } + + case 105: { + // i --> Integer + new iValue = GetArrayCell(hParams, 0); + RemoveFromArray(hParams, 0); + + return json_integer(iValue); + } + } + + SetFailState("Invalid pack String '%s'. Type '%s' not supported at %i", sFormat, this_char, iPos); + return json_null(); +} + + + + + +/** + * Not yet implemented + * + * native json_object_foreach(Handle:hObj, ForEachCallback:cb); + * native Handle:json_unpack(const String:sFormat[], ...); + * + */ + + + + + + +/** + * Do not edit below this line! + */ +public Extension:__ext_smjansson = +{ + name = "SMJansson", + file = "smjansson.ext", +#if defined AUTOLOAD_EXTENSIONS + autoload = 1, +#else + autoload = 0, +#endif +#if defined REQUIRE_EXTENSIONS + required = 1, +#else + required = 0, +#endif +}; + +#if !defined REQUIRE_EXTENSIONS +public __ext_smjansson_SetNTVOptional() +{ + MarkNativeAsOptional("json_typeof"); + MarkNativeAsOptional("json_equal"); + + MarkNativeAsOptional("json_copy"); + MarkNativeAsOptional("json_deep_copy"); + + MarkNativeAsOptional("json_object"); + MarkNativeAsOptional("json_object_size"); + MarkNativeAsOptional("json_object_get"); + MarkNativeAsOptional("json_object_set"); + MarkNativeAsOptional("json_object_set_new"); + MarkNativeAsOptional("json_object_del"); + MarkNativeAsOptional("json_object_clear"); + MarkNativeAsOptional("json_object_update"); + MarkNativeAsOptional("json_object_update_existing"); + MarkNativeAsOptional("json_object_update_missing"); + + MarkNativeAsOptional("json_object_iter"); + MarkNativeAsOptional("json_object_iter_at"); + MarkNativeAsOptional("json_object_iter_next"); + MarkNativeAsOptional("json_object_iter_key"); + MarkNativeAsOptional("json_object_iter_value"); + MarkNativeAsOptional("json_object_iter_set"); + MarkNativeAsOptional("json_object_iter_set_new"); + + MarkNativeAsOptional("json_array"); + MarkNativeAsOptional("json_array_size"); + MarkNativeAsOptional("json_array_get"); + MarkNativeAsOptional("json_array_set"); + MarkNativeAsOptional("json_array_set_new"); + MarkNativeAsOptional("json_array_append"); + MarkNativeAsOptional("json_array_append_new"); + MarkNativeAsOptional("json_array_insert"); + MarkNativeAsOptional("json_array_insert_new"); + MarkNativeAsOptional("json_array_remove"); + MarkNativeAsOptional("json_array_clear"); + MarkNativeAsOptional("json_array_extend"); + + MarkNativeAsOptional("json_string"); + MarkNativeAsOptional("json_string_value"); + MarkNativeAsOptional("json_string_set"); + + MarkNativeAsOptional("json_integer"); + MarkNativeAsOptional("json_integer_value"); + MarkNativeAsOptional("json_integer_set"); + + MarkNativeAsOptional("json_real"); + MarkNativeAsOptional("json_real_value"); + MarkNativeAsOptional("json_real_set"); + MarkNativeAsOptional("json_number_value"); + + MarkNativeAsOptional("json_boolean"); + MarkNativeAsOptional("json_true"); + MarkNativeAsOptional("json_false"); + MarkNativeAsOptional("json_null"); + + MarkNativeAsOptional("json_load"); + MarkNativeAsOptional("json_load_file"); + + MarkNativeAsOptional("json_dump"); + MarkNativeAsOptional("json_dump_file"); +} +#endif diff --git a/sourcemod/scripting/include/sourcebanspp.inc b/sourcemod/scripting/include/sourcebanspp.inc new file mode 100644 index 0000000..c984bff --- /dev/null +++ b/sourcemod/scripting/include/sourcebanspp.inc @@ -0,0 +1,106 @@ +// ************************************************************************* +// This file is part of SourceBans++. +// +// Copyright (C) 2014-2019 SourceBans++ Dev Team <https://github.com/sbpp> +// +// SourceBans++ is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, per version 3 of the License. +// +// SourceBans++ 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 SourceBans++. If not, see <http://www.gnu.org/licenses/>. +// +// This file based off work(s) covered by the following copyright(s): +// +// SourceBans 1.4.11 +// Copyright (C) 2007-2015 SourceBans Team - Part of GameConnect +// Licensed under GNU GPL version 3, or later. +// Page: <http://www.sourcebans.net/> - <https://github.com/GameConnect/sourcebansv1> +// +// ************************************************************************* + +#if defined _sourcebanspp_included +#endinput +#endif +#define _sourcebanspp_included + +public SharedPlugin __pl_sourcebanspp = +{ + name = "sourcebans++", + file = "sbpp_main.smx", + #if defined REQUIRE_PLUGIN + required = 1 + #else + required = 0 + #endif +}; + +#if !defined REQUIRE_PLUGIN +public void __pl_sourcebanspp_SetNTVOptional() +{ + MarkNativeAsOptional("SBBanPlayer"); + MarkNativeAsOptional("SBPP_BanPlayer"); + MarkNativeAsOptional("SBPP_ReportPlayer"); +} +#endif + + +/********************************************************* + * Ban Player from server + * + * @param iAdmin The client index of the admin who is banning the client + * @param iTarget The client index of the player to ban + * @param iTime The time to ban the player for (in minutes, 0 = permanent) + * @param sReason The reason to ban the player from the server + * @noreturn + *********************************************************/ +#pragma deprecated Use SBPP_BanPlayer() instead. +native void SBBanPlayer(int iAdmin, int iTarget, int iTime, const char[] sReason); + +/********************************************************* + * Ban Player from server + * + * @param iAdmin The client index of the admin who is banning the client + * @param iTarget The client index of the player to ban + * @param iTime The time to ban the player for (in minutes, 0 = permanent) + * @param sReason The reason to ban the player from the server + * @noreturn + *********************************************************/ +native void SBPP_BanPlayer(int iAdmin, int iTarget, int iTime, const char[] sReason); + +/********************************************************* + * Reports a player + * + * @param iReporter The client index of the reporter + * @param iTarget The client index of the player to report + * @param sReason The reason to report the player + * @noreturn + *********************************************************/ +native void SBPP_ReportPlayer(int iReporter, int iTarget, const char[] sReason); + +/********************************************************* + * Called when the admin banning the player. + * + * @param iAdmin The client index of the admin who is banning the client + * @param iTarget The client index of the player to ban + * @param iTime The time to ban the player for (in minutes, 0 = permanent) + * @param sReason The reason to ban the player from the server + *********************************************************/ +forward void SBPP_OnBanPlayer(int iAdmin, int iTarget, int iTime, const char[] sReason); + +/********************************************************* + * Called when a new report is inserted + * + * @param iReporter The client index of the reporter + * @param iTarget The client index of the player to report + * @param sReason The reason to report the player + * @noreturn + *********************************************************/ +forward void SBPP_OnReportPlayer(int iReporter, int iTarget, const char[] sReason); + +//Yarr! diff --git a/sourcemod/scripting/include/sourcemod-colors.inc b/sourcemod/scripting/include/sourcemod-colors.inc new file mode 100644 index 0000000..66bc97b --- /dev/null +++ b/sourcemod/scripting/include/sourcemod-colors.inc @@ -0,0 +1,921 @@ +#if defined _sourcemod_colors_included + #endinput +#endif +#define _sourcemod_colors_included "1.0" + +/* +* _____ _ _____ _ +* / ____| | | / ____| | | +* | (___ ___ _ _ _ __ ___ ___ _ __ ___ ___ __| | | | ___ | | ___ _ __ ___ +* \___ \ / _ \| | | | '__/ __/ _ \ '_ ` _ \ / _ \ / _` | | | / _ \| |/ _ \| '__/ __| +* ____) | (_) | |_| | | | (_| __/ | | | | | (_) | (_| | | |___| (_) | | (_) | | \__ \ +* |_____/ \___/ \__,_|_| \___\___|_| |_| |_|\___/ \__,_| \_____\___/|_|\___/|_| |___/ +* +* +* - Author: Keith Warren (Drixevel) +* - Original By: Raska aka KissLick (ColorVariables) +* +* This is meant to be a drop-in replacement for every Source Engine game to add colors to chat and more cheat features. +*/ + +// ---------------------------------------------------------------------------------------- +#define MAX_BUFFER_SIZE 1024 + +static bool g_bInit; +static StringMap g_hColors; +static char g_sChatPrefix[64]; + +static bool g_bIgnorePrefix; +static int g_iAuthor; +static bool g_bSkipPlayers[MAXPLAYERS + 1]; +// ---------------------------------------------------------------------------------------- + +/* +* Sets the prefix for all chat prints to use. +* +* prefix - String to use. +* any - Extra Parameters +* +* Return - N/A +*/ +stock void CSetPrefix(const char[] prefix, any ...) +{ + VFormat(g_sChatPrefix, sizeof(g_sChatPrefix), prefix, 2); +} + +/* +* Setup the next print to skip using the prefix. +* +* +* Return - N/A +*/ +stock void CSkipNextPrefix() +{ + g_bIgnorePrefix = true; +} + +/* +* Sets the author for the next print. (Mostly applies colors) +* +* client - Author index. +* +* Return - N/A +*/ +stock void CSetNextAuthor(int client) +{ + if (client < 1 || client > MaxClients || !IsClientInGame(client)) + ThrowError("Invalid client index %i", client); + + g_iAuthor = client; +} + +/* +* Setup the next chat print to not be sent to this client. +* +* client - Client index. +* +* Return - N/A +*/ +stock void CSkipNextClient(int client) +{ + if (client < 1 || client > MaxClients) + ThrowError("Invalid client index %i", client); + + g_bSkipPlayers[client] = true; +} + +/* +* Sends a chat print to the client. +* +* client - Client index. +* message - Message string. +* any - Extra Parameters +* +* Return - N/A +*/ +stock void CPrintToChat(int client, const char[] message, any ...) +{ + if ((client < 1 || client > MaxClients || !IsClientInGame(client) || IsFakeClient(client)) && !IsClientSourceTV(client)) + return; + + SetGlobalTransTarget(client); + + char buffer[MAX_BUFFER_SIZE]; + VFormat(buffer, sizeof(buffer), message, 3); + + AddPrefixAndDefaultColor(buffer, sizeof(buffer)); + g_bIgnorePrefix = false; + + CProcessVariables(buffer, sizeof(buffer)); + CAddWhiteSpace(buffer, sizeof(buffer)); + + SendPlayerMessage(client, buffer, g_iAuthor); + g_iAuthor = 0; +} + +/* +* Sends a chat print to the client with a specified author. +* +* client - Client index. +* author - Author index. +* message - Message string. +* any - Extra Parameters +* +* Return - N/A +*/ +stock void CPrintToChatEx(int client, int author, const char[] message, any ...) +{ + CSetNextAuthor(author); + char buffer[MAX_BUFFER_SIZE]; + VFormat(buffer, sizeof(buffer), message, 4); + CPrintToChat(client, buffer); +} + +/* +* Sends a chat print to all clients. +* +* message - Message string. +* any - Extra Parameters +* +* Return - N/A +*/ +stock void CPrintToChatAll(const char[] message, any ...) +{ + char buffer[MAX_BUFFER_SIZE]; + + for (int client = 1; client <= MaxClients; client++) + { + if (!IsClientInGame(client) || g_bSkipPlayers[client]) + { + g_bSkipPlayers[client] = false; + continue; + } + + SetGlobalTransTarget(client); + + VFormat(buffer, sizeof(buffer), message, 2); + + AddPrefixAndDefaultColor(buffer, sizeof(buffer)); + g_bIgnorePrefix = false; + + CProcessVariables(buffer, sizeof(buffer)); + CAddWhiteSpace(buffer, sizeof(buffer)); + + SendPlayerMessage(client, buffer, g_iAuthor); + } + + g_iAuthor = 0; +} + +/* +* Sends a chat print to all clients with a specified author. +* +* author - Author index. +* message - Message string. +* any - Extra Parameters +* +* Return - N/A +*/ +stock void CPrintToChatAllEx(int author, const char[] message, any ...) +{ + CSetNextAuthor(author); + char buffer[MAX_BUFFER_SIZE]; + VFormat(buffer, sizeof(buffer), message, 3); + CPrintToChatAll(buffer); +} + +/* +* Sends a chat print to a specified team. +* +* team - Team index. +* message - Message string. +* any - Extra Parameters +* +* Return - N/A +*/ +stock void CPrintToChatTeam(int team, const char[] message, any ...) +{ + char buffer[MAX_BUFFER_SIZE]; + + for (int client = 1; client <= MaxClients; client++) + { + if (!IsClientInGame(client) || GetClientTeam(client) != team || g_bSkipPlayers[client]) + { + g_bSkipPlayers[client] = false; + continue; + } + + SetGlobalTransTarget(client); + VFormat(buffer, sizeof(buffer), message, 3); + + AddPrefixAndDefaultColor(buffer, sizeof(buffer)); + g_bIgnorePrefix = false; + + CProcessVariables(buffer, sizeof(buffer)); + CAddWhiteSpace(buffer, sizeof(buffer)); + + SendPlayerMessage(client, buffer, g_iAuthor); + } + + g_iAuthor = 0; +} + +/* +* Sends a chat print to a specified team with a specified author. +* +* team - Team index. +* message - Message string. +* any - Extra Parameters +* +* Return - N/A +*/ +stock void CPrintToChatTeamEx(int team, int author, const char[] message, any ...) +{ + CSetNextAuthor(author); + char buffer[MAX_BUFFER_SIZE]; + VFormat(buffer, sizeof(buffer), message, 4); + CPrintToChatTeam(team, buffer); +} + +/* +* Sends a chat print to available admins. +* Example for bitflags: (ADMFLAG_RESERVATION | ADMFLAG_GENERIC) +* +* bitflags - Bit Flags. +* message - Message string. +* any - Extra Parameters +* +* Return - N/A +*/ +stock void CPrintToChatAdmins(int bitflags, const char[] message, any ...) +{ + char buffer[MAX_BUFFER_SIZE]; + AdminId iAdminID; + + for (int client = 1; client <= MaxClients; client++) + { + if (!IsClientInGame(client) || g_bSkipPlayers[client]) + { + g_bSkipPlayers[client] = false; + continue; + } + + iAdminID = GetUserAdmin(client); + + if (iAdminID == INVALID_ADMIN_ID || !(GetAdminFlags(iAdminID, Access_Effective) & bitflags)) + continue; + + SetGlobalTransTarget(client); + VFormat(buffer, sizeof(buffer), message, 3); + + AddPrefixAndDefaultColor(buffer, sizeof(buffer)); + g_bIgnorePrefix = false; + + CProcessVariables(buffer, sizeof(buffer)); + CAddWhiteSpace(buffer, sizeof(buffer)); + + SendPlayerMessage(client, buffer, g_iAuthor); + } + + g_iAuthor = 0; +} + +/* +* Sends a chat print to available admins with a specified author. +* Example for bitflags: (ADMFLAG_RESERVATION | ADMFLAG_GENERIC) +* +* bitflags - Bit Flags. +* message - Message string. +* any - Extra Parameters +* +* Return - N/A +*/ +stock void CPrintToChatAdminsEx(int bitflags, int author, const char[] message, any ...) +{ + CSetNextAuthor(author); + char buffer[MAX_BUFFER_SIZE]; + VFormat(buffer, sizeof(buffer), message, 4); + CPrintToChatTeam(bitflags, buffer); +} + +/* +* Sends a reply message to the client. (This is useful because it works for console as well) +* +* client - Client index. +* message - Message string. +* any - Extra Parameters +* +* Return - N/A +*/ +stock void CReplyToCommand(int client, const char[] message, any ...) +{ + if (client < 0 || client > MaxClients) + ThrowError("Invalid client index %d", client); + + if (client != 0 && !IsClientInGame(client)) + ThrowError("Client %d is not in game", client); + + char buffer[MAX_BUFFER_SIZE]; + SetGlobalTransTarget(client); + VFormat(buffer, sizeof(buffer), message, 3); + + AddPrefixAndDefaultColor(buffer, sizeof(buffer), "engine 1"); + g_bIgnorePrefix = false; + + if (GetCmdReplySource() == SM_REPLY_TO_CONSOLE) + { + CRemoveColors(buffer, sizeof(buffer)); + PrintToConsole(client, "%s", buffer); + } + else + CPrintToChat(client, "%s", buffer); +} + +/* +* Displays usage of an admin command to users depending on the setting of the sm_show_activity cvar. +* This version does not display a message to the originating client if used from chat triggers or menus. +* If manual replies are used for these cases, then this function will suffice. +* Otherwise, ShowActivity2() is slightly more useful. +* +* client - Client index. +* message - Message string. +* any - Extra Parameters +* +* Return - N/A +*/ +stock void CShowActivity(int client, const char[] message, any ...) +{ + if (client < 0 || client > MaxClients) + ThrowError("Invalid client index %d", client); + + if (client != 0 && !IsClientInGame(client)) + ThrowError("Client %d is not in game", client); + + char buffer[MAX_BUFFER_SIZE]; + SetGlobalTransTarget(client); + VFormat(buffer, sizeof(buffer), message, 3); + Format(buffer, sizeof(buffer), "{engine 1}%s", buffer); + CProcessVariables(buffer, sizeof(buffer)); + CAddWhiteSpace(buffer, sizeof(buffer)); + + ShowActivity(client, "%s", buffer); +} + +/* +* Displays usage of an admin command to users depending on the setting of the sm_show_activity cvar. +* All users receive a message in their chat text, except for the originating client, who receives the message based on the current ReplySource. +* +* client - Client index. +* tag - Tag to show. +* message - Message string. +* any - Extra Parameters +* +* Return - N/A +*/ +stock void CShowActivityEx(int client, const char[] tag, const char[] message, any ...) +{ + if (client < 0 || client > MaxClients) + ThrowError("Invalid client index %d", client); + + if (client != 0 && !IsClientInGame(client)) + ThrowError("Client %d is not in game", client); + + char buffer[MAX_BUFFER_SIZE]; char sBufferTag[MAX_BUFFER_SIZE]; + SetGlobalTransTarget(client); + VFormat(buffer, sizeof(buffer), message, 4); + Format(buffer, sizeof(buffer), "{engine 1}%s", buffer); + CProcessVariables(buffer, sizeof(buffer)); + Format(sBufferTag, sizeof(sBufferTag), "{prefix}%s", tag); + CProcessVariables(sBufferTag, sizeof(sBufferTag)); + CAddWhiteSpace(buffer, sizeof(buffer)); + CAddWhiteSpace(sBufferTag, sizeof(sBufferTag)); + + ShowActivityEx(client, sBufferTag, " %s", buffer); +} + +/* +* Same as ShowActivity(), except the tag parameter is used instead of "[SM] " (note that you must supply any spacing). +* +* client - Client index. +* tag - Tag to show. +* message - Message string. +* any - Extra Parameters +* +* Return - N/A +*/ +stock void CShowActivity2(int client, const char[] tag, const char[] message, any ...) +{ + if (client < 0 || client > MaxClients) + ThrowError("Invalid client index %d", client); + + if (client != 0 && !IsClientInGame(client)) + ThrowError("Client %d is not in game", client); + + char buffer[MAX_BUFFER_SIZE]; char sBufferTag[MAX_BUFFER_SIZE]; + SetGlobalTransTarget(client); + VFormat(buffer, sizeof(buffer), message, 4); + Format(buffer, sizeof(buffer), "{engine 2}%s", buffer); + CProcessVariables(buffer, sizeof(buffer)); + Format(sBufferTag, sizeof(sBufferTag), "{prefix}%s", tag); + CProcessVariables(sBufferTag, sizeof(sBufferTag)); + CAddWhiteSpace(buffer, sizeof(buffer)); + CAddWhiteSpace(sBufferTag, sizeof(sBufferTag)); + + ShowActivityEx(client, sBufferTag, " %s", buffer); +} + +/* +* Strips all colors from the specified string. +* +* msg - String buffer. +* size - Size of the string. +* +* Return - N/A +*/ +stock void CRemoveColors(char[] msg, int size) +{ + CProcessVariables(msg, size, true); +} + +/* +* Processes colors in a string by replacing found tags with color/hex codes. +* +* msg - String buffer. +* size - Size of the string. +* removecolors - Whether to remove colors or keep them. (same as CRemoveColors) +* +* Return - N/A +*/ +stock void CProcessVariables(char[] msg, int size, bool removecolors = false) +{ + Init(); + + char[] sOut = new char[size]; char[] sCode = new char[size]; char[] color = new char[size]; + int iOutPos = 0; int iCodePos = -1; + int iMsgLen = strlen(msg); + + for (int i = 0; i < iMsgLen; i++) + { + if (msg[i] == '{') + iCodePos = 0; + + if (iCodePos > -1) + { + sCode[iCodePos] = msg[i]; + sCode[iCodePos + 1] = '\0'; + + if (msg[i] == '}' || i == iMsgLen - 1) + { + strcopy(sCode, strlen(sCode) - 1, sCode[1]); + StringToLower(sCode); + + if (CGetColor(sCode, color, size)) + { + if (!removecolors) + { + StrCat(sOut, size, color); + iOutPos += strlen(color); + } + } + else + { + Format(sOut, size, "%s{%s}", sOut, sCode); + iOutPos += strlen(sCode) + 2; + } + + iCodePos = -1; + strcopy(sCode, size, ""); + strcopy(color, size, ""); + } + else + iCodePos++; + + continue; + } + + sOut[iOutPos] = msg[i]; + iOutPos++; + sOut[iOutPos] = '\0'; + } + + strcopy(msg, size, sOut); +} + +/* +* Retrieves the color/hex code for a specified color name. +* +* name - Color to search for. +* color - String buffer. +* size - Size of the string. +* +* Return - True if found, false otherwise. +*/ +stock bool CGetColor(const char[] name, char[] color, int size) +{ + if (name[0] == '\0') + return false; + + if (name[0] == '@') + { + int iSpace; + char sData[64]; char m_sName[64]; + strcopy(m_sName, sizeof(m_sName), name[1]); + + if ((iSpace = FindCharInString(m_sName, ' ')) != -1 && (iSpace + 1 < strlen(m_sName))) + { + strcopy(m_sName, iSpace + 1, m_sName); + strcopy(sData, sizeof(sData), m_sName[iSpace + 1]); + } + + if (color[0] != '\0') + return true; + } + else if (name[0] == '#') + { + if (strlen(name) == 7) + { + Format(color, size, "\x07%s", name[1]); + return true; + } + + if (strlen(name) == 9) + { + Format(color, size, "\x08%s", name[1]); + return true; + } + } + else if (StrContains(name, "player ", false) == 0 && strlen(name) > 7) + { + int client = StringToInt(name[7]); + + if (client < 1 || client > MaxClients || !IsClientInGame(client)) + { + strcopy(color, size, "\x01"); + LogError("Invalid client index %d", client); + return false; + } + + strcopy(color, size, "\x01"); + + switch (GetClientTeam(client)) + { + case 1: g_hColors.GetString("engine 8", color, size); + case 2: g_hColors.GetString("engine 9", color, size); + case 3: g_hColors.GetString("engine 11", color, size); + } + + return true; + } + else + return g_hColors.GetString(name, color, size); + + return false; +} + +/* +* Checks if the specified color exists. +* +* name - Color to search for. +* +* Return - True if found, false otherwise. +*/ +stock bool CExistColor(const char[] name) +{ + if (name[0] == '\0' || name[0] == '@' || name[0] == '#') + return false; + + char color[64]; + return g_hColors.GetString(name, color, sizeof(color)); +} + +/* +* Sends a raw SayText2 usermsg to the specified client with settings. +* +* client - Client index. +* message - Message string. +* author - Author index. +* chat - "0 - raw text, 1 - sets CHAT_FILTER_PUBLICCHAT " +* +* Return - N/A +*/ +stock void CSayText2(int client, const char[] message, int author, bool chat = true) +{ + if (client < 1 || client > MaxClients) + return; + + Handle hMsg = StartMessageOne("SayText2", client, USERMSG_RELIABLE|USERMSG_BLOCKHOOKS); + if (GetFeatureStatus(FeatureType_Native, "GetUserMessageType") == FeatureStatus_Available && GetUserMessageType() == UM_Protobuf) + { + PbSetInt(hMsg, "ent_idx", author); + PbSetBool(hMsg, "chat", chat); + PbSetString(hMsg, "msg_name", message); + PbAddString(hMsg, "params", ""); + PbAddString(hMsg, "params", ""); + PbAddString(hMsg, "params", ""); + PbAddString(hMsg, "params", ""); + } + else + { + BfWriteByte(hMsg, author); + BfWriteByte(hMsg, true); + BfWriteString(hMsg, message); + } + + EndMessage(); +} + +/* +* Adds a space to the start a string buffer. +* +* buffer - String buffer. +* size - Size of the string. +* +* Return - N/A +*/ +stock void CAddWhiteSpace(char[] buffer, int size) +{ + if (!IsSource2009()) + Format(buffer, size, " %s", buffer); +} + +// ---------------------------------------------------------------------------------------- +// Private stuff +// ---------------------------------------------------------------------------------------- + +stock bool Init() +{ + if (g_bInit) + { + LoadColors(); + return true; + } + + for (int i = 1; i <= MaxClients; i++) + g_bSkipPlayers[i] = false; + + LoadColors(); + g_bInit = true; + + return true; +} + +stock void LoadColors() +{ + if (g_hColors == null) + g_hColors = new StringMap(); + else + g_hColors.Clear(); + + g_hColors.SetString("default", "\x01"); + g_hColors.SetString("teamcolor", "\x03"); + + if (IsSource2009()) + { + g_hColors.SetString("aliceblue", "\x07F0F8FF"); + g_hColors.SetString("allies", "\x074D7942"); + g_hColors.SetString("ancient", "\x07EB4B4B"); + g_hColors.SetString("antiquewhite", "\x07FAEBD7"); + g_hColors.SetString("aqua", "\x0700FFFF"); + g_hColors.SetString("aquamarine", "\x077FFFD4"); + g_hColors.SetString("arcana", "\x07ADE55C"); + g_hColors.SetString("axis", "\x07FF4040"); + g_hColors.SetString("azure", "\x07007FFF"); + g_hColors.SetString("beige", "\x07F5F5DC"); + g_hColors.SetString("bisque", "\x07FFE4C4"); + g_hColors.SetString("black", "\x07000000"); + g_hColors.SetString("blanchedalmond", "\x07FFEBCD"); + g_hColors.SetString("blue", "\x0799CCFF"); + g_hColors.SetString("blueviolet", "\x078A2BE2"); + g_hColors.SetString("brown", "\x07A52A2A"); + g_hColors.SetString("burlywood", "\x07DEB887"); + g_hColors.SetString("cadetblue", "\x075F9EA0"); + g_hColors.SetString("chartreuse", "\x077FFF00"); + g_hColors.SetString("chocolate", "\x07D2691E"); + g_hColors.SetString("collectors", "\x07AA0000"); + g_hColors.SetString("common", "\x07B0C3D9"); + g_hColors.SetString("community", "\x0770B04A"); + g_hColors.SetString("coral", "\x07FF7F50"); + g_hColors.SetString("cornflowerblue", "\x076495ED"); + g_hColors.SetString("cornsilk", "\x07FFF8DC"); + g_hColors.SetString("corrupted", "\x07A32C2E"); + g_hColors.SetString("crimson", "\x07DC143C"); + g_hColors.SetString("cyan", "\x0700FFFF"); + g_hColors.SetString("darkblue", "\x0700008B"); + g_hColors.SetString("darkcyan", "\x07008B8B"); + g_hColors.SetString("darkgoldenrod", "\x07B8860B"); + g_hColors.SetString("darkgray", "\x07A9A9A9"); + g_hColors.SetString("darkgrey", "\x07A9A9A9"); + g_hColors.SetString("darkgreen", "\x07006400"); + g_hColors.SetString("darkkhaki", "\x07BDB76B"); + g_hColors.SetString("darkmagenta", "\x078B008B"); + g_hColors.SetString("darkolivegreen", "\x07556B2F"); + g_hColors.SetString("darkorange", "\x07FF8C00"); + g_hColors.SetString("darkorchid", "\x079932CC"); + g_hColors.SetString("darkred", "\x078B0000"); + g_hColors.SetString("darksalmon", "\x07E9967A"); + g_hColors.SetString("darkseagreen", "\x078FBC8F"); + g_hColors.SetString("darkslateblue", "\x07483D8B"); + g_hColors.SetString("darkslategray", "\x072F4F4F"); + g_hColors.SetString("darkslategrey", "\x072F4F4F"); + g_hColors.SetString("darkturquoise", "\x0700CED1"); + g_hColors.SetString("darkviolet", "\x079400D3"); + g_hColors.SetString("deeppink", "\x07FF1493"); + g_hColors.SetString("deepskyblue", "\x0700BFFF"); + g_hColors.SetString("dimgray", "\x07696969"); + g_hColors.SetString("dimgrey", "\x07696969"); + g_hColors.SetString("dodgerblue", "\x071E90FF"); + g_hColors.SetString("exalted", "\x07CCCCCD"); + g_hColors.SetString("firebrick", "\x07B22222"); + g_hColors.SetString("floralwhite", "\x07FFFAF0"); + g_hColors.SetString("forestgreen", "\x07228B22"); + g_hColors.SetString("frozen", "\x074983B3"); + g_hColors.SetString("fuchsia", "\x07FF00FF"); + g_hColors.SetString("fullblue", "\x070000FF"); + g_hColors.SetString("fullred", "\x07FF0000"); + g_hColors.SetString("gainsboro", "\x07DCDCDC"); + g_hColors.SetString("genuine", "\x074D7455"); + g_hColors.SetString("ghostwhite", "\x07F8F8FF"); + g_hColors.SetString("gold", "\x07FFD700"); + g_hColors.SetString("goldenrod", "\x07DAA520"); + g_hColors.SetString("gray", "\x07CCCCCC"); + g_hColors.SetString("grey", "\x07CCCCCC"); + g_hColors.SetString("green", "\x073EFF3E"); + g_hColors.SetString("greenyellow", "\x07ADFF2F"); + g_hColors.SetString("haunted", "\x0738F3AB"); + g_hColors.SetString("honeydew", "\x07F0FFF0"); + g_hColors.SetString("hotpink", "\x07FF69B4"); + g_hColors.SetString("immortal", "\x07E4AE33"); + g_hColors.SetString("indianred", "\x07CD5C5C"); + g_hColors.SetString("indigo", "\x074B0082"); + g_hColors.SetString("ivory", "\x07FFFFF0"); + g_hColors.SetString("khaki", "\x07F0E68C"); + g_hColors.SetString("lavender", "\x07E6E6FA"); + g_hColors.SetString("lavenderblush", "\x07FFF0F5"); + g_hColors.SetString("lawngreen", "\x077CFC00"); + g_hColors.SetString("legendary", "\x07D32CE6"); + g_hColors.SetString("lemonchiffon", "\x07FFFACD"); + g_hColors.SetString("lightblue", "\x07ADD8E6"); + g_hColors.SetString("lightcoral", "\x07F08080"); + g_hColors.SetString("lightcyan", "\x07E0FFFF"); + g_hColors.SetString("lightgoldenrodyellow", "\x07FAFAD2"); + g_hColors.SetString("lightgray", "\x07D3D3D3"); + g_hColors.SetString("lightgrey", "\x07D3D3D3"); + g_hColors.SetString("lightgreen", "\x0799FF99"); + g_hColors.SetString("lightpink", "\x07FFB6C1"); + g_hColors.SetString("lightsalmon", "\x07FFA07A"); + g_hColors.SetString("lightseagreen", "\x0720B2AA"); + g_hColors.SetString("lightskyblue", "\x0787CEFA"); + g_hColors.SetString("lightslategray", "\x07778899"); + g_hColors.SetString("lightslategrey", "\x07778899"); + g_hColors.SetString("lightsteelblue", "\x07B0C4DE"); + g_hColors.SetString("lightyellow", "\x07FFFFE0"); + g_hColors.SetString("lime", "\x0700FF00"); + g_hColors.SetString("limegreen", "\x0732CD32"); + g_hColors.SetString("linen", "\x07FAF0E6"); + g_hColors.SetString("magenta", "\x07FF00FF"); + g_hColors.SetString("maroon", "\x07800000"); + g_hColors.SetString("mediumaquamarine", "\x0766CDAA"); + g_hColors.SetString("mediumblue", "\x070000CD"); + g_hColors.SetString("mediumorchid", "\x07BA55D3"); + g_hColors.SetString("mediumpurple", "\x079370D8"); + g_hColors.SetString("mediumseagreen", "\x073CB371"); + g_hColors.SetString("mediumslateblue", "\x077B68EE"); + g_hColors.SetString("mediumspringgreen", "\x0700FA9A"); + g_hColors.SetString("mediumturquoise", "\x0748D1CC"); + g_hColors.SetString("mediumvioletred", "\x07C71585"); + g_hColors.SetString("midnightblue", "\x07191970"); + g_hColors.SetString("mintcream", "\x07F5FFFA"); + g_hColors.SetString("mistyrose", "\x07FFE4E1"); + g_hColors.SetString("moccasin", "\x07FFE4B5"); + g_hColors.SetString("mythical", "\x078847FF"); + g_hColors.SetString("navajowhite", "\x07FFDEAD"); + g_hColors.SetString("navy", "\x07000080"); + g_hColors.SetString("normal", "\x07B2B2B2"); + g_hColors.SetString("oldlace", "\x07FDF5E6"); + g_hColors.SetString("olive", "\x079EC34F"); + g_hColors.SetString("olivedrab", "\x076B8E23"); + g_hColors.SetString("orange", "\x07FFA500"); + g_hColors.SetString("orangered", "\x07FF4500"); + g_hColors.SetString("orchid", "\x07DA70D6"); + g_hColors.SetString("palegoldenrod", "\x07EEE8AA"); + g_hColors.SetString("palegreen", "\x0798FB98"); + g_hColors.SetString("paleturquoise", "\x07AFEEEE"); + g_hColors.SetString("palevioletred", "\x07D87093"); + g_hColors.SetString("papayawhip", "\x07FFEFD5"); + g_hColors.SetString("peachpuff", "\x07FFDAB9"); + g_hColors.SetString("peru", "\x07CD853F"); + g_hColors.SetString("pink", "\x07FFC0CB"); + g_hColors.SetString("plum", "\x07DDA0DD"); + g_hColors.SetString("powderblue", "\x07B0E0E6"); + g_hColors.SetString("purple", "\x07800080"); + g_hColors.SetString("rare", "\x074B69FF"); + g_hColors.SetString("red", "\x07FF4040"); + g_hColors.SetString("rosybrown", "\x07BC8F8F"); + g_hColors.SetString("royalblue", "\x074169E1"); + g_hColors.SetString("saddlebrown", "\x078B4513"); + g_hColors.SetString("salmon", "\x07FA8072"); + g_hColors.SetString("sandybrown", "\x07F4A460"); + g_hColors.SetString("seagreen", "\x072E8B57"); + g_hColors.SetString("seashell", "\x07FFF5EE"); + g_hColors.SetString("selfmade", "\x0770B04A"); + g_hColors.SetString("sienna", "\x07A0522D"); + g_hColors.SetString("silver", "\x07C0C0C0"); + g_hColors.SetString("skyblue", "\x0787CEEB"); + g_hColors.SetString("slateblue", "\x076A5ACD"); + g_hColors.SetString("slategray", "\x07708090"); + g_hColors.SetString("slategrey", "\x07708090"); + g_hColors.SetString("snow", "\x07FFFAFA"); + g_hColors.SetString("springgreen", "\x0700FF7F"); + g_hColors.SetString("steelblue", "\x074682B4"); + g_hColors.SetString("strange", "\x07CF6A32"); + g_hColors.SetString("tan", "\x07D2B48C"); + g_hColors.SetString("teal", "\x07008080"); + g_hColors.SetString("thistle", "\x07D8BFD8"); + g_hColors.SetString("tomato", "\x07FF6347"); + g_hColors.SetString("turquoise", "\x0740E0D0"); + g_hColors.SetString("uncommon", "\x07B0C3D9"); + g_hColors.SetString("unique", "\x07FFD700"); + g_hColors.SetString("unusual", "\x078650AC"); + g_hColors.SetString("valve", "\x07A50F79"); + g_hColors.SetString("vintage", "\x07476291"); + g_hColors.SetString("violet", "\x07EE82EE"); + g_hColors.SetString("wheat", "\x07F5DEB3"); + g_hColors.SetString("white", "\x07FFFFFF"); + g_hColors.SetString("whitesmoke", "\x07F5F5F5"); + g_hColors.SetString("yellow", "\x07FFFF00"); + g_hColors.SetString("yellowgreen", "\x079ACD32"); + } + else + { + g_hColors.SetString("red", "\x07"); + g_hColors.SetString("lightred", "\x0F"); + g_hColors.SetString("darkred", "\x02"); + g_hColors.SetString("bluegrey", "\x0A"); + g_hColors.SetString("blue", "\x0B"); + g_hColors.SetString("darkblue", "\x0C"); + g_hColors.SetString("purple", "\x03"); + g_hColors.SetString("orchid", "\x0E"); + g_hColors.SetString("yellow", "\x09"); + g_hColors.SetString("gold", "\x10"); + g_hColors.SetString("lightgreen", "\x05"); + g_hColors.SetString("green", "\x04"); + g_hColors.SetString("lime", "\x06"); + g_hColors.SetString("grey", "\x08"); + g_hColors.SetString("grey2", "\x0D"); + } + + g_hColors.SetString("engine 1", "\x01"); + g_hColors.SetString("engine 2", "\x02"); + g_hColors.SetString("engine 3", "\x03"); + g_hColors.SetString("engine 4", "\x04"); + g_hColors.SetString("engine 5", "\x05"); + g_hColors.SetString("engine 6", "\x06"); + g_hColors.SetString("engine 7", "\x07"); + g_hColors.SetString("engine 8", "\x08"); + g_hColors.SetString("engine 9", "\x09"); + g_hColors.SetString("engine 10", "\x0A"); + g_hColors.SetString("engine 11", "\x0B"); + g_hColors.SetString("engine 12", "\x0C"); + g_hColors.SetString("engine 13", "\x0D"); + g_hColors.SetString("engine 14", "\x0E"); + g_hColors.SetString("engine 15", "\x0F"); + g_hColors.SetString("engine 16", "\x10"); +} + +stock bool HasBrackets(const char[] sSource) +{ + return (sSource[0] == '{' && sSource[strlen(sSource) - 1] == '}'); +} + +stock void StringToLower(char[] sSource) +{ + for (int i = 0; i < strlen(sSource); i++) + { + if (sSource[i] == '\0') + break; + + sSource[i] = CharToLower(sSource[i]); + } +} + +stock bool IsSource2009() +{ + EngineVersion iEngineVersion = GetEngineVersion(); + return (iEngineVersion == Engine_CSS || iEngineVersion == Engine_TF2 || iEngineVersion == Engine_HL2DM || iEngineVersion == Engine_DODS); +} + +stock void AddPrefixAndDefaultColor(char[] message, int size, char[] sDefaultColor = "engine 1", char[] sPrefixColor = "engine 2") +{ + if (g_sChatPrefix[0] != '\0' && !g_bIgnorePrefix) + Format(message, size, "{%s}[%s]{%s} %s", sPrefixColor, g_sChatPrefix, sDefaultColor, message); + else + Format(message, size, "{%s}%s", sDefaultColor, message); +} + +stock void SendPlayerMessage(int client, char[] message, int author = 0) +{ + if (author > 0 && author <= MaxClients && IsClientInGame(author)) + CSayText2(client, message, author); + else + PrintToChat(client, message); +}
\ No newline at end of file diff --git a/sourcemod/scripting/include/updater.inc b/sourcemod/scripting/include/updater.inc new file mode 100644 index 0000000..f37bdf2 --- /dev/null +++ b/sourcemod/scripting/include/updater.inc @@ -0,0 +1,97 @@ +#if defined _updater_included + #endinput +#endif +#define _updater_included + +/** + * Adds your plugin to the updater. The URL will be updated if + * your plugin was previously added. + * + * @param url URL to your plugin's update file. + * @noreturn + */ +native Updater_AddPlugin(const String:url[]); + +/** + * Removes your plugin from the updater. This does not need to + * be called during OnPluginEnd. + * + * @noreturn + */ +native Updater_RemovePlugin(); + +/** + * Forces your plugin to be checked for updates. The behaviour + * of the update is dependant on the server's configuration. + * + * @return True if an update was triggered. False otherwise. + * @error Plugin not found in updater. + */ +native bool:Updater_ForceUpdate(); + +/** + * Called when your plugin is about to be checked for updates. + * + * @return Plugin_Handled to prevent checking, Plugin_Continue to allow it. + */ +forward Action:Updater_OnPluginChecking(); + +/** + * Called when your plugin is about to begin downloading an available update. + * + * @return Plugin_Handled to prevent downloading, Plugin_Continue to allow it. + */ +forward Action:Updater_OnPluginDownloading(); + +/** + * Called when your plugin's update files have been fully downloaded + * and are about to write to their proper location. This should be used + * to free read-only resources that require write access for your update. + * + * @note OnPluginUpdated will be called later during the same frame. + * + * @noreturn + */ +forward Updater_OnPluginUpdating(); + +/** + * Called when your plugin's update has been completed. It is safe + * to reload your plugin at this time. + * + * @noreturn + */ +forward Updater_OnPluginUpdated(); + +/** + * @brief Reloads a plugin. + * + * @param plugin Plugin Handle (INVALID_HANDLE uses the calling plugin). + * @noreturn + */ +stock ReloadPlugin(Handle:plugin=INVALID_HANDLE) +{ + decl String:filename[64]; + GetPluginFilename(plugin, filename, sizeof(filename)); + ServerCommand("sm plugins reload %s", filename); +} + + +public SharedPlugin:__pl_updater = +{ + name = "updater", + file = "updater.smx", +#if defined REQUIRE_PLUGIN + required = 1, +#else + required = 0, +#endif +}; + +#if !defined REQUIRE_PLUGIN +public __pl_updater_SetNTVOptional() +{ + MarkNativeAsOptional("Updater_AddPlugin"); + MarkNativeAsOptional("Updater_RemovePlugin"); + MarkNativeAsOptional("Updater_ForceUpdate"); +} +#endif |
