Форум программистов
 

Восстановите пароль или Зарегистрируйтесь на форуме, о проблемах и с заказом рекламы пишите сюда - alarforum@yandex.ru, проверяйте папку спам!

Вернуться   Форум программистов > разработка игр, графический дизайн и моделирование > Gamedev - cоздание игр: Unity, OpenGL, DirectX
Регистрация

Восстановить пароль
Повторная активизация e-mail

Купить рекламу на форуме - 42 тыс руб за месяц

Ответ
 
Опции темы Поиск в этой теме
Старый 26.07.2009, 19:47   #1
Beermonza
Инженер ИС
Старожил
 
Аватар для Beermonza
 
Регистрация: 13.12.2006
Сообщений: 2,671
По умолчанию Сеть в игре (MMO 2D RPG)

Братцы, выручайте! ...очень прошу.
Дело такое. Вот я написал сокетное соединение, игроки подключаются/отключаются, все работает, но! ...известно, что TServerSocket при уходе одного из каналов переназначает список Connection. Стало быть, мой массив с записями игроков хранит теперь неверные идентификаторы каналов, ведь сокет переписал свой список каналов "не сообщив" массиву как он его изменил. Наступает такая ситуация, когда пробежав по массиву записей пользователей сервер пытается отправить данные конкретному по устаревшему номеру канала, которого может уже не существовать, или отправляет их не в тот канал. Когда игрок был один все было нормально, ...два - тоже, но когда игроков (кроме ботов) поболее, то возникает путаница или вылетают ошибки.
Есть ли какие-нибудь решения по этому поводу? ...а то башня уже закипает. По идее нужно заставить сервер одновременно со своим обновлением списка каналов, обновить и записи массива, указав правильные номера каналов, на основании прежнего списка и нового.
Буду весьма благодарен за советы, идеи, ссылки на инфу и пр.
Руководитель проекта MMO 2D RPG: Настоящее имя Денис Стрижак (10.05.1981-6.02.2019) Мир духу его
Beermonza вне форума Ответить с цитированием
Старый 27.07.2009, 00:56   #2
MaTBeu
Eclipse Foundation
Старожил
 
Аватар для MaTBeu
 
Регистрация: 19.09.2007
Сообщений: 2,604
По умолчанию

Попробуйте сделать соответствие между списком каналов сервеного сокета, и массивом клиентов. Можно ввести дополнительный массив, который будет служить флагом для каждого канала, то есть типа канал изменен - флаг по данному индексу в массиве флагом установлен в true или в 1. То есть нужно проверить соедниение. А если флаг не установлен (false или 0) проверять соединение не нужно.

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

Но в обоих случаях нужно вести четкое соответствие списку каналов массива клиентов.
Этим можно решить еще одну проблему - два игрока с одного IP, просто проверять на наличие клиента с таким IP в списке и если есть - банить или высылать предпреждение о том, что кто-то хочет выйти с таким же IP в игру.
MaTBeu вне форума Ответить с цитированием
Старый 27.07.2009, 14:56   #3
Beermonza
Инженер ИС
Старожил
 
Аватар для Beermonza
 
Регистрация: 13.12.2006
Сообщений: 2,671
По умолчанию

С двумя игроками с одного IP все уже сделано, более того, сервер различает каждую машину, например из под сети с одним IP.

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

Дело в чем? ...мы не ждем, что пользователь выйдет корректно, т.е. как можно было бы с клиента отсылать пакет "я ушел" и этим самым определять, какой канал сейчас нужно искусственно закрыть. Тут все четко. Но, ...во-первых это тоже дыра в системе, во-вторых при сбое в сети или закрытии приложения перезапуском компа сервер будет считать, что пользователь в игре, номер его канала так же будет считаться в массиве рабочим, пока он не вызовется и сервер не выдаст ошибку "out of list". Но, минуточку, ...сокет не показывает номер отпавшего канала, он тихо создает новый список, сдвигая все числа к первым, т.е. если были каналы: 1,2,3,4,5,6,7 и вылетел 4-й, то список станет: 1,2,3,4,5,6 причем 4-й это бывший 5-й и т.д. Именно это и беспокоит, ...даже если организовать сравнение, все равно не удасться узнать номер отпавшего канала, ...а если этот способ есть, то достаточно пробежаться по массиву записей, найти отпавший номер, пометить запись как не используемую, а все последующие идентификаторы каналов заменить на "тот что записан минус 1".

Вот такие вот пироги. Помогите пожалуйста найти метод извлечения номера отпавшего канала, и все будет решено.

Послесловие: ...да, чуть не забыл, ...если использовать для идентификации IP-адрес пользователя, то мы отсекаем "подсетчиков", домашние сети, клубы, интернет-кафе и пр. ...только одна машина с одного интернет-IP-адреса.
Руководитель проекта MMO 2D RPG: Настоящее имя Денис Стрижак (10.05.1981-6.02.2019) Мир духу его

Последний раз редактировалось Beermonza; 27.07.2009 в 15:04.
Beermonza вне форума Ответить с цитированием
Старый 27.07.2009, 15:12   #4
MaTBeu
Eclipse Foundation
Старожил
 
Аватар для MaTBeu
 
Регистрация: 19.09.2007
Сообщений: 2,604
По умолчанию

Цитата:
ну например отправлять какой-то маленький тестовый пакет, по TCP протоколу(ну чтобы с подтверждением доставки было)
Я говорил не об ожидании ответа, а об ожидании доставки. Если пакет дошел - значит соединение есть, а если не дошел - нету, вот вам и искомый номер канала.

Можно попробовать написать свой класс, наследованный от TServerSocket, и самому контроллировать управление списком каналов. Но это влечет за собой необходимость переписывать основные функции исходного класса.

Но лучше сделать вот как. Просто поставить проверку на наличие соединения перед отправкой очередного пакета клиенту. Если клиент отрубился некорректно, вылетит исключение - ловите его, и в обработке исключения помечаете этот канал как неиспользуемый.

В данном случае важно поймать исключение, а то оно положит весь сервер.

На С++ это делается блоком
Код:
try 
{
//пытаемся отправить пакет клиенту, пакет обычный, любой пакет из игры
}
catch(тут исключение, что-то типа NoConnectionException)
{
//тут помечаете этого клиента как неактивного
}
В Делфи, не знаю.

Но, кстати, вам всеравно понадобится проверять весь список, потому что отключится могут сразу несколько.
Например было 1,2,3,4,5,6,7,8 отключились 2,5,7 вам нужно сдвинуть 3 и 4 на одну позицию, 6 на две, а 8 на 3

Последний раз редактировалось MaTBeu; 27.07.2009 в 15:19.
MaTBeu вне форума Ответить с цитированием
Старый 27.07.2009, 16:37   #5
Beermonza
Инженер ИС
Старожил
 
Аватар для Beermonza
 
Регистрация: 13.12.2006
Сообщений: 2,671
По умолчанию

Хорошо, буду проверять, ...отлов исключений получше будет ожидания пакетов, или что-то в этом роде.
Руководитель проекта MMO 2D RPG: Настоящее имя Денис Стрижак (10.05.1981-6.02.2019) Мир духу его
Beermonza вне форума Ответить с цитированием
Старый 27.07.2009, 16:58   #6
Beermonza
Инженер ИС
Старожил
 
Аватар для Beermonza
 
Регистрация: 13.12.2006
Сообщений: 2,671
По умолчанию

Вот что было предложено.

Цитата:
Сообщение от alexBlack
По-моему лучше вообще без параллельного массива.

В OnClientConnect мы получаем сокет, который будет добавлен в connections. В его поле Data помещаем ссылку на наши данные.

При отключении (штатно или по ошибке) будет вызван OnClientDisconnect или OnError (смотря что раньше случиться, но это не важно). Поле Data переданного нам сокета по-прежнему содержит наши данные и мы можем определить какой клиент отключился или хотя бы уничтожить связанные с ним данные.

а вместо отдельного массива используем список Connections:

Код:
for i:=0 to ActiveConnections-1 do begin
Connections[i].Data - ссылка на наши данные
end;
Руководитель проекта MMO 2D RPG: Настоящее имя Денис Стрижак (10.05.1981-6.02.2019) Мир духу его
Beermonza вне форума Ответить с цитированием
Старый 27.07.2009, 23:36   #7
Beermonza
Инженер ИС
Старожил
 
Аватар для Beermonza
 
Регистрация: 13.12.2006
Сообщений: 2,671
Хорошо Как синхронизировать сокет с записями в массиве

Значит так. Выбрал я пример alexBlack'a и пристроил в свой протокол. Работает без сбоев.

Принцип

У меня все персонажи числятся в одном массиве записей UnitsMas, собственного типа TGUnits. В нем есть некая запись типа Word - UChannel. Когда идет подключение и опрос пользователя этой переменной присваивается порядковый номер в массиве, который система выдала ему:

Код:
// после поиска пустого места в массиве
// перебор массива далее может быть по другим алгоритмам, поэтому запись с фактом позиции нужна внутри
UnitsMas[u].UChannel := u;

// запишем указатель на номер в массиве
ServerSocket.Socket.Connections[i].Data := @UnitsMas[u].UChannel;
Таким образом, при обращении к сокету можно точно узнать где в массиве записи для конкретного пользователя:

Код:
// если есть подключения
If ServerSocket.Socket.ActiveConnections > 0 then
  Begin
    // пробегаем по всем каналам
    For j := 0 to ServerSocket.Socket.ActiveConnections-1 do
      Begin
        // работаем с записями массива по указателю
        With UnitsMas[Word(ServerSocket.Socket.Connections[j].Data^)] do
           Begin
             ...
           end;
      end;
  end;
А вот так можно узнать какой канал отпал, например в корректном случае (Disconnect):

Код:
procedure TForm1.ServerSocketClientDisconnect(Sender: TObject;
  Socket: TCustomWinSocket);
begin
  // отключение отсоединившегося пользователя
  With UnitsMas[Word(Socket.Data^)] do
    Begin
      // обработка записей
    end;
end;
Если кто сможет проверить параллельно и укажет замечания если таковые существуют, то буду весьма признателен.
Спасибо всем за советы.
Руководитель проекта MMO 2D RPG: Настоящее имя Денис Стрижак (10.05.1981-6.02.2019) Мир духу его
Beermonza вне форума Ответить с цитированием
Ответ


Купить рекламу на форуме - 42 тыс руб за месяц



Похожие темы
Тема Автор Раздел Ответов Последнее сообщение
Как сделать сеть в простенькой игре??? Руслантус Gamedev - cоздание игр: Unity, OpenGL, DirectX 16 21.05.2010 20:44
RPG на Дельфи Juffin Gamedev - cоздание игр: Unity, OpenGL, DirectX 10 10.04.2009 21:37
Создание текстового RPG KORN Софт 3 15.07.2008 09:21
3D-RPG Кронос Gamedev - cоздание игр: Unity, OpenGL, DirectX 25 12.05.2007 11:21