Teraz jest Cz 28 mar, 2024 13:19


Wzorzec komunikacji klient serwer

Archiwum działów sekcji Armed Assault
  • Autor
  • Wiadomość
Offline
Avatar użytkownika

kondor

Major

Major

  • Posty: 880
  • Dołączył(a): Pt 11 lut, 2005 04:00
  • Lokalizacja: Berlin (wschodni)

Wzorzec komunikacji klient serwer

PostŚr 18 lut, 2009 12:05

Witam.
Poniższy tekst dotyczy przynajmniej średnio-zaawansowanych misji - jeśli tworzysz coś prostego, poniższy framework może niepotrzebnie skomplikować Twoją misję!

Zapewne wielu z nas miało nieraz problem z synchronizowaniem zmiennych, zdarzeń itp.
Join in progress, który przyszedł wraz z armą spowodował bardzo dużą komplikację, gdyż gracze JIP mają zazwyczaj początkowe wartości zmiennych.
Dobrym rozwiązaniem jest (znana praktyka) trzymania całego "silnika" (np liczącego punkty, sprawdzającego czy zadanie zostało wykonane itp) po stronie tylko serwera, natomiast na komputerach klientów widoczne są tylko jakieś graficzne adnotacje (np zmiany koloru markera, wyświetlanie komunikatów itp).
W teorii ładnie to brzmi, ale jak w praktyce to zrobić/napisać?
Moje rozwiązanie:
Polega na wstawieniu kilku poniższych skryptów (do misji), które umożliwią (względnie prostą komunikację serwer<->klient)
Skrypt 1. - powinien być uruchomiony na starcie misji (np wywołany w init.sqf):
Kod: Zaznacz cały
//init.sqf
call compile preprocessFile "init_locality.sqf";

Kod: Zaznacz cały
//init_locality.sqf
/*5 pierwszych linijek bazuje na rozwiązaniu sickboya (patrz: http://community.bistudio.com/wiki/6thSense.eu:EG#Determining_if_machine_is_Ingame_Server.2C_Ded_Server.2C_Player_or_JIP_Player )
*/

isDedicatedServer = isServer AND (isNull player);
isClientServer = isServer AND (not isNull player);
isOnlyClient = not isServer;
isClient = isOnlyClient OR isClientServer;
isJIP = (isNull player) AND (not isServer);

//for sending commands to clients use ptr_send_commands_to_clients
commandFromServer = "";
if( isOnlyClient )then
{
   "commandFromServer" addPublicVariableEventHandler
   {
      call compile commandFromServer;
      commandFromServer="";
   };
};

//for sending commands to server use ptr_send_commands_to_server
commandToServer = "";
if( isServer )then
{
   "commandToServer" addPublicVariableEventHandler
   { 
      call compile commandToServer;
      commandToServer="";
   };
};

ptr_send_commands_to_clients = compile preprocessFile "send_commands_to_clients.sqf";
ptr_send_commands_to_server = compile preprocessFile "send_commands_to_server.sqf";

powyższy skrypt wykrywa, czy maszyna to serwer, serwer dedykowany, itd (mało istotne).
Ponadto dodaje 2 kanały komunikacyjne: serwer->klient (ukryty w zmiennej commandFromServer) oraz klient->serwer (w zmiennej commandToServer)
Jednak, żeby tego poprawnie użyć trzeba to zrobić przez wywołanie jednego ze skryptów umieszczonych w zmiennych ptr_send_commands_to_clients oraz ptr_send_commands_to_server.

Treści skryptów (uwzględniają przypadki szczególne):
Kod: Zaznacz cały
//send_commands_to_clients.sqf
commandFromServer = _this select 0;
publicVariable "commandFromServer"; //do wszystkich klientów
if( isClientServer )then //przypadek szczegolny - serwer tez musi sam to sobie "wyslac"
{
   call compile commandFromServer;
};
commandFromServer = "";


Kod: Zaznacz cały
//send_commands_to_server.sqf
private ["_command"];

_command = _this select 0;
if( isServer )then //przypadek szczegolny - klient jest tez serwerem
{
   call compile _command;
}
else
{
   commandToServer = _command;
   publicVariable "commandToServer";
   commandToServer = "";
};


To tyle.

prymitywny przykład:
Misja C&H - wykrywanie przejmowania flag i liczenie punktów wykonuje się tylko na serwerze (daje to większą przejrzystość, a co do punktów to daje pewność, że punkty są liczone od początku). Liczenie punktów odbywa się w skrypcie punkty.sqf:
Kod: Zaznacz cały
if( isServer )then
{
  while{ true }do
  {
     punktyWest = ....
     punktyEast = ...
     _komendaDoWyslania = format["hint ""WEST: %1\nEAST: %2"" ", punktyWest, punktyEast];
     [_komendaDoWyslania] call ptr_send_commands_to_clients;
     sleep 10;
  };
};

Co spowoduje, że u klientów wyświetli się hint z punktacją.
Powyższy przykład może nie przekonuje do użycia tego sposobu, ale uwierzcie mi, że przy większych projektach pomaga ułożyć sobie gdzie co się wykonuje. Pozwala odseparować główny silnik misji od interfejsu gracza.
Lub wyobraźmy sobie, że robimy wyścig - niektórym sie szybciej załaduje misja, innym wolniej - stosując powyższy framework możemy jednocześnie wszystkim zezwolić na start, np:
Kod: Zaznacz cały
["(vehicle player) setFuel 1"] call ptr_send_commands_to_clients;

(ustawiając na starcie oczywiści pusty bak)

Uwaga: komendy, jak widać, są po prostu stringami. To tak jakby skopiować kilka linijek ze skryptu, dołożyć cudzysłowy z obu stron i włala - można wysyłać do klientów.

Mile widziane opinie, usprawnienia itp.

Powrót do Armed Assault

Kto przegląda forum

Użytkownicy przeglądający ten dział: Brak zidentyfikowanych użytkowników i 13 gości

cron