PDA

Просмотр полной версии : [Include] dc_foreach_veh - быстрый перебор транспорта



Daniel_Cortez
24.12.2015, 19:10
Внимание: Данный инклуд морально устарел. Аналогичный функционал уже реализован в foreach (https://github.com/Open-GTO/foreach), пользуйтесь им вместо стороннего дополнения.

Старое содержимое темы оставлено по историческим причинам.

Довольно простой инклуд, добавляющий для foreach новый итератор Vehicle, содержащий в себе ID всех транспортных средств, заспавненных на сервере.
Транспорт автоматически добавляется в итератор при создании (CreateVehicle/AddStaticVehicle(Ex)) и удаляется при уничтожении (DestroyVehicle).

Для работы инклуда требуется foreach/y_iterate от Y_Less.
Можете закинуть их как в папку "include" (в корне сервера), так и в "pawno/include", в инклуде учитываются оба варианта.

Примеры использования:


#include <a_samp>
#include "../include/foreach.inc"
#include "../include/dc_foreach_veh.inc"

CMD:respawnvehicles(playerid, params[])
{
if (0 == IsPlayerAdmin(playerid))
return 0;
foreach (new v:Vehicle)
SetVehicleToRespawn(v);
return SendClientMessage(playerid, -1, "Вы зареспавнили весь транспорт.");
}
ALTX:respawnvehicles("/resveh");



GetNearestVehicle(Float:x, Float:y, Float:z, Float:distance)
{
new Float:t, nearest_veh_id = INVALID_VEHICLE_ID;
foreach (new v:Vehicle)
if (floatcmp((t = GetVehicleDistanceFromPoint(v, x, y, z)), distance) != 1)
distance = t, nearest_veh_id = v;
return nearest_veh_id;
}
GetNearestVehicleToPlayer(playerid, Float:distance)
{
new Float:x, Float:y, Float:z;
if (0 == GetPlayerPos(playerid, x, y, z))
return INVALID_VEHICLE_ID;
return GetNearestVehicle(x, y, z, distance);
}



Примечания:
Нельзя удалять машины (DestroyVehicle) во время перебора итератора Vehicle с помощью foreach.
Это связано с тем, что удаление элементов из итератора во время цикла по его элементам может привести к непредсказуемым последствиям.
Пример кода, который может привести к крашу:


foreach (v:Vehicle)
DestroyVehicle(v);

Не пытайтесь в своём моде изменить значение макроса MAX_VEHICLES.
Даже если вы это значение измените, скажем, на 500, с помощью CreateVehicle/AddStaticVehicle(Ex) всё равно можно будет создать более 500 машин.
Примеры вмешательства в MAX_VEHICLES (лучше уберите их из своего скрипта):


#undef MAX_VEHICLES
#define MAX_VEHICLES 500



#if defined MAX_VEHICLES
#undef MAX_VEHICLES
#endif
#define MAX_VEHICLES 500



#if defined MAX_VEHICLES
#undef MAX_VEHICLES
#endif
enum { MAX_VEHICLES = 500 };

Инклуд работает только в том скрипте, в котором создаётся и удаляется весь транспорт на сервере. Т.е. если вы используете инклуд в моде, то и создавать/удалять транспорт тоже следует из мода, а не из фильтрскоиптов, т.к. невозможно отследить создание и удаление транспорта из другого скрипта.



Скачать: https://www.dropbox.com/s/rgv8k3h9l1bz3nb/dc_foreach_veh.inc

Автор: Daniel_Cortez (http://pro-pawn.ru/member.php?100-Daniel_Cortez)


Специально для Pro-Pawn.ru (http://www.pro-pawn.ru)
Копирование данной статьи на других ресурсах без разрешения автора запрещено!

Profyan
24.12.2015, 19:26
Сильно отличается в быстродействии от этого (http://pro-pawn.ru/showthread.php?8834-%D0%9F%D0%B5%D1%80%D0%B5%D0%B1%D0%BE%D1%80-%D0%BC%D0%B0%D1%88%D0%B8%D0%BD-%D0%BF%D0%BE-%D0%BF%D1%80%D0%B8%D0%BD%D1%86%D0%B8%D0%BF%D1%83-foreach&highlight=foreach+%D1%82%D1%80%D0%B0%D0%BD%D1%81%D0%BF%D0%BE%D1%80%D1%82) варианта ?

UPD:Если вы не обновляли include a_vehicles.inc до версии 0.3.7, то могут появится warning's по поводу addsiren.Чтобы это исправить, обновите ваши include's.

Daniel_Cortez
24.12.2015, 20:37
Я провёл несколько тестов, регулируя кол-во оборотов в циклах профайлера и кол-во машин на сервере.
Для количества машин брались значения MAX_VEHICLES, MAX_VEHICLES/8 (=250) и 0.


// Profiler v1.1 (copyright (c) 2014-2015 Daniel_Cortez) \\ Pro-Pawn.ru
// Условия использования данного кода: http://pro-pawn.ru/showthread.php?12585


/*Настройки.*/
#include <a_samp>

//==============================================================================
new forveh_vehicles[MAX_VEHICLES];
new forveh_count = 0;

#if !defined IsValidVehicle
native IsValidVehicle(vehicleid);
#endif

#define forveh(%0) for(new ouf, %0=forveh_vehicles[ouf]; ouf<forveh_count; %0=forveh_vehicles[++ouf])

stock Forveh_CreateVehicle(modelid, Float:x, Float:y, Float:z, Float:angle, color1, color2, respawn_delay, addsiren=0)
{
new vehid = CreateVehicle(modelid, x, y, z,angle, color1, color2, respawn_delay, addsiren);
forveh_vehicles[forveh_count++]=vehid;
return vehid;
}
#if defined _ALS_CreateVehicle
#undef CreateVehicle
#else
#define _ALS_CreateVehicle
#endif
#define CreateVehicle Forveh_CreateVehicle

stock Forveh_AddStaticVehicle(modelid, Float:spawn_x, Float:spawn_y, Float:spawn_z, Float:angle, color1, color2)
{
new vehid = AddStaticVehicle(modelid, spawn_x, spawn_y, spawn_z, angle, color1, color2);
forveh_vehicles[forveh_count++]=vehid;
return vehid;
}
#if defined _ALS_AddStaticVehicle
#undef AddStaticVehicle
#else
#define _ALS_AddStaticVehicle
#endif
#define AddStaticVehicle Forveh_AddStaticVehicle

stock Forveh_AddStaticVehicleEx(modelid, Float:spawn_x, Float:spawn_y, Float:spawn_z, Float:angle, color1, color2, respawn_delay, addsiren=0)
{
new vehid = AddStaticVehicleEx(modelid, spawn_x, spawn_y, spawn_z, angle, color1, color2,respawn_delay, addsiren);
forveh_vehicles[forveh_count++]=vehid;
return vehid;
}
#if defined _ALS_AddStaticVehicleEx
#undef AddStaticVehicleEx
#else
#define _ALS_AddStaticVehicleEx
#endif
#define AddStaticVehicleEx Forveh_AddStaticVehicleEx

stock Forveh_DestroyVehicle(vehicleid)
{
if(!IsValidVehicle(vehicleid)) return 0;
for(new i=0;i<forveh_count;i++)
{
if(forveh_vehicles[i]==vehicleid)
{
forveh_vehicles[i]=forveh_vehicles[--forveh_count];
break;
}
}
return DestroyVehicle(vehicleid);
}
#if defined _ALS_DestroyVehicle
#undef DestroyVehicle
#else
#define _ALS_DestroyVehicle
#endif
#define DestroyVehicle Forveh_DestroyVehicle
//==============================================================================

#include "../include/dc_foreach_veh.inc"

const PROFILE_VEHICLES_COUNT = MAX_VEHICLES;
const PROFILE_ITERATIONS_MAJOR = 1_000_000;
const PROFILE_ITERATIONS_MINOR = 10;

new const code_snippets_names[2][] =
{
{"forveh(c)"},
{"foreach(v:Vehicle)"}
};

#define Prerequisites();\
printf("Кол-во транспорта: %d.", PROFILE_VEHICLES_COUNT);\
new v;\
for (i = 0; i < PROFILE_VEHICLES_COUNT; ++i)\
CreateVehicle(411, 0.0, 0.0, 0.0, 0.0, 0, 0, -1, false);

#define CodeSnippet1();\
forveh(c){}

#define CodeSnippet2();\
foreach (v:Vehicle){}
/*Конец настроек.*/


#tryinclude <a_samp>
#if defined _samp_included
#define LINE_BREAK ""
#else
#define LINE_BREAK "\n"
#include <core>
#include <time>
#define GetTickCount() tickcount()
#endif

new code_snippets_time[sizeof(code_snippets_names)] = {0, ...};

main()
{
new t1, t2, i, j;
#emit zero.pri
#emit lctrl 7
#emit stor.s.pri i
printf(
"Тестирование: <%s> vs <%s>" LINE_BREAK,
code_snippets_names[0], code_snippets_names[1]
);
static const JIT_status_strings[2][] = {"интерпретируемый", "с JIT-компиляцией"};
printf(
"Режим: %s, %dx%d итераций.\a" LINE_BREAK,
JIT_status_strings[i],
PROFILE_ITERATIONS_MAJOR, PROFILE_ITERATIONS_MINOR
);
Prerequisites();
for (i = 0; i < PROFILE_ITERATIONS_MAJOR; ++i)
{
t1 = GetTickCount();
for (j = 0; j < PROFILE_ITERATIONS_MINOR; ++j)
{
CodeSnippet1();
}
t1 = GetTickCount()-t1;
#if !defined _samp_included
if(t1 < 0)
continue;
#endif
t2 = GetTickCount();
for (j = 0; j < PROFILE_ITERATIONS_MINOR; ++j)
{
CodeSnippet2();
}
t2 = GetTickCount()-t2;
#if !defined _samp_included
if(t2 < 0)
continue;
#endif
code_snippets_time[0] += t1;
code_snippets_time[1] += t2;
}
for (i = 0; i < sizeof(code_snippets_names); ++i)
printf(
"%s: %d" LINE_BREAK,
code_snippets_names[i], code_snippets_time[i]
);
print("\a\a" LINE_BREAK);
}


Результаты:


Тестирование: <forveh(c)> vs <foreach(v:Vehicle)>
Режим: интерпретируемый, 10000x10 итераций.
Кол-во транспорта: 2000.
forveh(c): 9125
foreach(v:Vehicle): 5383

Тестирование: <forveh(c)> vs <foreach(v:Vehicle)>
Режим: интерпретируемый, 100000x10 итераций.
Кол-во транспорта: 250.
forveh(c): 11461
foreach(v:Vehicle): 6750

Тестирование: <forveh(c)> vs <foreach(v:Vehicle)>
Режим: интерпретируемый, 10000000x10 итераций.
Кол-во транспорта: 0.
forveh(c): 7564
foreach(v:Vehicle): 6757

Тестирование: <forveh(c)> vs <foreach(v:Vehicle)>
Режим: с JIT-компиляцией, 100000x10 итераций.
Кол-во транспорта: 2000.
forveh(c): 8122
foreach(v:Vehicle): 8995

Тестирование: <forveh(c)> vs <foreach(v:Vehicle)>
Режим: с JIT-компиляцией, 1000000x10 итераций.
Кол-во транспорта: 250.
forveh(c): 10918
foreach(v:Vehicle): 11330

Тестирование: <forveh(c)> vs <foreach(v:Vehicle)>
Режим: с JIT-компиляцией, 100000000x10 итераций.
Кол-во транспорта: 0.
forveh(c): 12985
foreach(v:Vehicle): 11423


Вариант с foreach почти в 2 раза опережает forveh, а с использованием JIT лишь слегка уступает (и то не во всех ситуациях: если машин мало, foreach обгоняет forveh даже с JIT).
К тому же, forveh подходит только для перебора машин, а foreach - более универсальный вариант.
Также хотелось бы заметить, что в forveh(v) внутри цикла создаётся локальная переменная v, в то время, как в foreach можно использовать уже существующую, чтобы не тратилось время на создание новой локальной переменной - именно так я и поступил в тесте.


UPD:Если вы не обновляли include a_vehicles.inc до версии 0.3.7, то могут появится warning's по поводу addsiren.Чтобы это исправить, обновите ваши include's.
Ах да, чуть не забыл: в forveh мне пришлось самому добавлять параметр addsiren, т.к. код давно не обновлялся. Сам по себе этот код не может работать с SA:MP 0.3.7 R1+.

vovandolg
28.12.2015, 06:41
Велосипеды, скутеры и моцаклет обычный не хотят работать с этим перебором,может быть из за того что версия 0.3е(пробовал инклуд новее, пробовал старее с удалением addsiren разницы не повидал).
Остальное вроде бы всё гуд.

Daniel_Cortez
28.12.2015, 14:50
Велосипеды, скутеры и моцаклет обычный не хотят работать с этим перебором,может быть из за того что версия 0.3е(пробовал инклуд новее, пробовал старее с удалением addsiren разницы не повидал).
Остальное вроде бы всё гуд.
Да быть такого не может. Все эти "велосипеды, скутеры и моцаклеты" создаются точно так же, как и другой транспорт (через CreateVehicle/AddStaticVehicle(Ex)) и точно так же добавляются в итератор Vehicle.
Смотрите свой код. Вполне возможно, что какие-то машины создаются через фильтрскрипт - тогда они не будут добавлены в итератор. Либо, если в ФС тоже подключить dc_foreach_veh, то транспорт будет добавляться в уже другой экземпляр итератора, не в тот, который в моде.

В общем, смотрите, что у вас там не так. Если в каком-то ФС есть создание транспорта - пишите сюда, что-нибудь придумаю. Иначе - в раздел вопросов.

vovandolg
28.12.2015, 15:31
В общем, смотрите, что у вас там не так. Если в каком-то ФС есть создание транспорта - пишите сюда, что-нибудь придумаю. Иначе - в раздел вопросов.

Вот с этого момента я вспомнил что эти ребусы двухколесные были заспавнены через include :blum3:
Только если я не ошибаюсь в инклуде инклуды как то по другому крепятся, в общем от помощи не отказался бы)

Daniel_Cortez
28.12.2015, 15:36
Вот с этого момента я вспомнил что эти ребусы двухколесные были заспавнены через include :blum3:
Только если я не ошибаюсь в инклуде инклуды как то по другому крепятся, в общем от помощи не отказался бы)
Подключайте dc_foreach_veh.inc сразу же после foreach.inc, как показано в примере в 1-м посте.

vovandolg
28.12.2015, 16:03
Я в моде у себя нашёл такую строку

for(new i; i != MAX_VEHICLES; i++)
Её так же можно заменить на данный перебор?

- - - Добавлено - - -


Подключайте dc_foreach_veh.inc сразу же после foreach.inc, как показано в примере в 1-м посте.
Спасибо, теперь с этими двухколесными всё гуд, подключил теперь все кары на карте респаются :new_russian:

vovandolg
03.01.2016, 07:17
Та же проблема что и самая первая, решил просто из интереса через FS подключить и получилось что не чего не получилось..
инклуды так же самые первые вставлены остались просто тоже самое всё что и в инклуде было то и сюда закинул, FS не клюёт так.

Daniel_Cortez
03.01.2016, 12:18
Та же проблема что и самая первая, решил просто из интереса через FS подключить и получилось что не чего не получилось..
инклуды так же самые первые вставлены остались просто тоже самое всё что и в инклуде было то и сюда закинул, FS не клюёт так.
Он и не должен работать в отдельном FS. Посты выше внимательней читайте.

vovandolg
06.01.2016, 05:43
Вот код с обычным перебором всё кашерно:


else if(strcmp(cmd, "/aspecdelveh", true) == 0)
{
if(PlayerInfo[playerid][pAdmin] < 13) return 1;
tmp = strcharsplit(cmdtext,idx);
if(!strlen(tmp)) return SendClientMessage(playerid,COLOR_WHITE," Ââåäèòå: /specdelveh [ðàäèóñ]");
new Float:pos = floatstr(tmp),Float:car_x,Float:car_y,Float:car_z;
//for(new ro=0;ro<MAX_VEHICLES;ro++)
foreach (new ro:Vehicle)
{
if(GetVehicleModel(ro) > 399)
{
GetVehiclePos(ro,car_x,car_y,car_z);
if(IsPlayerInRangeOfPoint(playerid,pos,car_x,car_y,car_z))
{
RemoveFromVehicle(playerid);
DestroyVehicle(ro);
TeleportTime[playerid] = 5;
}
}
}
format(stringer, sizeof(stringer), "Àäìèíèñòðàòîð %s óäàëèë âåñü òðàíñïîðò ðÿäîì ñ íèì.",PlayerInfo[playerid][pNames]);
ABroadCast(COLOR_LIGHTRED,stringer,1);
return 1;
}
А с оптимизированным перебором вот такой шлак лезет:

[debug] Accessing element at index 2001 past array upper bound 2000
Это что нужно инклуд под себя штопать или как поступать?

Daniel_Cortez
06.01.2016, 14:09
Вот код с обычным перебором всё кашерно:


else if(strcmp(cmd, "/aspecdelveh", true) == 0)
{
if(PlayerInfo[playerid][pAdmin] < 13) return 1;
tmp = strcharsplit(cmdtext,idx);
if(!strlen(tmp)) return SendClientMessage(playerid,COLOR_WHITE," Ââåäèòå: /specdelveh [ðàäèóñ]");
new Float:pos = floatstr(tmp),Float:car_x,Float:car_y,Float:car_z;
//for(new ro=0;ro<MAX_VEHICLES;ro++)
foreach (new ro:Vehicle)
{
if(GetVehicleModel(ro) > 399)
{
GetVehiclePos(ro,car_x,car_y,car_z);
if(IsPlayerInRangeOfPoint(playerid,pos,car_x,car_y,car_z))
{
RemoveFromVehicle(playerid);
DestroyVehicle(ro);
TeleportTime[playerid] = 5;
}
}
}
format(stringer, sizeof(stringer), "Àäìèíèñòðàòîð %s óäàëèë âåñü òðàíñïîðò ðÿäîì ñ íèì.",PlayerInfo[playerid][pNames]);
ABroadCast(COLOR_LIGHTRED,stringer,1);
return 1;
}
А с оптимизированным перебором вот такой шлак лезет:

[debug] Accessing element at index 2001 past array upper bound 2000
Это что нужно инклуд под себя штопать или как поступать?
Добавил пару примечаний в первый пост, проверьте.
Btw, нумерация транспорта начинается не с 0, а с 1, и заканчивается на MAX_VEHICLES, а не на MAX_VEHICLES-1.

ziggi
07.03.2016, 15:50
Можно добавить подобную функцию для удаления транспорта внутри цикла.


stock DestroyVehicleSafe(&vehicleid)
{
Iter_SafeRemove(Vehicle, vehicleid, vehicleid);
return DestroyVehicle(vehicleid);
}

Geebrox
04.10.2016, 23:04
у меня почему-то не работает, подключил foreach, потом этот инклуд, создал 1 тс на сервере. попытался зареспавнить через форич, 0 эффекта
P.S. y_iterate не подключен. Подключен только форич от Y_Less

Сначала сам перехватил все функции связанные с тс и поработал с итераторами, мой код выглядело почти так же как ваш, но там тоже ничего не работало. Потом нашел ваш инклуд, но эффект тот же, может я что-то не так делаю?


/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

P.S. Исправил, перехватил все функции заново и добавил итераторы

Geebrox
05.10.2016, 16:38
Можно добавить подобную функцию для удаления транспорта внутри цикла.


stock DestroyVehicleSafe(&vehicleid)
{
Iter_SafeRemove(Vehicle, vehicleid, vehicleid);
return DestroyVehicle(vehicleid);
}


можно и так:



stock DestroyVehicleEx(vehicleid)
{
new ans = DestroyVehicle(vehicleid);
if(ans)
Iter_Remove(Vehicle, vehicleid);
return ans;
}

DeimoS
05.10.2016, 16:47
можно и так:



stock DestroyVehicleEx(vehicleid)
{
new ans = DestroyVehicle(vehicleid);
if(ans)
Iter_Remove(Vehicle, vehicleid);
return ans;
}


Тогда нужно ещё и в цикле переменную итератора приравнивать к DestroyVehicleEx. Зачем изобретать велосипед, если есть нативные функции?

ziggi
05.10.2016, 16:54
можно и так:



stock DestroyVehicleEx(vehicleid)
{
new ans = DestroyVehicle(vehicleid);
if(ans)
Iter_Remove(Vehicle, vehicleid);
return ans;
}


Не об этом функция. Попробуй удалить транспорт в этом цикле обычной функцией - получишь проблемы. Удалять итератор во время перебора нужно с помощью Iter_SafeRemove.

Geebrox
05.10.2016, 17:10
Не об этом функция. Попробуй удалить транспорт в этом цикле обычной функцией - получишь проблемы. Удалять итератор во время перебора нужно с помощью Iter_SafeRemove.

понятно, тогда уж сразу надо в перехват это сунуть

ziggi
05.10.2016, 19:41
понятно, тогда уж сразу надо в перехват это сунуть

Не очень правильно изменять параметры функции в перехвате. Но да, можно заменить стандартную DestroyVehicle при условии, что этот перехват будет первым. Тогда код будет выглядеть примерно так:

#if defined _ALS_DestroyVehicle
#error Include dc_foreach_veh.inc higher
#endif

stock dc_veh_iter__DestroyVehicle(&vehicleid)
{
Iter_SafeRemove(Vehicle, vehicleid, vehicleid);
return DestroyVehicle(vehicleid);
}
#if defined _ALS_DestroyVehicle
#undef DestroyVehicle
#else
#define _ALS_DestroyVehicle
#endif
#define DestroyVehicle(%0) dc_veh_iter__DestroyVehicle(%0)

Geebrox
05.10.2016, 23:37
да у меня так и есть, этот перехват идет первым, но чуть чуть по другому

123
05.09.2017, 08:46
Можно добавить подобную функцию для удаления транспорта внутри цикла.


stock DestroyVehicleSafe(&vehicleid)
{
Iter_SafeRemove(Vehicle, vehicleid, vehicleid);
return DestroyVehicle(vehicleid);
}


Не работает. Машину не удаляет, вот рабочий вариант:


stock DestroyVehicleSafe(&vehicleid)
{
new vehid = DestroyVehicle(vehicleid);

Iter_SafeRemove(Vehicle, vehicleid, vehicleid);
return vehid ;
}

ziggi
05.09.2017, 16:09
Не работает. Машину не удаляет, вот рабочий вариант:


stock DestroyVehicleSafe(&vehicleid)
{
new vehid = DestroyVehicle(vehicleid);

Iter_SafeRemove(Vehicle, vehicleid, vehicleid);
return vehid ;
}

Угу, в своём foreach я это реализовал правильно (https://github.com/Open-GTO/foreach/commit/c610ca69ba6de0dd35cf5e1e57db787c3275aa9d), а здесь исправить забыл.

Daniel_Cortez
05.09.2017, 18:08
Что ж, полагаю, от данного инклуда больше нет толку, раз аналогичный функционал уже реализован и отработан в форке foreach.

Изменил 1-й пост, тема закрыта.