//----------------------------------------------------------
//
//   xStreamer - By Boylett
//
//----------------------------------------------------------

#define LINUX 1
//#define DEBUG 1

#include <iostream>
#include <new>
#include <memory>
#include <math.h>

#ifdef LINUX

#include <sys/time.h>
#include <time.h>

#else

#include <Windows.h>
#include <Winbase.h>

#endif

#include "../SDK/amx/amx.h"
#include "../SDK/plugincommon.h"

using namespace std;

#define MAX_PLAYERS 200
#define MAX_OBJECTS 150

#ifdef DEBUG

#define debug(a,b) logprintf("%d: %s",a,b);

#else

#define debug(a,b)

#endif

//----------------------------------------------------------

typedef void (*logprintf_t)(char* format, ...);

logprintf_t logprintf;
void **ppPluginData;
extern void *pAMXFunctions;

//----------------------------------------------------------

#define MIN_AREA -4000.0 // map is -3000, but allow for maps over the sea
#define MAX_AREA 4000.0
#define AREA_SIZE 100.0 // This means only objects within a 300.0 by 300.0 square around the player are checked.

#define TOTAL_SIZE (MAX_AREA - MIN_AREA)
#define AREA_SPLIT (TOTAL_SIZE / AREA_SIZE)

//----------------------------------------------------------

struct object_layout
{
	int objid;
	int modelid;
	float x;
	float y;
	float z;
	float rx;
	float ry;
	float rz;
	bool moving;
	float speed;
	float mx;
	float my;
	float mz;
	int moving_start;
	int vw;
	int interior;
	object_layout * prevobj;
	object_layout * nextobj;
	AMX * amx;
};

object_layout * objects;

struct playerobj_list
{
	object_layout * objectid;
	float dist;
	int sampid;
};

playerobj_list player_objects[MAX_PLAYERS][MAX_OBJECTS];

struct area_layout
{
	object_layout * object;
	area_layout * prevobj;
	area_layout * nextobj;
};

area_layout * areas[(int)AREA_SPLIT][(int)AREA_SPLIT];

int fs_loaded = 0;
AMX * fs_amx;

//----------------------------------------------------------

PLUGIN_EXPORT unsigned int PLUGIN_CALL Supports() 
{
	return SUPPORTS_VERSION | SUPPORTS_AMX_NATIVES;
}

//----------------------------------------------------------

PLUGIN_EXPORT bool PLUGIN_CALL Load(void **ppData) 
{
	pAMXFunctions = ppData[PLUGIN_DATA_AMX_EXPORTS];
	logprintf = (logprintf_t)ppData[PLUGIN_DATA_LOGPRINTF];

	logprintf("xStreamer plugin loaded");
	return true;
}

//----------------------------------------------------------

PLUGIN_EXPORT void PLUGIN_CALL Unload()
{
	logprintf("xStreamer plugin unloaded");
}

//----------------------------------------------------------

#ifdef LINUX

int TickCount()
{
    long ticks, usec, sec;
    struct timeval tempo;
    gettimeofday(&tempo, NULL);
    usec = tempo.tv_usec;
    sec = tempo.tv_sec;
    ticks = (1000 * sec) + (usec / 1000);
	return (int)ticks;
}

#else

int TickCount()
{
	return (int)GetTickCount();
}

#endif

float GetPointDistanceToPointSquared(float x,float y,float z,float x2,float y2,float z2)
{
	return pow(x - x2,2) + pow(y - y2,2) + pow(z - z2,2);
}

int GetPlayerObjectID(int playerid,object_layout * objectid)
{
	for(int i = 0; i < MAX_OBJECTS; i++)
	{
		if(player_objects[playerid][i].objectid == objectid)
		{
			return i;
		}
	}
	return -1;
}

int GetFurtherstPlayerObject(int playerid, float * dist)
{
	int furtherst = -1;
	*dist = -1.0;
	for(int i = 0; i < MAX_OBJECTS; i++)
	{
		if(player_objects[playerid][i].objectid == 0)
		{
			*dist = 1000000000.0;
			return i;
		}
		else if(player_objects[playerid][i].dist > *dist)
		{
			*dist = player_objects[playerid][i].dist;
			furtherst = i;
		}
	}
	return furtherst;
}

int sampDestroyPlayerObject(int playerid,int objectid)
{
	if(objectid == -1) return 0;
	if(fs_amx == 0) return 0;
	int idx;
	if(!amx_FindPublic(fs_amx, "sampDestroyPlayerObject", &idx))
	{
		cell ret;
		amx_Push(fs_amx, objectid);
		amx_Push(fs_amx, playerid);
		amx_Exec(fs_amx, &ret, idx);
		return (int)ret;
	}
	return 0;
}

int sampCreatePlayerObject(int playerid,int modelid,float x,float y,float z,float rx,float ry,float rz)
{
	if(fs_amx == 0) return -1;
	int idx;
	if(!amx_FindPublic(fs_amx, "sampCreatePlayerObject", &idx))
	{
		cell ret;
		amx_Push(fs_amx, amx_ftoc(rz));
		amx_Push(fs_amx, amx_ftoc(ry));
		amx_Push(fs_amx, amx_ftoc(rx));
		amx_Push(fs_amx, amx_ftoc(z));
		amx_Push(fs_amx, amx_ftoc(y));
		amx_Push(fs_amx, amx_ftoc(x));
		amx_Push(fs_amx, modelid);
		amx_Push(fs_amx, playerid);
		amx_Exec(fs_amx, &ret, idx);
		return (int)ret;
	}
	return -1;
}

int sampMovePlayerObject(int playerid,int objectid,float x,float y,float z,float speed)
{
	if(fs_amx == 0) return 0;
	int idx;
	if(!amx_FindPublic(fs_amx, "sampMovePlayerObject", &idx))
	{
		cell ret;
		amx_Push(fs_amx, amx_ftoc(speed));
		amx_Push(fs_amx, amx_ftoc(z));
		amx_Push(fs_amx, amx_ftoc(y));
		amx_Push(fs_amx, amx_ftoc(x));
		amx_Push(fs_amx, objectid);
		amx_Push(fs_amx, playerid);
		amx_Exec(fs_amx, &ret, idx);
		return (int)ret;
	}
	return 0;
}

int sampOnStreamedObjectMoved(AMX * amx, int objectid)
{
	int idx;
	if(!amx_FindPublic(amx, "OnStreamedObjectMoved", &idx))
	{
		cell ret;
		amx_Push(amx, objectid);
		amx_Exec(amx, &ret, idx);
		return (int)ret;
	}
	return 0;
}

int sampStopPlayerObject(int playerid,int objectid)
{
	if(fs_amx == 0) return 0;
	int idx;
	if(!amx_FindPublic(fs_amx, "sampStopPlayerObject", &idx))
	{
		cell ret;
		amx_Push(fs_amx, objectid);
		amx_Push(fs_amx, playerid);
		amx_Exec(fs_amx, &ret, idx);
		return (int)ret;
	}
	return 0;
}

int GetAreaID(float position)
{
	int id = (int)((position - MIN_AREA) / AREA_SIZE);
	if(id < 0) id = -1;
	else if(id >= AREA_SPLIT) id = -1;
	return id;
}

void AddToArea(int x,int y,object_layout * object)
{
	if(areas[x][y] == 0)
	{
		areas[x][y] = new area_layout;
		(*areas[x][y]).object = object;
		(*areas[x][y]).prevobj = 0;
		(*areas[x][y]).nextobj = 0;
	}
	else
	{
		area_layout * oldarea;
		oldarea = areas[x][y];
		areas[x][y] = new area_layout;
		(*areas[x][y]).object = object;
		(*areas[x][y]).prevobj = oldarea;
		(*oldarea).nextobj = areas[x][y];
		(*areas[x][y]).nextobj = 0; // dimonml
	}
}

void RemFromArea(int x,int y,object_layout * object)
{
	area_layout * curarea;
	curarea = areas[x][y];

	while(curarea != 0)
	{
		if((*curarea).object == object)
		{
			if((*curarea).nextobj != 0)
			{
				(*(*curarea).nextobj).prevobj = (*curarea).prevobj;
			}

			if((*curarea).prevobj != 0)
			{
				(*(*curarea).prevobj).nextobj = (*curarea).nextobj;
			}

			if(curarea == areas[x][y])
			{
				if((*curarea).prevobj != 0)
				{
					areas[x][y] = (*curarea).prevobj;
				}
				else
				{
					areas[x][y] = 0;
				}
			}

			delete curarea;;
			break;
		}
		else
		{
			curarea = (*curarea).prevobj;
		}
	}
}

void GetMovementXYZ(object_layout * objectid)
{
	if((*(objectid)).moving)
	{
		int time = TickCount() - (*(objectid)).moving_start;
		float utime = ((float)time) / 1000.0;
		float dist = sqrt(GetPointDistanceToPointSquared(
			(*(objectid)).x,
			(*(objectid)).y,
			(*(objectid)).z,
			(*(objectid)).mx,
			(*(objectid)).my,
			(*(objectid)).mz
		));
		float donedist = (*(objectid)).speed * utime;
		int oldx = GetAreaID((*(objectid)).x);
		int oldy = GetAreaID((*(objectid)).y);
		if(donedist >= dist)
		{
			(*(objectid)).moving = false;
			(*(objectid)).x = (*(objectid)).mx;
			(*(objectid)).y = (*(objectid)).my;
			(*(objectid)).z = (*(objectid)).mz;
			sampOnStreamedObjectMoved((*(objectid)).amx,(*(objectid)).objid);
		}
		else
		{
			float frdone = (donedist / dist);
			(*(objectid)).x += ((*(objectid)).mx - (*(objectid)).x) * frdone;
			(*(objectid)).y += ((*(objectid)).my - (*(objectid)).y) * frdone;
			(*(objectid)).z += ((*(objectid)).mz - (*(objectid)).z) * frdone;
			(*(objectid)).moving_start = TickCount();
		}
		int newx = GetAreaID((*(objectid)).x);
		int newy = GetAreaID((*(objectid)).y);
		if(newx != oldx || newy != oldy)
		{
			if(oldx != -1 && oldy != -1)
			{
				RemFromArea(oldx,oldy,objectid);
			}
			if(newx != -1 && newy != -1)
			{
				AddToArea(newx,newy,objectid);
			}
		}
	}
}

//----------------------------------------------------------
// native ConnectPlayer(playerid);

static cell AMX_NATIVE_CALL n_ConnectPlayer( AMX* amx, cell* params ) // These are only called by the filterscript, so no fs_loaded check is needed
{
	if(params[0] != 4)
	{
		logprintf("ConnectPlayer: Expecting 1 param, but found %d",params[0] / 4);
		return 0;
	}

	int playerid = params[1];

	for(int i = 0; i < MAX_OBJECTS; i++)
	{
		player_objects[playerid][i].objectid = 0;
	}

	return 1;
}

//----------------------------------------------------------
// native DisconnectPlayer(playerid);

static cell AMX_NATIVE_CALL n_DisconnectPlayer( AMX* amx, cell* params )
{
	if(params[0] != 4)
	{
		logprintf("DisconnectPlayer: Expecting 1 param, but found %d",params[0] / 4);
		return 0;
	}

	int playerid = params[1];

	for(int i = 0; i < MAX_OBJECTS; i++)
	{
		player_objects[playerid][i].objectid = 0;
	}

	return 1;
}

//----------------------------------------------------------
// native UpdateMovingObjects();

static cell AMX_NATIVE_CALL n_UpdateMovingObjects( AMX* amx, cell* params )
{
	if(fs_loaded != 1)
	{
		if(fs_loaded != -1)
		{
			logprintf("xStreamer error: xStreamer.amx filterscript not loaded!");
			fs_loaded = -1;
		}
	}

	object_layout * curobj = objects;
	while(curobj != 0)
	{
		GetMovementXYZ(curobj);
		curobj = (*curobj).prevobj;
	}

	return 1;
}

//----------------------------------------------------------
// native StreamObjects(playerid,Float:x,Float:y,Float:z,virtualworld,interior);

static cell AMX_NATIVE_CALL n_StreamObjects( AMX* amx, cell* params )
{
	if(fs_loaded != 1)
	{
		if(fs_loaded != -1)
		{
			logprintf("xStreamer error: xStreamer.amx filterscript not loaded!");
			fs_loaded = -1;
		}
		return 0; // this function won't work without the filterscript...
	}

	if(params[0] != 24)
	{
		logprintf("StreamObjects: Expecting 6 params, but found %d",params[0] / 4);
		return 0;
	}

	debug(0,"-- StreamObjects")

	int playerid = params[1];
	float
		x = amx_ctof(params[2]),
		y = amx_ctof(params[3]),
		z = amx_ctof(params[4]);
	int vw = params[5];
	int interior = params[6];
	int farobj = 0;
	float fardist = -1.0;
	bool objchanged[MAX_OBJECTS];
	int startx = GetAreaID(x) - 1;
	int endx = startx + 3;
	int starty = GetAreaID(y) - 1;
	int endy = starty + 3;

	debug(0,"Defined variables")

	if(startx < 0) startx = 0;
	if(starty < 0) starty = 0;
	if(startx >= AREA_SPLIT) startx = AREA_SPLIT - 1;
	if(starty >= AREA_SPLIT) starty = AREA_SPLIT - 1;
	if(endx < 0) endx = 0;
	if(endy < 0) endy = 0;
	if(endx > AREA_SPLIT) endx = AREA_SPLIT;
	if(endy > AREA_SPLIT) endy = AREA_SPLIT;

	debug(0,"Set up area")

	for(int i = 0; i < MAX_OBJECTS; i++)
	{
		objchanged[i] = false;
		debug(0,"Loop 1")
		if(player_objects[playerid][i].objectid != 0)
		{
			debug(0,"Got object")
			if((*(player_objects[playerid][i].objectid)).vw != vw || (*(player_objects[playerid][i].objectid)).interior != interior)
			{
				debug(0,"Not in same int/vw")
				sampDestroyPlayerObject(playerid,player_objects[playerid][i].sampid);
				debug(0,"Destroyed")
				player_objects[playerid][i].objectid = 0;
				debug(0,"Zeroed")
				farobj = i;
				fardist = 1000000000.0;
			}
			else
			{
				debug(0,"In same int/vw")
				player_objects[playerid][i].dist = GetPointDistanceToPointSquared(x,y,z,
						(*(player_objects[playerid][i].objectid)).x,
						(*(player_objects[playerid][i].objectid)).y,
						(*(player_objects[playerid][i].objectid)).z
					);
				debug(0,"Distance")
				if(player_objects[playerid][i].dist > fardist)
				{
					debug(0,"Far distance")
					farobj = i;
					fardist = player_objects[playerid][i].dist;
				}
				debug(0,"Done distance")
			}
		}
		else
		{
			debug(0,"Set far")
			farobj = i;
			fardist = 1000000000.0;
		}
	}

	debug(0,"Done loop 1")

	for(int ix = startx; ix < endx; ix++)
	{
		for(int iy = starty; iy < endy; iy++)
		{
			debug(0,"Loop 2")
			area_layout * curareaobj = areas[ix][iy];
			debug(0,"Area")
			while(curareaobj != 0)
			{
				debug(0,"Exists")
				int playerobj = GetPlayerObjectID(playerid,(*curareaobj).object);
				debug(0,"Player objects")
				if(playerobj == -1)
				{
					debug(0,"Is -1")
					if((*(*curareaobj).object).vw == vw && (*(*curareaobj).object).interior == interior)
					{
						debug(0,"Distance check 2")
						float dist = GetPointDistanceToPointSquared(x,y,z,
							(*(*curareaobj).object).x,
							(*(*curareaobj).object).y,
							(*(*curareaobj).object).z
						);
						debug(0,"Done distance check 2")
						if(dist < fardist)
						{
							debug(0,"Smaller than far distance")
							if(player_objects[playerid][farobj].objectid != 0 && !objchanged[farobj])
							{
								debug(0,"Destroy 2")
								sampDestroyPlayerObject(playerid,player_objects[playerid][farobj].sampid);
							}
							debug(0,"Destroyed 2")
							player_objects[playerid][farobj].dist = dist;
							debug(0,"Dist 2")
							player_objects[playerid][farobj].objectid = (*curareaobj).object;
							debug(0,"object 2")
							objchanged[farobj] = true;
							farobj = GetFurtherstPlayerObject(playerid,&fardist);
							debug(0,"Far distance 2")
						}
					}
				}
				debug(0,"Next object 2")
				curareaobj = (*curareaobj).prevobj;
				debug(0,"Next object done 2")
			}
		}
	}
	
	debug(0,"Loop 2 done")

	for(int i = 0; i < MAX_OBJECTS; i++)
	{
		debug(0,"Loop 3")
		if(objchanged[i])
		{
			debug(0,"Changed")
			player_objects[playerid][i].sampid = sampCreatePlayerObject(playerid,
					(*(player_objects[playerid][i].objectid)).modelid,
					(*(player_objects[playerid][i].objectid)).x,
					(*(player_objects[playerid][i].objectid)).y,
					(*(player_objects[playerid][i].objectid)).z,
					(*(player_objects[playerid][i].objectid)).rx,
					(*(player_objects[playerid][i].objectid)).ry,
					(*(player_objects[playerid][i].objectid)).rz						
				);
			debug(0,"Created")
			if((*(player_objects[playerid][i].objectid)).moving)
			{
				debug(0,"Moving")
				sampMovePlayerObject(playerid,player_objects[playerid][i].sampid,
					(*(player_objects[playerid][i].objectid)).mx,
					(*(player_objects[playerid][i].objectid)).my,
					(*(player_objects[playerid][i].objectid)).mz,
					(*(player_objects[playerid][i].objectid)).speed
				);
				debug(0,"Moved")
			}
		}
	}

	debug(0,"Return")

	return 1;
}

//----------------------------------------------------------
// native CreateStreamedObject(modelid,Float:x,Float:y,Float:z,Float:rx,Float:ry,Float:rz,virtualworld = 0,interior = 0);

static cell AMX_NATIVE_CALL n_CreateStreamedObject( AMX* amx, cell* params )
{
	if(fs_loaded != 1)
	{
		if(fs_loaded != -1)
		{
			logprintf("xStreamer error: xStreamer filterscript not loaded!");
			fs_loaded = -1;
		}
	}

	if(params[0] != 36)
	{
		logprintf("CreateStreamedObject: Expecting 9 params, but found %d",params[0] / 4);
		return -1;
	}

	object_layout newobj;

	newobj.modelid = params[1];
	newobj.x = amx_ctof(params[2]),
	newobj.y = amx_ctof(params[3]),
	newobj.z = amx_ctof(params[4]);
	newobj.rx = amx_ctof(params[5]),
	newobj.ry = amx_ctof(params[6]),
	newobj.rz = amx_ctof(params[7]);
	newobj.vw = params[8];
	newobj.interior = params[9];
	newobj.prevobj = 0;
	newobj.nextobj = 0;
	newobj.moving = false;
	newobj.amx = amx;

	int xid = GetAreaID(newobj.x);
	int yid = GetAreaID(newobj.y);

	if(xid == -1 || yid == -1)
	{
		return -1;
	}

	if(objects == 0)
	{
		newobj.objid = 0;
		objects = new object_layout;
		*objects = newobj;
	}
	else
	{
		object_layout * oldobj;
		oldobj = objects;
		objects = new object_layout;
		newobj.objid = (*oldobj).objid + 1;
		*objects = newobj;
		(*objects).prevobj = oldobj;
		(*oldobj).nextobj = objects;
	}

	AddToArea(xid,yid,objects);

	return newobj.objid;
}

//----------------------------------------------------------
// native DestroyStreamedObject(objectid);

static cell AMX_NATIVE_CALL n_DestroyStreamedObject( AMX* amx, cell* params )
{
	if(fs_loaded != 1)
	{
		if(fs_loaded != -1)
		{
			logprintf("xStreamer error: xStreamer filterscript not loaded!");
			fs_loaded = -1;
		}
	}

	if(params[0] != 4)
	{
		logprintf("DestroyStreamedObject: Expecting 1 param, but found %d",params[0] / 4);
		return 0;
	}

	int objectid = params[1];

	if(objectid == -1)
	{
		return 0;
	}

	object_layout * curobj = objects;

	while(curobj != 0)
	{
		if((*curobj).objid == objectid)
		{
			if((*curobj).nextobj != 0)
			{
				(*(*curobj).nextobj).prevobj = (*curobj).prevobj;
			}

			if((*curobj).prevobj != 0)
			{
				(*(*curobj).prevobj).nextobj = (*curobj).nextobj;
			}

			if(objects == curobj)
			{
				if((*curobj).prevobj != 0)
				{
					objects = (*curobj).prevobj;
				}
				else
				{
					objects = 0;
				}
			}

			for(int playerid = 0; playerid < MAX_PLAYERS; playerid++)
			{
				for(int i = 0; i < MAX_OBJECTS; i++)
				{
					if(player_objects[playerid][i].objectid == curobj)
					{
						sampDestroyPlayerObject(playerid,player_objects[playerid][i].sampid);
						player_objects[playerid][i].objectid = 0;
					}
				}
			}

			int xid = GetAreaID((*curobj).x);
			int yid = GetAreaID((*curobj).y);
			if(xid != -1 && yid != -1)
			{
				RemFromArea(xid,yid,curobj);
			}

			delete curobj;

			return 1;
		}
		else
		{
			curobj = (*curobj).prevobj;
		}
	}

	return 0;
}

//----------------------------------------------------------
// native MoveStreamedObject(objectid,float x,float y,float z,float speed);

static cell AMX_NATIVE_CALL n_MoveStreamedObject( AMX* amx, cell* params )
{
	if(fs_loaded != 1)
	{
		if(fs_loaded != -1)
		{
			logprintf("xStreamer error: xStreamer filterscript not loaded!");
			fs_loaded = -1;
		}
	}

	if(params[0] != 20)
	{
		logprintf("MoveStreamedObject: Expecting 5 params, but found %d",params[0] / 4);
		return 0;
	}

	int objectid = params[1];

	if(objectid == -1)
	{
		return 0;
	}

	object_layout * curobj = objects;

	while(curobj != 0)
	{
		if((*curobj).objid == objectid)
		{
			GetMovementXYZ(curobj);
			(*(curobj)).mx = amx_ctof(params[2]);
			(*(curobj)).my = amx_ctof(params[3]);
			(*(curobj)).mz = amx_ctof(params[4]);
			(*(curobj)).speed = amx_ctof(params[5]);
			(*(curobj)).moving_start = TickCount();
			(*(curobj)).moving = true;

			for(int playerid = 0; playerid < MAX_PLAYERS; playerid++)
			{
				for(int i = 0; i < MAX_OBJECTS; i++)
				{
					if(player_objects[playerid][i].objectid == curobj)
					{
						sampMovePlayerObject(playerid,
							player_objects[playerid][i].sampid,
							(*curobj).mx,
							(*curobj).my,
							(*curobj).mz,
							(*curobj).speed
						);
						break;
					}
				}
			}

			return 1;
		}
		else
		{
			curobj = (*curobj).prevobj;
		}
	}
	return 0;
}

//----------------------------------------------------------
// native StopStreamedObject(objectid);

static cell AMX_NATIVE_CALL n_StopStreamedObject( AMX* amx, cell* params )
{
	if(fs_loaded != 1)
	{
		if(fs_loaded != -1)
		{
			logprintf("xStreamer error: xStreamer filterscript not loaded!");
			fs_loaded = -1;
		}
	}

	if(params[0] != 4)
	{
		logprintf("StopStreamedObject: Expecting 1 param, but found %d",params[0] / 4);
		return 0;
	}

	int objectid = params[1];

	if(objectid == -1)
	{
		return 0;
	}

	object_layout * curobj = objects;

	while(curobj != 0)
	{
		if((*curobj).objid == objectid)
		{

			if(!(*(curobj)).moving) return 0;
			GetMovementXYZ(curobj);
			(*(curobj)).moving = false;

			for(int playerid = 0; playerid < MAX_PLAYERS; playerid++)
			{
				for(int i = 0; i < MAX_OBJECTS; i++)
				{
					if(player_objects[playerid][i].objectid == curobj)
					{
						sampStopPlayerObject(playerid,
							player_objects[playerid][i].sampid
						);
						break;
					}
				}
			}

			return 1;
		}
		curobj = (*curobj).prevobj;
	}

	return 0;
}

//----------------------------------------------------------
// native DestroyObjectsCreatedByMe();

static cell AMX_NATIVE_CALL n_DestroyObjectsCreatedByMe( AMX* amx, cell* params )
{
	if(fs_loaded != 1)
	{
		if(fs_loaded != -1)
		{
			logprintf("xStreamer error: xStreamer filterscript not loaded!");
			fs_loaded = -1;
		}
	}

	object_layout * curobj = objects;

	while(curobj != 0)
	{
		if((*curobj).amx == amx)
		{
			if((*curobj).nextobj != 0)
			{
				(*(*curobj).nextobj).prevobj = (*curobj).prevobj;
			}

			if((*curobj).prevobj != 0)
			{
				(*(*curobj).prevobj).nextobj = (*curobj).nextobj;
			}
			if(objects == curobj)
			{
				if((*curobj).prevobj != 0)
				{
					objects = (*curobj).prevobj;
				}
				else
				{
					objects = 0;
				}
			}

			for(int playerid = 0; playerid < MAX_PLAYERS; playerid++)
			{
				for(int i = 0; i < MAX_OBJECTS; i++)
				{
					if(player_objects[playerid][i].objectid == curobj)
					{
						sampDestroyPlayerObject(playerid,player_objects[playerid][i].sampid);
						player_objects[playerid][i].objectid = 0;
						break;
					}
				}
			}

			int xid = GetAreaID((*curobj).x);
			int yid = GetAreaID((*curobj).y);
			if(xid != -1 && yid != -1)
			{
				RemFromArea(xid,yid,curobj);
			}

			object_layout * tempobj = curobj;
			curobj = (*curobj).prevobj;
			delete tempobj;
		}
		else
		{
			curobj = (*curobj).prevobj;
		}
	}

	return 1;
}

//----------------------------------------------------------
// native xStreamer_Filterscript();

static cell AMX_NATIVE_CALL n_xStreamer_Filterscript( AMX* amx, cell* params )
{
	fs_amx = amx;
	fs_loaded = 1;
	logprintf("xStreamer: xStreamer filterscript has been detected");
	return 1;
}

//----------------------------------------------------------

AMX_NATIVE_INFO Natives[ ] =
{
	{ "ConnectPlayer",					n_ConnectPlayer },
	{ "DisconnectPlayer",				n_DisconnectPlayer },
	{ "StreamObjects",					n_StreamObjects },
	{ "CreateStreamedObject",			n_CreateStreamedObject },
	{ "DestroyStreamedObject",			n_DestroyStreamedObject },
	{ "MoveStreamedObject",				n_MoveStreamedObject },
	{ "StopStreamedObject",				n_StopStreamedObject },
	{ "DestroyObjectsCreatedByMe",		n_DestroyObjectsCreatedByMe },
	{ "xStreamer_Filterscript",			n_xStreamer_Filterscript },
	{ "UpdateMovingObjects",			n_UpdateMovingObjects },
	{ 0,0 }
};

//----------------------------------------------------------

PLUGIN_EXPORT int PLUGIN_CALL AmxLoad( AMX *amx ) 
{
	return amx_Register( amx, Natives, -1 );
}

//----------------------------------------------------------

PLUGIN_EXPORT int PLUGIN_CALL AmxUnload( AMX *amx ) 
{
	if(amx == fs_amx)
	{
		logprintf("xStreamer: xStreamer filterscript has just unloaded! The streamer won't work until you reload it");
		fs_amx = 0;
		fs_loaded = -1;
	}
	return AMX_ERR_NONE;
}
