TypeFriendly - tworzenie dokumentacji i podręczników

Witajcie, w dzisiejszym wpisie chciałbym przedstawić Wam jedno z narzędzi przydatne przy tworzeniu projektów (i to nie tylko typowo webowych). Bohaterem dzisiejszego wpisu będzie TypeFriendly, czyli generator dokumentacji, manuali czy podręczników.

TypeFriendly to skrypt PHP działający w konsoli (CLI), napisany przez Tomasza „Zyxa” Jędrzejewskiego. Jest dostępny jako open source. Autor przedstawił inne podejście do tworzenia dokumentacji niż autorzy np. phpDocumentatora (phpDoc). Zamiast bazowania na setkach komentarzy porozrzucanych po plikach PHP i opisujących poszczególne zmienne, funkcje, metody czy klasy, TypeFriendly generuje dokumentację na bazie plików w specjalnym formacie – jednakże tworzonych w całości przez użytkownika. To powoduje, że może być on wykorzystywany nie tylko do PHP lub w ogóle nie do programowania – można w nim po prostu stworzyć dowolny podręcznik.

Materiał wejściowy dla skryptu to zbiór plików *.txt o ustandaryzowanych nazwach i zawartości opisywanej za pomocą składni Markdown z pewnymi ulepszeniami i dodatkami. Autor bardzo intuicyjnie przewidział tworzenie poszczególnych rozdziałów z nieskończoną ilością zagnieżdżeń, tworzenie linkowania pomiędzy nimi oraz takich elementów jak spisu treści. Większość elementów nawigacyjnych jest generowana automatycznie na podstawie pewnych szczególnych tagów dodawanych do każdego pliku oraz nazw tych plików.

Składnia Mardown została wzbogacona o możliwości, które mogą okazać się przydatne przy tworzeniu różnego rodzaju manuali – między innymi automatyczne kolorowanie kodu z użyciem GeSHi czy wstawianie różnego rodzaju wskazówek, ostrzeżeń czy informacji do tekstu.

Skrypt wspiera bezproblemowo opcję tworzenia dokumentacji w wielu językach. Dużym plusem jest też fakt, iż na bazie tego samego materiału wejściowego (czyli zbioru odpowiednich plików *.txt) TypeFriendly jest w stanie wygenerować kilka rodzajów podręczników (różniących się głównie nawigacją pomiędzy poszczególnymi częściami) oraz kilka ich formatów – aktualnie dostępna jest możliwość generowania osobnych rozdziałów w osobnych plikach lub wygenerowanie wszystkiego w jednym duży pliku HTML.

Żeby nie rozpisywać się już zbytnio, o tym co niepotrzebne – skrypt został zaopatrzony w bardzo dobrą dokumentację – wygenerowaną oczywiście w nim samym ;) Z własnego doświadczenia mogę powiedzieć, że jego użytkowanie nie wymaga dużego nakładu pracy, a efekty bardzo szybko się zwracają w postaci uporządkowanych i estetycznych podręczników.

Skrypt i wspomniana dokumentacja jest dostępna do pobrania na stronach grupy Invezzia zajmującej się jego tworzeniem i rozwojem.

Mam nadzieję, że ta lekko chaotyczna prezentacja skryptu zachęciła Was do skorzystania z niego. Jeśli ktoś zakładał tworzenie dokumentacji dla swojego projektu, to przedstawione rozwiązanie ułatwi i przyspieszy ten proces.

Przegląd skryptów genialnych #3 - xNova

Witajcie, w kontynuacji dawno zaczętej serii, w której przyglądam się największym „perełkom” pośród skryptów PHP. Dzisiaj pod nóż pójdzie coś specjalnego, prawdziwy as wśród asów. Drodzy Państwo, przywitajcie xNovę.

Najpierw krótko o samym skrypcie. xNova to skrypt, który ma umożliwić tworzenie gier MMO via WWW wyglądających dokładnie jak OGame (mówimy tutaj o starej wersji, bo od czasu wydania ocenianej wersji skryptu, OGame doczekał się całkowitej przebudowy interfejsu).

Ciężko jest nawet powiedzieć jaką wersję będę opisywał, bo xNova od wieków nie posiada oficjalnej strony/forum/bloga. Tak więc namnożyło się bez liku wersji (najczęściej po prostu przeróbek prywatnych osób, które połatały 3% bugów i zoptymalizowały kilka zapytań). Wersja którą mam, była opisana jako „xNova 0.3 Multilanguage”, ale zdefiniowana wewnątrz skryptu stała VERSION miała wartość 0.8, więc mamy niezły rozrzut.

W związku z ilością bugów oraz masą amatorsko tworzonych „nowych wersji” najczęstszymi poradami na bugi zamieszczonymi na „profesjonalnych suportach xNovy” nie są wcale poprawki błędnej linijki, tylko zdanie „podmień sobie plik z wersji 1.1A/5.5/x.x by Ktośtam” – to też bardzo skutecznie tworzy chaos w plikach.

No, ale przejdźmy do właściwej części wpisu:

  1. Już patrząc w strukturę katalogów możemy mieć pewne pojęcie o skrypcie. W katalogu głównym umieszczono foldery scripts i js. Oba zawierają skrypty JavaScripts, część z nich jest identyczna i po analizie można dojść do tego, że około 12 z plików nie jest nigdzie używanych.

  2. Wygląd skryptu opiera się na dwóch folderach: templates, w którym leżą pliki dla systemu szablonów i skins, w którym leżą obrazki i pliki CSS. Bardzo piękne założenie, szkoda tylko , że poza tymi folderami w katalogu głównym mamy jeszcze foldery css i images, więc zmiana szablonu nie daje możliwości pełnej modyfikacji wyglądu bez modyfikacji zawartości tych folderów. Ponadto wg mnie pliki JavaScript też powinny być zależne od wybranego theme’u, a więc leżeć w jego katalogu.

  3. Przejdźmy do instalacji. Uruchamiam folder z xNovą a tam bardzo prosty do zdiagnozowania błąd. Skrypt wymaga włączonej dyrektywy short_open_tag Wracamy po szybkim restarcie serwera :)

  4. Po zmianie konfiguracji próbuję jeszcze raz i co widzę? Błąd połączenia z bazą danych zamiast instalatora. W pliku index.php jest odpowiednia instrukcja, która przekierowuje do instalatora jeśli config.php jest pusty. Niestety, ktoś udostępniając tą paczkę ludziom nie pomyślał i zostawił swojego wypełnionego configa. Tak więc poprawmy jego błędy i wyczyśćmy plik config.php. Do trzech razy sztuka…

  5. Próbujemy jeszcze raz i oto naszym oczom ukazuje się przepiękny instalator… po francusku. Szkoda tylko, że silnik posiada możliwość zmiany języka w całej grze, w instalatorze także. Niestety nikt nie pomyślał nad możliwością wyboru języka instalacji.

  6. Lecąc na czuja wypełniamy tabelkę z danymi do DB, standardowy układ jak w każdym skrypcie. Ukazuje nam się podstrona z pociesznym napisem „La base de donnée a bien été installée!”. Szkoda tylko, że ktoś jej pożarł całego CSS-a.

  7. Styl powraca, tworzymy konto admina i mamy rejestrację z głowy. Czas się zalogować.

  8. Bez problemu loguję się do gry, a tam wszystko po… tak, zgadliście, po francusku. Instalator z automatu ustawia adminowi język na francuski. Trzeba go przestawić w opcjach konta/w bazie danych.

  9. Teraz, kiedy gra już działa, przyjrzyjmy się plikom: widać wyraźnie, że każda z podstron najpierw dołącza plik common.php, a więc to jemu przyjrzymy się na początku. Co my tam mamy? Widać, że istnieje możliwość ustawienia własnych ścieżek do kilku folderów oraz domyślnego języka gry. Tylko dlaczego to nie jest w configu? Potem widzimy dołączanie kilku plików, w tym todofleetcontrol.php.

  10. Ten plik jest na tyle wyjątkowy, że zdecydowałem się go pokazać w całości:

    <?php
    /**
    *
    *
    * @version 1
    * @copyright 2008 By Chlorel for XNova
    */
    
    // Fonctions deja 'au propre'
    include($ugamela_root_path . 'includes/functions/FlyingFleetHandler.'.$phpEx);
    include($ugamela_root_path . 'includes/functions/MissionCaseAttack.'.$phpEx);
    include($ugamela_root_path . 'includes/functions/MissionCaseStay.'.$phpEx);
    include($ugamela_root_path . 'includes/functions/MissionCaseStayAlly.'.$phpEx);
    include($ugamela_root_path . 'includes/functions/MissionCaseTransport.'.$phpEx);
    include($ugamela_root_path . 'includes/functions/MissionCaseSpy.'.$phpEx);
    include($ugamela_root_path . 'includes/functions/MissionCaseRecycling.'.$phpEx);
    include($ugamela_root_path . 'includes/functions/MissionCaseDestruction.'.$phpEx);
    include($ugamela_root_path . 'includes/functions/MissionCaseColonisation.'.$phpEx);
    include($ugamela_root_path . 'includes/functions/MissionCaseExpedition.'.$phpEx);
    include($ugamela_root_path . 'includes/functions/SendSimpleMessage.'.$phpEx);
    include($ugamela_root_path . 'includes/functions/SpyTarget.'.$phpEx);
    include($ugamela_root_path . 'includes/functions/RestoreFleetToPlanet.'.$phpEx);
    include($ugamela_root_path . 'includes/functions/StoreGoodsToPlanet.'.$phpEx);
    include($ugamela_root_path . 'includes/functions/CheckPlanetBuildingQueue.'.$phpEx);
    include($ugamela_root_path . 'includes/functions/CheckPlanetUsedFields.'.$phpEx);
    include($ugamela_root_path . 'includes/functions/CreateOneMoonRecord.'.$phpEx);
    include($ugamela_root_path . 'includes/functions/CreateOnePlanetRecord.'.$phpEx);
    include($ugamela_root_path . 'includes/functions/InsertJavaScriptChronoApplet.'.$phpEx);
    include($ugamela_root_path . 'includes/functions/IsTechnologieAccessible.'.$phpEx);
    include($ugamela_root_path . 'includes/functions/GetBuildingTime.'.$phpEx);
    include($ugamela_root_path . 'includes/functions/GetRestPrice.'.$phpEx);
    include($ugamela_root_path . 'includes/functions/GetElementPrice.'.$phpEx);
    include($ugamela_root_path . 'includes/functions/GetBuildingPrice.'.$phpEx);
    include($ugamela_root_path . 'includes/functions/IsElementBuyable.'.$phpEx);
    include($ugamela_root_path . 'includes/functions/CheckCookies.'.$phpEx);
    include($ugamela_root_path . 'includes/functions/ChekUser.'.$phpEx);
    include($ugamela_root_path . 'includes/functions/InsertGalaxyScripts.'.$phpEx);
    include($ugamela_root_path . 'includes/functions/GalaxyCheckFunctions.'.$phpEx);
    include($ugamela_root_path . 'includes/functions/ShowGalaxyRows.'.$phpEx);
    include($ugamela_root_path . 'includes/functions/GetPhalanxRange.'.$phpEx);
    include($ugamela_root_path . 'includes/functions/GetMissileRange.'.$phpEx);
    include($ugamela_root_path . 'includes/functions/GalaxyRowPos.'.$phpEx);
    include($ugamela_root_path . 'includes/functions/GalaxyRowPlanet.'.$phpEx);
    include($ugamela_root_path . 'includes/functions/GalaxyRowPlanetName.'.$phpEx);
    include($ugamela_root_path . 'includes/functions/GalaxyRowMoon.'.$phpEx);
    include($ugamela_root_path . 'includes/functions/GalaxyRowDebris.'.$phpEx);
    include($ugamela_root_path . 'includes/functions/GalaxyRowUser.'.$phpEx);
    include($ugamela_root_path . 'includes/functions/GalaxyRowava.'.$phpEx);
    include($ugamela_root_path . 'includes/functions/GalaxyRowAlly.'.$phpEx);
    include($ugamela_root_path . 'includes/functions/GalaxyRowActions.'.$phpEx);
    include($ugamela_root_path . 'includes/functions/ShowGalaxySelector.'.$phpEx);
    include($ugamela_root_path . 'includes/functions/ShowGalaxyMISelector.'.$phpEx);
    include($ugamela_root_path . 'includes/functions/ShowGalaxyTitles.'.$phpEx);
    include($ugamela_root_path . 'includes/functions/GalaxyLegendPopup.'.$phpEx);
    include($ugamela_root_path . 'includes/functions/ShowGalaxyFooter.'.$phpEx);
    include($ugamela_root_path . 'includes/functions/GetMaxConstructibleElements.'.$phpEx);
    include($ugamela_root_path . 'includes/functions/GetElementRessources.'.$phpEx);
    include($ugamela_root_path . 'includes/functions/ElementBuildListBox.'.$phpEx);
    include($ugamela_root_path . 'includes/functions/ElementBuildListQueue.'.$phpEx);
    include($ugamela_root_path . 'includes/functions/FleetBuildingPage.'.$phpEx);
    include($ugamela_root_path . 'includes/functions/DefensesBuildingPage.'.$phpEx);
    include($ugamela_root_path . 'includes/functions/ResearchBuildingPage.'.$phpEx);
    include($ugamela_root_path . 'includes/functions/BatimentBuildingPage.'.$phpEx);
    include($ugamela_root_path . 'includes/functions/CheckLabSettingsInQueue.'.$phpEx);
    include($ugamela_root_path . 'includes/functions/InsertBuildListScript.'.$phpEx);
    include($ugamela_root_path . 'includes/functions/AddBuildingToQueue.'.$phpEx);
    include($ugamela_root_path . 'includes/functions/ShowBuildingQueue.'.$phpEx);
    include($ugamela_root_path . 'includes/functions/HandleTechnologieBuild.'.$phpEx);
    include($ugamela_root_path . 'includes/functions/BuildingSavePlanetRecord.'.$phpEx);
    include($ugamela_root_path . 'includes/functions/BuildingSaveUserRecord.'.$phpEx);
    include($ugamela_root_path . 'includes/functions/RemoveBuildingFromQueue.'.$phpEx);
    include($ugamela_root_path . 'includes/functions/CancelBuildingFromQueue.'.$phpEx);
    include($ugamela_root_path . 'includes/functions/SetNextQueueElementOnTop.'.$phpEx);
    include($ugamela_root_path . 'includes/functions/ShowTopNavigationBar.'.$phpEx);
    include($ugamela_root_path . 'includes/functions/SetSelectedPlanet.'.$phpEx);
    include($ugamela_root_path . 'includes/functions/MessageForm.'.$phpEx);
    include($ugamela_root_path . 'includes/functions/PlanetResourceUpdate.'.$phpEx);
    include($ugamela_root_path . 'includes/functions/BuildFlyingFleetTable.'.$phpEx);
    include($ugamela_root_path . 'includes/functions/SendNewPassword.'.$phpEx);
    include($ugamela_root_path . 'includes/functions/HandleElementBuildingQueue.'.$phpEx);
    include($ugamela_root_path . 'includes/functions/UpdatePlanetBatimentQueueList.'.$phpEx);
    include($ugamela_root_path . 'includes/functions/IsOfficierAccessible.'.$phpEx);
    include($ugamela_root_path . 'includes/functions/CheckInputStrings.'.$phpEx);
    include($ugamela_root_path . 'includes/functions/MipCombatEngine.'.$phpEx);
    include($ugamela_root_path . 'includes/functions/DeleteSelectedUser.'.$phpEx);
    include($ugamela_root_path . 'includes/functions/SortUserPlanets.'.$phpEx);
    include($ugamela_root_path . 'includes/functions/BuildFleetEventTable.'.$phpEx);
    include($ugamela_root_path . 'includes/functions/BuildRessourcePage.'.$phpEx);
    
    ?>
    

    Co my tu mamy? Bezmyślne dołączanie 78 plików za każdym wywołaniem strony. Nawet gdy się logujemy, rejestrujemy, oglądamy podstronę kontakt itd. silnik ładuje 78 plików potrzebnych tylko w grze.

  11. Kolejnym plikiem ładowanym w common.php jest plik /includes/db.php. On też jest w pewien sposób wyjątkowy: jedyne co robi to include’uje kolejny (jeden) plik. Gdzie tu logika?

  12. Wymieniony wyżej plik includes/db.php dołącza plik db/mysql.php. Jego zawartość też jest warta przytoczenia:

    <?
    function doquery($query, $table, $fetch = false){
    global $numqueries,$link,$debug,$ugamela_root_path;
    require($ugamela_root_path.'config.php');if(!$link)
    {$link = mysql_connect($dbsettings["server"], $dbsettings["user"],
    $dbsettings["pass"]) or
    $debug->error(mysql_error()."
    $query","SQL Error");
    
    mysql_select_db($dbsettings["name"]) or $debug->error(mysql_error()."
    $query","SQL Error");
    mysql_query("SET NAMES latin2");
    echo mysql_error();}
    
    $sql = str_replace("{{table}}", $dbsettings["prefix"].$table, $query);
    $sqlquery = mysql_query($sql) or
    $debug->error(mysql_error()."
    $sql
    ","SQL Error");
    
    unset($dbsettings);
    $numqueries++;
    $arr = debug_backtrace();
    $file = end(explode('/',$arr[1]['file']));
    $line = $arr[1]['line'];
    $debug->add("
    Query $numqueries:$query$file($line)$table$fetch
    
    ");
    
    if($fetch)
    {$sqlrow = mysql_fetch_array($sqlquery);
    return $sqlrow;
    }else{return $sqlquery;}}
    ?>
    

    Pierwszą rzeczą jaka rzuciła mi się w oczy poza genialnym układem kodu był fakt, że przy każdym zapytaniu jest dołączany config.php. No ktoś tam nie ma mózgu… nie można po prostu dołączyć go w common.php? Tak samo ewentualne łączenie powinno się odbywać nie funkcji wykonującej zapytania tylko tam.

  13. W folderze includes znajduje się plik databaseinfos.php, który na pierwszy rzut oka jest używany do tworzenia tabel przez instalator, a krótka analiza wykazuje, że używany jest tylko tam. W takim wypadku, czy nie logiczniej byłoby go usunąć automatycznie po zakończeniu instalacji (tak jak zresztą cały skrypt). Ponadto prościej byłoby chyba, gdyby znajdował się po prostu w folderze install.

  14. Samo nazewnictwo plików jest lekko mówiąc dziwne. Czy to mówiąc o sposobie zapisu (add_moon.php, ale już activeplanet.php), czy o języku (errors.php, paneladmina.php, verband.php). Do wyboru, do koloru!

  15. Jeśli chodzi o bazę danych, to widać jak na dłoni, że była dorabiana po kawałku zależnie od konieczności. Jednak nikt nie zadał sobie trudu, aby spojrzeć na nią całościowo, przeprojektować i choćby pousuwać zbędne pola z tabel czy ustawić bardziej optymalne typy dla pól. Jako najlepszy przykład można podać fakt, że informacje o występujących w grze Księżycach są trzymane w dwóch tabelach, przy czym jedna zawiera po prostu część danych z pierwszej tabeli. Niestety, aby to poprawić trzeba dokonać dość mocnej przeróbki skryptu, a mało kto ma tyle zawzięcia.

Powyższy skrypt jest bardzo wymownym przykładem na to jak nie powinno się tworzyć jakichkolwiek aplikacji. Zero organizacji pracy i początkowych założeń daje właśnie takie efekty. Można tylko współczuć ludziom którzy z wielkim zapałem chcą zrobić "super wypasionom grem na xNovie" nie zaglądając nawet do "jej wnętrza".

Nowy interfejs YouTube

Witajcie. Można uznać, że nadszedł czas na nadrobienie zaległości. Dzisiejszy wpis chciałbym poświęcić nowemu interfejsowi YouTube. Został wprowadzony już ponad miesiąc temu, więc leniąc się z napisaniem tego wpisu miałem naprawdę sporo czasu na wyrobienie sobie opinii o nim.

Pierwszą sprawą jaka rzuca się w oczy, jest oczywiście strona główna, która moim zdaniem zaczęła bardziej przypominać Tablicę znaną z Facebooka niż to, co mieliśmy wcześniej na YouTubie. Duży nacisk położono nam na pokazanie treści pochodzących od zasubskrybowanych użytkowników, więcej jest też miejsca na „Wybrane dla Ciebie”. Aby nie marnować miejsca, część rzeczy pojawia się dynamicznie, po wybraniu odpowiedniej zakładki w menu po lewej.

Dla mnie bardzo dużym plusem jest pokazywanie na stronie głównej tego, czy mamy jakieś wiadomości do przeczytania. Wcześniej jeśli przeoczyło się powiadomienie na e-mail, to zostawało nam tylko okresowe sprawdzanie pewnej podstrony. Teraz fakt posiadania nowej wiadomości jest łatwiej zauważalny.

Moim zdaniem lekka zmiana kolorystyki też wyszła temu serwisowi na plus. Szare tło nie jest tak bardzo kontrastowe i jest przyjemniejsze dla oczu.

Bardzo wiele osób nie mogło przeżyć zmiany faviconki, ale ja odpowiem standardowo – kwestia przyzwyczajenia. Jak dla mnie nowa ikonka jest ok, i nie widzę żadnego powodu, aby czuć sentyment do poprzednich 256 pikseli…

Nie wiem, jak dawniej wyglądała podstrona np. umożliwiająca rozpoczęcie zarabiania na swoich filmikach, ale obecna prezentuje się bardzo jasno i przejrzyście.

Ładnie wyglądają też maile z cotygodniową listą filmików, które pojawiły się na subskrybowanych przez nas kanałach. A wyglądałyby jeszcze piękniej, gdyby były przysyłane z jednego adresu i mógłbym w Thunderbirdzie na sztywno ustawić im pozwolenie na wyświetlanie obrazków (korzystając z okazji: wie ktoś może, jak w Thunderbirdzie ustawić pozwolenie dla wyświetlania obrazków dla wszystkich maili z danej domeny?)

Niestety, prawie jak zawsze w takich wypadkach, sytuacja „pod maską” nie wygląda już tak dobrze. O ile 2524 błędy CSS są spowodowane głównie przez stosowanie prefixów przeglądarek w CSS3, których validator nie łyka, to 179 błędów w kodzie HTML spokojnie można by poprawić.

Sądzę, że był to bardzo udany lifting, a zmiany wyszły na plus. W moim subiektywnym odczuciu, nie ma niczego co zmieniłoby się na gorsze, albo moja czarna dziura wyssała mi to z pamięci.

Krótkie porównanie wprowadzonych zmian na przykładzie strony odtwarzania filmiku. Kliknij na zdjęcie, aby je powiększyć.

Stary player YouTube Nowy player YouTube

Sobak skazany na 10 lat ciężkich robót w Minecrafcie.

Będzie to artykuł dość nietypowy, bo pisany przez dwie osoby - mnie i CapaciousCore. Na mocy pradawnego paktu zawartego w dniu dzisiejszym połączyliśmy siły i postanowiliśmy napisać artykuł na temat Minecrafta. Główną motywacją do napisania tego wywodu było wydanie pierwszej "stabilnej" wersji tej gry.

Wprawdzie nie miałem okazji obejrzenia MineCon'u, ale CapaciousCore aka CC podzielił się pewnym przemyśleniem na temat tego wielkiego wydarzenia w dziejach ludzkości:

"Moje pierwsze wrażenie było takie, że Polacy próbowali zawładnąć komentarzami pod filmem na YouTube. Dzięki polskim dzieciom neo przybywało około 50 wiadomości na sekundę, co czyniło komentarze niemożliwe do czytania.

Jeśli chodzi o sam MineCon, to było dużo gadania o niczym, a mało konkretów i właściwej prezentacji gry. Prezenterka moim zdaniem była… szurnięta. W trakcie konferencji puszczono kilka debilnych filmików, które były totalnie oderwane od rzeczywistości. Najwidoczniej Notch i ekipa żyją w swoim świecie, szczególnie udzieliło się to podczas oczekiwania na ostateczną prezentację.".

W Minecrafta zacząłem grać od wersji 1.7.3 i ponownie była to wina CC. Po prostu dostawałem relacje na bieżąco z jego początków gry i stwierdziłem, że sam muszę spróbować. Tak zacząłem odbywanie swojego wyroku w kopalni.

Minecrafta na pewno można nazwać jedną z najlepszych machin do pożerania czasu. Potrafił naprawdę zagiąć czasoprzestrzeń przez co w jednej chwili z 14:00 robiła się 20:30. Były to wzruszające chwile takie jak: ścięcie pierwszego drzewa gołą dłonią (Bruce Lee byłby dumny) oraz stanie z lawą we wiadrze i obserwowanie zachodu kwadratowego słońca. Bardzo miło wspominamy też pierwsze przeniesienie się do Netheru, czyli Minecraftowego piekła. CC w końcu znalazł się w naturalnym środowisku.

Gra jest genialną zabawą, pod warunkiem, że osoby w nią grające mają odpowiedni poziom kreatywności. Osoba pozbawiona wyobraźni znudzi się nią już po chwili albo będzie po prostu powielała gotowe rozwiązania z YouTube'a. Do grania w Minecrafta przyda się też pewna doza inteligencji - przynajmniej jeśli chcemy zacząć bawić się naprawdę rozbudowanymi możliwościami tworzenia układów korzystających z logiki. To sprawia, że wbrew pozorom Minecraft może być świetną platformą do nauki nie tylko logiki, ale też pracy zespołowej wymaganej przy budowaniu złożonych konstrukcji.

Grając z CC stosujemy pewien nieformalny podział. Ja zajmuję się większością rzeczy odpowiedzialnych za porządek na serwerze oraz budowaniem, za to CC uwielbia bawić się w kwadratowego elektryka i plątać się w kablach z redstone. Tylko on potrafi w ciągu 5 minut przemienić ładny budynek w opasany czerwonymi kablami klocek. Przydałaby się jeszcze trzecia osoba (np. biedny student) do obrabiania naszego PGR-u…

Minecraft podczas swojego rozwoju praktycznie przeszedł ewolucję od prostego sandboxa do gry pół-RPG. Jak mówi CC:

"Moim zdaniem rozwój gry poszedł w dziwną stronę. Z jednej strony niepotrzebnie pododawano takie rzeczy jak craftowanie itemów przy pomocy książki, warzenie mikstur i tym podobnych rzeczy. Jednak z drugiej strony jest to kolejne urozmaicenie gry.

Minecraft jest jak pudełko klocków Lego. Łącząc to z kilkoma prostymi narzędziami i wyobraźnią użytkownika możemy tworzyć niesamowite konstrukcje."

Nie grałem w żadną z wersji pre-release poprzedzających wersję 1.0 tak więc z wersji 1.8.1 przeskoczyłem od razu do 1.0. Od razu do głowy przyszło mi kilka przemyśleń na temat poczynionych zmian.

  1. Dużo wilków -  pierwsze co rzuciło mi się w oczy po zespawnowaniu. Dotychczas spotkanie wilka było bardzo trudne, a tutaj wprost mnie otoczyły.
  2. Czerwone róże - od update do 1.8 nie widziałem ich ani razu. Teraz jest ich dużo, nie wiem czy nie więcej niż żółtych kwiatów.
  3. Bagna - jak dla mnie ich wygląd jest lekko skopany. Jest za ciemny i zbyt monotonny.
  4. Grzybowe biomy - niewątpliwie ciekawe urozmaicenie. Patrząc na grzybowe krowy można stwierdzić, że ktoś w Mojangu ćpa coś dobrego.
  5. Dźwięki - w pierwszej chwili bardzo mi się nie podobały, wiem, że to po prostu efekt przyzwyczajenia. Teraz, z dłuższej perspektywy czasu stwierdzam, że nie jest tak źle, aczkolwiek część dźwięków wymaga poprawek (na przykład dźwięki drzwi i skrzyń) Przykładowo fajny jest dźwięk łamanych kości przy upadkach i wszystko byłoby fajnie gdyby nie to, że nasza postać wydaje taki sam dźwięk topiąc się…
  6. Minus w postaci nadal dużej ilości bugów. Krótki komentarz od CC: "Zamiast gonić z premierą na jedną konkretną datę mogli postarać się o poprawienie występujących bugów. Przykładowo Blizzard wielokrotnie przekładał premierę Diablo III, aby poprawić znalezione bugi [Tak na marginesie nie wydali jej do dziś - Sobak]". Moim zdaniem tutaj widać dokładnie minusy robienia tak hucznej premiery i przede wszystkim zapowiadania jej z tak ogromnym wyprzedzeniem.

W wersji 1.0 nadal brakuje mi kilku rzeczy. Dziwi mnie między innymi to, że po dziś dzień nie można sterować bramkami w płotkach (fence gate) za pomocą redstone. Wg mnie nie zaszkodziłaby też możliwość zapalania i gaszenia glowstone za pomocą pyłu redstone.

Ponadto mam jeszcze jedno spostrzeżenie. Wprawdzie nigdy nie bawiłem się w tworzenie pluginów do MC, ale widać wyraźnie, że do bukkita, który udostępnia zmienione API, jest ich dużo więcej, a po drugie do wielu pluginów trzeba doinstalowywać dodatkowe pluginy będące jakimiś nakładkami na API, o Mod Loaderach nie wspominając. Nie świadczy to najlepiej o domyślnym API Minecrafta, choć tu powinien wypowiedzieć się ktoś siedzący w temacie.

I w ten sposób kończymy krótki przegląd naszych opinii o Minecrafcie. Jeśli chcecie się czymś podzielić, to piszcie śmiało w komentarzach ;)

Przegląd skryptów genialnych

Nie wiem czy ktokolwiek pamięta jeszcze pierwszy wpis z tej serii. Ja w każdym razie o nim zapomniałem. Jednak przy okazji dzisiejszego przeglądania jednego ze skryptów do tworzenia gry MMORPG via WWW przypomniałem sobie o tej serii i postanowiłem co nieco napisać.

Miałem zamiar przetestować skrypt MafiaWarz służący do tworzenia gier MMORPG w klimatach gangsterskich. Oczywiście nie spodziewałem się żadnych rewelacji po darmowym skrypcie tego typu, ale to co tam zobaczyłem dało kilka okazji do śmiechu dlatego postanowiłem się tym podzielić ze światem ;)

Zacznijmy rzecz jasna od początku, bo tak bywa najłatwiej. Instrukcji instalacji/pliku README nigdzie nie było, ale akurat zaimportowanie jednego pliku SQL i uzupełnienie danych w configu (choć dość trudnym do znalezienia) nie należało do bardzo trudnych.

Problem pojawił się podczas gdy chciałem zarejestrować konto. Na szczęście Wujek Google pozwolił szybko dojść do rozwiązania tego problemu - rozbiło się w zasadzie o jedną linijkę, więc na razie idzie dość gładko. Teraz zacznijmy ocenę właściwą:

  1. Błąd uniemożliwiający rejestrację na samym początku. Wielu ludzi może to odstraszyć, ale może i słusznie? :P
  2. Hasło miało zostać wysłane mailem, ale jako że skrypt testowałem na localhoście, to postanowiłem podmienić hasło w bazie danych na nowe. Długo szukałem charakterystycznego zahashowanego hasła w tabeli users, ale go nie znalazłem, bo hasła są trzymane w niezakodowanej postaci.
  3. Kontynuując temat bazy danych - brak prefiksów tabel, a ich nazwy zbyt oryginalne nie są, więc może być mały problem.
  4. Pokręcone typy pól: money jako int, online oraz lastcrime (trzymane jako znacznik czasu) jest polem typu varchar(100) zamiast po prostu int
  5. Jako że skrypt jest po angielsku, postanowiłem go przynajmniej częściowo spolszczyć. Niestety mimo tego, że użytkownicy mają możliwość zmiany języka w profilu to pliki językowe są przygotowane tylko dla dwóch z kilkudziesięciu zakładek. Dodatkowo oba te pliki zawierają dużo nigdzie niewystępujących tekstów, a nie mają tych najważniejszych.
  6. Totalny bajzel w plikach. Dużo zbędnych, po kilka kopii tego samego pliku, pliki do niczego niepotrzebne (przykładowo plik w00t.php zawierający jedynie pustą tabelę w HTML). Takie rzeczy zdecydowanie nie powinny leżeć w publikowanej wersji.
  7. Zero konwencji kodowania dla PHP
  8. Kod HTML na poziomie internetu łupanego. Można by go nazwać HTML-em 2.0 gdyby nie brak doctype. Nazwijmy go więc umownie HTML-em 0
  9. Design oparty na ramkach, znaczniki noframes oczywiście puste…
  10. Postanowiłem znaleźć panel administracyjny. Zajrzałem do jedynego pliku który wydał mi się właściwy (admincp.php), aby poszukać jakie pole w bazie muszę zmienić. Zabezpieczenie tego pliku naprawdę mnie oczarowało.
    if ($userlevel != 3){
        echo "Your not a admin.";
    }
    
    faktycznie - większość użytkowników na pewno przestraszy się tej informacji i czym prędzej ucieknie. Co jednak z pozostałymi niegodziwcami? Czy użycie die() byłoby zbyt trudne?
  11. Używanie " praktycznie wszędzie gdzie to niepotrzebne, np. dla zwyczajnych stringów lub nawet stosowanie echo "$style";. Nie szkoda serwera?
  12. Prawie w każdym pliku występuje podwójne łącznie z bazą. Najpierw dołączany jest plik includes/db_connect.php, a potem includes/functions.php na którego początku jest dołączany includes/db_connect.php
  13. Masa obrazków leżących w katalogu głównym oraz includes mimo istnienia katalogu images
  14. Źle zaprojektowana baza danych - 45 tabel dla tak w sumie nieskomplikowanego skryptu mówi samo za siebie
  15. Totalnie niespójny desing. Wygląda to tak, jakby każda zakładka była robiona przez inną osobę. Poza tym nie ma to jak biały tekst na jasnożółtym tle (no kto tutaj coś widzi?)
  16. Krótka analiza zakładek z menu - kilka to biała strona, jedna to "coming soon!", inna to 404. Zabrakło natomiast linków w menu do tak mało istotnych zakładek jak np. panel admina.
  17. Skrypt wymaga do działania włączonej opcji short_open_tags
  18. Podatność na SQL Injection
  19. CSS rozbite na masę poszczególnych plików
  20. W wielu miejscach widać pozostałości albo podwaliny systemu do aktywacji użytkowników - nie jest on jednak w żaden sposób wykorzystywany.

Skrypt ma na pewno dużo więcej błędów i niedoróbek, których nie wypisałem w tym wpisie. Powyższa lista to tylko jego ogólny przegląd i pewnego rodzaju przestroga dla osób które chciałyby stawiać gry online na takich gotowcach. Szkoda czasu i nerwów na poprawianie takich pseudoskryptów - mówię to także z własnego doświadczenia ;)

Sądzę że w niedługim czasie mogę się pokusić o opisanie większej ilości skryptów tego typu.

Przepraszam też za ewidentną chaotyczność tego wpisu, był on pisany jeszcze podczas "badania" skryptu dlatego nowe rzeczy były dopisywane na bieżąco.

Przegląd skryptów genialnych #1

Od pewnego czasu zacząłem się interesować CMS-ami (od strony developerskiej). Dlatego też postanowiłem zobaczyć jak wyglądają polskie osiągnięcia na tej scenie. Znalazłem mały spis naszych rodzimych tworów i zacząłem sprawdzać. Dzisiaj przygotowałem dla Was krótki przegląd skryptu znad Wisły o wdzięcznej nazwie CMS Zaba (pamiętajcie

  • nie żaba ;)). Od razu uprzedzam wszelkie (oczywiste) skojarzenia z wymyślną formą promocji mojego "CMS-a". Faktycznie, na przeglądanie innych CMS-ów naszło mnie właśnie przy okazji pisania irona, ale nie zamierzam tym pokazywać jakichś ewentualnych przewag nad innymi skryptami. Zdaję sobie sprawę z tego, że IronCMS to wytwór amatorski i nie może się równać z CMS-ami nieraz tworzonymi przez profesjonalne firmy.

No, ale teraz przechodzimy do rzeczy ;)

  1. Brak instalatora - chociaż to tylko drobna uwaga. Dla większości osób zaimportowanie bazy danych i wypełnienie pliku konfiguracyjnego to chyba nie jest wielki problem (chyba…)
  2. Krótkie znaczniki otwierające (<?) - na nowych konfiguracjach nie zadziała
  3. Brak wielu ikon po zainstalowaniu.
  4. Widać, że z polskiego autor geniuszem nie był. Pozwolę sobie zacytować newsa powitalnego:

    Witam login dla admina to admin haslo admin tak samo jak loguje sie na stronie! Mamy tu klasyczny przykład wiersza białego prozy białej.

  5. Z angielskiego też najlepiej mu nie szło :(. W stopce mamy link o pięknym opisie "Cms Power by Zaba". Dodatkowo przed linkiem nie ma http://, przez co oczywiście przeglądarka traktuje go tak jakby prowadził do katalogu. Na dokładkę mamy też genialną nazwę theme'u domyślnego - defult
  6. Dobrze. Przyszedł czas się zalogować. Dane podane przez autora są poprawne. Co jednak stanie się gdy podamy złe dane? Ano nic - nagłówek "Logowanie", reszta strony pusta. Żadnego komunikatu… Tak samo z resztą jest przy logowaniu poprawnym - jedynym znakiem jest zmiana menu.
  7. Klikam w link "Panel" i oto - ponownie nagłówek "Logowanie"… Wygląda na  to, że z jakichś powodów skrypt nie łapie zmiennych. Pewnie potrzebne włączone register_globals? Zaraz się przekonamy.
  8. W międzyczasie nadszedł czas na przyjrzenie się lekko bazie danych.
    • Brak prefixów tabel
    • Wchodzimy do tabeli "uzytkownicy". Hasła szyfrowane… ale samym md5 bez soli. Słabo.
    • Patrzę na pola w tej tabeli. Mamy tam praktycznie wszystko: logowan, komentarzy, dzwiek, procesor, pamiec, monitor, avart (jak sama nazwa wskazuje, zawiera URL do avatara ;)), lacze, system, myszka, podkladka, potrawa, muzyka, film, aktor, sport etc… Ogólnie wszystko co niezbędne. Szkoda tylko, że nie znalazły się tam tak wyszukane pola jak np. płeć :/
    • Wszystkie powyżej wymienione pola wypełnione ofc danymi autora skryptu.
  9. Tymczasem powracamy do punktu siódmego. "Niespodziewanie" moja diagnoza o konieczności włączenia register_globals przyniosła efekty. Podstrony zaczęły działać, tak samo ew. błędy przy logowaniu.
  10. Czas więc ponownie spróbować wejść do panelu administracyjnego. Klikam więc link "Panel" i znajduję się w… profilu. Okazuje się bowiem, że to co miałem mylnie za panel administratora, okazało się panelem użytkownika. Więc gdzie jest panel administratora? W paczce znajdował się folder "Administracja", dopisałem go do adresu i oto…
  11. …błędy MySQL. Powód? Panel admina posiada osobny plik config.php…
  12. Dobrze, pojawiła się strona logowania. Ale zaraz, jak to? Na stronie jestem zalogowany, a w panelu jeszcze nie, tak? No cóż, za łatwo być nie może :) Powodem tego jest to, że admin, czyli user tworzony domyślnie jest trzymany w dwóch tabelach "uzytkownicy" i "admin". Wprawdzie daje to powalającą możliwość oddzielnego hasła do strony i do panelu, ale… co komu po tym?
  13. Ustawienie stopki przy dłuższych stronach nie zachwyca. Takie rzeczy jak 404 pod jednym linkiem i brak linka do strony też nie….
  14. Zszokował mnie pewien fakt… Otóż po wejściu w zakładkę "Aktualności", okazało się, że… nie da się ich edytować. Jakaś możliwość specjalna?
  15. Postanowiłem dodać kategorię artykułów, lecz zamiast jakiegoś komunikatu/linka zobaczyłem "Artykuly Kategorie - ok0: Update finish". Podobna sytuacja po edycji profilu. Pozostałości po debugu w wersji publicznej? Niedobrze…
  16. Nadszedł czas ustawić kategorię dla mojego artykułu, aby mógł się wyświetlić. Niestety widząc link "[E]", odruchowo założyłem, że edycja dla artykułów jest możliwa. Przeceniłem "Zabę"- pusty link :(
  17. Po interfejsie można by jeździć w nieskończoność, ale przejdźmy do kodu…
  18. Panel admina, sądząc po DTD, jest pisany w xHTML 1.0 Strict. Ambitnie - jednak co z tego skoro mamy 25 błędów?
  19. Kod strony głównej przebija - 35 błędów i to… w HTML 4.01. Widać kto tu rządzi :)
  20. Taki krótki wycinek kodu z panelu administracyjnego:
    <div id="header">
    <ul>
     <li id="current"><a href="index.php">Strona Główna</a></li>
     <li id="current"><a href="index.php?cmd=1">Aktualności</a></li>
     <li id="current"><a href="index.php?cmd=2">Strony Serwisu</a></li>
     <li id="current"><a href="index.php?cmd=10">Artykuly</a></li>
     <li id="current"><a href="index.php?cmd=3">Linki</a></li>
     <li id="current"><a href="index.php?cmd=4">Księga Gości</a></li>
     <li id="current"><a href="index.php?cmd=5">Newsletter</a></li>
     <li id="current"><a href="index.php?cmd=6">Użytkownicy</a></li>
     <li id="current"><a href="index.php?cmd=8">Menu</a></li>
     <li id="current"><a href="index.php?cmd=12">Bannery</a></li>
     <li id="current"><a href="index.php?cmd=11">Pliki</a></li>
     <li id="current"><a href="index.php?cmd=7">Forum</a></li>
     <li id="current"><a href="index.php?cmd=13">Ban</a></li>
     <li id="current"><a href="index.php?cmd=9">Konfiguracja</a></li>
    </ul></div>
    
    Hmm… o ile nie przerobiłem kursu dla idiotów, to ID może być tylko jedno :P. Sam sposób jego nadawania też miażdży.
  21. Reszta strony to tabelki, font w xhtml, tabelki, style inline, zero klasy dla powtarzanych elementów, tabelki, style inline i… czy wspomniałem już o tabelkach?
  22. Jeśli chodzi o nazewnictwo w kodzie. Raz mamy sobie f-cję menu_update(), a za chwilę pokaz_plik(). Totalny mix dwóch języków, z przewagą oczywiście polskiego.
  23. Mógłbym zrobić jeszcze pizdylion^666 punktów, ale mi się nie chce, tak więc pokażę kilka rzeczy na screenie: klik.

Ogólnie można pewnie wytknąć jeszcze masę błędów. Nie oceniałem przecież praktycznie w ogóle kodu PHP. Poza tym jest sporo możliwości samego skryptu których nie testowałem. Forum, download i masę innych… Jeśli komuś jeszcze chciałoby się pośmiać, to skrypt można ściągnąć tutaj. Ja tymczasem z powrotem ustawiam konfigurację PHP na bezpieczną.

PS: Dodam jeszcze króciutkie wyjaśnienie. Moim celem nie było wyśmiewanie autora skryptu. Trzeba docenić to, że zrobił darmowy produkt i udostępnił go ludziom (choć w readme groził, że "Kopiowanie kodu jest surowo karane lub tez kopiowanie plikow….."). Po prostu można potraktować to jako krótką listę błędów których nie należy popełniać.

PS2: Przepraszam za ewidentny bałagan w poście. Był on pisany "na żywo" podczas testów.

PS3: Wynikiem 996 słów uzyskujemy prawdopodobnie najdłuższy dotychczasowy wpis na sobak.pl :)

NetBeans 7.0

Jak już pisałem w jednym z wcześniejszych wpisów jestem zadowolonym użytkownikiem NetBeans'a, czyli zintegrowanego środowiska programistycznego m. in. dla PHP. Dzisiaj podczas przeglądania nowości w RSS'ach zauważyłem informację o wydaniu NetBeansa 7.0 (do tej pory używałem 6.9.1). Pomijam wszystkie changelogi, skupię się na własnych wrażeniach.

Zacznę oczywiście od instalacji. Już na starcie będzie troszkę narzekania, bo tak jak w poprzednich wersjach, nie mamy możliwości ustawiania czegokolwiek poza ścieżką do instalacji. Naprawdę nie widzę sensu instalowania m. in. chińskiej, rosyjskiej, portugalskiej i japońskiej wersji językowej "na sztywno", powinno się dać to odznaczyć.

Świeżo po instalacji IDE zajmuje u mnie 142 MB. Po skasowaniu wszystkich innych wersji językowych niż angielska pozbyłem się 1291 plików i całych 7,41 MB :P Mam jednak wrażenie, że program uruchamia się troszkę szybciej.

Po uruchomieniu programu pojawiło się okienko informujące, że na komputerze wykryto ustawienia z wersji 6.9 z możliwością ich zaimportowania. Krok wręcz obowiązkowy. Wszystko poszło bez problemu, wszystkie projekty wczytały się bez zmian.

Właśnie odnośnie zmian - otóż tych w wersji siódmej jest naprawdę niewiele. Pojawiło się m. in. wsparcie dla systemu kontroli wersji GIT i zawijanie linii - oba wyłączyłem. Natomiast przydać się może wsparcie dla znaczników HTML5. Podejrzewam, że sytuacja przedstawia się inaczej jeśli chodzi o wersję dla Javy, bo to przecież pod ten język był pisany NetBeans.

Standardowo wyłączyłem kilka nieprzydatnych pluginów (z 17 aktywnych po instalacji zostało 10). Generalnie jak dla mnie w programie zmieniło się niewiele. Jednak z drugiej strony nie muszę przyzwyczajać się do nowego interace'u itd, a program działa sporo szybciej (przynajmniej na moim sprzęcie). Nie mam na co narzekać :)

Trzy dni z Linuksem

Trzy dni z Linuksem i… żyję. Nie pożarła mnie konsola, nie zgubiłem się pośród pakietów, mam większość potrzebnych mi programów, a wygląd mojego Linuxa to wcale nie tapeta w pingwiny lub konsola rodem z Matrixa…

Czytaj dalej →

NetBeans i Sourceforge - pierwsze wrażenia

W pierwszej kolejności przepraszam za bardzo długą przerwę w publikowaniu wpisów i obiecuję, że postaram się to nadrobić.

W ostatnim czasie byłem pochłonięty realizacją mojego nowego projektu - CMSa. Powiem tylko krótko, że CMS to taki fajny rodzaj projektu gdzie programista zbiera wszystkie umiejętności i łączy w jedną całość. O CMSie napiszę więcej już niedługo.

Przy okazji pisania tego sporego jak na moje standardy skryptu postanowiłem się przestawić na jakieś IDE (Zintegrowane Środowisko Programistyczne). Wybór padł na NetBeans…

Czytaj dalej →

Opera 11 - krótka relacja

Jak zawsze po powrocie do domu, ślęczenie przed komputerem zacząłem od przejrzenia nowości w kanałach RSS. Z zadowoleniem zobaczyłem informację o wydaniu finalnej Opery 11. Swoją drogą, to DobreProgramy wprowadziły ciekawy styl Operowy na tą okazję (jeśli już go nie widać to kliknijce na przycisk wygląd po lewej).

Od razu po przeczytaniu newsa włączyłem Operę i zacząłem pobierać najnowszą wersję. I o to co nasuwa mi się na pierwszy rzut oka. Interface zmieniony nieznacznie także starzy userzy Opery spokojnie się w nim połapią, wygląd został jednak lekko odświeżony (i przycisk menu skopiowany od Fx :P*).

Wady
Czym był by tak optymistyczny wpis bez wspomnienia o wadach nowej Opery.

  • Brak polskiego słownika - podejrzewam, że jest gdzieś do pobrania/ustawienia, ale jak na razie nie znalazłem a to mocno utrudnia pisanie.
  • W Fx w momencie gdy chciałem zaznaczyć cały akapit tekstu do skopiowania, to po prostu klikałem na niego dwa razy. W Operze do dwukliku pojawia się menu kontekstowe i skrót ctr+c nie działa
  • Sytuacja występująca m. in. na YT: po najechaniu na player mam komunikat "Kliknij aby aktywować i używać tej kontrolki". Obiekt flash działa jednak jeszcze przed kliknięciem i kiedy najedziemy na niego myszą bez klikania, to zaczyna się on przycinać (przynajmniej na YT)

Adblock
Sprawą wymagającą szerszego omówienia jest kwestia zastępnika AdBlocka w Operze, bo jak wiadomo user Firefoxa nie może bez niego żyć :P.

Na stronie z dodatkami do Opery jest udostępnione rozszerzenie NoAds (Adblock+NoScript). Rozszerzenie w zasadzie działa bo np. onet jest wolny od reklam, ale menu ustawień nie działa u mnie tzn. po kliknięciu na "Settings" nic się nie dzieje.

Dla mnie Adblock bez wygodnej konfiguracji filtrów, przycisku "Zablokuj" na flashah i opcji zablokuj w menu kontekstowym dla obrazków to nie AdBlock.

Inne dodatki
Z innych dodatków istniejących dla Fx, a dla Opery nie, brakuje mi:

  • Firebug - tak, wiem, że jest Dragonfly, ale opinie na jego temat są różne. Muszę sam przetestować
  • WebDeveloper - wszystko dla webmasterów w jednym miejscu.

Podsumowanie
Opera była i jest dobrą przeglądarką. Wersja 11 wprowadza spory postęp m. in. przez dodatki. Jednak jako fanboy Fx muszę zmartwić fanów Opery. Dopóki nie doczekam się dobrych zamienników wspomnianych wyżej dodatków - zostaję na Firefoxie. Planuję jednak z Opery uczynić przeglądarkę portable używaną na wyjazdach. Na szczęście portableapps.com już przygotowało wersję pod swój pakiet :)

* - możliwe, że tym dopiskiem wywołam burzę, ale co tam :P