Просмотр полной версии : [Мануал] Слежка за игроком (/spectate)
Введение
Приветствую всех! Сегодня мы с вами рассмотрим небольшой способ создания команды для слежки за игроком. Преимущества данного варианта, в достаточно практичном автоматическом обновлении режима слежки в необходимых случаях (смена интерьера, смена виртуального мира, посадка в транспорт).
Не буду лгать что идея возникла в моей голове внезапно. Я всего-лишь наткнулся на заморский топик, после чего решил перенести это в наше комьюнити.
Поехали...
Для начала, создадим необходимую нам переменную, которая будет отвечать за 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
for(new i = 0; i < GetMaxPlayers(); i++)
Ась?
Что на счёт того, чтобы добавить переключение между игроками во время наблюдения? Например нажав на KEY_JUMP ты начинаешь наблюдать за следующим игроком ( естественно этот игрок должен быть не мёртв, подключён и т.д. ). И на KEY_SPRINT аналогично, - наблюдение за предыдущим игроком.
Ась?
Что на счёт того, чтобы добавить переключение между игроками во время наблюдения? Например нажав на KEY_JUMP ты начинаешь наблюдать за следующим игроком ( естественно этот игрок должен быть не мёртв, подключён и т.д. ). И на KEY_SPRINT аналогично, - наблюдение за предыдущим игроком.
Ась? Ась?
А что касаемо дополнения, то обязательно будет.
Ась? Ась?
А что касаемо дополнения, то обязательно будет.
Ты действительно решил вызывать GetMaxPlayers при каждой итерации?
Обновил мануал и добавил пункт "Дополнение", в котором описана возможность переключения между игроками за которыми ведется наблюдение, посредством клавиш 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;
}
Эту команду нужно доработать т.к. когда админ будет включать слежение за игроком находясь в интерьере/др. вирт. мире, то отключая слежение он будет проваливаться. Можно использовать дополнительные переменные или 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.
http://forum.sa-mp.com/showthread.php?p=3376297
http://forum.sa-mp.com/showthread.php?p=3376297
Не буду лгать что идея возникла в моей голове внезапно. Я всего-лишь наткнулся на заморский топик, после чего решил перенести это в наше комьюнити. Читаем внимательнее и замечаем перевод и дополнения. Заметили? Будут еще претензии?
Читаем внимательнее и замечаем перевод и дополнения. Заметили? Будут еще претензии?
косяк ищи, боты следят друг за другом и ресуются по коордам других ботов, потом резко к себе и так по циклу.
ищи дырку.
косяк ищи, боты следят друг за другом и ресуются по коордам других ботов, потом резко к себе и так по циклу.
ищи дырку.
Что? Респавн "ботов" должен быть через 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 сто тысяч проверок - зачем ?
Могу сказать одно - система требует доработки, представим что админ находится на мероприятии, к примеру гонки, для гонок переменным админа присваиваются определенные данные, виртуальный мир, возможно интерьер, время отсчета и т.п., админ начинает участвовать в гонке, и тут внезапно ему потребовалось понаблюдать за каким либо игроком. что происходит:
админ пишет /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;
}
Все дело в функциональности, чем функциональность больше, тем приятнее использовать, вам ведь неудобно будет использовать телефон/смартфон в котором есть многое, но нет динамиков, т.е. пользоваться им можно, но не удобно, нужно постоянно подключать гарнитуру.
Признаю, действительно я перепутал 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, код в разы меньше был...
Делал как то подобное, с помощью контролированного 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)
К сожалению код был утерен, и сейчас восстановить не смогу, т.к не смогу провести тесты с нубо-компом (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;
}
Смысл будет понятен:
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
Ага Влад помогал с этим)
Но делалось для своего сервера, однако код в разы короче...
И что с того что короче? Вам твердят Всё время размер кода не влияет на работоспособность. Pwn разве что меньше будет.
$continue$
12.03.2015, 13:25
И что с того что короче? Вам твердят Всё время размер кода не влияет на работоспособность. Pwn разве что меньше будет.
Ну сказал что это влияет на amx файл?
Что то тут болбольством пахнет..
И давай признаем что лучше использовать так чем использовать громоздкий код....
Ну сказал что это влияет на amx файл?
Что то тут болбольством пахнет..
И давай признаем что лучше использовать так чем использовать громоздкий код....
Что-то у тебя в носу застряло похоже, раз запах топика начал чувствовать. Что лучше использовать, решат сами юзеры которые заглянут сюда. И да, "громоздкий код", ты просто предложил функцию, она не заменит весь мануал. Да и для другой кнопки, придется писать аналогичную функцию с обратным отсчетом.
if(spectating[playerid] <= 0) spectating[playerid] = GetMaxPlayers(); // если игрок наблюдает за минимальным ID, то перекидываем его на максимальный
У GetMaxPlayers() аргумент уберите
и
stock UpdateSpectatingStatus(spectatorid, spectedid)
уберите двоеточие
if(spectating[playerid] <= 0) spectating[playerid] = GetMaxPlayers(); // если игрок наблюдает за минимальным ID, то перекидываем его на максимальный
У GetMaxPlayers() аргумент уберите
и
stock UpdateSpectatingStatus(spectatorid, spectedid)
уберите двоеточие
Ок, Thanks.
$continue$
12.03.2015, 21:47
Что-то у тебя в носу застряло похоже, раз запах топика начал чувствовать. Что лучше использовать, решат сами юзеры которые заглянут сюда. И да, "громоздкий код", ты просто предложил функцию, она не заменит весь мануал. Да и для другой кнопки, придется писать аналогичную функцию с обратным отсчетом.
Написать обратный алгоритм с переключением на num4 и num6?
Все равно код выйдет меньше даже с двумя функциями :punish:
Написать обратный алгоритм с переключением на num4 и num6?
Все равно код выйдет меньше даже с двумя функциями :punish:
МЕНЬШЕ != БЫСТРЕЕ
А не лучше, вместо GetMaxPlayers() использовать GetPlayerPoolSize()?
NewGreen
13.03.2015, 15:49
А не лучше, вместо GetMaxPlayers() использовать GetPlayerPoolSize()?
Конечно лучше, но есть одно но, GetPlayerPoolSize() работает только в 0.3.7 которая находится в стадии разработки, а это значит что в 90% случаев данный мануал работать не будет!
Конечно лучше, но есть одно но, GetPlayerPoolSize() работает только в 0.3.7 которая находится в стадии разработки, а это значит что в 90% случаев данный мануал работать не будет!
Но всё же GetMaxPlayers() возвращает не максимальный id на сервере, а максимальное кол-во игроков, которые МОГУТ быть на сервере.
- - - Добавлено - - -
Кстати, примечание 2. Можно сделать, чтобы при дисконнекте стояла проверка, если last_id == playerid и playerid-1 подключен к серверу last_id = playerid-1. Возможно, я не уверен в этом.
Но всё же GetMaxPlayers() возвращает не максимальный id на сервере, а максимальное кол-во игроков, которые МОГУТ быть на сервере.
- - - Добавлено - - -
Кстати, примечание 2. Можно сделать, чтобы при дисконнекте стояла проверка, если last_id == playerid и playerid-1 подключен к серверу last_id = playerid-1. Возможно, я не уверен в этом.
По любому нужно будет после дисконнекта, высчитывать ID самого большого (после disconnect игрока) с помощью цикла. Я по моему написал об этом.
if(playerid == last_id)
for(new i = GetMaxPlayers() - 1;i != -1;--i)
if(i > last_id ) last_id = i;
Может так?
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
МЕНЬШЕ != БЫСТРЕЕ
Тестировал на скорость?
Краткость - сестра таланта.
Все гениальное - просто.
Тестировал на скорость?
Краткость - сестра таланта.
Все гениальное - просто.
Да нет... Не беру вообще твой вариант во внимание.
Astrakhan30
25.05.2016, 17:38
Не нужно ничего доделывать. Игрок после выхода из слежки, отправляется на spawn.
Думаю, стоит заменить в /offspec на:
TogglePlayerSpectating(playerid, 0);
vovandolg
17.06.2016, 22:08
//Отключаем режим наблюдения у следящего
TogglePlayerSpectating(playerid, 1);
Чтожжж отключилии
При выходе наблюдаемого игрока должна вызываться OnPlayerStreamOut? На этот случай тестили?
У меня не вызывается, пришлось добавить код на OnPlayerDisconnect
vovandolg
31.07.2016, 03:12
У меня вызывалась, только я на всякий пожарный ещё добавлял проверку игрока на INVALID_PLAYER_ID
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;
}
OnPlayerStreamOut не вызывается при смене интерьера и виртуального мира. Система требует доработок, причем довольно значительных.
При смене интерьера игрок выходит из зоны стрима у следящего, так как их интерьеры не совпадают.
vovandolg
25.08.2016, 02:48
При смене интерьера игрок выходит из зоны стрима у следящего, так как их интерьеры не совпадают.
Не всегда же)
При смене интерьера игрок выходит из зоны стрима у следящего, так как их интерьеры не совпадают.
Ты сначала протести, потом утверждай.
Ты сначала протести, потом утверждай.
Я тестировал и работал с этой системой.
Я тестировал и работал с этой системой.
Тоже самое делал и я (конечно не с этом системой, а конкретно с методом с 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:
Ну вот. Я об этом изначально и писал)
Powered by vBulletin® Version 4.2.0 Copyright © 2024 vBulletin Solutions, Inc. All rights reserved. Перевод: zCarot