12.01.2015

КОНКУРС приз GoPRO HERO4 SILVER

11.12.2011





Команда TrainYard закончила разработку и тестирование проекта Process.
Process повествует о событиях, происходящих в нескольких вагонах поезда метро за 20 минут до неминуемой катастрофы.


Здесь совсем немного игровых ситуаций, чтобы проект можно было назвать полноценной игрой. Акцент сделан на формировании эмоционального фона. Process ближе к интерактивной инсталляции, эксперименту. Идея заключалась в том, чтобы за счет истории и аудиовизуального окружения погрузить игрока в тяжелую атмосферу неопределенности, ощущения сна и нереальности происходящего.

Узнать подробнее об игре можно на сайте проекта.

Страница загрузки русской версии
Страница загрузки английской версии




22.08.2011

Проект в котором я участвую

Process: О проекте: Process - игровой проект в жанре adventure, действие которого разворачивается в нескольких вагонах поезда метро. Через 20 минут произойдет...

27.03.2011

Process. Трейлер



Process - игровой проект в жанре adventure, действие которого разворачивается в нескольких вагонах поезда метро. Через 20 минут произойдет катастрофа - поезд на огромной скорости слетит с рельс. Игровой процесс длится ровно отведенное до аварии время, в этот период в мрачных плохо освещенных интерьерах, сочетающих эстетику киберпанка и индастриала, игроку предстоит разобраться в сложившейся ситуации, перебрать все возможные варианты спасения и под конец совершенно по новому взглянуть на происходящее. Это игра-размышление о предопределенности событий и субъективности восприятия окружающего мира.


В действительности проект сложно позиционировать как полноценную игру, Process ближе к интерактивной инсталляции или небольшой визуальной новелле. Но технологически это классический adventure с видом от первого лица, панорамными локациями с возможностью свободного обзора и дискретным перемещением между панорамами за счет point-and-click интерфейса.

Созданием проекта Process занимается TrainYard - небольшая команда независимых разработчиков компьютерных игр. Разработка ведется удаленно, в свободное от основной работы участников время.

Ориентировочная дата выхода проекта конец лета 2011. Process не позиционируется как коммерческий проект. Так как создается игра в свободное время (а с этим всегда сложно), то точной даты окончания работ нет.

14.08.2009

2D Convex hull - выпуклая оболочка на плоскости




Задача
Имется набор точек на плоскости (рис. 1), нужно расчитать такой набор точек который образует выпуклый многоугольник. т.е. точки из первоначального набора будут либо его вершинами либо будут лежать внутри него (рис. 2).


Решение
Найдем такой прямоугольник чтобы все точки из первоначального набора лежали либо внутри него, либо на его сторонах. Выберем 8 точек лежащих на сторонах найденного прямоугольника (рис. 3).
2 точки лежащие на левой стороне прямоугольника самую нижнюю и самую верхнюю.
2 точки лежащие на верхней стороне прямоугольника самую левую и самую правую.
2 точки лежащие на правой стороне прямоугольника самую верхнюю и самую нижнюю.
2 точки лежащие на нижней стороне прямоугольника самую правую и самую левую.
Если на любой из этих сторон прямоугольника только одна точка, то берем только ее одну для соответствующей стороны. В результате получиться основная фигура - выпуклый многоугольник с обходом по часовой стрелке.


Основной набор легко найти за 1 проход по набору начальных точек.
Точки из основного набора попарно образуют прямоугольники. AB образует фиолетовый прямоугольник, BC - синий, CD - коричневый, DE - зеленый, EA - красный (рис. 4)
Если площадь образованного прямоугольника равна нулю, то точки образовавшие этот прямоугольник можно добавить в набор точек результатов(например фиолетовый прямоугольник на рис. 4).

Если же площадь прямоугольника отлична от нуля необходимо проверить и при необходимости включить в результаты точки лежащие внутри этого прямоугольника.
Например сегмент BC (на рис. 5) рассматриваются только точки лежащие в прямоугольнике образованые точками B и C. Серые точки исключены так как лежат справа от BC.

Оставшиеся точки попроядку вставляются в сегмент BC (рис. 6 - 8). Точка вставляется только если она находится выше любого сегмента рассматриваемого промежутка. Например точка P1 находится выше сегмента BP0 поэтому ее вставляют в набор результатов до точки P0. После вставки точки необходимо проверить появились ли после вставки новой точки точки лежащие справа от сегментов входящих в обрабатываемый основной сегмент. Так как точка P0 на (рис. 7) стала правее сегмента P1C ее удалают.



Приложение пример

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

18.07.2009

Простой однопоточный TCP сервер на Win Sockets.

Прежде всего для использования Winsock необходимо подключить заголовочный winsock2.h .
Так же для того чтобы проект слинковался без ошибок необходимо указать библиотеку wsock32.lib .

Подробный алгоритм программы
1. Установка обработчика консольного окна SetConsoleCtrlHandler();
2. Инициализация Win Socks WSAStartup();
3. Создание сокета сервера socket();
4. Установка сокета в неблокирующий режим ioctlsocket();
5. Привязывание сокета к адресу и порту bind();
6. Установка сокета в режим прослушивания listen();
7. Цикл работы сервера (см Алгоритм цикла работы сервера).
8. Закрытие прослушивающего сокета closesocket();
9. Закрытие открытых клиентских сокетов shutdown(); + closesocket();
10. Деинициализаци WniSocks WSACleanup();
11. Снятие обработчика консольного окна SetConsoleCtrlHandler();

Алгоритм цикла работы сервера
1. Принятие подключений от клиентов accept();
2. Добавление вновь подключенных клиентов в общий спиок.
3. Получение данных от подключенных клиентов recv();
4. Закрытие сокетов с ошибкой либо отключившихся shutdown(); + closesocket();
5. Удаление отключенных клиентских сокетов из общего списка.
6. Обработка данных полученных от клиентов.
7. Отсылка данных клиентам send();
8. Закрытие сокетов с ошибкой либо отключившихся shutdown(); + closesocket();
9. Удаление отключенных клиентских сокетов из общего списка.
10. Ожидание (даем время остальным процессам и потокам) Sleep();
11. Выход из цикла если сервер заканчивает работу иначе переход к шагу 1.

Что такое обработчик консольного окна и зачем он нужен в этой программе
Так как наше приложение является однопоточным сервером, то при закрытии консольного окна клиенты подключенные к серверу могут зависнуть на некоторое время, так как не клиентские сокеты не были закрыты. Получается ситуация что клиенты ждут ответа сервера не зная что сервер завершил свою работу (либо просто "упал"). Поэтому мы сделаем так что при закрытии консольного окна все клиенты будут корректно отключены, произойдет деинициализация Winsocks и т.д (Корректное завершение). Затем произойдет выход из программы.

Инициализировать/деинициализация Winsocks
Для инициализации и деинициализации используются функции WSAStartup() и WSACleanup() соответственно. Если в программе не был инициализирован Winsocks то все функции Winsocks будут завершаться с ошибкой а WSAGetLastError() вернет WSANOTINITIALISED. Проще говоря инициализация Winsocks обязательна если вам нужна сеть будь то ваше приложение клиентом или сервером.

Создание серверного (прослушивающего) сокета
Сокет создается функцией socket().
Установка сокета в неблокирующий режим функцией ioctlsocket().
Привязка сокета к интерфейсу (конкретному доступному локальному IP адресу) и порту bind().
Установка сокета в режим прослушивания listen().

Что такое блокирующие и не блокирующие сокеты
При использовании блокирующих сокетов многие функции как бы "зависают" и не дают исполнятся программе до тех пор пока они не получат конкретный результат либо ошибку.
К примеру при выполнении функции recv() если клиент не посылал данные то сервер будет ожидать когда клиент их пришлет либо пока клиент отключится. Соответственно если было подключено несколько других клиентов то они тоже будут ждать пока не произойдет завершение функции recv(). Для решения данной проблемы применяется многопоточность (для каждого клиента выделяется отдельный поток) либо при помощи функции select().
Не блокирующие же сокеты в случае если операция не может завершиться сразу (например еще не пришло никаких данных от клиента при вызове recv()) просто возвращается с ошибкой. При этом эта ошибка возвращаемая функцией WSAGetLastError() устанавливается в значение WSAWOULDBLOCK.

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

Подключение клиентов
После того как создали серверный сокет клиенты уже могут подключаться к серверу. Все подключенные клиенты будут помещаться в очередь. Функция accept() вернет дескриптор сокета клиента и удалит его из очереди. Через него наше приложение и будет общаться с конкретным клиентом. Так же accept() вернет данные о клиенте IP адрес и порт с которого произошло подключение, эту информацию к примеру можно использовать для отключения неугодных клиентов (забаненных IP арресов).

Работа с подключенными клиентами
После того как клиент подключен можно посылать данные либо получать от него. Посылают данные клиенту при помощи функции send(). При этом функция возвращает количество посланных байт и если это значение отличается от заданного нами то придется остатки послать позже. Если же функция вернет SOCKET_ERROR то необходимо проверить код ошибки при помощи WSAGetLastError().
Функция recv() получает данные от подключенного клиента. Функция возвращает количество полученных байт, но не более чем размер буффера для данных. Так же функция возвращает SOCKET_ERROR в случае ошибке (ошибку можно получить функцией WSAGetLastError()). Если же функция recv() возвращает значение 0 это означает что клиент решил закрыть подключение (вызвал shutdown с SD_SEND). После этого (если к примеру положено по протоколу) он может дополнительно послать данные (принять от клиента уже ничего не получится) и закрыть сокет.
Приложение в примере к статье при появлении ошибки отличной от WSAEWOULDBLOCK при вызовах функций send() и recv() считает что клиент отключился и закрывает сокет. Однако лучше всего обрабатывать эти ошибки так как некоторые из них могут быть не критические.

Закрытие сокетов
"Неправильное" закрытие сокета
Сокет можно закрыть просто функцией closesocket(), при этом противоположная сторона при выполнении функции recv() получит сообщение об ошибке ECONNRESET.
"Правильное" закрытие сокета (graceful close).
Если же перед закрытием используется функция shutdown() с параметром SD_SEND то при выполнении функции recv() программа не получит ошибки но функция вернет значение 0. Рекомендуется использовать именно "правильное закрытие", хотя при "Неправильном" закрытии никаких утечек системных ресурсов не происходит.

Приложение пример
Открывает на всех интерфейсах порт 8000. К серверу можно подключиться например используя браузер (http://127.0.0.1:8000, http://localhost:8000 либо http://<ваш ип адрес>:8000 ). При подключении сервер сгенерирует простейшую HTML страницу с текстом "test" после чего закроет соединение.