From d563ecb06078e3d2b70346bea4670124c2f9a54b Mon Sep 17 00:00:00 2001 From: Steve Dudenhoeffer Date: Tue, 24 Apr 2007 16:38:36 +0000 Subject: [PATCH] Added pseudo dynamic array natives. Changed some of the "..." tags to "any". --- amxmodx/Makefile | 2 +- amxmodx/amxmodx.h | 1 + amxmodx/datastructs.cpp | 607 ++++++++++++++++++++++++++++++++ amxmodx/datastructs.h | 323 +++++++++++++++++ amxmodx/format.cpp | 16 + amxmodx/meta_api.cpp | 7 + amxmodx/modules.cpp | 1 + amxmodx/msvc8/amxmodx_mm.vcproj | 8 + plugins/admin.sma | 7 + plugins/include/amxmodx.inc | 27 +- plugins/include/array.inc | 250 +++++++++++++ plugins/include/engine.inc | 8 +- plugins/include/fakemeta.inc | 10 +- plugins/include/file.inc | 4 +- plugins/include/ns2amx.inc | 2 +- plugins/include/string.inc | 4 +- plugins/testsuite/arraytest.sma | 456 ++++++++++++++++++++++++ 17 files changed, 1705 insertions(+), 28 deletions(-) create mode 100644 amxmodx/datastructs.cpp create mode 100644 amxmodx/datastructs.h create mode 100644 plugins/include/array.inc create mode 100644 plugins/testsuite/arraytest.sma diff --git a/amxmodx/Makefile b/amxmodx/Makefile index 15cac948..bd4f2375 100755 --- a/amxmodx/Makefile +++ b/amxmodx/Makefile @@ -20,7 +20,7 @@ OBJECTS = meta_api.cpp CFile.cpp CVault.cpp vault.cpp float.cpp file.cpp modules amxxfile.cpp CLang.cpp md5.cpp emsg.cpp CForward.cpp CPlugin.cpp CModule.cpp \ CMenu.cpp util.cpp amx.cpp amxdbg.cpp natives.cpp newmenus.cpp debugger.cpp \ optimizer.cpp format.cpp messages.cpp libraries.cpp vector.cpp sorting.cpp \ - amxmod_compat.cpp nongpl_matches.cpp CFlagManager.cpp + amxmod_compat.cpp nongpl_matches.cpp CFlagManager.cpp datastructs.cpp LINK = -lgcc -static-libgcc diff --git a/amxmodx/amxmodx.h b/amxmodx/amxmodx.h index 12be564b..233a1100 100755 --- a/amxmodx/amxmodx.h +++ b/amxmodx/amxmodx.h @@ -85,6 +85,7 @@ extern AMX_NATIVE_INFO vault_Natives[]; extern AMX_NATIVE_INFO msg_Natives[]; extern AMX_NATIVE_INFO vector_Natives[]; extern AMX_NATIVE_INFO g_SortNatives[]; +extern AMX_NATIVE_INFO g_DataStructNatives[]; #ifndef __linux__ #define DLLOAD(path) (DLHANDLE)LoadLibrary(path) diff --git a/amxmodx/datastructs.cpp b/amxmodx/datastructs.cpp new file mode 100644 index 00000000..934bd899 --- /dev/null +++ b/amxmodx/datastructs.cpp @@ -0,0 +1,607 @@ +/* AMX Mod X +* +* by the AMX Mod X Development Team +* originally developed by OLO +* +* 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 2 of the License, or (at +* your option) 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, write to the Free Software Foundation, +* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +* In addition, as a special exception, the author gives permission to +* link the code of this program with the Half-Life Game Engine ("HL +* Engine") and Modified Game Libraries ("MODs") developed by Valve, +* L.L.C ("Valve"). You must obey the GNU General Public License in all +* respects for all of the code used other than the HL Engine and MODs +* from Valve. If you modify this file, you may extend this exception +* to your version of the file, but you are not obligated to do so. If +* you do not wish to do so, delete this exception statement from your +* version. +*/ + +#include "amxmodx.h" +#include "datastructs.h" + + +// Note: All handles start at 1. 0 and below are invalid handles. +// This way, a plugin that doesn't initialize a vector or +// string will not be able to modify another plugin's data +// on accident. +CVector VectorHolder; + + +// Array:ArrayCreate(cellsize=1, reserved=32); +static cell AMX_NATIVE_CALL ArrayCreate(AMX* amx, cell* params) +{ + // params[1] (cellsize) is how big in cells each element is. + // this MUST be greater than 0! + int cellsize=params[1]; + + // params[2] (reserved) is how many elements to allocate + // immediately when the list is created. + // this MUST be greater than 0! + int reserved=params[2]; + + if (cellsize<=0) + { + LogError(amx, AMX_ERR_NATIVE, "Invalid array size (%d)", cellsize); + return -1; + } + if (reserved<=0) + { + LogError(amx, AMX_ERR_NATIVE, "Invalid reserved size (%d)", reserved); + return -1; + } + + // Scan through the vector list to see if any are NULL. + // NULL means the vector was previously destroyed. + for (unsigned int i=0; i < VectorHolder.size(); ++i) + { + if (VectorHolder[i]==NULL) + { + VectorHolder[i]=new CellVector(cellsize); + VectorHolder[i]->Grow(reserved); + return i + 1; + } + } + + // None are NULL, create a new vector + CellVector* NewVector=new CellVector(cellsize); + NewVector->Grow(reserved); + + VectorHolder.push_back(NewVector); + + return VectorHolder.size(); +} +// ArrayClear(Array:which) +static cell AMX_NATIVE_CALL ArrayClear(AMX* amx, cell* params) +{ + CellVector* vec=HandleToVector(amx, params[1]); + + if (vec==NULL) + { + return 0; + } + + vec->Clear(); + + return 1; +} +// ArraySize(Array:which) +static cell AMX_NATIVE_CALL ArraySize(AMX* amx, cell* params) +{ + CellVector* vec=HandleToVector(amx, params[1]); + + if (vec==NULL) + { + return 0; + } + + return vec->Size(); +} +// ArrayGetArray(Array:which, item, any:output[]); +static cell AMX_NATIVE_CALL ArrayGetArray(AMX* amx, cell* params) +{ + CellVector* vec=HandleToVector(amx, params[1]); + + if (vec==NULL) + { + return 0; + } + + if (vec->GetArray(params[2],get_amxaddr(amx, params[3]))!=1) + { + LogError(amx, AMX_ERR_NATIVE, "Invalid cellvector handle provided (%d:%d:%d)", params[1], params[2], vec->Size()); + return 0; + } + + return 1; +} +// ArrayGetCell(Array:which, item, any:&output); +static cell AMX_NATIVE_CALL ArrayGetCell(AMX* amx, cell* params) +{ + CellVector* vec=HandleToVector(amx, params[1]); + + if (vec==NULL) + { + return 0; + } + + if (vec->GetCell(params[2],get_amxaddr(amx, params[3]))!=1) + { + LogError(amx, AMX_ERR_NATIVE, "Invalid cellvector handle provided (%d:%d:%d)", params[1], params[2], vec->Size()); + return 0; + } + + return 1; +} +// ArrayGetString(Array:which, item, any:output[], size); +static cell AMX_NATIVE_CALL ArrayGetString(AMX* amx, cell* params) +{ + CellVector* vec=HandleToVector(amx, params[1]); + + if (vec==NULL) + { + return 0; + } + + if (vec->GetString(params[2],get_amxaddr(amx, params[3]),params[4])!=1) + { + LogError(amx, AMX_ERR_NATIVE, "Invalid cellvector handle provided (%d:%d:%d)", params[1], params[2], vec->Size()); + return 0; + } + + return 1; +} +// ArraySetArray(Array:which, item, any:output[]); +static cell AMX_NATIVE_CALL ArraySetArray(AMX* amx, cell* params) +{ + CellVector* vec=HandleToVector(amx, params[1]); + + if (vec==NULL) + { + return 0; + } + + if (vec->SetArray(params[2],get_amxaddr(amx, params[3]))!=1) + { + LogError(amx, AMX_ERR_NATIVE, "Invalid cellvector handle provided (%d:%d:%d)", params[1], params[2], vec->Size()); + return 0; + } + + return 1; +} +// ArraySetCell(Array:which, item, any:&output); +static cell AMX_NATIVE_CALL ArraySetCell(AMX* amx, cell* params) +{ + CellVector* vec=HandleToVector(amx, params[1]); + + if (vec==NULL) + { + return 0; + } + + if (vec->SetCell(params[2], params[3])!=1) + { + LogError(amx, AMX_ERR_NATIVE, "Invalid cellvector handle provided (%d:%d:%d)", params[1], params[2], vec->Size()); + return 0; + } + + return 1; +} +// ArraySetString(Array:which, item, any:output[]); +static cell AMX_NATIVE_CALL ArraySetString(AMX* amx, cell* params) +{ + CellVector* vec=HandleToVector(amx, params[1]); + + if (vec==NULL) + { + return 0; + } + + if (vec->SetString(params[2],get_amxaddr(amx, params[3]))!=1) + { + LogError(amx, AMX_ERR_NATIVE, "Invalid cellvector handle provided (%d:%d:%d)", params[1], params[2], vec->Size()); + return 0; + } + + return 1; +} +// ArrayPushArray(Array:which, any:output[]); +static cell AMX_NATIVE_CALL ArrayPushArray(AMX* amx, cell* params) +{ + CellVector* vec=HandleToVector(amx, params[1]); + + if (vec==NULL) + { + return 0; + } + + vec->SetArray(vec->Push(),get_amxaddr(amx, params[2])); + + return 1; +} +// ArrayPushCell(Array:which, &any:output); +static cell AMX_NATIVE_CALL ArrayPushCell(AMX* amx, cell* params) +{ + CellVector* vec=HandleToVector(amx, params[1]); + + if (vec==NULL) + { + return 0; + } + + vec->SetCell(vec->Push(), params[2]); + + return 1; +} +// ArrayPushString(Array:which, any:output[]); +static cell AMX_NATIVE_CALL ArrayPushString(AMX* amx, cell* params) +{ + CellVector* vec=HandleToVector(amx, params[1]); + + if (vec==NULL) + { + return 0; + } + + vec->SetString(vec->Push(),get_amxaddr(amx, params[2])); + + return 1; +} +static cell AMX_NATIVE_CALL ArrayGetStringHandle(AMX* amx, cell* params) +{ + CellVector* vec=HandleToVector(amx, params[1]); + + if (vec == NULL) + { + return 0; + } + + cell* ptr=vec->GetCellPointer(params[2]); + + if (ptr == NULL) + { + return 0; + } + + return reinterpret_cast(ptr); + +} +// ArrayInsertArrayAfter(Array:which, item, const value[]) +static cell AMX_NATIVE_CALL ArrayInsertArrayAfter(AMX* amx, cell* params) +{ + CellVector* vec=HandleToVector(amx, params[1]); + + if (!vec) + { + return 0; + } + + int item=params[2]+1; + + if (vec->ShiftUpFrom(item)!=1) + { + LogError(amx, AMX_ERR_NATIVE, "Invalid item specified in ArrayInsertArrayAfter (%d:%d)", params[1], vec->Size()); + return 0; + } + + vec->SetArray(item, get_amxaddr(amx, params[3])); + + return 1; +} +// ArrayInsertCellAfter(Array:which, item, value[]) +static cell AMX_NATIVE_CALL ArrayInsertCellAfter(AMX* amx, cell* params) +{ + CellVector* vec=HandleToVector(amx, params[1]); + + if (!vec) + { + return 0; + } + + int item=params[2]+1; + + if (vec->ShiftUpFrom(item)!=1) + { + LogError(amx, AMX_ERR_NATIVE, "Invalid item specified in ArrayInsertCellAfter (%d:%d)", params[1], vec->Size()); + return 0; + } + + vec->SetCell(item, params[3]); + + return 1; +} +// ArrayInsertStringAfter(Array:which, item, const value[]) +static cell AMX_NATIVE_CALL ArrayInsertStringAfter(AMX* amx, cell* params) +{ + CellVector* vec=HandleToVector(amx, params[1]); + + if (!vec) + { + return 0; + } + + int item=params[2]+1; + + if (vec->ShiftUpFrom(item)!=1) + { + LogError(amx, AMX_ERR_NATIVE, "Invalid item specified in ArrayInsertStringAfter (%d:%d)", params[1], vec->Size()); + return 0; + } + + vec->SetString(item, get_amxaddr(amx, params[3])); + + return 1; +} +// ArrayInsertArrayBefore(Array:which, item, const value[]) +static cell AMX_NATIVE_CALL ArrayInsertArrayBefore(AMX* amx, cell* params) +{ + CellVector* vec=HandleToVector(amx, params[1]); + + if (!vec) + { + return 0; + } + + int item=params[2]; + + if (item==vec->Size()) + { + LogError(amx, AMX_ERR_NATIVE, "Invalid item specified in ArrayInsertArrayBefore (%d:%d)", params[2], vec->Size()); + return 0; + } + if (vec->ShiftUpFrom(item)!=1) + { + LogError(amx, AMX_ERR_NATIVE, "Invalid item specified in ArrayInsertArrayBefore (%d:%d)", params[2], vec->Size()); + return 0; + } + + vec->SetArray(item, get_amxaddr(amx, params[3])); + + return 1; +} +// ArrayInsertCellBefore(Array:which, item, const value) +static cell AMX_NATIVE_CALL ArrayInsertCellBefore(AMX* amx, cell* params) +{ + CellVector* vec=HandleToVector(amx, params[1]); + + if (!vec) + { + return 0; + } + + int item=params[2]; + + if (item==vec->Size()) + { + LogError(amx, AMX_ERR_NATIVE, "Invalid item specified in ArrayInsertCellBefore (%d:%d)", params[2], vec->Size()); + return 0; + } + if (vec->ShiftUpFrom(item)!=1) + { + LogError(amx, AMX_ERR_NATIVE, "Invalid item specified in ArrayInsertCellBefore (%d:%d)", params[2], vec->Size()); + return 0; + } + + vec->SetCell(item, params[3]); + + return 1; +} +// ArrayInsertStringBefore(Array:which, item, const value[]) +static cell AMX_NATIVE_CALL ArrayInsertStringBefore(AMX* amx, cell* params) +{ + CellVector* vec=HandleToVector(amx, params[1]); + + if (!vec) + { + return 0; + } + + int item=params[2]; + + if (item==vec->Size()) + { + LogError(amx, AMX_ERR_NATIVE, "Invalid item specified in ArrayInsertStringBefore (%d:%d)", params[2], vec->Size()); + return 0; + } + if (vec->ShiftUpFrom(item)!=1) + { + LogError(amx, AMX_ERR_NATIVE, "Invalid item specified in ArrayInsertStringBefore (%d:%d)", params[2], vec->Size()); + return 0; + } + + vec->SetString(item, get_amxaddr(amx, params[3])); + + return 1; +} + +// ArraySwap(Array:which, item1, item2) +static cell AMX_NATIVE_CALL ArraySwap(AMX* amx, cell* params) +{ + CellVector* vec=HandleToVector(amx, params[1]); + + if (!vec) + { + return 0; + } + if (vec->Swap(params[2], params[3])!=1) + { + LogError(amx, AMX_ERR_NATIVE, "Invalid item specified in ArraySwap (%d , %d:%d)",params[2], params[3], vec->Size()); + return 0; + } + + return 1; +} + +// ArrayDeleteItem(Array:which, item); +static cell AMX_NATIVE_CALL ArrayDeleteItem(AMX* amx, cell* params) +{ + CellVector* vec=HandleToVector(amx, params[1]); + + if (!vec) + { + return 0; + } + + if (vec->Delete(params[2])!=1) + { + LogError(amx, AMX_ERR_NATIVE, "Invalid item specified in ArrayDeleteItem (%d:%d)", params[2], vec->Size()); + return 0; + } + return 1; +} + + +// ArrayDestroy(Array:&which) +static cell AMX_NATIVE_CALL ArrayDestroy(AMX* amx, cell* params) +{ + // byref the handle here so we can zero it out after destroying + // this way they cannot accidentally reuse it + cell* handle=get_amxaddr(amx,params[1]); + CellVector* vec=HandleToVector(amx, *handle); + + if (!vec) + { + return 0; + } + + delete vec; + + VectorHolder[*handle-1]=NULL; + + *handle=0; + + return 1; +} + +typedef struct ArraySort_s +{ + int handle; + int forward; + cell data; + cell size; + +} ArraySort_t; + +static CStack ArraySortStack; + +int SortArrayList(const void *itema, const void *itemb) +{ + ArraySort_t *Info = ArraySortStack.front(); + + return executeForwards(Info->forward, Info->handle, *((int *)itema), *((int *)itemb), Info->data, Info->size); + +} +// native ArraySort(Array:array, const comparefunc[], data[]="", data_size=0); +static cell AMX_NATIVE_CALL ArraySort(AMX* amx, cell* params) +{ + int handle=params[1]; + CellVector* vec=HandleToVector(amx, handle); + + if (!vec) + { + return 0; + } + + // This is kind of a cheating way to go about this but... + // Create an array of integers as big as however many elements are in the vector. + // Pass that array to qsort + // After the array is sorted out, then create a NEW cellvector + // and copy in the old data in the order of what was sorted + int len; + char* FuncName=get_amxstring(amx, params[2], 0, len); + // MySortFunc(Array:array, item1, item2, const data[], data_size) + int Forward = registerSPForwardByName(amx, FuncName, FP_CELL, FP_CELL, FP_CELL, FP_CELL, FP_CELL, FP_DONE); + if (Forward < 0) + { + LogError(amx, AMX_ERR_NATIVE, "The public function \"%s\" was not found.", FuncName); + return 0; + } + + int *IntList=new int[vec->Size()]; + + for (int i=0; i< vec->Size(); i++) + { + IntList[i]=i; + } + + ArraySort_t *Info=new ArraySort_t; + + Info->handle=handle; + Info->forward=Forward; + Info->data=params[3]; + Info->size=params[4]; + + ArraySortStack.push(Info); + qsort(IntList, vec->Size(), sizeof(int), SortArrayList); + ArraySortStack.pop(); + + CellVector* newvec=new CellVector(vec->GetCellCount()); + + // Set the new vector's values + for (int i=0; i< vec->Size(); i++) + { + if (newvec->SetArray(newvec->Push(), vec->GetCellPointer(IntList[i]))!=1) + { + // This should never happen.. + LogError(amx, AMX_ERR_NATIVE, "Failed to SetArray in ArraySort (i=%d, IntList=%d)",i,IntList[i]); + + return 0; + } + } + + // Delete the old vector + delete vec; + + // Now save the new vector in its handle location + VectorHolder[handle-1]=newvec; + + // Cleanup + delete Info; + delete IntList; + + unregisterSPForward(Forward); + + return 1; +} + + + +AMX_NATIVE_INFO g_DataStructNatives[] = +{ + { "ArrayCreate", ArrayCreate }, + { "ArrayClear", ArrayClear }, + { "ArraySize", ArraySize }, + { "ArrayGetArray", ArrayGetArray }, + { "ArrayGetCell", ArrayGetCell }, + { "ArrayGetString", ArrayGetString }, + { "ArraySetArray", ArraySetArray }, + { "ArraySetCell", ArraySetCell }, + { "ArraySetString", ArraySetString }, + { "ArrayPushArray", ArrayPushArray }, + { "ArrayPushCell", ArrayPushCell }, + { "ArrayPushString", ArrayPushString }, + { "ArrayInsertArrayAfter", ArrayInsertArrayAfter }, + { "ArrayInsertCellAfter", ArrayInsertCellAfter }, + { "ArrayInsertStringAfter", ArrayInsertStringAfter }, + { "ArrayInsertArrayBefore", ArrayInsertArrayBefore }, + { "ArrayInsertCellBefore", ArrayInsertCellBefore }, + { "ArrayInsertStringBefore", ArrayInsertStringBefore }, + { "ArraySwap", ArraySwap }, + { "ArrayDeleteItem", ArrayDeleteItem }, + { "ArrayGetStringHandle", ArrayGetStringHandle }, + { "ArrayDestroy", ArrayDestroy }, + { "ArraySort", ArraySort }, + + { NULL, NULL } +}; diff --git a/amxmodx/datastructs.h b/amxmodx/datastructs.h new file mode 100644 index 00000000..a1936314 --- /dev/null +++ b/amxmodx/datastructs.h @@ -0,0 +1,323 @@ +/* AMX Mod X +* +* by the AMX Mod X Development Team +* originally developed by OLO +* +* 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 2 of the License, or (at +* your option) 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, write to the Free Software Foundation, +* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +* In addition, as a special exception, the author gives permission to +* link the code of this program with the Half-Life Game Engine ("HL +* Engine") and Modified Game Libraries ("MODs") developed by Valve, +* L.L.C ("Valve"). You must obey the GNU General Public License in all +* respects for all of the code used other than the HL Engine and MODs +* from Valve. If you modify this file, you may extend this exception +* to your version of the file, but you are not obligated to do so. If +* you do not wish to do so, delete this exception statement from your +* version. +*/ + +#ifndef DATASTRUCTS_H +#define DATASTRUCTS_H + +class CellVector +{ +private: + cell* data; // allocated with malloc + size_t cellcount; // how many cells per element + size_t cursize; // current size of the vector (maximum elements) + size_t count; // how many units of the vector are in use + +public: + CellVector(): data(NULL), cellcount(0), cursize(0), count(0) + { + }; + CellVector(int cellsize): data(NULL), cellcount(cellsize), cursize(0), count(0) + { + }; + ~CellVector() + { + if (data) + { + free(data); + } + }; + size_t GetCellCount() + { + return cellcount; + }; + void Grow(size_t howmany) + { + cursize+=howmany; + if (data) + { + data=(cell*)realloc(data, (sizeof(cell) * cellcount) * cursize); + } + else + { + data=(cell*)malloc((sizeof(cell) * cellcount) * cursize); + } + }; + void FreeUnused(void) + { + if (cursize != count && + data != NULL) + { + cursize=count; + data=(cell*)realloc(data, cursize * (sizeof(cell) * cellcount)); + } + }; + // Returns 1 on success + // 0 on out of bounds. + int GetArray(size_t which, cell* output) + { + // make sure it is in bounds. + if (which >= count) + { + return 0; + } + // align output data + cell* out=data + (cellcount * which); + + memcpy(output, out, sizeof(cell) * cellcount); + + return 1; + }; + // Returns 1 on success + // 0 on out of bounds + int GetCell(size_t which, cell* output) + { + // check bounds + if (which >= count) + { + return 0; + } + *output=*(data + (cellcount * which)); + + return 1; + } + // Returns 1 on success + // 0 on out of bounds + int GetString(size_t which, cell* output, size_t size) + { + // check bounds + if (which >= count) + { + return 0; + } + cell* out=data + (cellcount * which); + + size_t count=cellcount; + + while (size-- && + count-- && + (*output++=*out++)!='\0') + /* do nothing */ ; + + // If size is zero here, then the string was never null terminated. + if (size==0) + { + *out='\0'; + } + + return 1; + } + // Returns 1 on success + // 0 on out of bounds + int SetArray(size_t which, cell* output) + { + if (which >= count) + { + return 0; + } + // align output + cell* out=data + (cellcount * which); + + memcpy(out, output, sizeof(cell) * cellcount); + + return 1; + }; + // Returns 1 on success + // 0 on out of bounds + int SetCell(size_t which, cell output) + { + if (which >= count) + { + return 0; + } + // align output + *(data + (cellcount * which))=output; + + return 1; + }; + // Returns 1 on success + // 0 on out of bounds + int SetString(size_t which, cell* output) + { + if (which >= count) + { + return 0; + } + // align output + cell* out=data + (cellcount * which); + + memcpy(out, output, sizeof(cell) * cellcount); + + // now force a null terminator on the last entry. + out+=(cellcount - 1); + *out='\0'; + + return 1; + }; + int Push() + { + if (count >= cursize) + { + // Grow in 8s to cause less reallocation + this->Grow(8); + }; + + this->count++; + + return this->count-1; + }; + int Size() + { + return this->count; + }; + void Clear() + { + free(data); + data=(cell*)malloc(sizeof(cell) * cellcount); + cursize=1; + count=0; + }; + cell* GetCellPointer(size_t which) + { + if (which >= count) + { + return NULL; + } + return data + (which * cellcount); + }; + // Shifts all items from this item, and including this item up 1. + int ShiftUpFrom(size_t which) + { + // No point shifting this. + if (this->count < 0 || + which > this->count) + { + + return 0; + } + // First make a new item. + this->Push(); + + // If we got an InsertAfter(lastitem), then which will equal this->count - 1 + // all we needed to do was Push() + if (which == this->count || + which == this->count - 1) + { + return 1; + } + + // Allocate a temporary buffer to store data in + size_t tempbuffsize=(sizeof(cell) * cellcount) * (this->count - which); + + cell* temp=(cell*)malloc(tempbuffsize); + + // Copy old data to temp buffer + memcpy(temp, GetCellPointer(which), tempbuffsize); + + // Now copy temp buffer to adjusted location + memcpy(GetCellPointer(which+1), temp, tempbuffsize); + + // cleanup + free(temp); + + return 1; + + }; + // Shifts all items from this item, and including this item down 1. + // This deletes the item specified. + int Delete(size_t which) + { + // No point shifting this. + if (this->count < 0 || + which >= this->count) + { + return 0; + } + + + for (size_t i=which; icount - 1; i++) + { + memcpy(GetCellPointer(i), GetCellPointer(i + 1), sizeof(cell) * cellcount); + } + this->count--; + return 1; + }; + int Swap(size_t item1, size_t item2) + { + if (item1 >= this->count || + item2 >= this->count) + { + return 0; + } + + // Make a temp buffer to store item2 + cell* temp=(cell*)malloc(sizeof(cell) * cellcount); + memcpy(temp, GetCellPointer(item2), sizeof(cell) * cellcount); + + // copy item1 to item2 + memcpy(GetCellPointer(item2), GetCellPointer(item1), sizeof(cell) * cellcount); + + // copy item2 to item1 + memcpy(GetCellPointer(item1), temp, sizeof(cell) * cellcount); + + // Cleanup + free(temp); + + return 1; + }; + +}; + +extern CVector VectorHolder; + + +inline CellVector* HandleToVector(AMX* amx, int handle) +{ + if (handle <= 0 || + handle > (int)VectorHolder.size()) + { + LogError(amx, AMX_ERR_NATIVE, "Invalid array handle provided (%d)", handle); + + return NULL; + } + + CellVector* ret=VectorHolder[handle-1]; + + if (ret == NULL) + { + LogError(amx, AMX_ERR_NATIVE, "Invalid array handle provided (%d)", handle); + + return NULL; + } + + return ret; +} + + +#endif diff --git a/amxmodx/format.cpp b/amxmodx/format.cpp index 28463d42..e270cf2b 100644 --- a/amxmodx/format.cpp +++ b/amxmodx/format.cpp @@ -1,5 +1,6 @@ #include "amxmodx.h" #include "format.h" +#include "datastructs.h" #include "amxmod_compat.h" //Adapted from Quake3's vsprintf @@ -494,6 +495,21 @@ reswitch: AddHex(&buf_p, llen, static_cast(*get_amxaddr(amx, params[arg])), width, flags); arg++; break; + case 'S': + { + CHECK_ARGS(0); + // %S is passed a pointer directly to a cell string. + cell* ptr=reinterpret_cast(*get_amxaddr(amx, params[arg])); + if (!ptr) + { + LogError(amx, AMX_ERR_NATIVE, "Invalid vector string handle provided (%d)", *get_amxaddr(amx, params[arg])); + return 0; + } + + AddString(&buf_p, llen, ptr, width, prec); + arg++; + break; + } case 's': CHECK_ARGS(0); if (amx->flags & AMX_FLAG_OLDFILE) diff --git a/amxmodx/meta_api.cpp b/amxmodx/meta_api.cpp index 79842def..624c30d2 100755 --- a/amxmodx/meta_api.cpp +++ b/amxmodx/meta_api.cpp @@ -47,6 +47,7 @@ #include "messages.h" #include "amxmod_compat.h" +#include "datastructs.h" #include "CFlagManager.h" #include "svn_version.h" @@ -388,6 +389,12 @@ int C_Spawn(edict_t *pent) FlagMan.LoadFile(); + for (unsigned int i=0; iflags & AMX_FLAG_OLDFILE) { diff --git a/amxmodx/msvc8/amxmodx_mm.vcproj b/amxmodx/msvc8/amxmodx_mm.vcproj index 13125410..350b683d 100644 --- a/amxmodx/msvc8/amxmodx_mm.vcproj +++ b/amxmodx/msvc8/amxmodx_mm.vcproj @@ -770,6 +770,14 @@ RelativePath="..\CVector.h" > + + + + diff --git a/plugins/admin.sma b/plugins/admin.sma index 3bfd684a..72c87942 100755 --- a/plugins/admin.sma +++ b/plugins/admin.sma @@ -41,6 +41,8 @@ #include #endif +new Vector:AdminList; + new AdminCount; new PLUGINNAME[] = "AMX Mod X" @@ -108,6 +110,11 @@ public plugin_init() server_cmd("exec %s/amxx.cfg", configsDir) // Execute main configuration file server_cmd("exec %s/sql.cfg", configsDir) + + // Create a vector of 5 cells to store the info. + AdminList=vector_create(5); + + #if defined USING_SQL server_cmd("amx_sqladmins") #else diff --git a/plugins/include/amxmodx.inc b/plugins/include/amxmodx.inc index b3a9b697..81f8ec52 100755 --- a/plugins/include/amxmodx.inc +++ b/plugins/include/amxmodx.inc @@ -21,6 +21,7 @@ #include #include #include +#include /* Function is called just after server activation. * Good place for configuration loading, commands and cvars registration. */ @@ -97,16 +98,16 @@ native get_localinfo(const info[],output[],len); native show_motd(player,const message[],const header[]=""); /* Sends message to player. Set index to 0 to send text globaly. */ -native client_print(index,type,const message[],{Float,Sql,Result,_}:...); +native client_print(index,type,const message[],any:...); /* Sends message to player by engine. Set index to 0 to send text globaly. */ -native engclient_print(player,type,const message[],{Float,Sql,Result,_}:...); +native engclient_print(player,type,const message[],any:...); /* Sends message to console. */ -native console_print(id,const message[],{Float,Sql,Result,_}:...); +native console_print(id,const message[],any:...); /* Sends command to console. */ -native console_cmd(id,const cmd[],{Float,Sql,Result,_}:...); +native console_cmd(id,const cmd[],any:...); /* Registers event on which a given function will be called * Flags: @@ -141,7 +142,7 @@ native register_logevent(const function[], argsnum, ... ); native set_hudmessage(red=200, green=100, blue=0, Float:x=-1.0, Float:y=0.35, effects=0, Float:fxtime=6.0, Float:holdtime=12.0, Float:fadeintime=0.1, Float:fadeouttime=0.2,channel=4); /* Displays HUD message to given player. */ -native show_hudmessage(index,const message[],{Float,Sql,Result,_}:...); +native show_hudmessage(index,const message[],any:...); /* Displays menu. Keys have bit values (key 1 is (1<<0), key 5 is (1<<4) etc.). */ native show_menu(index,keys,const menu[], time = -1, const title[] = ""); @@ -150,7 +151,7 @@ native show_menu(index,keys,const menu[], time = -1, const title[] = ""); * When you are asking for string the array and length is needed (read_data(2,name,len)). * Integer is returned by function (new me = read_data(3)). * Float is set in second parameter (read_data(3,value)). */ -native read_data(value, {Float,Sql,Result,_}:... ); +native read_data(value, any:... ); /* Returns number of values in client message. */ native read_datanum(); @@ -171,7 +172,7 @@ native parse_loguser(const text[], name[], nlen, &userid = -2, authid[] = "", al /* Prints message to server console. * You may use text formating (f.e. server_print("%-32s %.2f!","hello",7.345)) */ -native server_print(const message[], {Float,Sql,Result,_}:...); +native server_print(const message[], any:...); /* Returns 1 or 0. */ native is_map_valid(const mapname[]); @@ -305,13 +306,13 @@ native user_kill(index,flag=0); * ... - optional parameters * Return value: * always 0 */ -native log_amx(const string[], {Float,Sql,Result,_}:...); +native log_amx(const string[], any:...); /* Sends message to standard HL logs. */ -native log_message(const message[],{Float,Sql,Result,_}:...); +native log_message(const message[],any:...); /* Sends log message to specified file. */ -native log_to_file(const file[],const message[],{Float,Sql,Result,_}:...); +native log_to_file(const file[],const message[],any:...); /* Returns number of players put in server. * If flag is set then also connecting are counted. */ @@ -367,7 +368,7 @@ native find_player(const flags[], ... ); native remove_quotes(text[]); /* Executes command on player. */ -native client_cmd(index,const command[],{Float,Sql,Result,_}:...); +native client_cmd(index,const command[],any:...); /* This is an emulation of a client command (commands aren't send to client!). * It allows to execute some commands on players and bots. @@ -376,7 +377,7 @@ native client_cmd(index,const command[],{Float,Sql,Result,_}:...); native engclient_cmd(index,const command[],const arg1[]="",const arg2[]=""); /* Executes command on a server console. */ -native server_cmd(const command[],{Float,Sql,Result,_}:...); +native server_cmd(const command[],any:...); /* Sets a cvar to given value. */ native set_cvar_string(const cvar[],const value[]); @@ -991,7 +992,7 @@ native CreateHudSyncObj(num=0, ...); * You must use set_hudmessage, although the channel parameter is * entirely ignored. */ -native ShowSyncHudMsg(target, syncObj, const fmt[], {Float,Sql,Result,_}:...); +native ShowSyncHudMsg(target, syncObj, const fmt[], any:...); /** * Clears the display on a HudSync Object. This is essentially the same diff --git a/plugins/include/array.inc b/plugins/include/array.inc new file mode 100644 index 00000000..962ad4a9 --- /dev/null +++ b/plugins/include/array.inc @@ -0,0 +1,250 @@ +#if defined _array_included + #endinput +#endif + +#define _array_included + +/** + * These arrays are intended to be used for a form of global storage without + * requiring a #define that needs to be increased each time a person needs more + * storage. + * These are not designed to be used as a replacement for normal arrays, as + * normal arrays are faster and should be used whenever possible. + */ + +/** + * Creates a handle to a dynamically sized array. + * It is very important that the cellsize you provide matches up with the buffer sizes + * that you pass with subsequent Array{Get,Set,Push} calls. + * + * @param cellsize How many cells each entry in the array is. + * @param reserved How many blank entries are created immediately when the array is created. These entries are not valid to read from until called with ArraySet. + * @return Handle to the array. + */ +native Array:ArrayCreate(cellsize=1, reserved=32); + +/** + * Clears all entries from the array. + * + * @param which The array to clear. + * @return 1 on success, 0 on failure. + */ +native ArrayClear(Array:which); + +/** + * Returns the number of elements in the array. + * + * @param which The array to check. + * @return How many elements are in the array. + */ +native ArraySize(Array:which); + +/** + * Returns data within an array. + * Make sure the output buffer matches the size the array was created with! + * + * @param which The array to retrieve the item from. + * @param item The item to retrieve (zero-based). + * @param output The output buffer to write. + */ +native ArrayGetArray(Array:which, item, any:output[]); + +/** + * Returns a single cell of data from an array. + * Use this only with arrays that were created with a cellsize of 1! + * + * @param which The array to retrieve the item from. + * @param item The item to retrieve (zero-based). + * @param output The variable to store the value in. + */ +native ArrayGetCell(Array:which, item, &any:output); + +/** + * Returns a string value from an array. + * + * @param which The array to retrieve the item from. + * @param item The item to retrieve (zero-based). + * @param output The variable to store the value in. + * @param size Character size of the output buffer. + */ +native ArrayGetString(Array:which, item, output[], size); + +/** + * Sets an item's data with that of a local buffer. + * The buffer size must match what the cellsize that the array was created with! + * The item must already exist, use ArrayPushArray to create a new item within the array. + * + * @param which The array to set the item from within. + * @param item The item to set (zero-based). + * @param input The input buffer to store. + */ +native ArraySetArray(Array:which, item, const any:input[]); + +/** + * Sets an array's single cell value. Use this only on array that were created with a cellsize of 1! + * The item must already exist, use ArrayPushCell to create a new item within the array. + * + * @param which The array to set the item from within. + * @param item The item to set (zero-based). + * @param input The value to set. + */ +native ArraySetCell(Array:which, item, any:input); + +/** + * Sets a string value from an array. + * The stored string will be truncated if it is longer than the cellsize the array was created with! + * The item must already exist, use ArrayPushString to create a new item within the array. + * + * @param which The array to set the item from within. + * @param item The item to set (zero-based). + * @param input The string to set the item as. + */ +native ArraySetString(Array:which, item, const input[]); + +/** + * Creates a new item at the end of the array and sets its data with that of a local buffer. + * The buffer size must match what the cellsize that the array was created with! + * + * @param which The array to add the item to. + * @param input The input buffer to store. + */ +native ArrayPushArray(Array:which, const any:input[]); + +/** + * Creates a new item and sets the array's single cell value. + * Use this only on array that were created with a cellsize of 1! + * + * @param which The array to add the item to. + * @param input The value to set. + */ +native ArrayPushCell(Array:which, any:input); + +/** + * Creates a new element in the array and sets its value to the input buffer. + * The stored string will be truncated if it is longer than the cellsize the array was created with! + * + * @param which The array to add the item to. + * @param input The string to set the item as. + */ +native ArrayPushString(Array:which, const input[]); + +/** + * Inserts an item after the selected item. All items beyond it get shifted up 1 space. + * The buffer size must match what the cellsize that the array was created with! + * + * @param which The array to add the item to. + * @param item The item to insert after. + * @param input The input buffer to store. + */ +native ArrayInsertArrayAfter(Array:which, item, const any:input[]); + +/** + * Inserts an item after the selected item. All items beyond it get shifted up 1 space. + * Use this only on an array that was created with a cellsize of 1! + * + * @param which The array to add the item to. + * @param item The item to insert after. + * @param input The value to set. + */ +native ArrayInsertCellAfter(Array:which, item, any:input); + +/** + * Inserts an item after the selected item. All items beyond it get shifted up 1 space. + * The stored string will be truncated if it is longer than the cellsize the array was created with! + * + * @param which The array to add the item to. + * @param item The item to insert after. + * @param input The value to set. + */ +native ArrayInsertStringAfter(Array:which, item, const input[]); + +/** + * Inserts an item before the selected item. All items beyond it, and the selected item get shifted up 1 space. + * The buffer size must match what the cellsize that the array was created with! + * + * @param which The array to add the item to. + * @param item The item to insert before. + * @param input The input buffer to store. + */ +native ArrayInsertArrayBefore(Array:which, item, const any:input[]); + +/** + * Inserts an item before the selected item. All items beyond it, and the selected item get shifted up 1 space. + * Use this only on an array that was created with a cellsize of 1! + * + * @param which The array to add the item to. + * @param item The item to insert after. + * @param input The value to set. + */ +native ArrayInsertCellBefore(Array:which, item, const any:input); + +/** + * Inserts an item before the selected item. All items beyond it, and the selected item get shifted up 1 space. + * The stored string will be truncated if it is longer than the cellsize the array was created with! + * + * @param which The array to add the item to. + * @param item The item to insert before. + * @param input The value to set. + */ +native ArrayInsertStringBefore(Array:which, item, const input[]); + +/** + * Swaps the position of two items. + * + * @param which The array that contains the items. + * @param item1 The first item to swap. + * @param item2 The second item to swap. + */ +native ArraySwap(Array:which, item1, item2); + +/** + * Deletes an item from the array. All items beyond it get shifted down 1 space. + * + * @param which The array that contains the item to delete. + * @param item The item to delete. + */ +native ArrayDeleteItem(Array:which, item); + +/** + * Creates a handle that is passable to a format compliant routine for printing as a string (with the %S parameter). + * It is suggested to pass the function directly as a parameter to the format routine. + * The array contents must be a null-terminated string! + * + * An example usage: client_print(id, print_chat, "%S", ArrayGetStringHandle(MessageArray, i)); + * + * @param which The array the string is stored in. + * @param item Which item to print the string value of. + * @return Handle to the item directly. Do not use or save stale handles. + */ +native DoNotUse:ArrayGetStringHandle(Array:which, item); + +/** + * Destroys the array, and resets the handle to 0 to prevent accidental usage after it is destroyed. + * + * @param which The array to destroy. + */ +native ArrayDestroy(&Array:which); + + + +/** + * Similar to sorting.inc's CustomSort. + * The sorting algorithm then uses your comparison function to sort the data. + * The function is called in the following manner: + * + * public MySortFunc(Array:array, item1, item2, const data[], data_size) + * + * array - Array handle in its current un-sorted state. + * item1, item2 - Current item pair being compared + * data[] - Extra data array you passed to the sort func. + * data_size - Size of extra data you passed to the sort func. + * + * Your function should return: + * -1 if item1 should go before item2 + * 0 if item1 and item2 are equal + * 1 if item1 should go after item2 + * Note that the parameters after item2 are all optional and you do not need to specify them. + * + * Note that unlike the sorting.inc versions, the array passed to the callback is not in mid-sorted state. + */ +native ArraySort(Array:array, const comparefunc[], data[]="", data_size=0); diff --git a/plugins/include/engine.inc b/plugins/include/engine.inc index 5695a843..ada8e5b5 100755 --- a/plugins/include/engine.inc +++ b/plugins/include/engine.inc @@ -22,7 +22,7 @@ #pragma library engine #endif -native traceresult(type,{Float,Sql,Result,_}:...); +native traceresult(type,any:...); /* Registers a client impulse to a function. Function is passed the ID of the user. */ native register_impulse(impulse, const function[]); @@ -39,7 +39,7 @@ native register_think(const Classname[], const function[]); */ /* Precaches an event. */ -native precache_event(type, const Name[], {Float,Sql,Result,_}:...); +native precache_event(type, const Name[], any:...); /* set/get a user's speak flags */ native set_speak(iIndex, iSpeakFlags); @@ -174,11 +174,11 @@ native playback_event(flags,invoker,eventindex,Float:delay,const Float:origin[3] /* Gets parameters sent from CmdStart. Note that you will receive modified values if any other plugin have changed them. */ -native get_usercmd(type,{Float,Sql,Result,_}:...); +native get_usercmd(type,any:...); /* Sets the parameters sent from CmdStart. Note that your changes will be seen by any other plugin doing get_usercmd() */ -native set_usercmd(type,{Float,Sql,Result,_}:...); +native set_usercmd(type,any:...); /* Converts a string offset into a real string. Some of the forwards in fakemeta uses string offsets. (FM_CreateNamedEntity) */ diff --git a/plugins/include/fakemeta.inc b/plugins/include/fakemeta.inc index 18021f9f..0c3a5a3d 100755 --- a/plugins/include/fakemeta.inc +++ b/plugins/include/fakemeta.inc @@ -31,10 +31,10 @@ * new ptr, classname[32] * pev(entid, pev_classname, ptr, classname, 31) */ -native pev(_index,_value,{Float,Sql,Result,_}:...); +native pev(_index,_value,any:...); /* Sets entvar data for an entity. Use the pev_* enum */ -native set_pev(_index,_value,{Float,Sql,Result,_}:...); +native set_pev(_index,_value,any:...); /* returns 0 if ent is invalid, >0 if valid * (1 == valid, 2 == valid+pvPrivateData valid) @@ -50,7 +50,7 @@ native pev_valid(entindex); * new ptr = pev(id, pev_viewmodel) * global_get(glb_pStringBase, ptr, model, 127) */ -native global_get(_value, {Float,Sql,Result,_}:...); +native global_get(_value, any:...); /* Returns an integer from private data. _linuxdiff is added into the _Offset if it's used on a linux server. */ native get_pdata_int(_index,_Offset,_linuxdiff=5); @@ -77,7 +77,7 @@ native register_forward(_forwardType,const _function[],_post=0); native unregister_forward(_forwardType, registerId, post=0); /* Returns data for metamod */ -native forward_return(type,{Float,Sql,Result,_}:...); +native forward_return(type,any:...); /* Returns the original return value of an engine function. * This is only valid in forwards that were registered as post. @@ -89,7 +89,7 @@ native forward_return(type,{Float,Sql,Result,_}:...); native get_orig_retval({Float,_}:...); native engfunc(type,{Float,Sql,Result,AlertType,_}:...); -native dllfunc(type,{Float,Sql,Result,_}:...); +native dllfunc(type,any:...); //only use this with functions that pass a Trace // get: zero extra params - return int, one extra param = byref float or vector diff --git a/plugins/include/file.inc b/plugins/include/file.inc index 86523b85..bf439912 100755 --- a/plugins/include/file.inc +++ b/plugins/include/file.inc @@ -83,7 +83,7 @@ native fgets(file, buffer[], maxlength); native fputs(file, const text[]); //Writes a line to the file -native fprintf(file, const fmt[], {Float,Sql,Result,_}:...); +native fprintf(file, const fmt[], any:...); //Sets the current position in a file (see SEEK_ values above) native fseek(file, position, start); @@ -97,7 +97,7 @@ native fputc(file, data); native fungetc(file, data); //Return the size of a file -native filesize(const filename[], {Float,Sql,Result,_}:...); +native filesize(const filename[], any:...); //Attempts to remove a directory. //Note that you cannot remove a directory that has files on most diff --git a/plugins/include/ns2amx.inc b/plugins/include/ns2amx.inc index e50cf2df..dbaee011 100755 --- a/plugins/include/ns2amx.inc +++ b/plugins/include/ns2amx.inc @@ -18,7 +18,7 @@ stock is_entity(id) return is_valid_ent(id); /* The end of the native is buffered incase the plugin is including an NS_VERSION (no longer supported), ignore it */ -stock get_build(classname[], value, number=0,{Float,Sql,Result,_}:...) +stock get_build(classname[], value, number=0,any:...) return ns_get_build(classname, value, number); stock get_private_i(index, offset, linuxdiff=5) diff --git a/plugins/include/string.inc b/plugins/include/string.inc index e8a958e0..043c613b 100755 --- a/plugins/include/string.inc +++ b/plugins/include/string.inc @@ -34,7 +34,7 @@ native add(dest[],len,const src[],max=0); * slower, so you should using a source string that is the same as * the destination. */ -native format(output[] ,len ,const format[] , {Float,Sql,Result,_}:...); +native format(output[] ,len ,const format[] , any:...); /* Same as format(), except does not perform a "copy back" check. * This means formatex() is faster, but DOES NOT ALLOW this type @@ -45,7 +45,7 @@ native format(output[] ,len ,const format[] , {Float,Sql,Result,_}:...); * This is because the output is directly stored into "buffer", * rather than copied back at the end. */ -native formatex(output[] ,len ,const format[] , {Float,Sql,Result,_}:...); +native formatex(output[] ,len ,const format[] , any:...); /* Replacement for format_args. Much faster and %L compatible. * This works exactly like vsnprintf() from C. diff --git a/plugins/testsuite/arraytest.sma b/plugins/testsuite/arraytest.sma new file mode 100644 index 00000000..b2fb5bff --- /dev/null +++ b/plugins/testsuite/arraytest.sma @@ -0,0 +1,456 @@ +#include + + +new __testnumber; +new errcount; +new __testfunc[32]; +new __testfuncnum; + +enum TestType +{ + TT_Equal = 0, + TT_LessThan, + TT_GreaterThan, + TT_LessThanEqual, + TT_GreaterThanEqual, + TT_NotEqual +}; + +new TestWords[6][] = { + "==", + "<", + ">", + "<=", + ">=", + "!=" +}; + + + +stock test(A,B=0,TestType:Type=TT_Equal) +{ + ++__testnumber; + + new passed=0; + + switch (Type) + { + case TT_Equal: if (A==B) passed=1; + case TT_LessThan: if (AB) passed=1; + case TT_LessThanEqual: if (A<=B) passed=1; + case TT_GreaterThanEqual: if (A>=B) passed=1; + case TT_NotEqual: if (A!=B) passed=1; + } + + if (!passed) + { + log_amx("Failed test #%d (%d %s %d)",__testnumber,A,TestWords[_:Type],B); + errcount++; + } +} +stock starttests(const startfunc[]) +{ + __testnumber=0; + errcount=0; + __testfuncnum=1; + server_print("Starting tests..."); + formatex(__testfunc,sizeof(__testfunc)-1,"%s",startfunc); + + new func[32]; + formatex(func,sizeof(func)-1,"%s%d",__testfunc,__testfuncnum++); + set_task(0.1,func); +} + +stock showres() +{ + if (errcount==0) + { + new func[32]; + formatex(func,sizeof(func)-1,"%s%d",__testfunc,__testfuncnum++); + if (get_func_id(func)==-1) + { + server_print("All tests ok!"); + } + else + { + server_print("Test ok, moving on..."); + + set_task(0.1,func); + } + } + else + { + server_print("Test failed, aborting."); + } +} + + + +public plugin_init() +{ + register_srvcmd("arraytest","arraytest"); +} + +public arraytest() +{ + starttests("arraytest"); +} +public arraytest1() +{ + server_print("Testing 1000 iterations of 1-cell arrays..."); + + new Float:f; + new Array:a=ArrayCreate(1); + for (new i=0; i<1000; i++) + { + f=float(i); + ArrayPushCell(a,f); + } + new Float:r; + for (new i=0; i<1000; i++) + { + f=float(i); + ArrayGetCell(a,i,r); + + // This is normally bad for float "casting", but in this case it should be fine. + test(_:f, _:r); + + + // Reset with inversed values + new g=_:f; + g=~g; + + ArraySetCell(a,i,g); + + ArrayGetCell(a,i,r); + + test(g, _:r); + + } + + ArrayDestroy(a); + + showres(); +} +stock bool:checkarray(const a[], const b[], size) +{ + while (size--) + { + if (a[size]!=b[size]) + { + return false; + } + } + + return true; +} +stock invarray(a[],size) +{ + while (size--) + { + a[size] = ~a[size]; + } + +} +public arraytest2() +{ + server_print("Testing 1000 iterations of 40-cell arrays..."); + + new Array:a=ArrayCreate(40); + new buff[40]; + new buffb[40]; + for (new i=0; i<1000; i++) + { + arrayset(buff,i,sizeof(buff)); + + ArrayPushArray(a, buff); + } + for (new i=0; i<1000; i++) + { + arrayset(buff, i, sizeof(buff)); + + ArrayGetArray(a, i, buffb); + + test(_:checkarray(buff,buffb,sizeof(buff)),1); + + // Now overwrite the array with inversed value + invarray(buff,sizeof(buff)); + + ArraySetArray(a, i, buff); + + ArrayGetArray(a, i, buffb); + + test(_:checkarray(buff,buffb,sizeof(buff)),1); + } + + ArrayDestroy(a); + + showres(); +} +public arraytest3() +{ + server_print("Testing 1000 iterations of strings..."); + + // The string is 10 long, the string we're trying to pass is 20 long. + + new Array:a=ArrayCreate(10); + + new buff[20]="1234567890abcdefghi"; + new buffb[20]; + + for (new i=0; i<1000; i++) + { + ArrayPushString(a, buff); + } + for (new i=0; i<1000; i++) + { + ArrayGetString(a, i, buffb, sizeof(buffb)-1); + + test(strcmp(buffb,"123456789"),0); + + ArraySetString(a, i, "9876543210"); + + ArrayGetString(a, i, buffb, sizeof(buffb)-1); + + test(strcmp(buffb,"987654321"),0); + + buffb[0]=0; + + formatex(buffb,sizeof(buffb)-1,"%S", ArrayGetStringHandle(a, i)); + + test(strcmp(buffb, "987654321"),0); + } + + ArrayDestroy(a); + + showres(); +} + +public sortcallback(Array:a, b, c) +{ + static stra[40]; + static strb[40]; + + ArrayGetString(a, b, stra, sizeof(stra)-1); + ArrayGetString(a, c, strb, sizeof(strb)-1); + return strcmp(stra,strb); +} +public arraytest4() +{ + server_print("Testing sorting function..."); + + new Array:a=ArrayCreate(40); + + ArrayPushString(a, "z"); + ArrayPushString(a, "yz"); + ArrayPushString(a, "xyz"); + ArrayPushString(a, "wxyz"); + ArrayPushString(a, "vwxyz"); + ArrayPushString(a, "uvwxyz"); + ArrayPushString(a, "tuvwxyz"); + ArrayPushString(a, "stuvwxyz"); + ArrayPushString(a, "rstuvwxyz"); + ArrayPushString(a, "qrstuvwxyz"); + ArrayPushString(a, "pqrstuvwxyz"); + ArrayPushString(a, "opqrstuvwxyz"); + ArrayPushString(a, "nopqrstuvwxyz"); + ArrayPushString(a, "mnopqrstuvwxyz"); + ArrayPushString(a, "lmnopqrstuvwxyz"); + ArrayPushString(a, "klmnopqrstuvwxyz"); + ArrayPushString(a, "jklmnopqrstuvwxyz"); + ArrayPushString(a, "ijklmnopqrstuvwxyz"); + ArrayPushString(a, "hijklmnopqrstuvwxyz"); + ArrayPushString(a, "ghijklmnopqrstuvwxyz"); + ArrayPushString(a, "fghijklmnopqrstuvwxyz"); + ArrayPushString(a, "efghijklmnopqrstuvwxyz"); + ArrayPushString(a, "defghijklmnopqrstuvwxyz"); + ArrayPushString(a, "cdefghijklmnopqrstuvwxyz"); + ArrayPushString(a, "bcdefghijklmnopqrstuvwxyz"); + ArrayPushString(a, "abcdefghijklmnopqrstuvwxyz"); + + new OldSize=ArraySize(a); + + ArraySort(a, "sortcallback"); + + test(ArraySize(a),OldSize); + + new buff[40]; + + ArrayGetString(a,0,buff,sizeof(buff)-1); + + test(strcmp(buff,"abcdefghijklmnopqrstuvwxyz"),0); + + ArrayGetString(a,25,buff,sizeof(buff)-1); + + test(strcmp(buff,"z"),0); + + + new start='a'; + + for (new i=0;i=0 ; i--) + { + if (i % 2 == 0) + { + ArrayDeleteItem(a, i); + } + } + test(ArraySize(a), 500); + for (new i=0; i< 500; i++) + { + ArrayGetCell(a, i, v); + + // All items should be incrementing odd numbers + test(((i + 1) * 2) - 1, v); + + // All remaining entries should be odd + test((v & 1), 1); + } + ArrayDestroy(a); + + a=ArrayCreate(1); + // Repeat the same test, but check even numbers + for (new i=0; i<1000; i++) + { + ArrayPushCell(a, i); + } + for (new i=ArraySize(a) - 1; i>=0 ; i--) + { + if (i % 2 == 1) + { + ArrayDeleteItem(a, i); + } + } + test(ArraySize(a), 500); + for (new i=0; i< 500; i++) + { + ArrayGetCell(a, i, v); + + // All items should be incrementing even numbers + test(((i + 1) * 2) - 2, v); + + // All remaining entries should be even + test((v & 1), 0); + } + ArrayDestroy(a); + + showres(); +} +public arraytest6() +{ + server_print("Testing ArrayInsertCellAfter()..."); + + new Array:a=ArrayCreate(1); + + for (new i=0; i<10;i++) + { + ArrayPushCell(a, i); + new item=ArraySize(a)-1; + for (new j=0; j<10; j++) + { + ArrayInsertCellAfter(a, item + j, j); + } + } + + test(ArraySize(a), 110); + + new v; + for (new i=0; i<110; i++) + { + ArrayGetCell(a, i, v); + + test(v, i / 10); + for (new j=0; j<10; j++) + { + ArrayGetCell(a, ++i, v); + test(v, j); + } + } + + + ArrayDestroy(a); + + showres(); +} +public arraytest7() +{ + server_print("Testing ArrayInsertCellBefore()..."); + + new Array:a=ArrayCreate(1); + + for (new i=0; i<10;i++) + { + ArrayPushCell(a, i); + new item=ArraySize(a)-1; + for (new j=0; j<10; j++) + { + ArrayInsertCellBefore(a, item, j); + } + } + + test(ArraySize(a), 110); + + new v; + for (new i=0; i<110; i++) + { + for (new j=9; j>=0; j--) + { + ArrayGetCell(a, i++, v); + test(v, j); + } + + ArrayGetCell(a, i, v); + + test(v, (i - 10) / 10); + } + + + ArrayDestroy(a); + + showres(); +} +public arraytest8() +{ + server_print("Testing ArraySwap()..."); + new Array:a=ArrayCreate(1); + + for (new i=0; i<10; i++) + { + ArrayPushCell(a, i); + } + for (new i=0; i<5; i++) + { + ArraySwap(a, i, (10 - (i + 1))); + } + new v; + for (new i=0; i<5; i++) + { + ArrayGetCell(a, i, v); + + test(v, (10 - (i + 1))); + } + + ArrayDestroy(a); + + showres(); +} \ No newline at end of file