Главная Случайная страница


Полезное:

Как сделать разговор полезным и приятным Как сделать объемную звезду своими руками Как сделать то, что делать не хочется? Как сделать погремушку Как сделать так чтобы женщины сами знакомились с вами Как сделать идею коммерческой Как сделать хорошую растяжку ног? Как сделать наш разум здоровым? Как сделать, чтобы люди обманывали меньше Вопрос 4. Как сделать так, чтобы вас уважали и ценили? Как сделать лучше себе и другим людям Как сделать свидание интересным?


Категории:

АрхитектураАстрономияБиологияГеографияГеологияИнформатикаИскусствоИсторияКулинарияКультураМаркетингМатематикаМедицинаМенеджментОхрана трудаПравоПроизводствоПсихологияРелигияСоциологияСпортТехникаФизикаФилософияХимияЭкологияЭкономикаЭлектроника






Листинг 14.4. Коммуникационная программа NLINK (NLINK.C)





// ВКЛЮЧАЕМЫЕ ФАЙЛЫ /////////////////////////////////

#include <dos.h>

#include <bios.h>

#include <stdio.h>

#include <math.h>

#include <conio.h>

#include <graph.h>

// ОПРЕДЕЛЕНИЯ ////////////////////////////////////////////

// регистры UART

#define SER_RBF 0 // буфер чтения

#define SER_THR 0 // буфер записи

#define SER_IER 1 // регистр разрешения прерываний

#define SER_IIR 2 // регистр идентификации прерывания

#define SER_LCR 3 // регистр управляющих данных

// и разрешения загрузки делителя

#define SER_MCR 4 // регистр управления модемом

#define SER_LSR 5 // регистр состояния линии

#define SER_MSR 6 // регистр состояния модема

#define SER_DLL 0 // младший байт делителя

#define SER_DLH 1 // старший байт делителя

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

#define SER_BAUD_1200 96 // значения делителя

// для скоростей 1200-19200 бод

#define SER_BAUD_2400 48

#define SER_BAUD_9600 12

#define SER_BAUD_19200 6

#define SER_GP02 8 // разрешение прерываний

#define COM_1 0х3F8 // базовый адрес регистров СОМ1

#define COM_2 Ox2F8 // базовый адрес регистров COM2

#define SER_STOP_1 0 //1 стоп-бит на символ

#define SER_STOP_2 4 //2 стоп-бита на символ

#define SER_BITS_5 0 //5 значащих бит на символ

#define SER_BITS 6 1 //6 значащих бит на символ

#define SER_BITS_7 2 //7 значащих бит на символ

#define SER_BITS 8 3 //8 значащих бит на символ

#define SER_PARITY_NONE 0 // нет контроля четности

#define SER_PARITY_ODD 8 // контроль по нечетности

#define SER PARITY EVEN 24 // контроль по четности

#define SER_DIV_LATCH_ON 128 // используется при загрузке делителя

#define PIC_IMR 0х21 // маска для регистра прерываний

#define PIC ICR 0х20 // маска для контроллера

// прерываний (порт 20h)

#define INT_SER_PORT_0 0x0C // маска для управления

// прерываниями СОМ1 и COM3

#define INT_SER_PORT_1 0x0B // маска для управления

// прерываниями COM2 и COM4

#define SERIAL_BUFF_SI2E 128 // размер буфера

// ГЛОБАЛЬНЫЕ ПЕРЕМЕННЫЕ /////////////////////////////////////////

void (_interrupt _far *01d_Isr) (); // адрес старой подпрограммы

// обслуживания прерываний

// СОМ-порта

char ser_buffer[SERIAL_BUFF_SIZE];// буфер для приходящих символов

int ser_end = -1,ser_start=-l; // указатели позиции в буфере

int ser_ch, char_ready=0; // текущий символ и флаг

// готовности

int old_int_mask; // старое значение маски

// контроллера прерываний

int open_port; // текущий открытый порт

int serial_lock =,0; // "семафор" для процедуры

// обработки прерывания,

// управляющий записью

// в программный буфер

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

void _interrupt _far Serial_Isr(void)

(

// это процедура обработки прерывания СОМ-порта. Она очень проста.

// При вызове она читает полученный символ из- регистра 0 порта

// и помещает его в буфер программы.

// Примечание: язык Си сам заботится о сохранении, регистров

// и восстановлении состояния

// запрещаем работу всех других функций

//во избежание изменения буфера

serial_lock = 1;

// записываем символ в следующую позицию буфера

ser_ch = _inp(open_port + SER_RBF);

//Устанавливаем новую текущую позицию буфера

if (++ser_end > SERIAL_BUFF_SIZE-1) ser_end =0;

// помещаем символ в буфер

ser_buffer[ser_end] = ser_ch;

++char_ready;

// Восстанавливаем состояние контроллера прерываний

_outp(PIC_ICR,Ox20);

// Разрешаем работу с буфером

serial_lock = 0;

} // конец функции

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

int Ready_Serial()

{

// функция возвращает значение, отличное от нуля,

// если есть в буфере есть символы и 0 - в противном случае

return(char_ready);

} // конец функции

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

int Serial_Read() {

// функция возвращает последний записанный

//в программный буфер символ

int ch;

// ждем завершения функции обработки прерывания

while(serial_lock){}

// проверяем/ есть ли в символы в буфере

if (ser_end!= ser_start)

{

// изменяем значение начальной позиции буфера

if (++ser_start > SERIAL_BUFF_SIZE-1) ser_start = 0;

// читаем символ

ch = ser_buffer[ser_start];

//в буфере стало одним символом меньше

if (char_ready > 0) --char ready;

// возвращаем символ вызвавшей функции

return(ch);

// конец действий, если буфер не пуст

else

// буфер был пуст - возвращаем 0

return(0);

}// конец функции /////////////////////////////////

Serial_Write(char ch)

{ // эта функция записывает символ в буфер последовательного порта,

// но вначале она ожидает, пока он освободится.

// Примечание: эта функция не связана с прерываниями-

// и запрещает их на время работы

// ждем освобождения буфера

while(!(_inp(open_port + SER_LSR) 5 0х20)){}

// запрещаем прерывания

_asm cli

// записываем символ в порт

_outp(open_port + SER_THR, ch);

// снова разрешаем прерывания

_asm sti

} // конец функции /////////////////////////////////////

Open_Serial(int port_base, int baud, int configuration)

{

// Функция открывает последовательный порт, устанавливает его

// конфигурацию и разрешает прерывания при получении символа

// запоминаем базовый адрес порта

open_port = port_base;

// сначала устанавливаем скорость работы

// разрешаем загрузку делителя

_outp(port_base + SER_LCR, SER_DIV_LATCH_ON);

// посылаем младший и старший байты делителя

_outp(port_base + SER_DLL, baud);

_outp(port_base + ser_dlh, 0);

// устанавливаем конфигурацию порта

_outp(port_base + SER_LCR, configuration);

// разрешаем прерывания

_outp(port_base + SER_MCR, SER_GP02);

_outp(port_base + SER_IER, 1);

// откладываем, работу с контроллером прерываний,

// пока не установим процедуру обработки прерывания

if (port_base == СОМ_1)

{

Old_Isr = _dos_getvect(INT_SER_PORT 0);,

_dos_setvect(INT_SER_PORT_0, Serial_Isr);

printf("\n0pening Communications Channel Com Port #1...\n");

}

else

{

Old_Isr = _dos_getvect(INT_SER_PORT_1);

_dos_setvect(INT_SER_PORT_1, Serial_Isr);

printf("\n0pening Communications Channel Com Port #2...\n");

}

// разрешаем прерывание СОМ-порта на уровне контроллера прерываний

old_int_mask = _inp(PIC_IMR);

_outp(PIC_lMR, (port_base==COM_l)? (old_int_mask & OxEF):(old_int_mask & OxF7));

} // конец функции ///////////////////////////////////////////////////////////

Close_Serial (int port_base)

{

// функция закрывает СОМ-порт, запрещает вызов его прерываний

// и восстанавливает старый обработчик прерывания

// запрещаем прерывания по событиям СОМ-порта

_outp(port_base + SER_MCR, 0);

_outp(port_base + SER_IER, 0).;

_outp(PIC_IMR, old_int_mask);

// восстанавливаем старый обработчик прерывания

if (port_base == СОМ_1)

{

_dos_setvect(INT_SER_PORT_0, 01d_Isr);

printf("\nClosing Conuflunications Channel Corn Port #1.\n");

}

else

{

_dos_setvect(INT_SER_PORT_l, 0ld_Isr);

printf("\nClosing Communications Channel Com Port #2.\n");

}

// конец функции // ОСНОВНАЯ ФУНКЦИЯ /////////////////////////////

main ()

{

char ch;

int done=0;

printf("\nNull Modem Terminal Communications Program.\n\n");

// открываем СОМ1

Open_Serial(COM_1,SER_BAUD_9600,

SER_PARITY_NONE | SER_BITS_8 | SER_STOP_1);

// главный рабочий цикл

while (!done) {

// работа с символами на локальной машине

if (kbhit()) {

// получаем символ с клавиатуры

ch = getch(); printf("%c",ch);

// посылаем символ на удаленную машину

Serial_Write(ch);

// не была ли нажата клавиша ESC? Если да - конец работы

if (ch==27) done=l;

// Если был введен символ "перевод каретки" (CR),

// добавляем символ "новая строка" (LF)

if (ch==13)

{

printf("\n");

Serial_Write(10);

}

}// конец обработки клавиатуры

// пытаемся получить символ с удаленной машины

if (ch = Serial_Read()) printf("%c", ch);

if (ch == 27) { printf("\nRemote Machine Closing Connection.");

done=l;

} // конец обработки нажатия ESC на удаленной машине

} // конец цикла while

// закрываем связь и кончаем работу

Close_Serial(COM_l);

} // конец функции main

Изучение принципов коммуникации через последовательный порт ввода/вывода похоже на посещение зубного врача — никому не нравится, но всем приходится через это пройти. Мне жаль, что я подвергаю вас подобной пытке, но это исключительно важно знать. Посему не буду вас дольше истязать и перейду к более интересной теме игровых коммуникаций.

Стратегия игровых коммуникаций

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

В игре для двух участников, которая запускается на одном компьютере, оба игрока имеют равные шансы влиять на игровую ситуацию. Рисунок 14.4 показывает два различных представления этой разновидности игр.

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


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

Для преодоления этих проблем существуют следующие пути:

§ Можно передать на другой компьютер полную информацию о действиях игрока. В этом случае коммуникационный порт будет напоминать виртуальное устройство ввода данных, управляемое другим компьютером. Когда игрок тронет клавиши, переместит мышь или повернет ручку джойстика это действие тут же передается по кабелю другой машине, которая па основе полученных данных может скорректировать виртуальное местоположение игрока в собственном игровом пространстве;

§ Второй метод называется «синхронизацией вектора состояния». В этом случае вместо передачи данных от устройств ввода/вывода, мы передаем «состояние» игрового пространства в целом, так что принимающий компьютер может синхронизироваться с передающим, как показано на рисунке 14.6. Этот метод работает достаточно хорошо, однако при его использовании может значительно увеличиться количество передаваемой информации.

Вскоре мы разберемся с каждым из методов более, детально, а сейчас стоит поговорить о наиболее типичных ошибках, встречающихся при соединении двух ПК:

§ Наибольшая проблема возникает, когда две машины теряют синхронизацию. Скажем, одна из них имеет 586-й процессор, а другая - 386-й. При этом один ПК неизбежно окажется впереди другого и синхронизация будет потеряна. Этот фактор должен быть принят во внимание еще на этапе разработки игры;

§ Следующая потенциальная проблема может быть вызвана так называемым «недетерминированным эффектом наложения» (я расскажу лишь о некоторых лежащих иа поверхности вещах, однако этого достаточно для понимания сути проблемы). Обе игры должны быть полностью детерминированы. Это значит, например, что мины на разных компьютерах не могут оказаться в различных местах. Если на одной машине мина расположена скажем, в центре игрового поля, то и на другой машине ей лучше бы оказаться в том же месте. Точно так же, при использовании генератора случайных чисел для управления поведением существ, необходимо, чтобы на обеих машинах генерировалась одна и та же последовательность случайных величин. Единственным путем преодоления этой проблемы может служить передача полной информации об игровой ситуации, так чтобы даже случайные события, происходящие на одной машине, без искажений отражались на другой,

Эти проблемы действительно очень серьезны и вам необходимо их тщательно проработать. Мы кратко обсудили основные методы синхронизации и теперь уже можно поговорить о них более подробно.

Синхронизация вектора состояния

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

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

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


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

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

Синхронизация состояния ввода/вывода

Синхронизация состояния ввода/вывода является методом, при котором статус устройств ввода данных передается на другой ПК в реальном времени. Все, что игрок делает на одном компьютере, принимающая система воспринимает как входные данные, которые использует для корректировки в своем игрового пространстве поведения образа отдаленного игрока. Рисунок 14.8 пояснее сказанное.

Этот метод четко работает до тех пор, пока обе системы остаются синхронизированными и не происходит никаких случайных изменений игровой ситуации. Если же подобное произойдет, то другая машина не сможет «узнать» об этом, потому как данный способ не предназначен для передачи такого рода информации.

Если вам все же потребуется, чтобы происходили какие-то случайные события, вы должны воспользоваться первым методом, чтобы сообщить об изменениях другой машине. В дальнейшем мы объединим оба способа синхронизации вместе - это совершенно неизбежно.

Для синхронизации состояния ввода/вывода необходимо;

§ Опросить текущее состояние устройств ввода данных, будь то джойстик или клавиатура;

§ Объединить их вместе в пакет и послать через коммуникационный канал.

Термин пакет подразумевает объединение разносортной информации. Поэтому для пересылки пакетов мы должны принять ряд соглашений, чтобы последовательные коммуникационные системы «знали», что означает та или иная часть информации. Скажем, мы решили передать через коммуникационный канал положение ручки джойстика одновременно с состоянием его кнопок.

Формат пакета для передачи этих данных мог бы выглядеть примерно так, как это Показано в таблице 14.5.

 

Таблица 14.5. Образец пакета информационного пространства ввода/вывода.

Date: 2015-09-18; view: 382; Нарушение авторских прав; Помощь в написании работы --> СЮДА...



mydocx.ru - 2015-2024 year. (0.005 sec.) Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав - Пожаловаться на публикацию