PDA

Просмотр полной версии : [Мануал] Слежка за игроком (/spectate)



wAx
07.03.2015, 18:40
Введение
Приветствую всех! Сегодня мы с вами рассмотрим небольшой способ создания команды для слежки за игроком. Преимущества данного варианта, в достаточно практичном автоматическом обновлении режима слежки в необходимых случаях (смена интерьера, смена виртуального мира, посадка в транспорт).
Не буду лгать что идея возникла в моей голове внезапно. Я всего-лишь наткнулся на заморский топик, после чего решил перенести это в наше комьюнити.

Поехали...
Для начала, создадим необходимую нам переменную, которая будет отвечать за ID того игрока, за которым мы наблюдаем:

new spectating[MAX_PLAYERS];
Использовать эту переменную мы будем следующим образом:

spectating[playerid] = - 1; // данное значение переменной, означает что мы игрок не за кем не наблюдает
spectating[playerid] = playerid; // любое значение, отличающееся от -1, будет значить, что игрок за кем-то наблюдает
Создавать мы будем, следующие команды:

/spectate - для того чтобы начать слежку за определенным игроком.
/offspec - для того чтобы закончить слежку за определенным игроком.

Мы также введем новый колбэк в мод, это облегчит процесс обновления режима слежки за определенным игроком.

Перейдем непосредственно к коду...


// Я приведу пример на целом колбэке OnPlayerCommandText
// Для определения ID игрока после команды (как параметр к команде), я буду использовать sscanf.
// Подробнее как использовать OnPlayerCommandText и sscanf, можно узнать перейдя по ссылке в конце мануала
public OnPlayerCommandText(playerid, cmdtext[])
{
new cmd[32],params[128];
sscanf(cmdtext, "s[32]s[128]", cmd, params);

if(strcmp(cmd, "/spectate"))
{
new player_id;
if(sscanf(params, "d", player_id)) return SendClientMessage(playerid, 0xFFFFFF, "/spectate ");
if(IsPlayerConnected(player_id))
{
//Получаем виртуальный мир игрока за которым следим и устанавливаем такой-же игроку который следит, аналогично с интерьером
SetPlayerVirtualWorld(playerid,GetPlayerVirtualWorld(player_id));
SetPlayerInterior(playerid,GetPlayerInterior(player_id));
//Сейчас мы установим значение нашей переменной следящему игроку.
spectating[playerid] = player_id;
//Теперь, переведем следящего в режим наблюдения за игроком
TogglePlayerSpectating(playerid, 1);

//Разберёмся, находидтся ли игрок за которым следим в машине или нет. Установим соответствующий тип слежки.
if(IsPlayerInAnyVehicle(player_id)) PlayerSpectateVehicle(playerid, GetPlayerVehicleID(player_id), SPECTATE_MODE_NORMAL);
else PlayerSpectatePlayer(playerid, player_id, SPECTATE_MODE_NORMAL);
}
else return SendClientMessage(playerid, -1, "Игрок не найден!");
return 1;
}
if(strcmp(cmd, "/offspec", true) == 0)
{
if(IsPlayerConnected(playerid))
{
//Устанавливаем следящему виртуальный мир, который используется при spawn, аналогично с интерьером
SetPlayerVirtualWorld(playerid,0);
SetPlayerInterior(playerid,0);
//Очищаем переменную
spectating[playerid] = -1;
//Отключаем режим наблюдения у следящего
TogglePlayerSpectating(playerid, 0);
}
return 1;
}
return 0;
}

Теперь обновим режим слежки за игроком, в необходимых случаях.


public OnPlayerStreamOut(playerid, forplayerid)
{
//Данный паблик вызовется когда игрок за которым следим, пропадет из вида следящего (смена интерьера, виртуального мира)
if(IsPlayerConnected(forplayerid))
{
if(spectating[forplayerid] == playerid)//Проверяем действительно ли следит следящий игрок именно за этим игроком (см. примечание 1)
{
//Колбэк указанный ниже, поможет нам быстро обновить режим слежки
UpdateSpectatingStatus(forplayerid, playerid);
}
}
return 1;
}

public OnPlayerStateChange(playerid, newstate, oldstate)
{
//Если игрок за которым следим, меняет свой статус относительно нас (садится в машину)
for(new i = 0; i != MAX_PLAYERS; i++)
{
if(IsPlayerConnected(i))
{
if(spectating == playerid)//Проверяем действительно ли следит следящий игрок именно за этим игроком
{
//Колбэк указанный ниже, поможет нам быстро обновить режим слежки
UpdateSpectatingStatus(i, playerid);
}
}
}
return 1;
}

Ну и наконец, добавим в любую точку мода, нашу функцию обновления режима слежки:

stock UpdateSpectatingStatus(spectatorid, spectedid)
{
//Еще раз проверяем, совпадает ли значение в переменной следящего игрока с ID игрока за которым следят
if(spectating[spectatorid] == spectedid)
{
//Приведенные ниже строчки-оповещения, вы можете не использовать
if(GetPlayerState(spectedid) == PLAYER_STATE_WASTED) return GameTextForPlayer(spectatorid, "~r~Target wasted!", 5000, 3); // игрок убит
if(GetPlayerState(spectedid) == PLAYER_STATE_SPECTATING) return GameTextForPlayer(spectatorid, "~r~Target in spectating mode!", 5000, 3); // игрок перешел в режим слежки
if(!IsPlayerConnected(spectedid)) return GameTextForPlayer(spectatorid, "~r~Target disconnected!", 5000, 3); // игрок отключился

//Снова устанавливаем виртуальный мир следящему, то же самое, проделываем с интерьером
SetPlayerVirtualWorld(spectatorid,GetPlayerVirtualWorld(spectedid));
SetPlayerInterior(spectatorid,GetPlayerInterior(spectedid));
//Отправляем следящего снова в режим слежки
TogglePlayerSpectating(spectatorid, 1);
if(IsPlayerInAnyVehicle(spectedid)) PlayerSpectateVehicle(spectatorid, GetPlayerVehicleID(spectedid), SPECTATE_MODE_NORMAL);
else PlayerSpectatePlayer(spectatorid, spectedid, SPECTATE_MODE_NORMAL);
}
return 1;
}

Дополнение
Мне предложили отличное дополнение к режиму наблюдение - это переключение между игрокам за которыми следим, при помощи клавиш. Я выбрал клавиши NUM 4, NUM6. Итак, для этого нам потребуется задействовать паблик OnPlayerKeyStateChange:

public OnPlayerKeyStateChange(playerid, newkeys, oldkeys)
{
if(newkeys & KEY_ANALOG_LEFT) // проверяем нажатие клавиши NUM 4
{
if(spectating[playerid] != -1) // проверяем наблюдает ли за кем-либо игрок
{
if(spectating[playerid] <= 0) spectating[playerid] = GetMaxPlayers(); // если игрок наблюдает за минимальным ID, то перекидываем его на максимальный
else spectating[playerid]--; // или убавляем -1 от ID игрока за кем следящий наблюдал ранее
UpdateSpectatingStatus(playerid, spectating[playerid]); // обновляем режим слежки
}
}

if(newkeys & KEY_ANALOG_RIGHT) // проверяем нажатие клавиши NUM 6
{
if(spectating[playerid] != -1) // проверяем наблюдает ли за кем-либо игрок
{
if(spectating[playerid] >= GetMaxPlayers()) spectating[playerid] = 0; // если игрок наблюдает за максимальным ID, перекидываем его на минимальный
else spectating[playerid]++; // или добавляем +1 к ID игрока за кем следящий наблюдал ранее
UpdateSpectatingStatus(playerid, spectating[playerid]); // обновляем режим слежки
}
}
return 1;
// Не спорю, удобнее будет из цикла вытаскивать ID самого большого подключившегося игрока
// Но, я не думаю что это будет мелочью в плане быстродействия кода.
// Можно также завести переменную и записывать в нёё самый последний ID подключившегося игрока (см. примечание 2)
}

Заключение

Важно помнить и учитывать следующие факторы:
- В коде не используются, как может оказаться по вашему мнению, наиболее быстрые функции (исключение sscanf).
- Изменять код приведенный в примере, вы можете как душе угодно, я помогу вам разобраться с получившимися багами.

На этом всё...
Ах да!

[I]Примечание 1
OnPlayerStreamOut - вызывается для каждого игрока, который теряет из виду любого игрока сервера, именно поэтому нам необходима проверка на совпадение ID.
[I]Примечание 2
Как же нам определить самый большой ID среди игроков? Попробуем так:

new last_id; // создаем переменную для записи самого большого ID игрока

public OnPlayerConnect(playerid)
{
if(playerid > last_id) // проверяем, больше ли ID подключившегося игрока, чем предыдущее значении перменной
{
last_id = playerid; // если так, то обновляем значение
}
return 1;
}

// Теперь в дополнении, при переключении кнопок, вместо GetMaxPlayers, можно использовать значение данной переменной
// Единственный минус, в том, что если игрок отключится от сервера, максимальный ID - не обновится
// Но я думаю мне предложат варианты как исправить данный минус

Использование OnPlayerCommandText + sscanf (http://pro-pawn.ru/showthread.php?5560-sscanf-OnPlayerCommandText)



Автор мануала: wAx

L0ndl3m
07.03.2015, 18:45
for(new i = 0; i < GetMaxPlayers(); i++)

Ась?

Что на счёт того, чтобы добавить переключение между игроками во время наблюдения? Например нажав на KEY_JUMP ты начинаешь наблюдать за следующим игроком ( естественно этот игрок должен быть не мёртв, подключён и т.д. ). И на KEY_SPRINT аналогично, - наблюдение за предыдущим игроком.

wAx
07.03.2015, 18:49
Ась?

Что на счёт того, чтобы добавить переключение между игроками во время наблюдения? Например нажав на KEY_JUMP ты начинаешь наблюдать за следующим игроком ( естественно этот игрок должен быть не мёртв, подключён и т.д. ). И на KEY_SPRINT аналогично, - наблюдение за предыдущим игроком.

Ась? Ась?

А что касаемо дополнения, то обязательно будет.

L0ndl3m
07.03.2015, 18:52
Ась? Ась?

А что касаемо дополнения, то обязательно будет.
Ты действительно решил вызывать GetMaxPlayers при каждой итерации?

wAx
08.03.2015, 14:05
Обновил мануал и добавил пункт "Дополнение", в котором описана возможность переключения между игроками за которыми ведется наблюдение, посредством клавиш NUM 4 и NUM 6.

NewGreen
08.03.2015, 20:21
Эту команду нужно доработать т.к. когда админ будет включать слежение за игроком находясь в интерьере/др. вирт. мире, то отключая слежение он будет проваливаться. Можно использовать дополнительные переменные или PVar


if(strcmp(cmd, "/offspec", true) == 0)
{
if(IsPlayerConnected(playerid))
{
//Устанавливаем следящему виртуальный мир, который используется при spawn, аналогично с интерьером
SetPlayerVirtualWorld(playerid,0);
SetPlayerInterior(playerid,0);
//Очищаем переменную
spectating[playerid] = -1;
//Отключаем режим наблюдения у следящего
TogglePlayerSpectating(playerid, 1);
}
return 1;
}


Вариант решения с использованием PVar:



if(strcmp(cmd, "/spectate"))
{
new player_id;
if(sscanf(params, "d", player_id)) return SendClientMessage(playerid, 0xFFFFFF, "/spectate [ID]");
if(IsPlayerConnected(player_id))
{
SetPVarInt(playerid,"GetPLVirtualWorld",GetPlayerVirtualWorld(playerid));
SetPVarInt(playerid,"GetPLInterior",GetPlayerInterior(playerid));
//Получаем виртуальный мир игрока за которым следим и устанавливаем такой-же игроку который следит, аналогично с интерьером
SetPlayerVirtualWorld(playerid,GetPlayerVirtualWorld(player_id));
SetPlayerInterior(playerid,GetPlayerInterior(player_id));
// Получаем виртуальный мир и интерьер в котором находится админ

//Сейчас мы установим значение нашей переменной следящему игроку.
spectating[playerid] = player_id;
//Теперь, переведем следящего в режим наблюдения за игроком
TogglePlayerSpectating(playerid, 1);

//Разберёмся, находидтся ли игрок за которым следим в машине или нет. Установим соответствующий тип слежки.
if(IsPlayerInAnyVehicle(player_id)) PlayerSpectateVehicle(playerid, GetPlayerVehicleID(player_id), SPECTATE_MODE_NORMAL);
else PlayerSpectatePlayer(playerid, player_id, SPECTATE_MODE_NORMAL);
}
else return SendClientMessage(playerid, -1, "Игрок не найден!");
return 1;
}




if(strcmp(cmd, "/offspec", true) == 0)
{
if(IsPlayerConnected(playerid))
{
//Устанавливаем следящему сохраненный виртуальный мир, аналогично с интерьером
SetPlayerVirtualWorld(playerid,GetPVarInt(playerid,"GetPLVirtualWorld"));
SetPlayerInterior(playerid,GetPVarInt(playerid,"GetPLInterior"));
//Очищаем переменную
spectating[playerid] = -1;
//Отключаем режим наблюдения у следящего
TogglePlayerSpectating(playerid, 1);
}
return 1;
}

wAx
08.03.2015, 22:58
Эту команду нужно доработать т.к. когда админ будет включать слежение за игроком находясь в интерьере/др. вирт. мире, то отключая слежение он будет проваливаться. Можно использовать дополнительные переменные или PVar


if(strcmp(cmd, "/offspec", true) == 0)
{
if(IsPlayerConnected(playerid))
{
//Устанавливаем следящему виртуальный мир, который используется при spawn, аналогично с интерьером
SetPlayerVirtualWorld(playerid,0);
SetPlayerInterior(playerid,0);
//Очищаем переменную
spectating[playerid] = -1;
//Отключаем режим наблюдения у следящего
TogglePlayerSpectating(playerid, 1);
}
return 1;
}


Вариант решения с использованием PVar:



if(strcmp(cmd, "/spectate"))
{
new player_id;
if(sscanf(params, "d", player_id)) return SendClientMessage(playerid, 0xFFFFFF, "/spectate [ID]");
if(IsPlayerConnected(player_id))
{
//Получаем виртуальный мир игрока за которым следим и устанавливаем такой-же игроку который следит, аналогично с интерьером
SetPlayerVirtualWorld(playerid,GetPlayerVirtualWorld(player_id));
SetPlayerInterior(playerid,GetPlayerInterior(player_id));
// Получаем виртуальный мир и интерьер в котором находится админ
SetPVarInt(playerid,"GetPLVirtualWorld",GetPlayerVirtualWorld(playerid));
SetPVarInt(playerid,"GetPLInterior",GetPlayerInterior(playerid));
//Сейчас мы установим значение нашей переменной следящему игроку.
spectating[playerid] = player_id;
//Теперь, переведем следящего в режим наблюдения за игроком
TogglePlayerSpectating(playerid, 1);

//Разберёмся, находидтся ли игрок за которым следим в машине или нет. Установим соответствующий тип слежки.
if(IsPlayerInAnyVehicle(player_id)) PlayerSpectateVehicle(playerid, GetPlayerVehicleID(player_id), SPECTATE_MODE_NORMAL);
else PlayerSpectatePlayer(playerid, player_id, SPECTATE_MODE_NORMAL);
}
else return SendClientMessage(playerid, -1, "Игрок не найден!");
return 1;
}




if(strcmp(cmd, "/offspec", true) == 0)
{
if(IsPlayerConnected(playerid))
{
//Устанавливаем следящему сохраненный виртуальный мир, аналогично с интерьером
SetPlayerVirtualWorld(playerid,GetPVarInt(playerid,"GetPLVirtualWorld"));
SetPlayerInterior(playerid,GetPVarInt(playerid,"GetPLInterior"));
//Очищаем переменную
spectating[playerid] = -1;
//Отключаем режим наблюдения у следящего
TogglePlayerSpectating(playerid, 1);
}
return 1;
}


Не нужно ничего доделывать. Игрок после выхода из слежки, отправляется на spawn.

Scot
09.03.2015, 00:38
http://forum.sa-mp.com/showthread.php?p=3376297

wAx
09.03.2015, 06:23
http://forum.sa-mp.com/showthread.php?p=3376297
Не буду лгать что идея возникла в моей голове внезапно. Я всего-лишь наткнулся на заморский топик, после чего решил перенести это в наше комьюнити. Читаем внимательнее и замечаем перевод и дополнения. Заметили? Будут еще претензии?

Pe4en9
09.03.2015, 06:47
Читаем внимательнее и замечаем перевод и дополнения. Заметили? Будут еще претензии?

косяк ищи, боты следят друг за другом и ресуются по коордам других ботов, потом резко к себе и так по циклу.
ищи дырку.

wAx
09.03.2015, 12:27
косяк ищи, боты следят друг за другом и ресуются по коордам других ботов, потом резко к себе и так по циклу.
ищи дырку.

Что? Респавн "ботов" должен быть через OnPlayerSpawn, ты код то тестил? Если да, то предоставь более понятное описание, без использования жаргонизмов, а лучше всего видео.

NewGreen
09.03.2015, 17:24
Не нужно ничего доделывать. Игрок после выхода из слежки, отправляется на spawn.

Могу сказать одно - система требует доработки, представим что админ находится на мероприятии, к примеру гонки, для гонок переменным админа присваиваются определенные данные, виртуальный мир, возможно интерьер, время отсчета и т.п., админ начинает участвовать в гонке, и тут внезапно ему потребовалось понаблюдать за каким либо игроком. что происходит:
админ пишет /spectate и уходит наблюдать за игроком, потом пишет /offspec и появляется на спавне, но данные которые были записаны в переменные админа не обнулились и получается он находится на спавне и одновременно участвует в гонке - как это называется ?

Заметил еще один момент:


public OnPlayerStateChange(playerid, newstate, oldstate)
{
//Если игрок за которым следим, меняет свой статус относительно нас (садится в машину)
for(new i = 0; i != MAX_PLAYERS; i++)
{
if(IsPlayerConnected(i))
{
if(spectating[i] == playerid)//Проверяем действительно ли следит следящий игрок именно за этим игроком
{
//Колбэк указанный ниже, поможет нам быстро обновить режим слежки
UpdateSpectatingStatus(i, playerid);
}
}
}
return 1;
}

public OnPlayerStateChange(playerid, newstate, oldstate) вызывается когда игрок нажимает на клавишу, пусть онлайн 100 человек и каждый игрок нажмет кнопку мыши, что вызовет данную функцию 100 раз, MAX_PLAYERS - имеет значение 1000, 100*1000 = 100 000 сто тысяч проверок - зачем ?

wAx
09.03.2015, 17:57
Могу сказать одно - система требует доработки, представим что админ находится на мероприятии, к примеру гонки, для гонок переменным админа присваиваются определенные данные, виртуальный мир, возможно интерьер, время отсчета и т.п., админ начинает участвовать в гонке, и тут внезапно ему потребовалось понаблюдать за каким либо игроком. что происходит:
админ пишет /spectate и уходит наблюдать за игроком, потом пишет /offspec и появляется на спавне, но данные которые были записаны в переменные админа не обнулились и получается он находится на спавне и одновременно участвует в гонке - как это называется ?

Заметил еще один момент:


public OnPlayerStateChange(playerid, newstate, oldstate)
{
//Если игрок за которым следим, меняет свой статус относительно нас (садится в машину)
for(new i = 0; i != MAX_PLAYERS; i++)
{
if(IsPlayerConnected(i))
{
if(spectating[i] == playerid)//Проверяем действительно ли следит следящий игрок именно за этим игроком
{
//Колбэк указанный ниже, поможет нам быстро обновить режим слежки
UpdateSpectatingStatus(i, playerid);
}
}
}
return 1;
}

public OnPlayerStateChange(playerid, newstate, oldstate) вызывается когда игрок нажимает на клавишу, пусть онлайн 100 человек и каждый игрок нажмет кнопку мыши, что вызовет данную функцию 100 раз, MAX_PLAYERS - имеет значение 1000, 100*1000 = 100 000 сто тысяч проверок - зачем ?

Мануалом я хотел показать как сделать автоматическое обновление в режиме слежки, а не как тпшнуть администратора обратно на мп, это необязательное условие. Я понимаю, так удобнее, но полезность мануала отсутствие этого фактора не портит. Я обещаю подумать и может быть сделать дополнение в будущем.

OnPlayerState, кнопки, вызывается когда нажимает клавишу? Ничего не перепутали? А по поводу MAX_PLAYERS, да, действительно, в новой версии оно имеет значение 1000, но в текущей, можно переназначить MAX_PLAYERS под своё количество слотов, либо заменить циклом foreach. Это всё - возможные варианты, код в мануале сугубо примерный, как бы если он будет ничего серьезного не случится, но его можно с легкостью улучшить и ускорить его работу. В общем я надеюсь я донес мысль.

NewGreen
09.03.2015, 18:19
Мануалом я хотел показать как сделать автоматическое обновление в режиме слежки, а не как тпшнуть администратора обратно на мп, это необязательное условие. Я понимаю, так удобнее, но полезность мануала отсутствие этого фактора не портит. Я обещаю подумать и может быть сделать дополнение в будущем.
Все дело в функциональности, чем функциональность больше, тем приятнее использовать, вам ведь неудобно будет использовать телефон/смартфон в котором есть многое, но нет динамиков, т.е. пользоваться им можно, но не удобно, нужно постоянно подключать гарнитуру.



OnPlayerState, кнопки, вызывается когда нажимает клавишу? Ничего не перепутали? А по поводу MAX_PLAYERS, да, действительно, в новой версии оно имеет значение 1000, но в текущей, можно переназначить MAX_PLAYERS под своё количество слотов, либо заменить цикл foreach. Это всё - возможные варианты, код в мануале сугубо примерный, как бы если он будет ничего серьезного не случится, но его можно с легкостью улучшить и ускорить его работу. В общем я надеюсь я донес мысль.

Признаю, действительно я перепутал OnPlayerStateChange с OnPlayerKeyStateChange, по поводу MAX_PLAYERS, лучше не изобретать велосипед, а просто поставить дополнительную проверку впереди цикла:



public OnPlayerStateChange(playerid, newstate, oldstate)
{
//Если игрок за которым следим, меняет свой статус относительно нас (садится в машину)
if(spectating[playerid] != -1) {
for(new i = 0; i != MAX_PLAYERS; i++)
{
if(IsPlayerConnected(i))
{
if(spectating[i] == playerid)//Проверяем действительно ли следит следящий игрок именно за этим игроком
{
//Колбэк указанный ниже, поможет нам быстро обновить режим слежки
UpdateSpectatingStatus(i, playerid);
}
}
}
}
return 1;
}

wAx
09.03.2015, 18:32
Все дело в функциональности, чем функциональность больше, тем приятнее использовать, вам ведь неудобно будет использовать телефон/смартфон в котором есть многое, но нет динамиков, т.е. пользоваться им можно, но не удобно, нужно постоянно подключать гарнитуру.



Признаю, действительно я перепутал OnPlayerStateChange с OnPlayerKeyStateChange, по поводу MAX_PLAYERS, лучше не изобретать велосипед, а просто поставить дополнительную проверку впереди цикла:



public OnPlayerStateChange(playerid, newstate, oldstate)
{
//Если игрок за которым следим, меняет свой статус относительно нас (садится в машину)
if(spectating[playerid] != -1) {
for(new i = 0; i != MAX_PLAYERS; i++)
{
if(IsPlayerConnected(i))
{
if(spectating[i] == playerid)//Проверяем действительно ли следит следящий игрок именно за этим игроком
{
//Колбэк указанный ниже, поможет нам быстро обновить режим слежки
UpdateSpectatingStatus(i, playerid);
}
}
}
}
return 1;
}


Что ты хочешь проверить? Сейчас ты проверяешь следит ли игрок за кем нибудь. Еще раз: ты проверяешь, следит ли за кем нибудь, игрок за которым возможно мы следим. Зачем? Код не будет работать, ты можешь в этом убедится.

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

P.S. Код не вызовется, пока игрок который у нас является playerid в этом паблике не перейдет в режим слежки за кем нибудь. Не нужно изобретать велосипед? foreach, GetMaxPlayers и новые функции 0.3.7 RC2, это не велосипед, отнюдь не велосипед.

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

По поводу функциональности. Суть мануала в том, чтобы показать как удобнее и практичнее обновлять режим слежки в необходимых случаях (смена интерьера, виртуального мира). Вскоре я добавлю сохранение последней позиции.

NewGreen
09.03.2015, 18:51
Что ты хочешь проверить? Сейчас ты проверяешь следит ли игрок за кем нибудь. Еще раз: ты проверяешь, следит ли за кем нибудь, игрок за которым возможно мы следим. Зачем? Код не будет работать, ты можешь в этом убедится.

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

P.S. Код не вызовется, пока игрок который у нас является playerid в этом паблике не перейдет в режим слежки за кем нибудь. Не нужно изобретать велосипед? foreach, GetMaxPlayers и новые функции 0.3.7 RC2, это не велосипед, отнюдь не велосипед.

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

По поводу функциональности. Суть мануала в том, чтобы показать как удобнее и практичнее обновлять режим слежки в необходимых случаях (смена интерьера, виртуального мира). Вскоре я добавлю сохранение последней позиции.

Я поторопился с выводами в данной функции, точнее, я не просматривал ваш мануал полностью, а лишь глянул частично, в связи с чем критично отнесся к логики этой конструкции, думаю лучше оставить как есть, или добавить GetMaxPlayers, но не foreach, априори мануал должен быть универсальным.

Mazzilla
11.03.2015, 14:19
Всё хорошо, но в дополнении с NUM4 и NUM6 есть недочёт: нет проверки на то, что следующий айди вообще имеется на сервере (подключен) и не находится в режиме слежки. Я предлагаю свой вариант. Возможно, вы придумаете что-либо лучше, главное - уловите мысль:

public OnPlayerKeyStateChange(playerid, newkeys, oldkeys)
{
if(newkeys & KEY_ANALOG_LEFT) // проверяем нажатие клавиши NUM 4
{
if(spectating[playerid] != -1) // проверяем наблюдает ли за кем-либо игрок
{
if(spectating[playerid] <= 0) spectating[playerid] = GetMaxPlayers(); // если игрок наблюдает за минимальным ID, то перекидываем его на максимальный
else
{
for(new i = spectating[playerid]; i >= 0; i--) // поиск игрока с меньшим ID от того, за которым наблюдали ранее.
{
if(!IsPlayerConnected(i)) continue;
if(GetPlayerState(i) == PLAYER_STATE_SPECTATE) continue;
spectating[playerid] = i;
break;
}
}
UpdateSpectatingStatus(playerid, spectating[playerid]); // обновляем режим слежки
}
}

if(newkeys & KEY_ANALOG_RIGHT) // проверяем нажатие клавиши NUM 6
{
if(spectating[playerid] != -1) // проверяем наблюдает ли за кем-либо игрок
{
if(spectating[playerid] >= GetMaxPlayers()) spectating[playerid] = 0; // если игрок наблюдает за максимальным ID, перекидываем его на минимальный
else
{
for(new i = spectating[playerid]; i <= GetMaxPlayers(); i++) // поиск игрока с большим ID от того, за которым наблюдали ранее.
{
if(!IsPlayerConnected(i)) continue;
if(GetPlayerState(i) == PLAYER_STATE_SPECTATE) continue;
spectating[playerid] = i;
break;
}
}
UpdateSpectatingStatus(playerid, spectating[playerid]); // обновляем режим слежки
}
}
return 1;
// Не спорю, удобнее будет из цикла вытаскивать ID самого большого подключившегося игрока
// Но, я не думаю что это будет мелочью в плане быстродействия кода.
// Можно также завести переменную и записывать в нёё самый последний ID подключившегося игрока (см. примечание 2)
}

$continue$
11.03.2015, 14:45
Делал как то подобное, с помощью контролированного goto, код в разы меньше был...

wAx
11.03.2015, 19:51
Делал как то подобное, с помощью контролированного goto, код в разы меньше был...

ну поделись.

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


Всё хорошо, но в дополнении с NUM4 и NUM6 есть недочёт: нет проверки на то, что следующий айди вообще имеется на сервере (подключен) и не находится в режиме слежки. Я предлагаю свой вариант. Возможно, вы придумаете что-либо лучше, главное - уловите мысль:

public OnPlayerKeyStateChange(playerid, newkeys, oldkeys)
{
if(newkeys & KEY_ANALOG_LEFT) // проверяем нажатие клавиши NUM 4
{
if(spectating[playerid] != -1) // проверяем наблюдает ли за кем-либо игрок
{
if(spectating[playerid] <= 0) spectating[playerid] = GetMaxPlayers(); // если игрок наблюдает за минимальным ID, то перекидываем его на максимальный
else
{
for(new i = spectating[playerid]; i >= 0; i--) // поиск игрока с меньшим ID от того, за которым наблюдали ранее.
{
if(!IsPlayerConnected(i)) continue;
if(GetPlayerState(i) == PLAYER_STATE_SPECTATE) continue;
spectating[playerid] = i;
break;
}
}
UpdateSpectatingStatus(playerid, spectating[playerid]); // обновляем режим слежки
}
}

if(newkeys & KEY_ANALOG_RIGHT) // проверяем нажатие клавиши NUM 6
{
if(spectating[playerid] != -1) // проверяем наблюдает ли за кем-либо игрок
{
if(spectating[playerid] >= GetMaxPlayers()) spectating[playerid] = 0; // если игрок наблюдает за максимальным ID, перекидываем его на минимальный
else
{
for(new i = spectating[playerid]; i <= GetMaxPlayers(); i++) // поиск игрока с большим ID от того, за которым наблюдали ранее.
{
if(!IsPlayerConnected(i)) continue;
if(GetPlayerState(i) == PLAYER_STATE_SPECTATE) continue;
spectating[playerid] = i;
break;
}
}
UpdateSpectatingStatus(playerid, spectating[playerid]); // обновляем режим слежки
}
}
return 1;
// Не спорю, удобнее будет из цикла вытаскивать ID самого большого подключившегося игрока
// Но, я не думаю что это будет мелочью в плане быстродействия кода.
// Можно также завести переменную и записывать в нёё самый последний ID подключившегося игрока (см. примечание 2)
}

Циклы циклы циклы, ну я же оставил, похоже специально для тебя, подсчет максимального ID. Смотри примечание 2
И как мне кажется, переключение под ID, используют админы не имеющие конкретной цели для мониторинга. Выгоднее начинать с 0 ID и щелкать, а как они увидят что ID "закончились", перейдут снова в слежку за 0. Цикл конечно неплохо, твой вариант рабочий и весьма пригодный.

$continue$
11.03.2015, 19:55
К сожалению код был утерен, и сейчас восстановить не смогу, т.к не смогу провести тесты с нубо-компом (32 мб видеокарта :D)

wAx
11.03.2015, 19:57
К сожалению код был утерен, и сейчас восстановить не смогу, т.к не смогу провести тесты с нубо-компом (32 мб видеокарта :D)

тогда смысл твоего хвастовства? У меня тоже куча кода на чердаке, который в разы короче всех современных систем в модах. Только вот код утерян.

$continue$
11.03.2015, 20:11
Смысл будет понятен:


stock NextPlayerSpec(playerid)
{
_NextTick:
PI[playerid][pSpecID]++;
if(!IsPlayerConnected(PI[playerid][pSpecID]) || PlayerLogged[PI[playerid][pSpecID]] == 0 || PI[playerid][pSpecID] == playerid) goto _NextTick;
format(PI[playerid][pCMDstr], 69, "PLAYERID: %d || PI[playerid][pSpecID]: %d", playerid, PI[playerid][pSpecID]);
SendClientMessageToAll(COLOR_LIGHTRED, PI[playerid][pCMDstr]);
if(PI[playerid][pSpecID] == MAX_PLAYERS - 1) PI[playerid][pSpecID] = -1, SendClientMessageToAll(COLOR_LIGHTRED, "Сброс"), goto _NextTick;
StartSpectate(playerid, PI[playerid][pSpecID]);
return 1;
}

wAx
11.03.2015, 20:32
Смысл будет понятен:


stock NextPlayerSpec(playerid)
{
_NextTick:
PI[playerid][pSpecID]++;
if(!IsPlayerConnected(PI[playerid][pSpecID]) || PlayerLogged[PI[playerid][pSpecID]] == 0 || PI[playerid][pSpecID] == playerid) goto _NextTick;
format(PI[playerid][pCMDstr], 69, "PLAYERID: %d || PI[playerid][pSpecID]: %d", playerid, PI[playerid][pSpecID]);
SendClientMessageToAll(COLOR_LIGHTRED, PI[playerid][pCMDstr]);
if(PI[playerid][pSpecID] == MAX_PLAYERS - 1) PI[playerid][pSpecID] = -1, SendClientMessageToAll(COLOR_LIGHTRED, "Сброс"), goto _NextTick;
StartSpectate(playerid, PI[playerid][pSpecID]);
return 1;
}


Спасибо Deimos

$continue$
11.03.2015, 22:07
Спасибо Deimos

Ага Влад помогал с этим)
Но делалось для своего сервера, однако код в разы короче...

wAx
12.03.2015, 06:38
И что с того что короче? Вам твердят Всё время размер кода не влияет на работоспособность. Pwn разве что меньше будет.

$continue$
12.03.2015, 13:25
И что с того что короче? Вам твердят Всё время размер кода не влияет на работоспособность. Pwn разве что меньше будет.
Ну сказал что это влияет на amx файл?

Что то тут болбольством пахнет..

И давай признаем что лучше использовать так чем использовать громоздкий код....

wAx
12.03.2015, 18:52
Ну сказал что это влияет на amx файл?

Что то тут болбольством пахнет..

И давай признаем что лучше использовать так чем использовать громоздкий код....

Что-то у тебя в носу застряло похоже, раз запах топика начал чувствовать. Что лучше использовать, решат сами юзеры которые заглянут сюда. И да, "громоздкий код", ты просто предложил функцию, она не заменит весь мануал. Да и для другой кнопки, придется писать аналогичную функцию с обратным отсчетом.

MR_BEN
12.03.2015, 19:41
if(spectating[playerid] <= 0) spectating[playerid] = GetMaxPlayers(); // если игрок наблюдает за минимальным ID, то перекидываем его на максимальный

У GetMaxPlayers() аргумент уберите

и


stock UpdateSpectatingStatus(spectatorid, spectedid)

уберите двоеточие

wAx
12.03.2015, 20:33
if(spectating[playerid] <= 0) spectating[playerid] = GetMaxPlayers(); // если игрок наблюдает за минимальным ID, то перекидываем его на максимальный

У GetMaxPlayers() аргумент уберите

и


stock UpdateSpectatingStatus(spectatorid, spectedid)

уберите двоеточие

Ок, Thanks.

$continue$
12.03.2015, 21:47
Что-то у тебя в носу застряло похоже, раз запах топика начал чувствовать. Что лучше использовать, решат сами юзеры которые заглянут сюда. И да, "громоздкий код", ты просто предложил функцию, она не заменит весь мануал. Да и для другой кнопки, придется писать аналогичную функцию с обратным отсчетом.

Написать обратный алгоритм с переключением на num4 и num6?
Все равно код выйдет меньше даже с двумя функциями :punish:

wAx
13.03.2015, 12:04
Написать обратный алгоритм с переключением на num4 и num6?
Все равно код выйдет меньше даже с двумя функциями :punish:

МЕНЬШЕ != БЫСТРЕЕ

MR_BEN
13.03.2015, 15:23
А не лучше, вместо GetMaxPlayers() использовать GetPlayerPoolSize()?

NewGreen
13.03.2015, 15:49
А не лучше, вместо GetMaxPlayers() использовать GetPlayerPoolSize()?

Конечно лучше, но есть одно но, GetPlayerPoolSize() работает только в 0.3.7 которая находится в стадии разработки, а это значит что в 90% случаев данный мануал работать не будет!

MR_BEN
13.03.2015, 16:16
Конечно лучше, но есть одно но, GetPlayerPoolSize() работает только в 0.3.7 которая находится в стадии разработки, а это значит что в 90% случаев данный мануал работать не будет!

Но всё же GetMaxPlayers() возвращает не максимальный id на сервере, а максимальное кол-во игроков, которые МОГУТ быть на сервере.

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

Кстати, примечание 2. Можно сделать, чтобы при дисконнекте стояла проверка, если last_id == playerid и playerid-1 подключен к серверу last_id = playerid-1. Возможно, я не уверен в этом.

wAx
13.03.2015, 16:58
Но всё же GetMaxPlayers() возвращает не максимальный id на сервере, а максимальное кол-во игроков, которые МОГУТ быть на сервере.

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

Кстати, примечание 2. Можно сделать, чтобы при дисконнекте стояла проверка, если last_id == playerid и playerid-1 подключен к серверу last_id = playerid-1. Возможно, я не уверен в этом.

По любому нужно будет после дисконнекта, высчитывать ID самого большого (после disconnect игрока) с помощью цикла. Я по моему написал об этом.

MR_BEN
13.03.2015, 17:31
if(playerid == last_id)
for(new i = GetMaxPlayers() - 1;i != -1;--i)
if(i > last_id ) last_id = i;
Может так?

wAx
13.03.2015, 19:46
if(playerid == last_id)
for(new i = GetMaxPlayers() - 1;i != -1;--i)
if(i > last_id ) last_id = i;
Может так?

Может и так

$continue$
13.03.2015, 20:57
МЕНЬШЕ != БЫСТРЕЕ

Тестировал на скорость?



Краткость - сестра таланта.
Все гениальное - просто.

wAx
13.03.2015, 21:54
Тестировал на скорость?



Краткость - сестра таланта.
Все гениальное - просто.

Да нет... Не беру вообще твой вариант во внимание.

Astrakhan30
25.05.2016, 17:38
Не нужно ничего доделывать. Игрок после выхода из слежки, отправляется на spawn.
Думаю, стоит заменить в /offspec на:

TogglePlayerSpectating(playerid, 0);

vovandolg
17.06.2016, 22:08
//Отключаем режим наблюдения у следящего
TogglePlayerSpectating(playerid, 1);
Чтожжж отключилии

Glant
30.07.2016, 23:20
При выходе наблюдаемого игрока должна вызываться OnPlayerStreamOut? На этот случай тестили?
У меня не вызывается, пришлось добавить код на OnPlayerDisconnect

vovandolg
31.07.2016, 03:12
У меня вызывалась, только я на всякий пожарный ещё добавлял проверку игрока на INVALID_PLAYER_ID

123
02.08.2016, 10:15
OnPlayerStreamOut не вызывается при смене интерьера и виртуального мира. Система требует доработок, причем довольно значительных.

TheMallard
02.08.2016, 21:59
OnPlayerSpawn + OnPlayerInteriorChange + хук SetPlayerVirtualWorld

vovandolg
03.08.2016, 01:16
OnPlayerSpawn + OnPlayerInteriorChange + хук SetPlayerVirtualWorld

Spawn отслеживается в OnPlayerStateChange,...

Меняя интерьер игрок летает иногда где то в воздухе,
меняя виртуальный мир без интерьера он ходит по объектам,
следовательно берём только OnPlayerInteriorChange(ну глядя какой там у Вас мод ещё)
и вставляем тот же код что и в смене OnPlayerStateChange,
всё остальное должно работать...

Nexius_Tailer
03.08.2016, 20:24
Spawn отслеживается в OnPlayerStateChange,...

Меняя интерьер игрок летает иногда где то в воздухе,
меняя виртуальный мир без интерьера он ходит по объектам,
следовательно берём только OnPlayerInteriorChange(ну глядя какой там у Вас мод ещё)
и вставляем тот же код что и в смене OnPlayerStateChange,
всё остальное должно работать...
Смена виртуального мира через StreamOut не отслеживается (как и через State/InteriorChange), написали же выше.
Поэтому хук на SetPlayerVirtualWorld всё-же нужен.

vovandolg
03.08.2016, 23:46
А ещё лучше просто рядом со сменой вирта задавать ещё раз тоже значение интерьера или новое и не какой хук не нужен:aggressive:
Тобишь:


SetPlayerVirtualWorld(playerid, 1); //сперва вирт
SetPlayerInterior(playerid, 0); //сработал OnPlayerInteriorChange и обновил всё что нужно
//и если вирт выше меняется, обязательно меняем интерьер даже если он тот же остаётся
//вуаля без всяких хуков и итерацеподобных вжиков мы исполнили скрипт в OnPlayerInteriorChange

Nexius_Tailer
03.08.2016, 23:57
А ещё лучше просто рядом со сменой вирта задавать ещё раз тоже значение интерьера или новое и не какой хук не нужен:aggressive:
Тобишь:


SetPlayerVirtualWorld(playerid, 1); //сперва вирт
SetPlayerInterior(playerid, 0); //сработал OnPlayerInteriorChange и обновил всё что нужно
//и если вирт выше меняется, обязательно меняем интерьер даже если он тот же остаётся
//вуаля без всяких хуков и итерацеподобных вжиков мы исполнили скрипт в OnPlayerInteriorChange

А если я просто меняю виртуальный мир игроку, без интерьера?
Не катит такой вариант

vovandolg
04.08.2016, 08:12
А если я просто меняю виртуальный мир игроку, без интерьера?
Не катит такой вариант

Так что проще обновить интерьер через эту функцию и сработает 1 раз полный UpdateSpectate
или ставить все Ваши хуки и сработает не раз перебор лишний?

Nexius_Tailer
04.08.2016, 14:53
Так что проще обновить интерьер через эту функцию и сработает 1 раз полный UpdateSpectate
или ставить все Ваши хуки и сработает не раз перебор лишний?
Эту функцию это какую? OnPlayerInteriorChange или UpdateSpectatingStatus?
Хотя всё равно суть одна: важно обновлять данные тогда, когда они изменяются, а если обновлять виртуальный мир только при смене интерьера, то что мешает мне сменять виртуальный мир без смены интерьера?

vovandolg
04.08.2016, 23:28
Что ещё не ясного тут?

public OnPlayerStateChange(playerid, newstate, oldstate)
{
PlayerChange(playerid);
return 1;
}

//изменяя интерьер мы обновляем уже существующим перебором
//для этого я и говорю юзать смену интерьера вместе с вирт миром
public OnPlayerInteriorChange(playerid, newinteriorid, oldinteriorid)
{
PlayerChange(playerid);
return 1;
}

stock PlayerChange(playerid)
{
for(new i = 0; i != MAX_PLAYERS; i++)
{
if(IsPlayerConnected(i))
{
if(spectating[i] == playerid)//Проверяем действительно ли следит следящий игрок именно за этим игроком
{
//Колбэк указанный ниже, поможет нам быстро обновить режим слежки
UpdateSpectatingStatus(i, playerid);
break;
}
}
}
return 1;
}

wAx
25.08.2016, 02:11
OnPlayerStreamOut не вызывается при смене интерьера и виртуального мира. Система требует доработок, причем довольно значительных.

При смене интерьера игрок выходит из зоны стрима у следящего, так как их интерьеры не совпадают.

vovandolg
25.08.2016, 02:48
При смене интерьера игрок выходит из зоны стрима у следящего, так как их интерьеры не совпадают.

Не всегда же)

123
25.08.2016, 04:37
При смене интерьера игрок выходит из зоны стрима у следящего, так как их интерьеры не совпадают.

Ты сначала протести, потом утверждай.

wAx
25.08.2016, 08:10
Ты сначала протести, потом утверждай.
Я тестировал и работал с этой системой.

123
25.08.2016, 09:27
Я тестировал и работал с этой системой.

Тоже самое делал и я (конечно не с этом системой, а конкретно с методом с OnPlayerStreamOut), при этом на рабочем сервере с онлайном от 100 человек. И совершенно точно он не срабатывает при смене виртуального мира и интерьера (может конечно только при виртуальном мире или интерьере, поскольку везде у меня менялось и то, и то, но факта это не меняет и код системы требует значительных доработок, да и видно, что код устарел.).

Nexius_Tailer
25.08.2016, 11:41
Что ещё не ясного тут?
То, что при смене только лишь виртуального мира без смены интерьера (т.к. к примеру смена инта не будет надобна), такой способ не обновит этот самый виртуальный мир.


Тоже самое делал и я (конечно не с этом системой, а конкретно с методом с OnPlayerStreamOut), при этом на рабочем сервере с онлайном от 100 человек. И совершенно точно он не срабатывает при смене виртуального мира и интерьера (может конечно только при виртуальном мире или интерьере, поскольку везде у меня менялось и то, и то, но факта это не меняет и код системы требует значительных доработок, да и видно, что код устарел.).
Тоже самое. Придумал такой же метод со StreamOut'ом ещё до посещения этой темы - он не работал.

vovandolg
25.08.2016, 14:39
То, что при смене только лишь виртуального мира без смены интерьера (т.к. к примеру смена инта не будет надобна), такой способ не обновит этот самый виртуальный мир.


______________
ЛАЛАЛАЛА

А ещё лучше просто рядом со сменой вирта задавать ещё раз тоже значение интерьера или новое и не какой хук не нужен:aggressive:
Тобишь:


SetPlayerVirtualWorld(playerid, 1); //сперва вирт
SetPlayerInterior(playerid, 0); //сработал OnPlayerInteriorChange и обновил всё что нужно
//и если вирт выше меняется, обязательно меняем интерьер даже если он тот же остаётся
//вуаля без всяких хуков и итерацеподобных вжиков мы исполнили скрипт в OnPlayerInteriorChange



А если я просто меняю виртуальный мир игроку, без интерьера?
Не катит такой вариант


Так что проще обновить интерьер через эту функцию и сработает 1 раз полный UpdateSpectate
или ставить все Ваши хуки и сработает не раз перебор лишний?

Nexius_Tailer
25.08.2016, 14:44
______________
ЛАЛАЛАЛА
Так а разве вызовется OnPlayerInteriorChange, когда старый интерьер как был 0, так и остался?

vovandolg
25.08.2016, 20:14
Так а разве вызовется OnPlayerInteriorChange, когда старый интерьер как был 0, так и остался?

Нет, ну по сути можно сделать перехватчик чтоб ставил на любой другой и возвращал на который надо, но это вариант для bad boy:crazy:

Nexius_Tailer
25.08.2016, 21:36
Нет, ну по сути можно сделать перехватчик чтоб ставил на любой другой и возвращал на который надо, но это вариант для bad boy:crazy:
Ну вот. Я об этом изначально и писал)