Added pseudo dynamic array natives.

Changed some of the "..." tags to "any".
This commit is contained in:
Steve Dudenhoeffer 2007-04-24 16:38:36 +00:00
parent bfe1ff6e15
commit d563ecb060
17 changed files with 1705 additions and 28 deletions

View File

@ -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 \ 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 \ 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 \ 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 LINK = -lgcc -static-libgcc

View File

@ -85,6 +85,7 @@ extern AMX_NATIVE_INFO vault_Natives[];
extern AMX_NATIVE_INFO msg_Natives[]; extern AMX_NATIVE_INFO msg_Natives[];
extern AMX_NATIVE_INFO vector_Natives[]; extern AMX_NATIVE_INFO vector_Natives[];
extern AMX_NATIVE_INFO g_SortNatives[]; extern AMX_NATIVE_INFO g_SortNatives[];
extern AMX_NATIVE_INFO g_DataStructNatives[];
#ifndef __linux__ #ifndef __linux__
#define DLLOAD(path) (DLHANDLE)LoadLibrary(path) #define DLLOAD(path) (DLHANDLE)LoadLibrary(path)

607
amxmodx/datastructs.cpp Normal file
View File

@ -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<CellVector*> 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<cell>(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<ArraySort_t *> 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 }
};

323
amxmodx/datastructs.h Normal file
View File

@ -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; i<this->count - 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<CellVector*> 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

View File

@ -1,5 +1,6 @@
#include "amxmodx.h" #include "amxmodx.h"
#include "format.h" #include "format.h"
#include "datastructs.h"
#include "amxmod_compat.h" #include "amxmod_compat.h"
//Adapted from Quake3's vsprintf //Adapted from Quake3's vsprintf
@ -494,6 +495,21 @@ reswitch:
AddHex(&buf_p, llen, static_cast<unsigned int>(*get_amxaddr(amx, params[arg])), width, flags); AddHex(&buf_p, llen, static_cast<unsigned int>(*get_amxaddr(amx, params[arg])), width, flags);
arg++; arg++;
break; break;
case 'S':
{
CHECK_ARGS(0);
// %S is passed a pointer directly to a cell string.
cell* ptr=reinterpret_cast<cell*>(*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': case 's':
CHECK_ARGS(0); CHECK_ARGS(0);
if (amx->flags & AMX_FLAG_OLDFILE) if (amx->flags & AMX_FLAG_OLDFILE)

View File

@ -47,6 +47,7 @@
#include "messages.h" #include "messages.h"
#include "amxmod_compat.h" #include "amxmod_compat.h"
#include "datastructs.h"
#include "CFlagManager.h" #include "CFlagManager.h"
#include "svn_version.h" #include "svn_version.h"
@ -388,6 +389,12 @@ int C_Spawn(edict_t *pent)
FlagMan.LoadFile(); FlagMan.LoadFile();
for (unsigned int i=0; i<VectorHolder.size(); i++)
{
delete VectorHolder[i];
};
VectorHolder.clear();
char map_pluginsfile_path[256]; char map_pluginsfile_path[256];
char configs_dir[256]; char configs_dir[256];

View File

@ -578,6 +578,7 @@ int set_amxnatives(AMX* amx, char error[128])
amx_Register(amx, msg_Natives, -1); amx_Register(amx, msg_Natives, -1);
amx_Register(amx, vector_Natives, -1); amx_Register(amx, vector_Natives, -1);
amx_Register(amx, g_SortNatives, -1); amx_Register(amx, g_SortNatives, -1);
amx_Register(amx, g_DataStructNatives, -1);
if (amx->flags & AMX_FLAG_OLDFILE) if (amx->flags & AMX_FLAG_OLDFILE)
{ {

View File

@ -770,6 +770,14 @@
RelativePath="..\CVector.h" RelativePath="..\CVector.h"
> >
</File> </File>
<File
RelativePath="..\datastructs.cpp"
>
</File>
<File
RelativePath="..\datastructs.h"
>
</File>
<File <File
RelativePath="..\debugger.h" RelativePath="..\debugger.h"
> >

View File

@ -41,6 +41,8 @@
#include <sqlx> #include <sqlx>
#endif #endif
new Vector:AdminList;
new AdminCount; new AdminCount;
new PLUGINNAME[] = "AMX Mod X" 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/amxx.cfg", configsDir) // Execute main configuration file
server_cmd("exec %s/sql.cfg", configsDir) 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 #if defined USING_SQL
server_cmd("amx_sqladmins") server_cmd("amx_sqladmins")
#else #else

View File

@ -21,6 +21,7 @@
#include <messages> #include <messages>
#include <vector> #include <vector>
#include <sorting> #include <sorting>
#include <array>
/* Function is called just after server activation. /* Function is called just after server activation.
* Good place for configuration loading, commands and cvars registration. */ * 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[]=""); native show_motd(player,const message[],const header[]="");
/* Sends message to player. Set index to 0 to send text globaly. */ /* 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. */ /* 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. */ /* Sends message to console. */
native console_print(id,const message[],{Float,Sql,Result,_}:...); native console_print(id,const message[],any:...);
/* Sends command to console. */ /* 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 /* Registers event on which a given function will be called
* Flags: * 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); 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. */ /* 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.). */ /* 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[] = ""); 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)). * 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)). * Integer is returned by function (new me = read_data(3)).
* Float is set in second parameter (read_data(3,value)). */ * 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. */ /* Returns number of values in client message. */
native read_datanum(); native read_datanum();
@ -171,7 +172,7 @@ native parse_loguser(const text[], name[], nlen, &userid = -2, authid[] = "", al
/* Prints message to server console. /* Prints message to server console.
* You may use text formating (f.e. server_print("%-32s %.2f!","hello",7.345)) */ * 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. */ /* Returns 1 or 0. */
native is_map_valid(const mapname[]); native is_map_valid(const mapname[]);
@ -305,13 +306,13 @@ native user_kill(index,flag=0);
* ... - optional parameters * ... - optional parameters
* Return value: * Return value:
* always 0 */ * always 0 */
native log_amx(const string[], {Float,Sql,Result,_}:...); native log_amx(const string[], any:...);
/* Sends message to standard HL logs. */ /* 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. */ /* 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. /* Returns number of players put in server.
* If flag is set then also connecting are counted. */ * If flag is set then also connecting are counted. */
@ -367,7 +368,7 @@ native find_player(const flags[], ... );
native remove_quotes(text[]); native remove_quotes(text[]);
/* Executes command on player. */ /* 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!). /* This is an emulation of a client command (commands aren't send to client!).
* It allows to execute some commands on players and bots. * 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[]=""); native engclient_cmd(index,const command[],const arg1[]="",const arg2[]="");
/* Executes command on a server console. */ /* 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. */ /* Sets a cvar to given value. */
native set_cvar_string(const cvar[],const 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 * You must use set_hudmessage, although the channel parameter is
* entirely ignored. * 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 * Clears the display on a HudSync Object. This is essentially the same

250
plugins/include/array.inc Normal file
View File

@ -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);

View File

@ -22,7 +22,7 @@
#pragma library engine #pragma library engine
#endif #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. */ /* Registers a client impulse to a function. Function is passed the ID of the user. */
native register_impulse(impulse, const function[]); native register_impulse(impulse, const function[]);
@ -39,7 +39,7 @@ native register_think(const Classname[], const function[]);
*/ */
/* Precaches an event. */ /* 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 */ /* set/get a user's speak flags */
native set_speak(iIndex, iSpeakFlags); 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. /* Gets parameters sent from CmdStart.
Note that you will receive modified values if any other plugin have Note that you will receive modified values if any other plugin have
changed them. */ changed them. */
native get_usercmd(type,{Float,Sql,Result,_}:...); native get_usercmd(type,any:...);
/* Sets the parameters sent from CmdStart. /* Sets the parameters sent from CmdStart.
Note that your changes will be seen by any other plugin doing get_usercmd() */ 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 /* Converts a string offset into a real string. Some of the forwards in fakemeta
uses string offsets. (FM_CreateNamedEntity) */ uses string offsets. (FM_CreateNamedEntity) */

View File

@ -31,10 +31,10 @@
* new ptr, classname[32] * new ptr, classname[32]
* pev(entid, pev_classname, ptr, classname, 31) * 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 */ /* 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 /* returns 0 if ent is invalid, >0 if valid
* (1 == valid, 2 == valid+pvPrivateData valid) * (1 == valid, 2 == valid+pvPrivateData valid)
@ -50,7 +50,7 @@ native pev_valid(entindex);
* new ptr = pev(id, pev_viewmodel) * new ptr = pev(id, pev_viewmodel)
* global_get(glb_pStringBase, ptr, model, 127) * 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. */ /* 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); 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); native unregister_forward(_forwardType, registerId, post=0);
/* Returns data for metamod */ /* 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. /* Returns the original return value of an engine function.
* This is only valid in forwards that were registered as post. * 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 get_orig_retval({Float,_}:...);
native engfunc(type,{Float,Sql,Result,AlertType,_}:...); 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 //only use this with functions that pass a Trace
// get: zero extra params - return int, one extra param = byref float or vector // get: zero extra params - return int, one extra param = byref float or vector

View File

@ -83,7 +83,7 @@ native fgets(file, buffer[], maxlength);
native fputs(file, const text[]); native fputs(file, const text[]);
//Writes a line to the file //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) //Sets the current position in a file (see SEEK_ values above)
native fseek(file, position, start); native fseek(file, position, start);
@ -97,7 +97,7 @@ native fputc(file, data);
native fungetc(file, data); native fungetc(file, data);
//Return the size of a file //Return the size of a file
native filesize(const filename[], {Float,Sql,Result,_}:...); native filesize(const filename[], any:...);
//Attempts to remove a directory. //Attempts to remove a directory.
//Note that you cannot remove a directory that has files on most //Note that you cannot remove a directory that has files on most

View File

@ -18,7 +18,7 @@ stock is_entity(id)
return is_valid_ent(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 */ /* 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); return ns_get_build(classname, value, number);
stock get_private_i(index, offset, linuxdiff=5) stock get_private_i(index, offset, linuxdiff=5)

View File

@ -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 * slower, so you should using a source string that is the same as
* the destination. * 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. /* Same as format(), except does not perform a "copy back" check.
* This means formatex() is faster, but DOES NOT ALLOW this type * 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", * This is because the output is directly stored into "buffer",
* rather than copied back at the end. * 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. /* Replacement for format_args. Much faster and %L compatible.
* This works exactly like vsnprintf() from C. * This works exactly like vsnprintf() from C.

View File

@ -0,0 +1,456 @@
#include <amxmodx>
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 (A<B) passed=1;
case TT_GreaterThan: if (A>B) 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<OldSize;i++)
{
ArrayGetString(a,i,buff,sizeof(buff)-1)
test(buff[0],start++);
}
showres();
}
public arraytest5()
{
server_print("Testing ArrayDeleteItem()...");
new Array:a=ArrayCreate(1);
new v;
for (new i=0; i<1000; i++)
{
ArrayPushCell(a, i);
}
for (new i=ArraySize(a) - 1; 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();
}