0.7 i Linux

dikamilo, nie mam pojęcia czemu to wywołanie length() na stringu sypie. Gdyby to jeszcze był string pod jakimś wskaźnikiem, to by można spekulować, czy jest on poprawny, ale tak… wskaźnik będący normalnym polem klasy nie powinien robić problemów, zwłaszcza jeśli kilka poprzednich pól się zserializowało, więc okoliczna pamięc jest dostępna.

Problem może być z runtimem (runtime libraries - biblioteki uruchomieniowe). Nie wiem jak jest to zorganizowane w MinGW-ie, ale Visual posiada kilka wariantów runtime’u i wybiera się odpowiedni. Są bodajże wersje statycznie włączane do aplikacji jedno- i wielowątkowych oraz wersja w osobnej dll-ce. VCMI musi mieć runtime w dll-ce. W innym wypadku dochodzi do błędów, ponieważ zarówno do liba jak i klienta włączany jest runtime i otrzymujemy dwa runtime’y niekompatybilne ze sobą (dwie osobne sterty, nieprzenośne wskaźniki itp).
Przyjrzałbym się, jak to wygląda w MinGW-u.

EDIT:
Problem można by w sumie obejść jeszcze inaczej - pozbyć się liba jako autonomicznej części. Można by go albo ręcznie wepchać do klienta i serwera (po prostu modyfikując ich projekty, dodając pliki z liba), albo zbudować jako bibliotekę statyczną i ją potem włączać (chyba też zadziała?).
O ile to jest faktycznie problem z runtimem.

Pobawiłem się trochę debuggerem, ogólnie to sypie się przy serializowaniu zmiennej mainHeroName z PlayerInfo, w trakcie debugger pokazuje taką zawartość:

(gdb) display mainHeroName
1: this->mainHeroName = {static npos = 4294967295,
  _M_dataplus = {<std::allocator<char>> = {<__gnu_cxx::new_allocator<char>> = {<
No data fields>}, <No data fields>}, _M_p = 0xd8ae0c ""}}

Próbowałem zmienić w locie zawartość tej zmiennej to wyskakuje takie coś:

(gdb) set mainHeroName="oko"
Invalid cast.

Można natomiast zmienić na jakąś liczbę, ale przecież ta zmienna jest stringiem ? Ogólnie to problem z tą zmienną był chyba również przy błędach z mapami.

Ja tam będę obstawał, że to może być problem z runtimem, albo coś innego, niekonwencjonalnego.
Ten string jest w 3 miejscach używany. Gdy powstaje, przy wyborze mapy (MapSel::select - sprawdzamy jego długość i ew. kopiujemy do ustawień gracz) i w serializacji.
Jeżeli podczas wyboru mapy wywołanie length() na tym stringu nie powodowało problemów i potem nie był on ruszany, to w żaden konwencjonalny sposób nie miał prawa się popsuć i sypać później, przy serializacji. Albo runtime psuje, albo jest przez coś nadpisywany - ale w to drugie nie bardzo wierzę, jeste nad nim kilka raczej niegroźnych pól.
Co prawda po stronie serwera wyboru mapy i nie ma i ten string nie jest w ogóle ruszany, ale powstaje wskutek identycznej procedury, więc nie ma możliwości, by był inny.

Co do zawartości stringa wg debuggera, to generalnie dlatego nie lubię gdb, że nie bardzo wiadomo co jest w kontenerach STL-owskich w tych jego podglądach.
Czy to _M_p = 0xd8ae0c “” oznacza, że pod adresem 0xd8ae0c jest NULL (to by wskazywało na pusty string, rozsądnie zakładając, że _M_p to wskaźnik na początek tekstu). No ale nawet pusty string nie ma prawa sypać (length() powinien wtedy grzecznie zwrócić 0).

Teza o runtime tym bardziej uzasadniona, że przy crashu jeszcze są jakieś awantury o "static ios_base::Init __ioinit; ". Choć na moje oko, to w takim układzie toto powinno sypać inaczej…
Choć do końca nic nie potrafię tu powiedzieć…
Dziwne problemy.
Może sam powinienem w wolnej chwili zacisnąć zęby i spróbować skompilować VCMI MinGW-em…

Nie, to nie takie proste. Problem jest takie, że w C/C++ nie masz takiego typu jak string. std::string jest tak naprawdę klasą opakowującą wskaźnik na ciąg bajtów (znaków), długość i jakieś inne wewnętrzne sprawy implementacji. (debugger zresztą Ci wyświetla z czego tak naprawdę ten string się składa)
Z poziomu debuggera nie da się do czegoś takiego łatwo przypisać innej wartości.

EDIT:
A tak z ciekawości - jeśli byś wykomentował serializację tych imion (map.h l.130) bohaterów, to co się stanie? Jakiś inny string sypnie, czy przejdzie?

Po wywaleniu mainHeroName z (template void serialize(Handler &h, const int version)) : 127, nie wywala żadnego błędu, gra się zapisuje i nawet poprawnie ładuje.

Co do rutime’a mam ustawione standardowo na dynamic (DLL), przy zmianie na static sypało błędami, nie odnajdowało referencji do funkcji, myślę że dużo by trzeba było zmieniać w kodzie aby to zadziałało tak jak pisałeś w poprzednim poście.

Ma być właśnie na dynamic dll. Czyli dobrze jest, mój trop był błędny.

Cóż, skoro zakomentowanie serializacji tamtego stringu cudownie naprawiło sytuację, to możesz go sobie trzymać zakomentowany. Tak naprawdę nie jest on jeszcze nigdzie wykrozystywany - to imię głównego bohatera, które powinno być (nie jest) wyświetlane w oknie zaawansowanych opcji w pregamie.

Ale nadal nie mam pojęcia, dlaczego to sypało. Ktoś ma jakiś pomysł?

ja mam!
problem z wartością wskaźnika (string jest wskaźnikiem (char*) )
źle ustawiony wskaźnik powoduje niedozwoloną operację w length, z powodu zabezpieczeń pamięci w linuksie (brak sprawdzania warunków krytycznych), gdb sprawdza co i jak i zamiast się sypnąć daje NULLa (nie widzi nic sensownego)

błędy mogą wystąpić przez złę ustawienie wskaźnika, ustawienie w runtime wartości do (const (char )) (a może to było ((const char)) ?) niezgodność typów wskaźnikowych (np. podstawienie zvoidowanego wskaźnika nie do stringu lub przypisanie stałej liczby do wskaźnika) lub problem z samym length (sypie na static cośtam, zmienne wewnętrzne źle się ustawiają (zwłaszcza wskaźniki brrrrrr! - ten sam problem co wyżej) )

również problemem może być zapis do pamięci read-only, przypisanie danych do zawartości wskaźnika NULL zwróconego przez funkcję zarządzania pamięci (np. alloc), wyjście poza dozwolony zakres pamięci (pamięć bez praw do czytania przez proces)

proponuje update bibliotek (zwłaszcza tych domyślnych wraz z kompilatorem) i sprawdzenie czy dalej się sypie

ewentualnie w kodzie możesz wstawić globalny (char ) z wstawionym stringiem np “test string” i za pomocą gdb przypisać wątpliwemu wskaźnikowi (char) wstawić wartość tego globalnego wskaźnika (równe wskaźniki pokazują na to samo, co nie?)

W bardzo, bardzo dużym przybliżeniu. To jest klasa i ma jeszcze zazwyczej kilka innych pól oprócz tego wskaźnika.

Windowsy też sprawdzają pamięć a na nich nie sypie. Poza tym skoro gdb może odczytać wskaźnik, to prawdopodobnie length też powinno móc (gdy gdb nie może się dostać do pamięci, to daje “Cannot access memory at address”, a nie udaje, że tam jest NULL).

Jakie const char * i co to za nawiasy? Jaka niezgodność typów? Jakie zmienne wewnętrzne? Możesz składniej i bardziej po polsku, bo to mi wygląda na bełkot?

Wymieniasz wszystkie możliwe przyczyny sypania się programu, jakie znasz? My też je znamy, i jeszcze trochę innych. Lepiej napisz, co to na pewno nie jest, bardziej pomoże (bo na napisanie przez ciebie co to na pewno jest nie liczę).

Wiem, że nie zawsze można ufać bibliotekom standardowym, szczególnie z projrktów otwartych, ale bez przesady. Tego typu błędy w stringach są praktycznie wykluczone, kiedyś możnaby myśleć, że nie zrobili zabezpieczeń dla jednoczesnego korzystania ze stringa przez wiele wątków, ale na moje oko ani to ten przypadek, ani te czasy.

Dobra, a skąd wiesz że to nie psuje kompletnie stringa i nic z nim nie da się robić za pomocą zaimplementowanych metod? Nie wydaje mi się, żeby to mogło do czegokolwiek prowadzić.

Moim zdaniem bardziej interesujące jest, czemu wierzchołek stosu jest taki jaki jest (0x00486c38 in std::string::length () at iostream:77), iostream:77 ma niewiele wspólnego z std::string::length ().

hey guys!

i also tried to compile vcmi on linux and after initial fail i tried to search for others who strive for this goal.
so after skimming through a google translate of the thread there seems to be some achievements in this regard.
could you in short summarize the steps i have to take to make it initially compile for now? it seems the issues mentioned here are not yet in svn?
after a successful compile i can do some gdb debugging.

and sorry for intruding into your polish section - i thought that maybe this issue gets more attention when it is discussed in english :slight_smile:

no… iostream…
wygląda że serializacja poprzez klasę iostream pada (string ma operatory << i >> dla iostream)

jeśli mówiliście o stringu to fakt moja pomyłka std::string i (char *) to zupełnie co innego, ale oba nazywa się stringiem

@nawiasy, niezgodność typów
nawiasy przy typie wskaźnikowym i przy const, określają czy wskaźnik jest const, czy to na co wskazuje, co do niezgodności typów, chodziło mi głównie o rzutowanie consta na nie-consta a potem edycję

@windows nie sypie, błędy pamięci
windows jest niechlujny, pozwala nawet na injekcję kodu do innego procesu na niskim poziomie przy swojej minimalnej ochronie pamięci i nawet ostrzeżenia nie daje
linux ma zaawansowany menedżer pamięci w jądrze, każdy proces ma pewien stopień wirtualizacji i linux nie tylko stricte chroni pamięć, ale nawet adresy mogą być nieprzenośne między procesami. wykonywanie kodu też jest zwirtualizowane - np. nie powala wykonać instrukcji niskopoziomowego i/o na portach poza modułami jądra

***jest jescze trzecia opcja: ktoś zaimplementował jakąś niechlujną sztuczkę w zarządzaniu pamięcią, która jest dozwolona (jak wszystko) w windowsie, a w linuksie nawet nie wymyślono na nią obsługi błędów, jednak jest niedozwolona

@biblioteki std
kolega mógł mieć przestarzałą wersję (1997 rok ? :stuck_out_tongue: ) lub miał wersję beta/alfa i niedziała mu dobrze, ew. zamiaststable ściągnął starą betę, więc każdą z tych opcji trzeba wykluczyć

poza tym dość często (za często) biblioteki standardowe nie są zgodne z dodatkowymi, niektórzy zakomentowywują sprawiające problem linie w nagłówkach (np. jakąś dziwną deklarację) na czas kompilacji, co może mieć nieprzewidziane skutki (np. błędy runtime), lepiej jest więc jest zmienić wersję na choćby starszą lecz zgodną

***to najbardziej wygląda na problem z pamięcią, a C i C++ jest tak zrobiony (tak pomyślany), że praktycznie się nie robi obsługi sytuacji wyjątkowych bez dokładnej obsługi przez programistę, co ma skutkować (i skutkuje) dużo lepszą wydajnością
najbardziej znany problem to niebadanie rozmiarów tablicy

At least some of issues mentioned here have been uploaded into svn. I think you should download latest sources and try to overcome problems alone or wait until we upload files necessary for automatic build. The only advice I can give you is that runtime libraries should be set to dynamic (DLL).

Przyglądnij się temu a nie wypisuj głupoty, string jest serializowany przez c_str a to nigdy nie korzysta z iostream (bo i po co?).

Skąd ty wziąłeś takie odróżnianie wskaźników do stałych od stałych wskaźników? Wszystkie źródła które widziałem podają, że używa się const char * w pierwszym przypadku i char * const w drugim. Twoje też wygląda mi na możliwe, ale mimo wszystko jestem ciekaw.

W przypadku funkcji w pamięci dzielonej między procesorami nie widzę z tym problemu. A mógłbyś mi napisać jak zrobić program piszący do pamieci innego programu, bo jakoś nie spotkałem się z taką możliwością? Najchętniej zobaczyłbym kod.

A teraz mi wyjaśnij co to ma wspólnego z naszym błędem.

Mógłbyś podać przykład takiej biblioteki dodatkowej? Nie słyszałem o takich. Kolejny strzał na ślepo?

Testowałem to na 4 rożnych wersjach mingw, tej dołączonej do C:B, osobnej z oficjalnej strony, mingw-tdm wersja beta 4.3.3/4.3.2, oraz samego 4.3.2 z tej paczki. Na każdym jest ten sam błąd.

nie chciałem łamać cytatu więc dodałem odpowiedzi w znacznikach code

Czemu tu nie widzę “tak, masz rację, nie spojrzałem w kod VCMI”?

@dll injection

Jak widzę, nie potrafisz mi podać interesującej mnie informacji - jak to przebiega technicznie, konkretny kod. Jestem zbyt leniwy żeby się przebijać przez to do czego dałeś mi linki. Ciężko mi uwierzyć, że Windows jest taki be a Linux taki super. Poszukałem jednak pewnych materiałów sam i odkryłem, że istotnie można pisać do pamięci innego procesu jeśli nasz proces ma pewne przywileje (PROCESS_VM_OPERATION i PROCESS_VM_WRITE). Oczywiście jest pewien wyjątek, Vista oferuje wsparcie dla chronionych procesów dla których dostęp do ich pamieci z zewnątrz jest niemożliwy (nawiasem mówiąc, zostało to zaimplementowane z myślą o DRM, ale zdaje się być stosowalne do innych aplikacji). Możesz o tym poczytać np. tu: msdn.microsoft.com/en-us/library/ms684880(VS.85.aspx . A dostałbym linka do podobnego artykułu w kontekście Linuxa?

Jaki program ma robić coś podejrzanego?

Latest code from trunk should compile without problems.
The problem may be in the lack of up-to-date makefiles (codeblocks projects are AFAIK good, you may also (if possible) import project files from MSVC).
Maybe skoruppa will share his makefiles if he has finally successfully compiled all parts of AI.
VCMI should be basically working, the only remaining issue is problem with save/load options.

Btw it would be probably best if you create a separate thread on English forums.


majaczek, pytałem o pomysły, co może powodować tamten błąd, Ty zaś chyba postanowiłeś wypisać wszystko co wiesz o programowaniu, nie przeczytawszy nawet o co właściwie chodziło (std::string był ewidentnie wskazany). Część to truizmy nie na temat, część to już jakieś ich pomieszanie. Nie mam czasu tego rozplątywać.

Jedna tylko uwaga odnośnie dyskusji Linux a Windows - jest ona kompletnie niedotycząca tematu, ponieważ błąd zachodzi zarówno pod Windowsem jak i Linuksem. To nie kwestia systemu!

Obok przycisku “quote” na lewo masz taki mniejszy - on Ci cytuje zaznaczony fragment posta (wrzuca do okienka szybkiej odpowiedzi). Nie potrzeba się mordować z półśrodkami. :wink:

Nie wiem czy on jest poprawny… mocno wątpię. (Możesz pokazać działający kawałek kodu C++ z użyciem skłądni const (char*)?) Nawet gdyby działało, to nie wierzę, by ktokolwiek zdrowy na umyśle chciał go stosować (chyba, że w celu zaciemnienia kodu).

PS: nie ma multicytatów na przyciskach forum, a cytowanie selektywne nie działa na chrome

PPS: nie mogę dzisiaj skończyć więc bez obrazy jak coś będzie niekompletne lub ucięte

A chowanie kur coraz bardziej się w Polsce przyjmuje. Ten szary, niewielki ptak uprzyjemnia rolnikowi pracę swym miłym świergotem.

Może oszczędź czasu sobie i nam, NAJPIERW sprawdzając problematyczny kod w VCMI, a potem pisząc.
Żebyś nie musiał czytać całej poprzedniej strony:

  • błąd nie zachodzi pod Visualem, zachodzi pod GCC
  • nie ma związku z systemem operacyjnym
  • nie wrzucamy tego stringu do strumienia (właściwie, to wrzucamy, ale przy zapisie do pliku, czyli potem, więc na tym etapie nie dotyczy)

A od kiedy piszemy na forum spod Chrome’a? Chrome daje dodatkowe problemy z cytowaniem selektywnym.
Jeśli się nie mylę, kto nie pisze spod Firefoksa, ten korzysta z Opery.

Dlaczego nic nie piszesz o dzieleniu przez 0 ?! :imp:

Więc, dziś skompilowałem. Działa :D. Myszka też normalnie się zachowuje, jednak postać porusza się dziwacznie na mapie tak jakby przycinała co jest możliwe bo vcmi + vcmiserver zajmuję mi cały procesor oO (2.8ghz). Nie wiem co powinno działać w vcmi a co nie przez co trudno mi stwierdzić co jest błędem na Linuksie jednak kursor się nie zmienia na konia, ciągle jest strzałka. Poza menu nie mam dźwięku (w menu jest tylko “klik”). No i gra się wykracza jak chce wejść do Village Hall to gra się zamyka.

Tak, poprawiłem to kiedyś.

Kwestię koszmarnej procesorożerności obecnej implementacji ruchu bohatera już zgłaszałem Tow Dragonowi i usłyszałem, że jest dla niego jest OK, a wydajność została widać uznana za “wystarczającą”. Dobrze wiedzieć, że nie jestem w swojej opinii osamotniony.
Doraźną poprawę może zapewnić wyłączenie płynnego przewijania mapy w pliku settings.txt - wystarczy zamienić smoothMove=1 na smoothMove=0 w zapisie dla odpowiedniej rozdzielczości.
Zbudowanie VCMI bez informacji debugowych, ale za to z pełnymi optymalizacjami także powinno bardzo znacząco pomóc, choć na większych mapach wciąż może być nie do końca zadowalająco.

Myślę, że dość bezpiecznym założeniem jest, że jeżeli czegoś nie ma - jak w przypadku wymienionych przez Ciebie ficzerów - to nie zostało zaimplementowane. Zaś jeżeli VCMI się wysypuje, to błąd.
Village Hall absolutnie nie powinien sypać (gdb?).
[Zresztą sam, jak znajdę kiedyś trochę sił i czasu, chyba będę musiał zacisnąć zęby i spróbować zbudować VCMI pod Linuksem i zbadać te dziwne błędy]

Ale chyba nie jest to nic poważnego, skoro na setki osób ściągających VCMI to chyba pierwsza skarga na zbyt duże użycie procesora. Poza tym warto dodać, że obiecałem zająć się poprawą wydajności w czasie wakacji. Ktoś oprócz Towa uważa, że ta kwestia jest tak paląca, że do wakacji czekać nie może?

VCMI z optymalizacjami wykorzystuje mi na tyle mało mocy procesora, że jeden rdzeń 2.8 GHz powinien z zapasem wystarczać (wydaje mi się, że nawet 1.5 GHz byłoby wystarczające do w miarę płynnego przesuwania, przynajmniej przy 800x600). Linux nie powinien zachowywać się o tyle gorzej od Windowsa.

U mnie na XL-ka w rozdzielczości 1024x768 już odczuwalnie zwalnia przy ruchu. W 800x600 chodzi na styk. A mam jakieś 2 * 1,9 GHz. Kod, którego zalecane wymagania sprzętowe są na poziomie twojego procesora, jest moim zdaniem całkiem palący. Rdzeń 2,8 to nie jest mało.

Co do rzeczy odkładanych na rozmaite wakacje/ferie, to z wiadomych zapewne Ci przyczyn zaczynam nabierać tu pewnej ostrożności.
Czy sprawa może zaczekać? Pewnie i może, świat się nie zawali. Natomiast jestem przekonany, że powinna być możliwie szybko rozwiązana.

Ta setki razy ściągnięta wersja domyślnie ma wyłączone to nowe, płynne przesuwanie, o którym rozmawiamy.
I ciągle mam wątpliwości, czy dobrze zrobiłem, zostawiając je domyślnie włączonym w dalszych wersjach.